Error handling¶
The plugin reports failures through three channels:
- Plugin-level errno —
mysql_errno(connId)/mysql_error(connId, dest). Each connection has its own error slot;connId == 0reads the global state (set by connect failures, ORM build failures, etc.). - Threaded-query forward —
OnQueryErroris fired on every loaded AMX when amysql_query/mysql_pqueryfails. - Console +
logs/mysql.log— short message in the console, full details in the file.
mysql_errno¶
Returns one of the plugin error codes below. connId == 0 returns the global error, useful for mysql_connect failures.
| Constant | Value | Set when |
|---|---|---|
MYSQL_OK |
0 | No error since the last reset |
MYSQL_ERROR_CONNECTION_FAILED |
1 | mysql_connect failed (global) |
MYSQL_ERROR_INVALID_OPTIONS |
2 | mysql_connect received an unknown options handle (global) |
MYSQL_ERROR_INVALID_CONNECTION |
3 | mysql_query / mysql_pquery / orm_create received an unknown connId (global) |
MYSQL_ERROR_PING_FAILED |
4 | mysql_status could not fetch server stats (per-connection) |
MYSQL_ERROR_QUERY_FAILED |
5 | A threaded query failed (per-connection); the detailed message is the MySQL server text |
MYSQL_ERROR_NO_CACHE_ACTIVE |
6 | orm_apply_cache was called without an active cache (global) |
MYSQL_ERROR_INVALID_ORM |
7 | An ORM native received an unknown orm_id, or ORM insert/save could not build a query (global) |
MYSQL_ERROR_ORM_KEY_NOT_SET |
8 | ORM select/update/delete could not build a query because the key column is not set or no variables are bound (global) |
g_mysql = mysql_connect("127.0.0.1", "root", "pw", "samp_db");
if (mysql_errno() != MYSQL_OK)
{
new msg[256];
mysql_error(0, msg);
printf("connect failed (%d): %s", mysql_errno(), msg);
return 1;
}
mysql_error¶
Writes the text of the last error into dest. Returns true on success.
When connId == 0, reads the global error message. When connId is unknown, also falls back to the global error.
OnQueryError¶
Fired on every loaded AMX when a threaded query fails. Parameters:
| Parameter | Type | Description |
|---|---|---|
errorid |
int | MySQL server error code (1062, 1045, 1064, …) or 0 for transport-level errors (TCP drop, IO error) |
error |
string | Full error text from the mysql crate |
callback |
string | Name of the public the query asked for (empty if fire-and-forget) |
query |
string | Exact SQL that was sent to the server. Printing it puts the whole statement in your server log — see the note below |
connId |
int | Id of the connection that produced the error |
public OnQueryError(errorid, const error[], const callback[], const query[], connId)
{
printf("[mysql %d] %s", errorid, error);
printf(" callback: %s", callback);
printf(" query: %s", query);
printf(" conn: %d", connId);
return 1;
}
The plugin itself never logs query text — only MySQL's error message, which for a syntax error may echo a fragment of the statement. The full SQL reaches your gamemode through the
queryparameter above, so printing it is what puts it inserver_log.txt. When the statement may carry sensitive values, logcallbackanderroridinstead — or use prepared statements, where the values never enter the query text at all. See Security.
The plugin also sets the per-connection errno to MYSQL_ERROR_QUERY_FAILED after firing the forward — so mysql_errno(connId) / mysql_error(connId, ...) can be read afterwards if you want the message in a different context.
Common MySQL error codes¶
| Code | Cause |
|---|---|
| 1045 | Access denied (wrong user/password) |
| 1049 | Unknown database |
| 1062 | Duplicate entry (UNIQUE / PRIMARY KEY violation) |
| 1064 | SQL syntax error |
| 1146 | Table does not exist |
| 1451 | Foreign-key constraint blocked the operation |
| 2002 | Could not connect to the server (host unreachable) |
| 2006 | "MySQL server has gone away" |
| 0 | Transport/IO error — the mysql crate uses 0 for non-MySQL failures such as TCP drops. When MYSQL_OPT_AUTO_RECONNECT is on, the plugin retries the query once before reporting it. |
Logs¶
Console¶
The console gets only generic messages, never query text or credentials:
[MySQL] Connection failed (error 1). See logs/mysql.log for details.
[MySQL] Query failed on connection 1 (error 1064). See logs/mysql.log for details.
[MySQL] Query result truncated to 100000 rows.
logs/mysql.log¶
Detailed entries with timestamp:
[2026-05-18 14:30:15] [ERROR] Pool creation failed: Access denied for user 'root'@'localhost'
[2026-05-18 14:30:20] [ERROR] Query error: You have an error in your SQL syntax; check the manual ...
[2026-05-18 14:30:30] [WARN] cache_save failed: maximum saved caches reached (1024).
If the directory or the file is not writable, the plugin emits one console error and then stops trying. This avoids flooding the console when the disk is full or permissions are wrong.
Rotation¶
When logs/mysql.log passes 50 MB it is moved into logs/archive/mysql.log.{N}.gz and a fresh file is opened. Archives are gzipped (log text compresses roughly 10x) and are never deleted by the plugin — cleanup is the server operator's call. Set MYSQL_SAMP_LOG_ROTATION_KEEP to have it prune automatically.
Environment overrides¶
The log pipeline can be retuned without rebuilding the plugin. Every variable is optional; an invalid value is reported on the console and the default is kept.
| Variable | Effect |
|---|---|
MYSQL_SAMP_LOG_LEVEL |
Minimum severity (off, error, warn, info, debug, trace) |
MYSQL_SAMP_LOG_DIR |
Directory holding the log file (default logs) |
MYSQL_SAMP_LOG_FILE |
File name (default mysql.log) |
MYSQL_SAMP_LOG_ROTATION_MB |
Rotation threshold in MB (default 50) |
MYSQL_SAMP_LOG_ROTATION_KEEP |
Keep only the last N archives, deleting older ones |
MYSQL_SAMP_LOG_NO_ROTATION |
Disable rotation entirely |
MYSQL_SAMP_LOG_NO_BANNER |
Suppress the startup banner |
MYSQL_SAMP_LOG_COMPRESS |
0 writes uncompressed archives instead of .gz |
mysql_log() called from Pawn overrides MYSQL_SAMP_LOG_LEVEL at runtime.
Log level¶
mysql_log adjusts the minimum severity that the plugin emits:
mysql_log(MYSQL_LOG_NONE); // 0 — nothing
mysql_log(MYSQL_LOG_ERROR); // 1 — errors only
mysql_log(MYSQL_LOG_WARNING); // 2 — errors + warnings
mysql_log(MYSQL_LOG_INFO); // 3 — errors + warnings + info
mysql_log(MYSQL_LOG_ALL); // 4 — everything (default)
The setting is atomic and takes effect immediately.
Practical checklist¶
- Always check
mysql_errno()aftermysql_connect— a failed connect returns0. Without a check, the gamemode happily issues queries against connection0, which is invalid. - Implement
OnQueryError— a missing implementation is not a compile error, but you will be flying blind when a query fails in production. - Read
logs/mysql.logfor the long error text — the console does not have the SQL or the original server message. - Use prepared statements (
mysql_stmt_*) for player input — values are bound server-side, so there is no escaping to get wrong.mysql_formatwith%sescapes correctly too, but only as long as the rules match the server'ssql_mode. - Guard cache reads with
cache_get_row_count()— avoids spurious-1/0/ empty-string readings when the result is empty.