Skip to content

Latest commit

 

History

History
160 lines (120 loc) · 7.75 KB

File metadata and controls

160 lines (120 loc) · 7.75 KB

Error handling

The plugin reports failures through three channels:

  1. Plugin-level errnomysql_errno(connId) / mysql_error(connId, dest). Each connection has its own error slot; connId == 0 reads the global state (set by connect failures, ORM build failures, etc.).
  2. Threaded-query forwardOnQueryError is fired on every loaded AMX when a mysql_query / mysql_pquery fails.
  3. Console + logs/mysql.log — short message in the console, full details in the file.

mysql_errno

native mysql_errno(connId = 0);

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

native bool:mysql_error(connId, dest[], max_len = sizeof(dest));

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

forward OnQueryError(errorid, const error[], const callback[], const query[], connId);

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 query parameter above, so printing it is what puts it in server_log.txt. When the statement may carry sensitive values, log callback and errorid instead — 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

  1. Always check mysql_errno() after mysql_connect — a failed connect returns 0. Without a check, the gamemode happily issues queries against connection 0, which is invalid.
  2. Implement OnQueryError — a missing implementation is not a compile error, but you will be flying blind when a query fails in production.
  3. Read logs/mysql.log for the long error text — the console does not have the SQL or the original server message.
  4. Use prepared statements (mysql_stmt_*) for player input — values are bound server-side, so there is no escaping to get wrong. mysql_format with %s escapes correctly too, but only as long as the rules match the server's sql_mode.
  5. Guard cache reads with cache_get_row_count() — avoids spurious -1 / 0 / empty-string readings when the result is empty.