What changed (R41-4 → mysql_samp)
Flat reference of every difference between MySQL R41-4 and mysql_samp. See migration.md for the explained guide and migration-examples.md for side-by-side snippets.
Include
// R41-4
#include <a_mysql>
// mysql_samp
#include <mysql_samp>
mysql_samp does not use custom Pawn tags. Every handle is a plain int.
R41-4
mysql_samp
new MySQL:g_mysql
new g_mysql
new Cache:c = cache_save();
new c = cache_save();
new ORM:o = orm_create(...)
new o = orm_create(...)
cache_delete(Cache:c);
cache_delete(c);
MySQLOpt:o = mysql_init_options()
new o = mysql_options_new()
MYSQL_INVALID_HANDLE
plain 0
"d" and "i" are both accepted as int. If you already use "i", no change is needed.
R41-4
mysql_samp
Meaning
"i"
"d" or "i"
int
"f"
"f"
float
"s"
"s"
string
// R41-4
mysql_tquery ( g_mysql , query , "OnLoad" , "i" , playerid );
// mysql_samp (both work)
mysql_query ( g_mysql , query , "OnLoad" , "d" , playerid );
mysql_query ( g_mysql , query , "OnLoad" , "i" , playerid );
Specifier
R41-4
mysql_samp
%d, %i
int
int
%f
float
float (4 decimals — {:.4})
%s
raw (no escape)
escaped
%e
escaped
escaped (alias of %s)
%r
does not exist
raw (no escape)
%%
literal
literal
Rule of thumb: where you used %e, use %s (both escape). Where you used %s to inject a table or column name, switch to %r.
Queries
R41-4
mysql_samp
Behavior
Cache:mysql_query(handle, query, use_cache)
removed
The synchronous version blocked the server tick
mysql_tquery(handle, query, cb, fmt, ...)
mysql_query(connId, query, cb, fmt, ...)
Threaded, FIFO ordering (same as before)
mysql_pquery(handle, query, cb, fmt, ...)
mysql_pquery(connId, query, cb, fmt, ...)
Threaded, no order (same as before)
Every query is now non-blocking. The cache is only valid inside the callback (or after cache_set_active on a saved id).
Connection
R41-4
mysql_samp
MySQL:mysql_connect(host, user, pass, db, MySQLOpt:opt)
mysql_connect(host, user, pass, db, options)
MySQLOpt:mysql_init_options()
mysql_options_new()
mysql_set_option(opt, type, ...)
mysql_options_set_int(opt, type, val) / mysql_options_set_str(opt, type, val[])
mysql_escape_string(src, dest, max_len, MySQL:handle)
mysql_escape_string(src, dest, max_len)
mysql_stat(dest, max_len, MySQL:handle)
mysql_status(connId, dest, max_len)
mysql_error(dest, max_len, MySQL:handle)
mysql_error(connId, dest, max_len)
mysql_errno(MySQL:handle)
mysql_errno(connId)
mysql_set_charset(charset, MySQL:handle)
mysql_set_charset(connId, charset)
mysql_get_charset(dest, max_len, MySQL:handle)
mysql_get_charset(connId, dest, max_len)
mysql_close(MySQL:handle)
mysql_close(connId)
mysql_unprocessed_queries(MySQL:handle)
mysql_unprocessed_queries()
does not exist
mysql_log(level)
Argument order changed. In R41-4 the handle is the last (optional) parameter on most calls. In mysql_samp the connId is always the first parameter.
Port option name changed. In R41-4 you wrote mysql_set_option(opt, SERVER_PORT, 3307). In mysql_samp it is mysql_options_set_int(opt, MYSQL_OPT_PORT, 3307).
// R41-4
new MySQLOpt : opt = mysql_init_options ();
mysql_set_option ( opt , SERVER_PORT , 3307 );
new MySQL : g_mysql = mysql_connect ( "127.0.0.1" , "root" , "pass" , "db" , opt );
// mysql_samp
new opt = mysql_options_new ();
mysql_options_set_int ( opt , MYSQL_OPT_PORT , 3307 );
new g_mysql = mysql_connect ( "127.0.0.1" , "root" , "pass" , "db" , opt );
// mysql_samp (default port 3306 — no options needed)
new g_mysql = mysql_connect ( "127.0.0.1" , "root" , "pass" , "db" );
Cache — by-ref → return value
The biggest single change. R41-4 wrote the result through a pointer; mysql_samp returns the value directly.
Row/field counts
R41-4
mysql_samp
cache_get_row_count(&dest)
cache_get_row_count() → int
cache_get_field_count(&dest)
cache_get_field_count() → int
cache_num_rows() (stock wrapper)
cache_get_row_count()
cache_num_fields() (stock wrapper)
cache_get_field_count()
// R41-4
new rows ;
cache_get_row_count ( rows );
// or
new rows2 = cache_num_rows ();
// mysql_samp
new rows = cache_get_row_count ();
Values by index
R41-4
mysql_samp
cache_get_value_index(row, col, dest[], max_len)
unchanged
cache_get_value_index_int(row, col, &dest)
cache_get_value_index_int(row, col) → int
cache_get_value_index_float(row, col, &Float:dest)
cache_get_value_index_float(row, col) → Float
// R41-4
new score ;
cache_get_value_index_int ( 0 , 2 , score );
// mysql_samp
new score = cache_get_value_index_int ( 0 , 2 );
Values by name
R41-4
mysql_samp
cache_get_value_name(row, col_name, dest[], max_len)
unchanged
cache_get_value_name_int(row, col_name, &dest)
cache_get_value_name_int(row, col_name) → int
cache_get_value_name_float(row, col_name, &Float:dest)
cache_get_value_name_float(row, col_name) → Float
Column name lookup is case-insensitive in both plugins.
// R41-4
new score ;
cache_get_value_name_int ( 0 , "score" , score );
new Float : pos_x ;
cache_get_value_name_float ( 0 , "pos_x" , pos_x );
// mysql_samp
new score = cache_get_value_name_int ( 0 , "score" );
new Float : pos_x = cache_get_value_name_float ( 0 , "pos_x" );
NULL checks
R41-4
mysql_samp
cache_is_value_index_null(row, col, &bool:dest)
cache_is_value_index_null(row, col) → bool
cache_is_value_name_null(row, col_name, &bool:dest)
cache_is_value_name_null(row, col_name) → bool
// R41-4
new bool : is_null ;
cache_is_value_name_null ( 0 , "email" , is_null );
// mysql_samp
new bool : is_null = cache_is_value_name_null ( 0 , "email" );
Cache — natives with unchanged signatures
cache_get_value_index(row, col, dest[], max_len)
cache_get_value_name(row, col_name, dest[], max_len)
cache_get_field_name(idx, dest[], max_len)
cache_affected_rows()
cache_insert_id()
cache_warning_count()
cache_save()
cache_delete(cache_id) (without the Cache: tag)
cache_set_active(cache_id) (without the Cache: tag)
cache_unset_active()
cache_is_any_active()
cache_is_valid(cache_id) (without the Cache: tag)
cache_get_query_string(dest[], max_len)
Cache — new natives
Native
Description
cache_get_field_type(idx)
Raw MySQL ColumnType byte for the given column index
cache_get_query_exec_time()
Server-side execution time in milliseconds
Cache — removed natives
R41-4
Why
cache_get_result_count(&dest)
Multi-result sets — unused by SA-MP gamemodes
cache_set_result(idx)
Multi-result sets
cache_get_query_exec_time(unit)
Replaced by the no-argument version (always ms)
ORM
The API is almost identical, with these differences:
R41-4
mysql_samp
Difference
ORM:orm_create(table, MySQL:handle)
orm_create(table, connId)
No tags
orm_destroy(ORM:id)
orm_destroy(orm_id) → bool
Now returns bool
E_ORM_ERROR:orm_errno(ORM:id)
orm_errno(orm_id) → int
No enum tag
orm_apply_cache(ORM:id, row, result_idx)
orm_apply_cache(orm_id, row)
No result_idx (single result set)
orm_load(...)
removed
Was an alias of orm_select
orm_addvar_string(orm, var, max_len, col)
unchanged
max_len now capped at 4096
ORM error enum:
R41-4
mysql_samp
ERROR_INVALID = 0
does not exist
ERROR_OK = 1
ORM_OK = 0
ERROR_NO_DATA = 2
ORM_NO_DATA = 1
The rest of the ORM natives (orm_select, orm_update, orm_insert, orm_delete, orm_save, orm_addvar_*, orm_delvar, orm_clear_vars, orm_setkey) keep the same signatures, only without the ORM: tag.
Error
R41-4
mysql_samp
Difference
mysql_errno(MySQL:handle)
mysql_errno(connId = 0)
No tag, connId first
mysql_error(dest, max_len, MySQL:handle)
mysql_error(connId, dest, max_len)
connId first (was last)
Log
R41-4
mysql_samp
mysql_log(E_LOGLEVEL:level) — bitflags (DEBUG=1, INFO=2, WARNING=4, ERROR=8)
mysql_log(level) — sequential (NONE=0, ERROR=1, WARNING=2, INFO=3, ALL=4)
// R41-4
mysql_log ( ERROR | WARNING );
// mysql_samp
mysql_log ( MYSQL_LOG_WARNING ); // includes ERROR
Forward
R41-4
mysql_samp
OnQueryError(errorid, error[], callback[], query[], MySQL:handle)
OnQueryError(errorid, error[], callback[], query[], connId)
Same parameters, no MySQL: tag on the last one.
Mandatory checklist
Replace #include <a_mysql> with #include <mysql_samp>.
Replace mysql_tquery with mysql_query.
Replace %s (raw) with %r in mysql_format wherever the old %s was intentionally unescaped.
Replace mysql_escape_string(..., MySQL:handle) with mysql_escape_string(src, dest, max_len) (no handle).
Replace cache_num_rows() with cache_get_row_count().
Convert cache_get_value_*_int / cache_get_value_*_float from by-ref to return value.
Convert cache_is_value_*_null from by-ref to return value.
Convert cache_get_row_count / cache_get_field_count from by-ref to return value.
Strip every tag (MySQL:, Cache:, ORM:, MySQLOpt:, E_ORM_ERROR:).
Replace mysql_init_options() with mysql_options_new().
Replace mysql_set_option(opt, type, ...) with mysql_options_set_int / mysql_options_set_str.
Reverse the parameter order of mysql_error (connId is now first).
Replace mysql_stat with mysql_status (connId first).
Move any code that read the cache after a synchronous mysql_query into a proper callback.
Optional:
"i" → "d" in callback formats (both work in mysql_samp).