/**
* Copyright (c) 2013, Dan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* 2.6
*
* Data types:
* SQL - a SQL connection
* Result - the result of a query, whether it returns or not rows
*
*/
/**
* MySQL client error codes.
*/
#define MYSQL_CR_UNKNOWN_ERROR 2000
#define MYSQL_CR_SOCKET_CREATE_ERROR 2001
#define MYSQL_CR_CONNECTION_ERROR 2002
#define MYSQL_CR_CONN_HOST_ERROR 2003
#define MYSQL_CR_IPSOCK_ERROR 2004
#define MYSQL_CR_UNKNOWN_HOST 2005
#define MYSQL_CR_SERVER_GONE_ERROR 2006
#define MYSQL_CR_VERSION_ERROR 2007
#define MYSQL_CR_OUT_OF_MEMORY 2008
#define MYSQL_CR_WRONG_HOST_INFO 2009
#define MYSQL_CR_LOCALHOST_CONNECTION 2010
#define MYSQL_CR_TCP_CONNECTION 2011
#define MYSQL_CR_SERVER_HANDSHAKE_ERR 2012
#define MYSQL_CR_SERVER_LOST 2013
#define MYSQL_CR_COMMANDS_OUT_OF_SYNC 2014
#define MYSQL_CR_NAMEDPIPE_CONNECTION 2015
#define MYSQL_CR_NAMEDPIPEWAIT_ERROR 2016
#define MYSQL_CR_NAMEDPIPEOPEN_ERROR 2017
#define MYSQL_CR_NAMEDPIPESETSTATE_ERROR 2018
#define MYSQL_CR_CANT_READ_CHARSET 2019
#define MYSQL_CR_NET_PACKET_TOO_LARGE 2020
#define MYSQL_CR_EMBEDDED_CONNECTION 2021
#define MYSQL_CR_PROBE_SLAVE_STATUS 2022
#define MYSQL_CR_PROBE_SLAVE_HOSTS 2023
#define MYSQL_CR_PROBE_SLAVE_CONNECT 2024
#define MYSQL_CR_PROBE_MASTER_CONNECT 2025
#define MYSQL_CR_SSL_CONNECTION_ERROR 2026
#define MYSQL_CR_MALFORMED_PACKET 2027
#define MYSQL_CR_WRONG_LICENSE 2028
#define MYSQL_CR_NULL_POINTER 2029
#define MYSQL_CR_NO_PREPARE_STMT 2030
#define MYSQL_CR_PARAMS_NOT_BOUND 2031
#define MYSQL_CR_DATA_TRUNCATED 2032
#define MYSQL_CR_NO_PARAMETERS_EXISTS 2033
#define MYSQL_CR_INVALID_PARAMETER_NO 2034
#define MYSQL_CR_INVALID_BUFFER_USE 2035
#define MYSQL_CR_UNSUPPORTED_PARAM_TYPE 2036
#define MYSQL_CR_SHARED_MEMORY_CONNECTION 2037
#define MYSQL_CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR 2038
#define MYSQL_CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR 2039
#define MYSQL_CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR 2040
#define MYSQL_CR_SHARED_MEMORY_CONNECT_MAP_ERROR 2041
#define MYSQL_CR_SHARED_MEMORY_FILE_MAP_ERROR 2042
#define MYSQL_CR_SHARED_MEMORY_MAP_ERROR 2043
#define MYSQL_CR_SHARED_MEMORY_EVENT_ERROR 2044
#define MYSQL_CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR 2045
#define MYSQL_CR_SHARED_MEMORY_CONNECT_SET_ERROR 2046
#define MYSQL_CR_CONN_UNKNOW_PROTOCOL 2047
#define MYSQL_CR_INVALID_CONN_HANDLE 2048
#define MYSQL_CR_SECURE_AUTH 2049
#define MYSQL_CR_FETCH_CANCELED 2050
#define MYSQL_CR_NO_DATA 2051
#define MYSQL_CR_NO_STMT_METADATA 2052
/**
* MySQL
*/
#define SQL_HANDLER_MYSQL 1
/**
* Postgre SQL
*/
#define SQL_HANDLER_POSTGRESQL 2
/**
* The query will be executed in server's thread and the result is fetched on demand.
*/
#define QUERY_NONE 0
/**
* Runs the query in a different thread (other than main server's thread).
*/
#define QUERY_THREADED 1
/**
* Stores (temporarily) rows returned by a query in cache.
*/
#define QUERY_CACHED 2
/**
* Log levels. (@see sql_debug)
*/
#define LOG_ALL 0
#define LOG_DEBUG 1
#define LOG_INFO 2
#define LOG_WARNING 3
#define LOG_ERROR 4
#define LOG_NONE 5
/**
* Checks if a string is null.
*/
#if !defined isnull
#define isnull(%1) \
((!(%1[0])) || (((%1[0]) == '\1') && (!(%1[1]))))
#endif
/**
* Checks if a cell from a SQL table is null.
*/
#if !defined issqlnull
#define issqlnull(%1) \
((isnull(%1)) || (strcmp(%1, "NULL", false) == 0))
#endif
/**
* Aliases of natives defined below.
*/
#define sql_log sql_debug
//
#define mysql_connect(%0) sql_connect(SQL_HANDLER_MYSQL,%0)
#define pgsql_connect(%0) sql_connect(SQL_HANDLER_POSTGRESQL,%0)
//
#define mysql_disconnect(%0) sql_disconnect(%0)
#define pgsql_disconnect(%0) sql_disconnect(%0)
//
#define sql_open sql_connect
#define mysql_open(%0) mysql_connect(%0)
#define mysql_close(%0) mysql_disconnect(%0)
//
#define sql_close sql_disconnect
#define pgsql_open(%0) pgsql_connect(%0)
#define pgsql_close(%0) pgsql_disconnect(%0)
/**
* Called when a SQL error occurs during the execution of a query.
* The SQL handle used for execution of the query.
* The ID of the error (@see "C.4. Client Error Codes and Messages" from "SQL Reference Manual").
* The message of the error.
* Query which caused the error.
* Callback which should have been called normally.
* No return value is expected.
*/
forward OnSQLError(SQL:handle, errorid, error[], query[], callback[]);
/**
* Sets the minimum level of logged messages.
* The minimum level of the file (sql_log.txt) logs.
* The minimum level of the console logs.
* No return value.
*/
native sql_debug(file = LOG_ALL, console = LOG_WARNING);
/**
* Creates a new handle and connects to a SQL server.
* The SQL hostname.
* The SQL username.
* The SQL password assigned to the user used.
* The name of the targeted database.
* The port on which the SQL server listens.
* The ID of the handle.
*/
native SQL:sql_connect(sql_type, host[], user[], pass[], db[], port = 0);
/**
* Destroys the handle and disconnects from the SQL server.
* The SQL handle which has to be disconnected.
* No return value.
*/
native sql_disconnect(SQL:handle);
/**
* Waits for a handle to finish its activity (all queries to be executed).
* The SQL handle.
* No return value.
*/
native sql_wait(SQL:handle);
/**
* Sets default character set.
* The SQL handle.
* New default character set.
* True if succesful.
*/
native sql_set_charset(SQL:handle, charset[]);
/**
* Gets default character set.
* The destination where the character set will be stored.
* The capacity of the destination.
* True if succesful.
*/
native sql_get_charset(SQL:handle, dest[], dest_len = sizeof(dest));
/**
* Pings the SQL server.
* The SQL handle which has to be disconnected.
* Returns a SQL client error (if any) or 0 if the connection is alive.
*/
native sql_ping(SQL:handle);
/**
* Gets statistics from SQL server.
* The destination where the information will be stored.
* The capacity of the destination.
* True if succesful.
*/
native sql_get_stat(SQL:handle, dest[], dest_len = sizeof(dest));
/**
* Escapes a string to be used further in queries.
* The SQL handle used for escaping the string.
* The source of the unescaped string.
* The destination where the escaped string will be stored.
* The capacity of the destination.
* The length of the escaped string.
*/
native sql_escape_string(SQL:handle, src[], dest[], dest_len = sizeof(dest));
/**
* Formats the string.
* The SQL handle used for escaping some string.
* The destination where the formatted string will be stored.
* The capacity of the destination.
* The format of the string.
* The length of the formatted string.
*/
native sql_format(handle, dest[], dest_len = sizeof(dest), format[], {Float, _}:...);
/**
* Executes a SQL query and returns the result.
* The SQL handle used for execution of the query.
* The query.
* Query's flags.
* The callback which has to be called after the query was sucesfully executed.
* The format of the callback.
* a, A = arrays (must be followed by an integer: array's szie);
* b, B = boolean; c, C = character; d, D, i, I = integer;
* r, R = result; s, S = string
*
* The ID of the result.
*/
native Result:sql_query(SQL:handle, query[], flag = QUERY_NONE, callback[] = "", format[] = "", {Float,_}:...);
/**
* Stores the result for later use (if query is threaded).
* The ID of the result which has to be stored.
* No return value.
*/
native sql_store_result(Result:result);
/**
* Frees the result for later use (if the query was stored or is not threaded).
* The ID of the result which has to be freed.
* No return value.
*/
native sql_free_result(Result:result);
/**
* Gets the count of affected rows.
* The ID of the result.
* The count of affected rows.
*/
native sql_affected_rows(Result:result);
/**
* Gets the ID of the last insert query.
* The ID of the result.
* The ID of the latest inserted row.
*/
native sql_insert_id(Result:result);
/**
* Gets the ID of the error returned by this result.
* The ID of the result.
* The ID of the error.
*/
native sql_error(Result:result);
/**
* Gets the message of the error returned by this result.
* The ID of the result.
* The destination where the error message will be stored.
* The capacity of the destination.
* The ID of the error.
*/
native sql_error_string(Result:result, dest[], dest_len = sizeof(dest));
/**
* Gets the count of rows contained in a result.
* The ID of the result.
* The count of rows contained in the result.
*/
native sql_num_rows(Result:result);
/**
* Gets the count of fields contained in a result.
* The ID of the result.
* The count of fields contained in the result.
*/
native sql_num_fields(Result:result);
/**
* Jumps to a specific result set.
* The ID of the result.
* The index of the result set. If -1 is specified, next result set will be retrieved.
* `true` if there are any results left, `false` otherwise.
*/
native sql_next_result(Result:result, idx = -1);
/**
* Fetches the name of a field.
* The ID of the result.
* The index of the field.
* The destination where the field will be stored.
* The capacity of the destination.
* The length of field's name.
*/
native sql_field_name(Result:result, field, dest[], dest_len = sizeof(dest));
/**
* Fetches an entire row inserting the separator between each cell.
*
*
* The destination where the field will be stored.
* The capacity of the destination.
* The length of the row.
*/
native sql_fetch_row(Result:result, sep[], dest[], dest_len = sizeof(dest));
/**
* Jumps to a specific row.
* The ID of the result.
* The index of the row. If -1 is specified, next row will be retrieved.
* `true` if there are any rows left, `false` otherwise.
*/
native sql_next_row(Result:result, row = -1);
// ----------------------------------------------------------------------------
/**
* Fetches a cell containing a string by field index.
* The ID of the result.
* The index of the field.
* The destination where the string will be stored.
* The capacity of the destination.
* The length of the string.
*/
native sql_get_field(Result:result, field, dest[], dest_len = sizeof(dest));
/**
* Fetches a cell containing a string by field name.
* The ID of the result.
* The name of the field.
* The destination where the string will be stored.
* The capacity of the destination.
* The length of the string.
*/
native sql_get_field_assoc(Result:result, field[], dest[], dest_len = sizeof(dest));
/**
* Fetches a cell containing an integer value by field index.
* The ID of the result.
* The index of the field.
* The value of the cell.
*/
native sql_get_field_int(Result:result, field);
/**
* Fetches a cell containing an integer value by field name.
* The ID of the result.
* The name of the field.
* The value of the cell.
*/
native sql_get_field_assoc_int(Result:result, field[]);
/**
* Fetches a cell containing a float value by field index.
* The ID of the result.
* The index of the field.
* The value of the cell.
*/
native Float:sql_get_field_float(Result:result, field);
/**
* Fetches a cell containing a float value by field name.
* The ID of the result.
* The name of the field.
* The value of the cell.
*/
native Float:sql_get_field_assoc_float(Result:result, field[]);
// ----------------------------------------------------------------------------
/**
* Fetches a cell containing a string by field index.
* The ID of the result.
* The index of the row.
* The index of the field.
* The destination where the string will be stored.
* The capacity of the destination.
* The length of the string.
*/
native sql_get_field_ex(Result:result, row, field, dest[], dest_len = sizeof(dest));
/**
* Fetches a cell containing a string by field name.
* The ID of the result.
* The index of the row.
* The name of the field.
* The destination where the string will be stored.
* The capacity of the destination.
* The length of the string.
*/
native sql_get_field_assoc_ex(Result:result, row, field[], dest[], dest_len = sizeof(dest));
/**
* Fetches a cell containing an integer value by field index.
* The ID of the result.
* The index of the row.
* The index of the field.
* The value of the cell.
*/
native sql_get_field_int_ex(Result:result, row, field);
/**
* Fetches a cell containing an integer value by field name.
* The ID of the result.
* The index of the row.
* The name of the field.
* The value of the cell.
*/
native sql_get_field_assoc_int_ex(Result:result, row, field[]);
/**
* Fetches a cell containing a float value by field index.
* The ID of the result.
* The index of the row.
* The index of the field.
* The value of the cell.
*/
native Float:sql_get_field_float_ex(Result:result, row, field);
/**
* Fetches a cell containing a float value by field name.
* The ID of the result.
* The index of the row.
* The name of the field.
* The value of the cell.
*/
native Float:sql_get_field_assoc_float_ex(Result:result, row, field[]);
// ----------------------------------------------------------------------------
/**
* y_inline interface
*/
#if !defined SQL_USE_Y_INLINE
// NOTE: #emit ignores pre-processor's directives.
// So, if we disable y_inline support, the #emit instructions below still
// requirea variable to store the address of the first passed parameter.
//
// @Y_Less suggested that #endinput can be used here aswell.
new SQL_addr;
#pragma unused SQL_addr
#endif
#if defined SQL_USE_Y_INLINE
#if !defined SQL_MAX_INLINE_CALLBACKS
/**
* The maximum number of inline callbacks scheduled simultaneously.
*/
#define SQL_MAX_INLINE_CALLBACKS 512
#endif
#if !defined SQL_MAX_INLINE_PARAMETERS
/**
* The maximum number of parameters an inline function can have.
*/
#define SQL_MAX_INLINE_PARAMETERS 16
#endif
/**
* Executes a SQL query and calls an inline callback on return.
* The "format" parameter must be static.
*/
/*
* NOTE: This is a fake native. It has been put here to be detected by Pawno, the SA-MP's bundled IDE.
native Result:sql_query_inline(SQL:handle, query[], flag, callback:callback, format[] = "", {Float,_}:...);
*/
#define sql_query_inline(%0,%1,%2,%3,"%4"%5) \
sql_query(%0,%1,%2,"SQL_OnInlineCallback","i"#%4,SQL_SaveCallback(%3)%5)
/**
* Stores information about callbacks that are scheduled to be executed.
* This data is recycled. Once `SQL_LoadCallback` was called, that index is marked as free.
*
* TODO: If the query fails, the `SQL_OnInlineCallback` won't be called
* and the data won't be recycled. Fix it.
*/
stock SQL_callbacks[SQL_MAX_INLINE_CALLBACKS][E_CALLBACK_DATA];
/**
* Saves the callback data of a scheduled statement.
* Callback's ID.
* The index from `SQL_callbacks` where the callback was saved.
*/
stock SQL_SaveCallback(callback:callback) {
for (new i = 0; i != sizeof(SQL_callbacks); ++i) {
if (SQL_callbacks[i][E_CALLBACK_DATA_POINTER] != 0) {
continue;
}
if (!Callback_Get(callback, SQL_callbacks[i])) {
SQL_callbacks[i][E_CALLBACK_DATA_POINTER] = 0;
return -1;
}
return i;
}
print("[plugin.sql][warning] No more free space in `SQL_callbacks`.");
return -1;
}
/**
* The Interface between the plugin and y_inline, which loads the callback data and executes it.
* This function also recycles data.
* Callback's index from `SQL_callbacks`.
*/
forward SQL_OnInlineCallback(idx, {Float,_}:...);
public SQL_OnInlineCallback(idx, {Float,_}:...) {
if (idx == -1) {
return;
}
new numArgs = numargs(), SQL_addr[SQL_MAX_INLINE_PARAMETERS];
if (numArgs > 1) {
// Storing the address of the first dynamic parameter in SQL_addr[0].
#emit CONST.alt 16
#emit LCTRL 5
#emit ADD
#emit STOR.S.pri SQL_addr
//
--numArgs;
for (new i = 1; i != numArgs; ++i) {
SQL_addr[i] = SQL_addr[0] + (i * 4); // BYTES_PER_CELL = 4
}
}
Callback_Array(SQL_callbacks[idx], SQL_addr);
SQL_callbacks[idx][E_CALLBACK_DATA_POINTER] = 0;
}
#endif