/** * 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