sql.cpp 26 KB

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