![]() |
|
[Plugin] mysql_samp 1.4.0 — notas de versão para SA-MP e open.mp - Printable Version + open.mp forum (https://forum.open.mp) -- Forum: SA-MP (https://forum.open.mp/forumdisplay.php?fid=3) --- Forum: Releases (https://forum.open.mp/forumdisplay.php?fid=13) ---- Forum: Plugins (https://forum.open.mp/forumdisplay.php?fid=32) ---- Thread: [Plugin] mysql_samp 1.4.0 — notas de versão para SA-MP e open.mp (/showthread.php?tid=6558) |
mysql_samp 1.4.0 — notas de versão para SA-MP e open.mp - NullSablex - 2026-10-11 🦀 mysql_samp 1.4.0
MySQL para SA-MP e open.mp — o mesmo binário nos dois MySQL for SA-MP and open.mp — the same binary on both Download · GitHub · Documentação · CHANGELOG Quote:🇧🇷 Plugin MySQL em Rust, 79 natives, sem dependência para instalar. Esta versão, em três linhas: ⚠️ Mudança de comportamento: OnQueryError Behaviour change: OnQueryError 🇧🇷 O forward é declarado assim: 🇬🇧 The forward is declared as: Code: forward OnQueryError(errorid, error[], callback[], query[], connId);🇧🇷 Até a 1.3.0 o gamemode recebia os campos ao contrário: o id da conexão chegava como errorid, o texto da query como error, a mensagem de erro como query e o código do MySQL como connId. 🇧🇷 A causa estava no despacho. O exec_public! empilha os argumentos ao contrário — é a convenção da pilha do AMX — logo a ordem escrita na chamada já é a ordem que o public recebe. O código os listava de trás para frente, como se tivesse de inverter à mão. 🇧🇷 A inversão era consistente, então um handler podia parecer funcionar há bastante tempo lendo o id da conexão como código de erro. Presente desde a 1.0.0 — se você trata esse forward, é o primeiro lugar a olhar depois de atualizar. 🇬🇧 Up to 1.3.0 the fields arrived reversed: connection id as errorid, query text as error, error message as query, MySQL code as connId. exec_public! pushes arguments in reverse (the AMX stack convention), so the order written at the call site is already the order the public receives; the code listed them backwards as if it had to invert them by hand. Consistent enough that a handler could appear to work for a long time. Present since 1.0.0. 🇧🇷 E ao atualizar / 🇬🇧 And when updating
🐛 Correções Fixed 🇧🇷 Uma rajada de queries matava o servidor — e podia travar a fila para sempre 🇧🇷 Submeter 2000 queries num tick abortava o processo com memory allocation of 4194304 bytes failed. A causa é específica desta arquitetura: uma thread por query, com a pilha padrão de 8 MiB, dentro de um servidor 32 bits. Algumas centenas de workers vivos esgotam o espaço de endereçamento, e a próxima alocação que falha leva o processo junto. Foi achado por teste de força bruta, não por relato. 🇧🇷 Eram dois problemas, os dois corrigidos:
🇬🇧 2000 queries in one tick aborted the process — one thread per query, 8 MiB default stack, 32-bit server. Workers now get a 256 KiB stack (ceiling moved from ~1700 to past 9000), and a thread the OS refuses is now an ordinary failed result instead of a panic that lost the sequence number and stalled every ordered callback behind it forever. 🇧🇷 As natives de submissão passaram a dizer a verdade 🇧🇷 mysql_query, mysql_pquery, mysql_query_file, mysql_stmt_execute, mysql_stmt_pexecute, mysql_transaction_execute e as natives CRUD do ORM devolviam true sempre que o id da conexão era válido. Agora devolvem o que a submissão realmente fez. 🇬🇧 They returned true whenever the connection id was valid; they now return what the submission actually did. 🇧🇷 Pedir TLS em host local rodava em texto claro 🇧🇷 MYSQL_OPT_SSL = 1 contra 127.0.0.1 reportava sucesso enquanto o cipher voltava vazio. O driver tem prefer_socket ligado por padrão e, depois do handshake, move uma conexão de loopback para o socket unix do servidor — onde ela não protege nada. As duas condições se encontravam na configuração local mais comum que existe. Pedir TLS agora desliga essa otimização; sem TLS ela continua, que é onde ajuda. Verificado contra MariaDB 11.8: TLS_AES_256_GCM_SHA384 onde antes havia string vazia. 🇬🇧 MYSQL_OPT_SSL = 1 against 127.0.0.1 reported success with an empty cipher: the driver moves a loopback connection onto the unix socket after the handshake, where it secures nothing. Asking for TLS now switches that off. 🇧🇷 E ainda / 🇬🇧 And also
🆕 MYSQL_SYNC: bloquear quando a chamada pede New: MYSQL_SYNC 🇧🇷 Até a 1.3.0 não havia caminho bloqueante na API, de propósito. O problema é que existe trabalho que não é sobre desempenho: criar tabela no OnGameModeInit, ler uma configuração que a linha seguinte precisa, migrar schema. Exigir callback para isso é cerimônia, e a resposta para "como eu leio isto agora?" não podia ser "não leia". 🇬🇧 Up to 1.3.0 there was no blocking path in the API, by design. But some work isn't about performance: creating tables in OnGameModeInit, reading a setting the next line needs, migrating schema. Code: mysql_query(g_mysql, "SELECT COUNT(*) AS n FROM contas", MYSQL_SYNC);🇧🇷 Funciona em mysql_query, mysql_pquery e mysql_query_file — onde vive o trabalho de schema na subida do servidor. 🇬🇧 Supported on mysql_query, mysql_pquery and mysql_query_file. Por que é um valor e não uma native nova 🇧🇷 Porque uma native chamada mysql_query_sync acaba dentro do OnPlayerUpdate. Um valor escrito no lugar do callback põe a intenção onde o custo é pago: quem lê a linha vê que ela espera. E não podia ser um argumento novo — o Pawn não aceita parâmetro depois de {Float,_}:..., e colocá-lo antes do callback quebraria toda chamada existente. 🇬🇧 A native called mysql_query_sync ends up inside OnPlayerUpdate. A value at the call site states the intent where the cost is paid. It couldn't be a new argument either: Pawn refuses any parameter after {Float,_}:.... As três regras que mantêm isso honesto
⚖️ Como usar assíncrono e síncrono Using async and sync 🇧🇷 A regra em uma frase: se um jogador está esperando, é assíncrono; se o servidor ainda está subindo, pode ser síncrono. 🇬🇧 One sentence: if a player is waiting, async. If the server is still starting up, sync is fine. 1. Assíncrono com callback — o caso normal Code: mysql_query(g_mysql, "SELECT id, nome FROM contas WHERE nome = 'x'", "OnContaCarregada", "d", playerid);2. Assíncrono sem callback — quando o resultado não interessa 🇧🇷 O callback sempre foi opcional, mas isso estava num parágrafo no pé de uma página enquanto todos os exemplos sugeriam o contrário — daí a reclamação de que o plugin "obriga a escrever callback para tudo". Um UPDATE cujo resultado você não vai ler é uma linha: 🇬🇧 The callback was always optional; the docs buried it. An UPDATE whose result you won't read is one line: Code: mysql_query(g_mysql, "UPDATE contas SET online = 0 WHERE id = 7");3. Síncrono — trabalho de subida Code: public OnGameModeInit()4. FIFO ordena callbacks, não execução — a armadilha 🇧🇷 Isto merece destaque porque a documentação antiga recomendava errado. O mysql_query entrega os callbacks na ordem em que você submeteu, mas cada statement roda na sua própria conexão, ao mesmo tempo. Um CREATE TABLE seguido de um INSERT corre junto, e o insert falha. Quem serializa de verdade é o mysql_query_file (arquivo inteiro, em ordem) e a transação (lote atômico) — ou o MYSQL_SYNC, que simplesmente espera. 🇬🇧 mysql_query dispatches callbacks in submission order, but each statement runs on its own connection, concurrently: a CREATE TABLE followed by an INSERT races. What actually serialises is mysql_query_file, a transaction, or MYSQL_SYNC. 5. E o mysql_pquery? 🇧🇷 Entrega assim que cada uma termina, sem ordem garantida — bom para muitas queries independentes. Com MYSQL_SYNC ele é igual ao mysql_query: uma chamada que espera não tem com o que rodar em paralelo. 🇬🇧 Delivers as each finishes, no ordering. With MYSQL_SYNC it's identical to the FIFO native. 🔐 Outras novidades Also new in 1.4.0 Diagnóstico de TLS 🇧🇷 mysql_tls_active(connId) e mysql_tls_cipher(connId, dest[], max_len) dizem no que o handshake realmente terminou, não o que o MYSQL_OPT_SSL pediu. Os dois leem um valor capturado quando a conexão abriu, então nenhum custa uma query. Sessão sem criptografia devolve false e cipher vazio — essa é a resposta, e o retorno separa isso de uma conexão que não existe. 🇬🇧 What the handshake actually settled on, rather than what MYSQL_OPT_SSL asked for. Both read a value captured when the connection opened, so neither costs a query. Code: if (!mysql_tls_active(g_mysql)) print("[MySQL] atenção: conexão em texto claro");Os tetos de memória deixaram de ser fixos 🇧🇷 mysql_limit_set e mysql_limit_get mexem em três limites antes cravados no código: MYSQL_LIMIT_SAVED_CACHES (1024), MYSQL_LIMIT_RESULT_ROWS (100000) e MYSQL_LIMIT_ORM_STRING_LEN (4096). Quem precisa de mais, aumenta; máquina apertada, diminui. São globais e não por conexão, porque a memória protegida é a do servidor. O valor 0 é recusado em vez de ser lido como "ilimitado" — ilimitado é justamente o estado que esses limites existem para evitar — e abaixar um teto não joga fora o que já está guardado. 🇬🇧 Three caps that used to be hardcoded. Global, not per connection. 0 is refused rather than read as "unlimited", and lowering a cap keeps what is already held. Code: mysql_limit_set(MYSQL_LIMIT_RESULT_ROWS, 250000); // relatório grande${VARIÁVEL} no arquivo de conexão 🇧🇷 O mysql_connect_file já tirava a senha do gamemode; agora o arquivo pode citar o segredo em vez de guardá-lo, e uma cópia roubada não vale nada por si: 🇬🇧 The file can now name the secret instead of holding it: Code: host = 127.0.0.1🇧🇷 Nome não definido expande para vazio e diz no log qual faltou — mandar o literal ${MYSQL_PASSWORD} para o servidor falharia como "access denied" e mandaria você procurar no lugar errado. Só ${...} é referência, então senha com sinal de dólar continua funcionando. 🇬🇧 An unset name expands to nothing and logs which one was missing. Only ${...} is a reference, so a password with a dollar sign still works. IPv6 🇧🇷 É suportado, entre colchetes: [::1] é interpretado e validado como endereço. Sem colchetes vira nome de host — normalmente ainda conecta, mas não valida nada. 🇬🇧 Supported, in brackets: [::1] is parsed and validated as an address; without brackets it's taken as a host name. ⚙️ Por dentro: o SDK e o que ele destravou Under the hood 🇧🇷 O plugin roda sobre o rust-samp, o SDK em Rust para SA-MP e open.mp, que aqui vai para a 3.6.0 e sai de uma tag do git para o crates.io. A mudança já conserta uma esquisitice: a tag v3.5.0 declarava version = "3.4.0" no próprio manifesto, então o lockfile registrava 3.4.0 apontando para a tag v3.5.0. 🇧🇷 O que a 3.6.0 trouxe de útil aqui foi uma rota que faltava. Quando uma query termina numa worker thread, o resultado precisa aterrissar no estado do plugin — e até a 3.5.0 não havia caminho do SDK para isso, limitação que o plugin contornava com canal próprio. O post_with entrega o plugin ao job, e com ele os canais de resultado do plugin deixaram de existir, tanto na query quanto no hash de senha. 🇧🇷 Não é ganho de desempenho, e eu não vou vender como se fosse. O SDK drena essa fila imediatamente antes do on_tick, então o despacho continua acontecendo no on_tick e nada chega um tick depois do que chegava. O ganho é de arquitetura: menos código próprio fazendo o que o SDK passou a fazer, num ponto onde errar custa caro. Os ganhos de robustez que você sente nesta versão — a rajada de 9000 queries — são do plugin, não do SDK. 🇬🇧 It is not a performance win and I won't sell it as one. The SDK drains that queue immediately before on_tick, so dispatch still happens there and nothing arrives a tick later than it used to. The gain is architectural. The robustness wins you feel in this release are the plugin's, not the SDK's. 🇧🇷 Um detalhe de projeto que não é questão de gosto: entregar o resultado e despachar o callback tiveram de ficar em dois passos. Chamar o Pawn de dentro do job deixaria um public reentrar numa native e pegar um segundo empréstimo mutável do mesmo plugin, que é instantaneamente um bug de memória. Então o job só bufferiza, e quem invoca callback é o on_tick, que detém esse empréstimo legitimamente. A ordem FIFO continua sendo nossa, no buffer de sequência, porque os jobs terminam na ordem em que o banco responde, não na que você pediu. 🇬🇧 Handing the result over and dispatching the callback had to be two steps: calling Pawn from inside the job would let a public re-enter a native and take a second mutable borrow of the plugin. The job only buffers; on_tick dispatches. Gargalo antigo que já não é seu problema 🇧🇷 Vindo de outro plugin MySQL você provavelmente aprendeu a chamar mysql_tick() num timer. Aqui não é necessário desde a 3.0.0 do SDK: a fila é bombeada a cada tick, sozinha. A native continua existindo só por compatibilidade — e ela não libera o resultado do MYSQL_SYNC, que sai no tick seguinte de todo jeito. 🇬🇧 Coming from another MySQL plugin you probably learned to call mysql_tick() on a timer. Not needed here since SDK 3.0.0: the queue is pumped every tick automatically. 📖 A referência da API agora se escreve sozinha The API reference generates itself 🇧🇷 A referência da API era uma tabela escrita à mão com cada native, seu tipo e uma descrição — exatamente a informação que o include já carrega ao lado de cada declaração, mantida em sincronia a dedo. Agora ela é gerada do include a cada build: natives, o forward, as constantes documentadas e os enums com seus valores, cada um com assinatura, apelido open.mp, parâmetros e retorno. A cópia manual foi apagada, e a URL é a mesma. 🇧🇷 O gerador lê dois formatos de comentário: o JavaDoc (@param, @return), que é o que estes includes usam, e o pawndoc (<summary>, <param name="">, <returns>), que é o que você encontra em includes de terceiros. E tem autoteste: se uma native entrar sem bloco de documentação, o build da doc falha em vez de publicar uma linha vazia. 🇧🇷 Esses mesmos comentários chegam ao hover do seu editor — na extensão PawnPro para VS Code, por exemplo, você passa o mouse na native e lê o que cada parâmetro espera sem abrir o .inc. Nesta versão as três natives que aceitam MYSQL_SYNC carregam o exemplo bloqueante no próprio comentário, então o jeito de fazer uma chamada esperar aparece onde a native é documentada, não só na página de queries. 🇬🇧 The API reference is now generated from the includes on every docs build — natives, forward, documented constants and enums with their values. Two comment formats are read: JavaDoc, used here, and pawndoc. A native without a doc block fails the docs build. The same comments reach your editor's hover — e.g. the PawnPro VS Code extension. Quote:🇧🇷 Prepared statement continua sendo a via segura. O mysql_format e o ORM interpolam texto e dependem do escaping; o mysql_stmt_* manda os valores pelo protocolo binário e não tem esse risco. Para qualquer coisa que carregue o que o jogador digitou, prefira statement — isso não mudou nesta versão e não vai mudar. 📚 Links
🇧🇷 Projeto independente, mantido por uma pessoa. Não é afiliado, endossado nem patrocinado pelo SA-MP ou pelo open.mp. "SA-MP", "open.mp" e "MySQL" pertencem a seus respectivos donos e são citados apenas para descrever compatibilidade. 🇬🇧 Independent project, maintained by one person. Not affiliated with or endorsed by SA-MP or open.mp. 🇧🇷 Dúvidas, sugestões e relatos de bug são bem-vindos — aqui no tópico ou nas issues.
🇬🇧 Questions, suggestions and bug reports are welcome — here or on GitHub issues. ⭐ github.com/NullSablex/mysql_samp |