sql.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "cbasetypes.h"
  4. #include "malloc.h"
  5. #include "showmsg.h"
  6. #include "strlib.h"
  7. #include "timer.h"
  8. #include "sql.h"
  9. #ifdef WIN32
  10. #include "winapi.h"
  11. #endif
  12. #include <mysql.h>
  13. #include <stdlib.h>// strtoul
  14. #define SQL_CONF_NAME "conf/inter_athena.conf"
  15. void ra_mysql_error_handler(unsigned int ecode);
  16. int mysql_reconnect_type;
  17. unsigned int mysql_reconnect_count;
  18. /// Sql handle
  19. struct Sql
  20. {
  21. StringBuf buf;
  22. MYSQL handle;
  23. MYSQL_RES* result;
  24. MYSQL_ROW row;
  25. unsigned long* lengths;
  26. int keepalive;
  27. };
  28. // Column length receiver.
  29. // Takes care of the possible size missmatch between uint32 and unsigned long.
  30. struct s_column_length
  31. {
  32. uint32* out_length;
  33. unsigned long length;
  34. };
  35. typedef struct s_column_length s_column_length;
  36. /// Sql statement
  37. struct SqlStmt
  38. {
  39. StringBuf buf;
  40. MYSQL_STMT* stmt;
  41. MYSQL_BIND* params;
  42. MYSQL_BIND* columns;
  43. s_column_length* column_lengths;
  44. size_t max_params;
  45. size_t max_columns;
  46. bool bind_params;
  47. bool bind_columns;
  48. };
  49. ///////////////////////////////////////////////////////////////////////////////
  50. // Sql Handle
  51. ///////////////////////////////////////////////////////////////////////////////
  52. /// Allocates and initializes a new Sql handle.
  53. Sql* Sql_Malloc(void)
  54. {
  55. Sql* self;
  56. CREATE(self, Sql, 1);
  57. mysql_init(&self->handle);
  58. StringBuf_Init(&self->buf);
  59. self->lengths = NULL;
  60. self->result = NULL;
  61. self->keepalive = INVALID_TIMER;
  62. self->handle.reconnect = 1;
  63. return self;
  64. }
  65. static int Sql_P_Keepalive(Sql* self);
  66. /**
  67. * Establishes a connection to schema
  68. * @param self : sql handle
  69. * @param user : username to access
  70. * @param passwd : password
  71. * @param host : hostname
  72. * @param port : port
  73. * @param db : schema name
  74. * @return
  75. */
  76. int Sql_Connect(Sql* self, const char* user, const char* passwd, const char* host, uint16 port, const char* db)
  77. {
  78. if( self == NULL )
  79. return SQL_ERROR;
  80. StringBuf_Clear(&self->buf);
  81. if( !mysql_real_connect(&self->handle, host, user, passwd, db, (unsigned int)port, NULL/*unix_socket*/, 0/*clientflag*/) )
  82. {
  83. ShowSQL("%s\n", mysql_error(&self->handle));
  84. return SQL_ERROR;
  85. }
  86. self->keepalive = Sql_P_Keepalive(self);
  87. if( self->keepalive == INVALID_TIMER )
  88. {
  89. ShowSQL("Failed to establish keepalive for DB connection!\n");
  90. return SQL_ERROR;
  91. }
  92. return SQL_SUCCESS;
  93. }
  94. /// Retrieves the timeout of the connection.
  95. int Sql_GetTimeout(Sql* self, uint32* out_timeout)
  96. {
  97. if( self && out_timeout && SQL_SUCCESS == Sql_Query(self, "SHOW VARIABLES LIKE 'wait_timeout'") )
  98. {
  99. char* data;
  100. size_t len;
  101. if( SQL_SUCCESS == Sql_NextRow(self) &&
  102. SQL_SUCCESS == Sql_GetData(self, 1, &data, &len) )
  103. {
  104. *out_timeout = (uint32)strtoul(data, NULL, 10);
  105. Sql_FreeResult(self);
  106. return SQL_SUCCESS;
  107. }
  108. Sql_FreeResult(self);
  109. }
  110. return SQL_ERROR;
  111. }
  112. /// Retrieves the name of the columns of a table into out_buf, with the separator after each name.
  113. int Sql_GetColumnNames(Sql* self, const char* table, char* out_buf, size_t buf_len, char sep)
  114. {
  115. char* data;
  116. size_t len;
  117. size_t off = 0;
  118. if( self == NULL || SQL_ERROR == Sql_Query(self, "EXPLAIN `%s`", table) )
  119. return SQL_ERROR;
  120. out_buf[off] = '\0';
  121. while( SQL_SUCCESS == Sql_NextRow(self) && SQL_SUCCESS == Sql_GetData(self, 0, &data, &len) )
  122. {
  123. len = strnlen(data, len);
  124. if( off + len + 2 > buf_len )
  125. {
  126. ShowDebug("Sql_GetColumns: output buffer is too small\n");
  127. *out_buf = '\0';
  128. return SQL_ERROR;
  129. }
  130. memcpy(out_buf+off, data, len);
  131. off += len;
  132. out_buf[off++] = sep;
  133. }
  134. out_buf[off] = '\0';
  135. Sql_FreeResult(self);
  136. return SQL_SUCCESS;
  137. }
  138. /// Changes the encoding of the connection.
  139. int Sql_SetEncoding(Sql* self, const char* encoding)
  140. {
  141. if( self && Sql_Query(self, "SET NAMES %s", encoding) == 0 )
  142. return SQL_SUCCESS;
  143. return SQL_ERROR;
  144. }
  145. /// Pings the connection.
  146. int Sql_Ping(Sql* self)
  147. {
  148. if( self && mysql_ping(&self->handle) == 0 )
  149. return SQL_SUCCESS;
  150. return SQL_ERROR;
  151. }
  152. /// Wrapper function for Sql_Ping.
  153. ///
  154. /// @private
  155. static int Sql_P_KeepaliveTimer(int tid, unsigned int tick, int id, intptr_t data)
  156. {
  157. Sql* self = (Sql*)data;
  158. ShowInfo("Pinging SQL server to keep connection alive...\n");
  159. Sql_Ping(self);
  160. return 0;
  161. }
  162. /// Establishes keepalive (periodic ping) on the connection.
  163. ///
  164. /// @return the keepalive timer id, or INVALID_TIMER
  165. /// @private
  166. static int Sql_P_Keepalive(Sql* self)
  167. {
  168. uint32 timeout, ping_interval;
  169. // set a default value first
  170. timeout = 28800; // 8 hours
  171. // request the timeout value from the mysql server
  172. Sql_GetTimeout(self, &timeout);
  173. if( timeout < 60 )
  174. timeout = 60;
  175. // establish keepalive
  176. ping_interval = timeout - 30; // 30-second reserve
  177. //add_timer_func_list(Sql_P_KeepaliveTimer, "Sql_P_KeepaliveTimer");
  178. return add_timer_interval(gettick() + ping_interval*1000, Sql_P_KeepaliveTimer, 0, (intptr_t)self, ping_interval*1000);
  179. }
  180. /// Escapes a string.
  181. size_t Sql_EscapeString(Sql* self, char *out_to, const char *from)
  182. {
  183. if( self )
  184. return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)strlen(from));
  185. else
  186. return (size_t)mysql_escape_string(out_to, from, (unsigned long)strlen(from));
  187. }
  188. /// Escapes a string.
  189. size_t Sql_EscapeStringLen(Sql* self, char *out_to, const char *from, size_t from_len)
  190. {
  191. if( self )
  192. return (size_t)mysql_real_escape_string(&self->handle, out_to, from, (unsigned long)from_len);
  193. else
  194. return (size_t)mysql_escape_string(out_to, from, (unsigned long)from_len);
  195. }
  196. /// Executes a query.
  197. int Sql_Query(Sql* self, const char* query, ...)
  198. {
  199. int res;
  200. va_list args;
  201. va_start(args, query);
  202. res = Sql_QueryV(self, query, args);
  203. va_end(args);
  204. return res;
  205. }
  206. /// Executes a query.
  207. int Sql_QueryV(Sql* self, const char* query, va_list args)
  208. {
  209. if( self == NULL )
  210. return SQL_ERROR;
  211. Sql_FreeResult(self);
  212. StringBuf_Clear(&self->buf);
  213. StringBuf_Vprintf(&self->buf, query, args);
  214. if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
  215. {
  216. ShowSQL("DB error - %s\n", mysql_error(&self->handle));
  217. ra_mysql_error_handler(mysql_errno(&self->handle));
  218. return SQL_ERROR;
  219. }
  220. self->result = mysql_store_result(&self->handle);
  221. if( mysql_errno(&self->handle) != 0 )
  222. {
  223. ShowSQL("DB error - %s\n", mysql_error(&self->handle));
  224. ra_mysql_error_handler(mysql_errno(&self->handle));
  225. return SQL_ERROR;
  226. }
  227. return SQL_SUCCESS;
  228. }
  229. /// Executes a query.
  230. int Sql_QueryStr(Sql* self, const char* query)
  231. {
  232. if( self == NULL )
  233. return SQL_ERROR;
  234. Sql_FreeResult(self);
  235. StringBuf_Clear(&self->buf);
  236. StringBuf_AppendStr(&self->buf, query);
  237. if( mysql_real_query(&self->handle, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
  238. {
  239. ShowSQL("DB error - %s\n", mysql_error(&self->handle));
  240. ra_mysql_error_handler(mysql_errno(&self->handle));
  241. return SQL_ERROR;
  242. }
  243. self->result = mysql_store_result(&self->handle);
  244. if( mysql_errno(&self->handle) != 0 )
  245. {
  246. ShowSQL("DB error - %s\n", mysql_error(&self->handle));
  247. ra_mysql_error_handler(mysql_errno(&self->handle));
  248. return SQL_ERROR;
  249. }
  250. return SQL_SUCCESS;
  251. }
  252. /// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE query.
  253. uint64 Sql_LastInsertId(Sql* self)
  254. {
  255. if( self )
  256. return (uint64)mysql_insert_id(&self->handle);
  257. else
  258. return 0;
  259. }
  260. /// Returns the number of columns in each row of the result.
  261. uint32 Sql_NumColumns(Sql* self)
  262. {
  263. if( self && self->result )
  264. return (uint32)mysql_num_fields(self->result);
  265. return 0;
  266. }
  267. /// Returns the number of rows in the result.
  268. uint64 Sql_NumRows(Sql* self)
  269. {
  270. if( self && self->result )
  271. return (uint64)mysql_num_rows(self->result);
  272. return 0;
  273. }
  274. /// Returns the number of rows affected by the last query
  275. uint64 Sql_NumRowsAffected(Sql* self)
  276. {
  277. if( self )
  278. return (uint64)mysql_affected_rows(&self->handle);
  279. return 0;
  280. }
  281. /// Fetches the next row.
  282. int Sql_NextRow(Sql* self)
  283. {
  284. if( self && self->result )
  285. {
  286. self->row = mysql_fetch_row(self->result);
  287. if( self->row )
  288. {
  289. self->lengths = mysql_fetch_lengths(self->result);
  290. return SQL_SUCCESS;
  291. }
  292. self->lengths = NULL;
  293. if( mysql_errno(&self->handle) == 0 )
  294. return SQL_NO_DATA;
  295. }
  296. return SQL_ERROR;
  297. }
  298. /// Gets the data of a column.
  299. int Sql_GetData(Sql* self, size_t col, char** out_buf, size_t* out_len)
  300. {
  301. if( self && self->row )
  302. {
  303. if( col < Sql_NumColumns(self) )
  304. {
  305. if( out_buf ) *out_buf = self->row[col];
  306. if( out_len ) *out_len = (size_t)self->lengths[col];
  307. }
  308. else
  309. {// out of range - ignore
  310. if( out_buf ) *out_buf = NULL;
  311. if( out_len ) *out_len = 0;
  312. }
  313. return SQL_SUCCESS;
  314. }
  315. return SQL_ERROR;
  316. }
  317. /// Frees the result of the query.
  318. void Sql_FreeResult(Sql* self)
  319. {
  320. if( self && self->result )
  321. {
  322. mysql_free_result(self->result);
  323. self->result = NULL;
  324. self->row = NULL;
  325. self->lengths = NULL;
  326. }
  327. }
  328. /// Shows debug information (last query).
  329. void Sql_ShowDebug_(Sql* self, const char* debug_file, const unsigned long debug_line)
  330. {
  331. if( self == NULL )
  332. ShowDebug("at %s:%lu - self is NULL\n", debug_file, debug_line);
  333. else if( StringBuf_Length(&self->buf) > 0 )
  334. ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf));
  335. else
  336. ShowDebug("at %s:%lu\n", debug_file, debug_line);
  337. }
  338. /// Frees a Sql handle returned by Sql_Malloc.
  339. void Sql_Free(Sql* self)
  340. {
  341. if( self )
  342. {
  343. Sql_FreeResult(self);
  344. StringBuf_Destroy(&self->buf);
  345. if( self->keepalive != INVALID_TIMER ) delete_timer(self->keepalive, Sql_P_KeepaliveTimer);
  346. aFree(self);
  347. }
  348. }
  349. ///////////////////////////////////////////////////////////////////////////////
  350. // Prepared Statements
  351. ///////////////////////////////////////////////////////////////////////////////
  352. /// Returns the mysql integer type for the target size.
  353. ///
  354. /// @private
  355. static enum enum_field_types Sql_P_SizeToMysqlIntType(int sz)
  356. {
  357. switch( sz )
  358. {
  359. case 1: return MYSQL_TYPE_TINY;
  360. case 2: return MYSQL_TYPE_SHORT;
  361. case 4: return MYSQL_TYPE_LONG;
  362. case 8: return MYSQL_TYPE_LONGLONG;
  363. default:
  364. ShowDebug("SizeToMysqlIntType: unsupported size (%d)\n", sz);
  365. return MYSQL_TYPE_NULL;
  366. }
  367. }
  368. /// Binds a parameter/result.
  369. ///
  370. /// @private
  371. static int Sql_P_BindSqlDataType(MYSQL_BIND* bind, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, unsigned long* out_length, int8* out_is_null)
  372. {
  373. memset(bind, 0, sizeof(MYSQL_BIND));
  374. switch( buffer_type )
  375. {
  376. case SQLDT_NULL: bind->buffer_type = MYSQL_TYPE_NULL;
  377. buffer_len = 0;// FIXME length = ? [FlavioJS]
  378. break;
  379. // fixed size
  380. case SQLDT_UINT8: bind->is_unsigned = 1;
  381. case SQLDT_INT8: bind->buffer_type = MYSQL_TYPE_TINY;
  382. buffer_len = 1;
  383. break;
  384. case SQLDT_UINT16: bind->is_unsigned = 1;
  385. case SQLDT_INT16: bind->buffer_type = MYSQL_TYPE_SHORT;
  386. buffer_len = 2;
  387. break;
  388. case SQLDT_UINT32: bind->is_unsigned = 1;
  389. case SQLDT_INT32: bind->buffer_type = MYSQL_TYPE_LONG;
  390. buffer_len = 4;
  391. break;
  392. case SQLDT_UINT64: bind->is_unsigned = 1;
  393. case SQLDT_INT64: bind->buffer_type = MYSQL_TYPE_LONGLONG;
  394. buffer_len = 8;
  395. break;
  396. // platform dependent size
  397. case SQLDT_UCHAR: bind->is_unsigned = 1;
  398. case SQLDT_CHAR: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(char));
  399. buffer_len = sizeof(char);
  400. break;
  401. case SQLDT_USHORT: bind->is_unsigned = 1;
  402. case SQLDT_SHORT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(short));
  403. buffer_len = sizeof(short);
  404. break;
  405. case SQLDT_UINT: bind->is_unsigned = 1;
  406. case SQLDT_INT: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(int));
  407. buffer_len = sizeof(int);
  408. break;
  409. case SQLDT_ULONG: bind->is_unsigned = 1;
  410. case SQLDT_LONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(long));
  411. buffer_len = sizeof(long);
  412. break;
  413. case SQLDT_ULONGLONG: bind->is_unsigned = 1;
  414. case SQLDT_LONGLONG: bind->buffer_type = Sql_P_SizeToMysqlIntType(sizeof(int64));
  415. buffer_len = sizeof(int64);
  416. break;
  417. // floating point
  418. case SQLDT_FLOAT: bind->buffer_type = MYSQL_TYPE_FLOAT;
  419. buffer_len = 4;
  420. break;
  421. case SQLDT_DOUBLE: bind->buffer_type = MYSQL_TYPE_DOUBLE;
  422. buffer_len = 8;
  423. break;
  424. // other
  425. case SQLDT_STRING:
  426. case SQLDT_ENUM: bind->buffer_type = MYSQL_TYPE_STRING;
  427. break;
  428. case SQLDT_BLOB: bind->buffer_type = MYSQL_TYPE_BLOB;
  429. break;
  430. default:
  431. ShowDebug("Sql_P_BindSqlDataType: unsupported buffer type (%d)\n", buffer_type);
  432. return SQL_ERROR;
  433. }
  434. bind->buffer = buffer;
  435. bind->buffer_length = (unsigned long)buffer_len;
  436. bind->length = out_length;
  437. bind->is_null = (my_bool*)out_is_null;
  438. return SQL_SUCCESS;
  439. }
  440. /// Prints debug information about a field (type and length).
  441. ///
  442. /// @private
  443. static void Sql_P_ShowDebugMysqlFieldInfo(const char* prefix, enum enum_field_types type, int is_unsigned, unsigned long length, const char* length_postfix)
  444. {
  445. const char* sign = (is_unsigned ? "UNSIGNED " : "");
  446. const char* type_string;
  447. switch( type )
  448. {
  449. default:
  450. ShowDebug("%stype=%s%u, length=%d\n", prefix, sign, type, length);
  451. return;
  452. #define SHOW_DEBUG_OF(x) case x: type_string = #x; break
  453. SHOW_DEBUG_OF(MYSQL_TYPE_TINY);
  454. SHOW_DEBUG_OF(MYSQL_TYPE_SHORT);
  455. SHOW_DEBUG_OF(MYSQL_TYPE_LONG);
  456. SHOW_DEBUG_OF(MYSQL_TYPE_INT24);
  457. SHOW_DEBUG_OF(MYSQL_TYPE_LONGLONG);
  458. SHOW_DEBUG_OF(MYSQL_TYPE_DECIMAL);
  459. SHOW_DEBUG_OF(MYSQL_TYPE_FLOAT);
  460. SHOW_DEBUG_OF(MYSQL_TYPE_DOUBLE);
  461. SHOW_DEBUG_OF(MYSQL_TYPE_TIMESTAMP);
  462. SHOW_DEBUG_OF(MYSQL_TYPE_DATE);
  463. SHOW_DEBUG_OF(MYSQL_TYPE_TIME);
  464. SHOW_DEBUG_OF(MYSQL_TYPE_DATETIME);
  465. SHOW_DEBUG_OF(MYSQL_TYPE_YEAR);
  466. SHOW_DEBUG_OF(MYSQL_TYPE_STRING);
  467. SHOW_DEBUG_OF(MYSQL_TYPE_VAR_STRING);
  468. SHOW_DEBUG_OF(MYSQL_TYPE_BLOB);
  469. SHOW_DEBUG_OF(MYSQL_TYPE_SET);
  470. SHOW_DEBUG_OF(MYSQL_TYPE_ENUM);
  471. SHOW_DEBUG_OF(MYSQL_TYPE_NULL);
  472. #undef SHOW_DEBUG_TYPE_OF
  473. }
  474. ShowDebug("%stype=%s%s, length=%d%s\n", prefix, sign, type_string, length, length_postfix);
  475. }
  476. /// Reports debug information about a truncated column.
  477. ///
  478. /// @private
  479. static void SqlStmt_P_ShowDebugTruncatedColumn(SqlStmt* self, size_t i)
  480. {
  481. MYSQL_RES* meta;
  482. MYSQL_FIELD* field;
  483. MYSQL_BIND* column;
  484. meta = mysql_stmt_result_metadata(self->stmt);
  485. field = mysql_fetch_field_direct(meta, (unsigned int)i);
  486. ShowSQL("DB error - data of field '%s' was truncated.\n", field->name);
  487. ShowDebug("column - %lu\n", (unsigned long)i);
  488. Sql_P_ShowDebugMysqlFieldInfo("data - ", field->type, field->flags&UNSIGNED_FLAG, self->column_lengths[i].length, "");
  489. column = &self->columns[i];
  490. if( column->buffer_type == MYSQL_TYPE_STRING )
  491. Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "+1(nul-terminator)");
  492. else
  493. Sql_P_ShowDebugMysqlFieldInfo("buffer - ", column->buffer_type, column->is_unsigned, column->buffer_length, "");
  494. mysql_free_result(meta);
  495. }
  496. /// Allocates and initializes a new SqlStmt handle.
  497. SqlStmt* SqlStmt_Malloc(Sql* sql)
  498. {
  499. SqlStmt* self;
  500. MYSQL_STMT* stmt;
  501. if( sql == NULL )
  502. return NULL;
  503. stmt = mysql_stmt_init(&sql->handle);
  504. if( stmt == NULL )
  505. {
  506. ShowSQL("DB error - %s\n", mysql_error(&sql->handle));
  507. return NULL;
  508. }
  509. CREATE(self, SqlStmt, 1);
  510. StringBuf_Init(&self->buf);
  511. self->stmt = stmt;
  512. self->params = NULL;
  513. self->columns = NULL;
  514. self->column_lengths = NULL;
  515. self->max_params = 0;
  516. self->max_columns = 0;
  517. self->bind_params = false;
  518. self->bind_columns = false;
  519. return self;
  520. }
  521. /// Prepares the statement.
  522. int SqlStmt_Prepare(SqlStmt* self, const char* query, ...)
  523. {
  524. int res;
  525. va_list args;
  526. va_start(args, query);
  527. res = SqlStmt_PrepareV(self, query, args);
  528. va_end(args);
  529. return res;
  530. }
  531. /// Prepares the statement.
  532. int SqlStmt_PrepareV(SqlStmt* self, const char* query, va_list args)
  533. {
  534. if( self == NULL )
  535. return SQL_ERROR;
  536. SqlStmt_FreeResult(self);
  537. StringBuf_Clear(&self->buf);
  538. StringBuf_Vprintf(&self->buf, query, args);
  539. if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
  540. {
  541. ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
  542. ra_mysql_error_handler(mysql_stmt_errno(self->stmt));
  543. return SQL_ERROR;
  544. }
  545. self->bind_params = false;
  546. return SQL_SUCCESS;
  547. }
  548. /// Prepares the statement.
  549. int SqlStmt_PrepareStr(SqlStmt* self, const char* query)
  550. {
  551. if( self == NULL )
  552. return SQL_ERROR;
  553. SqlStmt_FreeResult(self);
  554. StringBuf_Clear(&self->buf);
  555. StringBuf_AppendStr(&self->buf, query);
  556. if( mysql_stmt_prepare(self->stmt, StringBuf_Value(&self->buf), (unsigned long)StringBuf_Length(&self->buf)) )
  557. {
  558. ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
  559. ra_mysql_error_handler(mysql_stmt_errno(self->stmt));
  560. return SQL_ERROR;
  561. }
  562. self->bind_params = false;
  563. return SQL_SUCCESS;
  564. }
  565. /// Returns the number of parameters in the prepared statement.
  566. size_t SqlStmt_NumParams(SqlStmt* self)
  567. {
  568. if( self )
  569. return (size_t)mysql_stmt_param_count(self->stmt);
  570. else
  571. return 0;
  572. }
  573. /// Binds a parameter to a buffer.
  574. int SqlStmt_BindParam(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len)
  575. {
  576. if( self == NULL )
  577. return SQL_ERROR;
  578. if( !self->bind_params )
  579. {// initialize the bindings
  580. size_t i;
  581. size_t count;
  582. count = SqlStmt_NumParams(self);
  583. if( self->max_params < count )
  584. {
  585. self->max_params = count;
  586. RECREATE(self->params, MYSQL_BIND, count);
  587. }
  588. memset(self->params, 0, count*sizeof(MYSQL_BIND));
  589. for( i = 0; i < count; ++i )
  590. self->params[i].buffer_type = MYSQL_TYPE_NULL;
  591. self->bind_params = true;
  592. }
  593. if( idx < self->max_params )
  594. return Sql_P_BindSqlDataType(self->params+idx, buffer_type, buffer, buffer_len, NULL, NULL);
  595. else
  596. return SQL_SUCCESS;// out of range - ignore
  597. }
  598. /// Executes the prepared statement.
  599. int SqlStmt_Execute(SqlStmt* self)
  600. {
  601. if( self == NULL )
  602. return SQL_ERROR;
  603. SqlStmt_FreeResult(self);
  604. if( (self->bind_params && mysql_stmt_bind_param(self->stmt, self->params)) ||
  605. mysql_stmt_execute(self->stmt) )
  606. {
  607. ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
  608. ra_mysql_error_handler(mysql_stmt_errno(self->stmt));
  609. return SQL_ERROR;
  610. }
  611. self->bind_columns = false;
  612. if( mysql_stmt_store_result(self->stmt) )// store all the data
  613. {
  614. ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
  615. ra_mysql_error_handler(mysql_stmt_errno(self->stmt));
  616. return SQL_ERROR;
  617. }
  618. return SQL_SUCCESS;
  619. }
  620. /// Returns the number of the AUTO_INCREMENT column of the last INSERT/UPDATE statement.
  621. uint64 SqlStmt_LastInsertId(SqlStmt* self)
  622. {
  623. if( self )
  624. return (uint64)mysql_stmt_insert_id(self->stmt);
  625. else
  626. return 0;
  627. }
  628. /// Returns the number of columns in each row of the result.
  629. size_t SqlStmt_NumColumns(SqlStmt* self)
  630. {
  631. if( self )
  632. return (size_t)mysql_stmt_field_count(self->stmt);
  633. else
  634. return 0;
  635. }
  636. /// Binds the result of a column to a buffer.
  637. int SqlStmt_BindColumn(SqlStmt* self, size_t idx, enum SqlDataType buffer_type, void* buffer, size_t buffer_len, uint32* out_length, int8* out_is_null)
  638. {
  639. if( self == NULL )
  640. return SQL_ERROR;
  641. if( buffer_type == SQLDT_STRING || buffer_type == SQLDT_ENUM )
  642. {
  643. if( buffer_len < 1 )
  644. {
  645. ShowDebug("SqlStmt_BindColumn: buffer_len(%d) is too small, no room for the nul-terminator\n", buffer_len);
  646. return SQL_ERROR;
  647. }
  648. --buffer_len;// nul-terminator
  649. }
  650. if( !self->bind_columns )
  651. {// initialize the bindings
  652. size_t i;
  653. size_t cols;
  654. cols = SqlStmt_NumColumns(self);
  655. if( self->max_columns < cols )
  656. {
  657. self->max_columns = cols;
  658. RECREATE(self->columns, MYSQL_BIND, cols);
  659. RECREATE(self->column_lengths, s_column_length, cols);
  660. }
  661. memset(self->columns, 0, cols*sizeof(MYSQL_BIND));
  662. memset(self->column_lengths, 0, cols*sizeof(s_column_length));
  663. for( i = 0; i < cols; ++i )
  664. self->columns[i].buffer_type = MYSQL_TYPE_NULL;
  665. self->bind_columns = true;
  666. }
  667. if( idx < self->max_columns )
  668. {
  669. self->column_lengths[idx].out_length = out_length;
  670. return Sql_P_BindSqlDataType(self->columns+idx, buffer_type, buffer, buffer_len, &self->column_lengths[idx].length, out_is_null);
  671. }
  672. else
  673. {
  674. return SQL_SUCCESS;// out of range - ignore
  675. }
  676. }
  677. /// Returns the number of rows in the result.
  678. uint64 SqlStmt_NumRows(SqlStmt* self)
  679. {
  680. if( self )
  681. return (uint64)mysql_stmt_num_rows(self->stmt);
  682. else
  683. return 0;
  684. }
  685. /// Fetches the next row.
  686. int SqlStmt_NextRow(SqlStmt* self)
  687. {
  688. int err;
  689. size_t i;
  690. size_t cols;
  691. if( self == NULL )
  692. return SQL_ERROR;
  693. // bind columns
  694. if( self->bind_columns && mysql_stmt_bind_result(self->stmt, self->columns) )
  695. err = 1;// error binding columns
  696. else
  697. err = mysql_stmt_fetch(self->stmt);// fetch row
  698. // check for errors
  699. if( err == MYSQL_NO_DATA )
  700. return SQL_NO_DATA;
  701. #if defined(MYSQL_DATA_TRUNCATED)
  702. // MySQL 5.0/5.1 defines and returns MYSQL_DATA_TRUNCATED [FlavioJS]
  703. if( err == MYSQL_DATA_TRUNCATED )
  704. {
  705. my_bool truncated;
  706. if( !self->bind_columns )
  707. {
  708. ShowSQL("DB error - data truncated (unknown source, columns are not bound)\n");
  709. return SQL_ERROR;
  710. }
  711. // find truncated column
  712. cols = SqlStmt_NumColumns(self);
  713. for( i = 0; i < cols; ++i )
  714. {
  715. MYSQL_BIND* column = &self->columns[i];
  716. column->error = &truncated;
  717. mysql_stmt_fetch_column(self->stmt, column, (unsigned int)i, 0);
  718. column->error = NULL;
  719. if( truncated )
  720. {// report truncated column
  721. SqlStmt_P_ShowDebugTruncatedColumn(self, i);
  722. return SQL_ERROR;
  723. }
  724. }
  725. ShowSQL("DB error - data truncated (unknown source)\n");
  726. return SQL_ERROR;
  727. }
  728. #endif
  729. if( err )
  730. {
  731. ShowSQL("DB error - %s\n", mysql_stmt_error(self->stmt));
  732. ra_mysql_error_handler(mysql_stmt_errno(self->stmt));
  733. return SQL_ERROR;
  734. }
  735. // propagate column lengths and clear unused parts of string/enum/blob buffers
  736. cols = SqlStmt_NumColumns(self);
  737. for( i = 0; i < cols; ++i )
  738. {
  739. unsigned long length = self->column_lengths[i].length;
  740. MYSQL_BIND* column = &self->columns[i];
  741. #if !defined(MYSQL_DATA_TRUNCATED)
  742. // MySQL 4.1/(below?) returns success even if data is truncated, so we test truncation manually [FlavioJS]
  743. if( column->buffer_length < length )
  744. {// report truncated column
  745. if( column->buffer_type == MYSQL_TYPE_STRING || column->buffer_type == MYSQL_TYPE_BLOB )
  746. {// string/enum/blob column
  747. SqlStmt_P_ShowDebugTruncatedColumn(self, i);
  748. return SQL_ERROR;
  749. }
  750. // FIXME numeric types and null [FlavioJS]
  751. }
  752. #endif
  753. if( self->column_lengths[i].out_length )
  754. *self->column_lengths[i].out_length = (uint32)length;
  755. if( column->buffer_type == MYSQL_TYPE_STRING )
  756. {// clear unused part of the string/enum buffer (and nul-terminate)
  757. memset((char*)column->buffer + length, 0, column->buffer_length - length + 1);
  758. }
  759. else if( column->buffer_type == MYSQL_TYPE_BLOB && length < column->buffer_length )
  760. {// clear unused part of the blob buffer
  761. memset((char*)column->buffer + length, 0, column->buffer_length - length);
  762. }
  763. }
  764. return SQL_SUCCESS;
  765. }
  766. /// Frees the result of the statement execution.
  767. void SqlStmt_FreeResult(SqlStmt* self)
  768. {
  769. if( self )
  770. mysql_stmt_free_result(self->stmt);
  771. }
  772. /// Shows debug information (with statement).
  773. void SqlStmt_ShowDebug_(SqlStmt* self, const char* debug_file, const unsigned long debug_line)
  774. {
  775. if( self == NULL )
  776. ShowDebug("at %s:%lu - self is NULL\n", debug_file, debug_line);
  777. else if( StringBuf_Length(&self->buf) > 0 )
  778. ShowDebug("at %s:%lu - %s\n", debug_file, debug_line, StringBuf_Value(&self->buf));
  779. else
  780. ShowDebug("at %s:%lu\n", debug_file, debug_line);
  781. }
  782. /// Frees a SqlStmt returned by SqlStmt_Malloc.
  783. void SqlStmt_Free(SqlStmt* self)
  784. {
  785. if( self )
  786. {
  787. SqlStmt_FreeResult(self);
  788. StringBuf_Destroy(&self->buf);
  789. mysql_stmt_close(self->stmt);
  790. if( self->params )
  791. aFree(self->params);
  792. if( self->columns )
  793. {
  794. aFree(self->columns);
  795. aFree(self->column_lengths);
  796. }
  797. aFree(self);
  798. }
  799. }
  800. /// Receives MySQL error codes during runtime (not on first-time-connects).
  801. void ra_mysql_error_handler(unsigned int ecode) {
  802. switch( ecode ) {
  803. case 2003:// Can't connect to MySQL (this error only happens here when failing to reconnect)
  804. if( mysql_reconnect_type == 1 ) {
  805. static unsigned int retry = 1;
  806. if( ++retry > mysql_reconnect_count ) {
  807. ShowFatalError("MySQL has been unreachable for too long, %d reconnects were attempted. Shutting Down\n", retry);
  808. exit(EXIT_FAILURE);
  809. }
  810. }
  811. break;
  812. }
  813. }
  814. void Sql_inter_server_read(const char* cfgName, bool first) {
  815. char line[1024], w1[1024], w2[1024];
  816. FILE* fp;
  817. fp = fopen(cfgName, "r");
  818. if(fp == NULL) {
  819. if( first ) {
  820. ShowFatalError("File not found: %s\n", cfgName);
  821. exit(EXIT_FAILURE);
  822. } else
  823. ShowError("File not found: %s\n", cfgName);
  824. return;
  825. }
  826. while(fgets(line, sizeof(line), fp)) {
  827. int i = sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2);
  828. if(i != 2)
  829. continue;
  830. if(!strcmpi(w1,"mysql_reconnect_type")) {
  831. mysql_reconnect_type = atoi(w2);
  832. switch( mysql_reconnect_type ) {
  833. case 1:
  834. case 2:
  835. break;
  836. default:
  837. ShowError("%s::mysql_reconnect_type is set to %d which is not valid, defaulting to 1...\n", cfgName, mysql_reconnect_type);
  838. mysql_reconnect_type = 1;
  839. break;
  840. }
  841. } else if(!strcmpi(w1,"mysql_reconnect_count")) {
  842. mysql_reconnect_count = atoi(w2);
  843. if( mysql_reconnect_count < 1 )
  844. mysql_reconnect_count = 1;
  845. } else if(!strcmpi(w1,"import"))
  846. Sql_inter_server_read(w2,false);
  847. }
  848. fclose(fp);
  849. return;
  850. }
  851. void Sql_Init(void) {
  852. Sql_inter_server_read(SQL_CONF_NAME,true);
  853. }