sql.c 22 KB

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