sql.c 25 KB

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