my_cmd.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374
  1. /*
  2. * Copyright (C) 2001-2003 FhG Fokus
  3. * Copyright (C) 2006-2007 iptelorg GmbH
  4. *
  5. * This file is part of ser, a free SIP server.
  6. *
  7. * ser is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version
  11. *
  12. * For a license to use the ser software under conditions
  13. * other than those described here, or to purchase support for this
  14. * software, please contact iptel.org by e-mail at the following addresses:
  15. * [email protected]
  16. *
  17. * ser is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, write to the Free Software
  24. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  25. */
  26. /** @addtogroup mysql
  27. * @{
  28. */
  29. /* the following macro will break the compile on solaris */
  30. #if !defined (__SVR4) && !defined (__sun)
  31. #define _XOPEN_SOURCE 4 /* bsd */
  32. #endif
  33. #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */
  34. #define _SVID_SOURCE 1 /* timegm */
  35. #include "my_cmd.h"
  36. #include "my_con.h"
  37. #include "mysql_mod.h"
  38. #include "my_fld.h"
  39. #include "../../mem/mem.h"
  40. #include "../../str.h"
  41. #include "../../lib/srdb2/db_cmd.h"
  42. #include "../../ut.h"
  43. #include "../../dprint.h"
  44. #include <strings.h>
  45. #include <stdio.h>
  46. #include <time.h> /*strptime, XOPEN issue must be >=4 */
  47. #include <string.h>
  48. #include <mysql/errmsg.h>
  49. #include <mysql/mysqld_error.h>
  50. #define STR_BUF_SIZE 1024
  51. #ifdef MYSQL_FAKE_NULL
  52. #define FAKE_NULL_STRING "[~NULL~]"
  53. static str FAKE_NULL_STR = STR_STATIC_INIT(FAKE_NULL_STRING);
  54. /* avoid warning: this decimal constant is unsigned only in ISO C90 :-) */
  55. #define FAKE_NULL_INT (-2147483647 - 1)
  56. #endif
  57. enum {
  58. STR_DELETE,
  59. STR_INSERT,
  60. STR_UPDATE,
  61. STR_SELECT,
  62. STR_REPLACE,
  63. STR_SET,
  64. STR_WHERE,
  65. STR_IS,
  66. STR_AND,
  67. STR_OR,
  68. STR_ESC,
  69. STR_OP_EQ,
  70. STR_OP_NE,
  71. STR_OP_LT,
  72. STR_OP_GT,
  73. STR_OP_LEQ,
  74. STR_OP_GEQ,
  75. STR_VALUES,
  76. STR_FROM
  77. };
  78. static str strings[] = {
  79. STR_STATIC_INIT("delete from "),
  80. STR_STATIC_INIT("insert into "),
  81. STR_STATIC_INIT("update "),
  82. STR_STATIC_INIT("select "),
  83. STR_STATIC_INIT("replace "),
  84. STR_STATIC_INIT(" set "),
  85. STR_STATIC_INIT(" where "),
  86. STR_STATIC_INIT(" is "),
  87. STR_STATIC_INIT(" and "),
  88. STR_STATIC_INIT(" or "),
  89. STR_STATIC_INIT("?"),
  90. STR_STATIC_INIT("="),
  91. STR_STATIC_INIT("!="),
  92. STR_STATIC_INIT("<"),
  93. STR_STATIC_INIT(">"),
  94. STR_STATIC_INIT("<="),
  95. STR_STATIC_INIT(">="),
  96. STR_STATIC_INIT(") values ("),
  97. STR_STATIC_INIT(" from ")
  98. };
  99. #define APPEND_STR(p, str) do { \
  100. memcpy((p), (str).s, (str).len); \
  101. (p) += (str).len; \
  102. } while(0)
  103. #define APPEND_CSTR(p, cstr) do { \
  104. int _len = strlen(cstr); \
  105. memcpy((p), (cstr), _len); \
  106. (p) += _len; \
  107. } while(0)
  108. static int upload_cmd(db_cmd_t* cmd);
  109. static void my_cmd_free(db_cmd_t* cmd, struct my_cmd* payload)
  110. {
  111. db_drv_free(&payload->gen);
  112. if (payload->sql_cmd.s) pkg_free(payload->sql_cmd.s);
  113. if (payload->st) mysql_stmt_close(payload->st);
  114. pkg_free(payload);
  115. }
  116. /** Builds a DELETE SQL statement.The function builds DELETE statement where
  117. * cmd->match specify WHERE clause.
  118. * @param sql_cmd SQL statement as a result of this function
  119. * @param cmd input for statement creation
  120. * @return -1 on error, 0 on success
  121. */
  122. static int build_delete_cmd(str* sql_cmd, db_cmd_t* cmd)
  123. {
  124. db_fld_t* fld;
  125. int i;
  126. char* p;
  127. sql_cmd->len = strings[STR_DELETE].len;
  128. sql_cmd->len += cmd->table.len;
  129. if (!DB_FLD_EMPTY(cmd->match)) {
  130. sql_cmd->len += strings[STR_WHERE].len;
  131. for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
  132. sql_cmd->len += strlen(fld[i].name);
  133. switch(fld[i].op) {
  134. case DB_EQ: sql_cmd->len += strings[STR_OP_EQ].len; break;
  135. case DB_NE: sql_cmd->len += strings[STR_OP_NE].len; break;
  136. case DB_LT: sql_cmd->len += strings[STR_OP_LT].len; break;
  137. case DB_GT: sql_cmd->len += strings[STR_OP_GT].len; break;
  138. case DB_LEQ: sql_cmd->len += strings[STR_OP_LEQ].len; break;
  139. case DB_GEQ: sql_cmd->len += strings[STR_OP_GEQ].len; break;
  140. default:
  141. ERR("mysql: Unsupported db_fld operator %d\n", fld[i].op);
  142. return -1;
  143. }
  144. sql_cmd->len += strings[STR_ESC].len;
  145. if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += strings[STR_AND].len;
  146. }
  147. }
  148. sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
  149. if (sql_cmd->s == NULL) {
  150. ERR("mysql: No memory left\n");
  151. return -1;
  152. }
  153. p = sql_cmd->s;
  154. APPEND_STR(p, strings[STR_DELETE]);
  155. APPEND_STR(p, cmd->table);
  156. if (!DB_FLD_EMPTY(cmd->match)) {
  157. APPEND_STR(p, strings[STR_WHERE]);
  158. for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
  159. APPEND_CSTR(p, fld[i].name);
  160. switch(fld[i].op) {
  161. case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
  162. case DB_NE: APPEND_STR(p, strings[STR_OP_NE]); break;
  163. case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
  164. case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
  165. case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
  166. case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
  167. }
  168. APPEND_STR(p, strings[STR_ESC]);
  169. if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
  170. }
  171. }
  172. *p = '\0';
  173. return 0;
  174. }
  175. /**
  176. * Builds SELECT statement where cmd->values specify column names
  177. * and cmd->match specify WHERE clause.
  178. * @param sql_cmd SQL statement as a result of this function
  179. * @param cmd input for statement creation
  180. */
  181. static int build_select_cmd(str* sql_cmd, db_cmd_t* cmd)
  182. {
  183. db_fld_t* fld;
  184. int i;
  185. char* p;
  186. sql_cmd->len = strings[STR_SELECT].len;
  187. if (DB_FLD_EMPTY(cmd->result)) {
  188. sql_cmd->len += 1; /* "*" */
  189. } else {
  190. for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
  191. sql_cmd->len += strlen(fld[i].name);
  192. if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += 1; /* , */
  193. }
  194. }
  195. sql_cmd->len += strings[STR_FROM].len;
  196. sql_cmd->len += cmd->table.len;
  197. if (!DB_FLD_EMPTY(cmd->match)) {
  198. sql_cmd->len += strings[STR_WHERE].len;
  199. for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
  200. sql_cmd->len += strlen(fld[i].name);
  201. switch(fld[i].op) {
  202. case DB_EQ: sql_cmd->len += strings[STR_OP_EQ].len; break;
  203. case DB_NE: sql_cmd->len += strings[STR_OP_NE].len; break;
  204. case DB_LT: sql_cmd->len += strings[STR_OP_LT].len; break;
  205. case DB_GT: sql_cmd->len += strings[STR_OP_GT].len; break;
  206. case DB_LEQ: sql_cmd->len += strings[STR_OP_LEQ].len; break;
  207. case DB_GEQ: sql_cmd->len += strings[STR_OP_GEQ].len; break;
  208. default:
  209. ERR("mysql: Unsupported db_fld operator %d\n", fld[i].op);
  210. return -1;
  211. }
  212. sql_cmd->len += strings[STR_ESC].len;
  213. if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += strings[STR_AND].len;
  214. }
  215. }
  216. sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
  217. if (sql_cmd->s == NULL) {
  218. ERR("mysql: No memory left\n");
  219. return -1;
  220. }
  221. p = sql_cmd->s;
  222. APPEND_STR(p, strings[STR_SELECT]);
  223. if (DB_FLD_EMPTY(cmd->result)) {
  224. *p++ = '*';
  225. } else {
  226. for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
  227. APPEND_CSTR(p, fld[i].name);
  228. if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
  229. }
  230. }
  231. APPEND_STR(p, strings[STR_FROM]);
  232. APPEND_STR(p, cmd->table);
  233. if (!DB_FLD_EMPTY(cmd->match)) {
  234. APPEND_STR(p, strings[STR_WHERE]);
  235. for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
  236. APPEND_CSTR(p, fld[i].name);
  237. switch(fld[i].op) {
  238. case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
  239. case DB_NE: APPEND_STR(p, strings[STR_OP_NE]); break;
  240. case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
  241. case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
  242. case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
  243. case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
  244. }
  245. APPEND_STR(p, strings[STR_ESC]);
  246. if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
  247. }
  248. }
  249. *p = '\0';
  250. return 0;
  251. }
  252. /**
  253. * Builds REPLACE statement where cmd->values specify column names.
  254. * @param sql_cmd SQL statement as a result of this function
  255. * @param cmd input for statement creation
  256. */
  257. static int build_replace_cmd(str* sql_cmd, db_cmd_t* cmd)
  258. {
  259. db_fld_t* fld;
  260. int i;
  261. char* p;
  262. sql_cmd->len = strings[STR_REPLACE].len;
  263. sql_cmd->len += cmd->table.len;
  264. sql_cmd->len += 2; /* " (" */
  265. for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
  266. sql_cmd->len += strlen(fld[i].name);
  267. sql_cmd->len += strings[STR_ESC].len;
  268. if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += 2; /* , twice */
  269. }
  270. sql_cmd->len += strings[STR_VALUES].len;
  271. sql_cmd->len += 1; /* ) */
  272. sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
  273. if (sql_cmd->s == NULL) {
  274. ERR("mysql: No memory left\n");
  275. return -1;
  276. }
  277. p = sql_cmd->s;
  278. APPEND_STR(p, strings[STR_REPLACE]);
  279. APPEND_STR(p, cmd->table);
  280. *p++ = ' ';
  281. *p++ = '(';
  282. for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
  283. APPEND_CSTR(p, fld[i].name);
  284. if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
  285. }
  286. APPEND_STR(p, strings[STR_VALUES]);
  287. for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
  288. APPEND_STR(p, strings[STR_ESC]);
  289. if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
  290. }
  291. *p++ = ')';
  292. *p = '\0';
  293. return 0;
  294. }
  295. /**
  296. * Reallocatable string buffer.
  297. */
  298. struct string_buffer {
  299. char *s; /**< allocated memory itself */
  300. int len; /**< used memory */
  301. int size; /**< total size of allocated memory */
  302. int increment; /**< increment when realloc is necessary */
  303. };
  304. /**
  305. * Add new string into string buffer.
  306. * @param sb string buffer
  307. * @param nstr string to add
  308. * @return 0 if OK, -1 if failed
  309. */
  310. static inline int sb_add(struct string_buffer *sb, str *nstr)
  311. {
  312. int new_size = 0;
  313. int rsize = sb->len + nstr->len;
  314. int asize;
  315. char *newp;
  316. if ( rsize > sb->size ) {
  317. asize = rsize - sb->size;
  318. new_size = sb->size + (asize / sb->increment + (asize % sb->increment > 0)) * sb->increment;
  319. newp = pkg_malloc(new_size);
  320. if (!newp) {
  321. ERR("mysql: No memory left\n");
  322. return -1;
  323. }
  324. if (sb->s) {
  325. memcpy(newp, sb->s, sb->len);
  326. pkg_free(sb->s);
  327. }
  328. sb->s = newp;
  329. sb->size = new_size;
  330. }
  331. memcpy(sb->s + sb->len, nstr->s, nstr->len);
  332. sb->len += nstr->len;
  333. return 0;
  334. }
  335. /**
  336. * Set members of str variable.
  337. * Used for temporary str variables.
  338. */
  339. static inline str* set_str(str *str, const char *s)
  340. {
  341. str->s = (char *)s;
  342. str->len = strlen(s);
  343. return str;
  344. }
  345. /**
  346. * Builds UPDATE statement where cmd->valss specify column name-value pairs
  347. * and cmd->match specify WHERE clause.
  348. * @param sql_cmd SQL statement as a result of this function
  349. * @param cmd input for statement creation
  350. */
  351. static int build_update_cmd(str* sql_cmd, db_cmd_t* cmd)
  352. {
  353. struct string_buffer sql_buf = {.s = NULL, .len = 0, .size = 0, .increment = 128};
  354. db_fld_t* fld;
  355. int i;
  356. int rv = 0;
  357. str tmpstr;
  358. rv = sb_add(&sql_buf, &strings[STR_UPDATE]); /* "UPDATE " */
  359. rv |= sb_add(&sql_buf, &cmd->table); /* table name */
  360. rv |= sb_add(&sql_buf, &strings[STR_SET]); /* " SET " */
  361. /* column name-value pairs */
  362. for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
  363. rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
  364. rv |= sb_add(&sql_buf, set_str(&tmpstr, " = "));
  365. rv |= sb_add(&sql_buf, &strings[STR_ESC]);
  366. if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ", "));
  367. }
  368. if (rv) {
  369. goto err;
  370. }
  371. if (!DB_FLD_EMPTY(cmd->match)) {
  372. rv |= sb_add(&sql_buf, &strings[STR_WHERE]);
  373. for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
  374. rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
  375. switch(fld[i].op) {
  376. case DB_EQ: rv |= sb_add(&sql_buf, &strings[STR_OP_EQ]); break;
  377. case DB_NE: rv |= sb_add(&sql_buf, &strings[STR_OP_NE]); break;
  378. case DB_LT: rv |= sb_add(&sql_buf, &strings[STR_OP_LT]); break;
  379. case DB_GT: rv |= sb_add(&sql_buf, &strings[STR_OP_GT]); break;
  380. case DB_LEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_LEQ]); break;
  381. case DB_GEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_GEQ]); break;
  382. }
  383. rv |= sb_add(&sql_buf, &strings[STR_ESC]);
  384. if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, &strings[STR_AND]);
  385. }
  386. }
  387. rv |= sb_add(&sql_buf, set_str(&tmpstr, "\0"));
  388. if (rv) {
  389. goto err;
  390. }
  391. sql_cmd->s = sql_buf.s;
  392. sql_cmd->len = sql_buf.len;
  393. return 0;
  394. err:
  395. if (sql_buf.s) pkg_free(sql_buf.s);
  396. return -1;
  397. }
  398. static inline void update_field(MYSQL_BIND *param, db_fld_t* fld)
  399. {
  400. struct my_fld* fp; /* field payload */
  401. struct tm* t;
  402. fp = DB_GET_PAYLOAD(fld);
  403. #ifndef MYSQL_FAKE_NULL
  404. fp->is_null = fld->flags & DB_NULL;
  405. if (fp->is_null) return;
  406. #else
  407. if (fld->flags & DB_NULL) {
  408. switch(fld->type) {
  409. case DB_STR:
  410. case DB_CSTR:
  411. param->buffer = FAKE_NULL_STR.s;
  412. fp->length = FAKE_NULL_STR.len;
  413. break;
  414. case DB_INT:
  415. *(int*)param->buffer = FAKE_NULL_INT;
  416. break;
  417. case DB_BLOB:
  418. case DB_DATETIME:
  419. case DB_NONE:
  420. case DB_FLOAT:
  421. case DB_DOUBLE:
  422. case DB_BITMAP:
  423. /* we don't have fake null value for these types */
  424. fp->is_null = DB_NULL;
  425. break;
  426. }
  427. return;
  428. }
  429. #endif
  430. switch(fld->type) {
  431. case DB_STR:
  432. param->buffer = fld->v.lstr.s;
  433. fp->length = fld->v.lstr.len;
  434. break;
  435. case DB_BLOB:
  436. param->buffer = fld->v.blob.s;
  437. fp->length = fld->v.blob.len;
  438. break;
  439. case DB_CSTR:
  440. param->buffer = (char*)fld->v.cstr;
  441. fp->length = strlen(fld->v.cstr);
  442. break;
  443. case DB_DATETIME:
  444. t = gmtime(&fld->v.time);
  445. fp->time.second = t->tm_sec;
  446. fp->time.minute = t->tm_min;
  447. fp->time.hour = t->tm_hour;
  448. fp->time.day = t->tm_mday;
  449. fp->time.month = t->tm_mon + 1;
  450. fp->time.year = t->tm_year + 1900;
  451. break;
  452. case DB_NONE:
  453. case DB_INT:
  454. case DB_FLOAT:
  455. case DB_DOUBLE:
  456. case DB_BITMAP:
  457. /* No need to do anything for these types */
  458. break;
  459. }
  460. }
  461. /**
  462. * Update values of MySQL bound parameters with values from
  463. * the DB API.
  464. * @param cmd Command structure which contains pointers to MYSQL_STMT and parameters values
  465. * @see bind_mysql_params
  466. */
  467. static inline void set_mysql_params(db_cmd_t* cmd)
  468. {
  469. struct my_cmd* mcmd;
  470. int i;
  471. mcmd = DB_GET_PAYLOAD(cmd);
  472. /* FIXME: We are updating internals of the prepared statement here,
  473. * this is probably not nice but I could not find another way of
  474. * updating the pointer to the buffer without the need to run
  475. * mysql_stmt_bind_param again (which would be innefficient)
  476. */
  477. for(i = 0; i < cmd->vals_count; i++) {
  478. update_field(mcmd->st->params + i, cmd->vals + i);
  479. }
  480. for(i = 0; i < cmd->match_count; i++) {
  481. update_field(mcmd->st->params + cmd->vals_count + i, cmd->match + i);
  482. }
  483. }
  484. static inline int update_result(db_fld_t* result, MYSQL_STMT* st)
  485. {
  486. int i;
  487. struct my_fld* rp; /* Payload of the current field in result */
  488. struct tm t;
  489. /* Iterate through all the fields returned by MySQL and convert
  490. * them to DB API representation if necessary
  491. */
  492. for(i = 0; i < st->field_count; i++) {
  493. rp = DB_GET_PAYLOAD(result + i);
  494. if (rp->is_null) {
  495. result[i].flags |= DB_NULL;
  496. continue;
  497. } else {
  498. result[i].flags &= ~DB_NULL;
  499. }
  500. switch(result[i].type) {
  501. case DB_STR:
  502. result[i].v.lstr.len = rp->length;
  503. #ifdef MYSQL_FAKE_NULL
  504. if (STR_EQ(FAKE_NULL_STR,result[i].v.lstr)) {
  505. result[i].flags |= DB_NULL;
  506. }
  507. #endif
  508. break;
  509. case DB_BLOB:
  510. result[i].v.blob.len = rp->length;
  511. break;
  512. case DB_CSTR:
  513. if (rp->length < STR_BUF_SIZE) {
  514. result[i].v.cstr[rp->length] = '\0';
  515. } else {
  516. /* Truncated field but rp->length contains full size,
  517. * zero terminated the last byte in the buffer
  518. */
  519. result[i].v.cstr[STR_BUF_SIZE - 1] = '\0';
  520. }
  521. #ifdef MYSQL_FAKE_NULL
  522. if (strcmp(FAKE_NULL_STR.s,result[i].v.cstr)==0) {
  523. result[i].flags |= DB_NULL;
  524. }
  525. #endif
  526. break;
  527. case DB_DATETIME:
  528. memset(&t, '\0', sizeof(struct tm));
  529. t.tm_sec = rp->time.second;
  530. t.tm_min = rp->time.minute;
  531. t.tm_hour = rp->time.hour;
  532. t.tm_mday = rp->time.day;
  533. t.tm_mon = rp->time.month - 1;
  534. t.tm_year = rp->time.year - 1900;
  535. /* Daylight saving information got lost in the database
  536. * so let timegm to guess it. This eliminates the bug when
  537. * contacts reloaded from the database have different time
  538. * of expiration by one hour when daylight saving is used
  539. */
  540. t.tm_isdst = -1;
  541. #ifdef HAVE_TIMEGM
  542. result[i].v.time = timegm(&t);
  543. #else
  544. result[i].v.time = _timegm(&t);
  545. #endif /* HAVE_TIMEGM */
  546. break;
  547. case DB_INT:
  548. #ifdef MYSQL_FAKE_NULL
  549. if (FAKE_NULL_INT==result[i].v.int4) {
  550. result[i].flags |= DB_NULL;
  551. }
  552. break;
  553. #endif
  554. case DB_NONE:
  555. case DB_FLOAT:
  556. case DB_DOUBLE:
  557. case DB_BITMAP:
  558. /* No need to do anything for these types */
  559. break;
  560. }
  561. }
  562. return 0;
  563. }
  564. /**
  565. * This is the main command execution function. The function contains
  566. * all the necessary logic to detect reset or disconnected database
  567. * connections and uploads commands to the server if necessary.
  568. * @param cmd Command to be executed
  569. * @return 0 if OK, <0 on MySQL failure, >0 on DB API failure
  570. */
  571. static int exec_cmd_safe(db_cmd_t* cmd)
  572. {
  573. int i, err;
  574. db_con_t* con;
  575. struct my_cmd* mcmd;
  576. struct my_con* mcon;
  577. /* First things first: retrieve connection info
  578. * from the currently active connection and also
  579. * mysql payload from the database command
  580. */
  581. mcmd = DB_GET_PAYLOAD(cmd);
  582. con = cmd->ctx->con[db_payload_idx];
  583. mcon = DB_GET_PAYLOAD(con);
  584. for(i = 0; i <= my_retries; i++) {
  585. if ((mcon->flags & MY_CONNECTED) == 0) {
  586. /* The connection is disconnected, try to reconnect */
  587. if (my_con_connect(con)) {
  588. INFO("mysql: exec_cmd_safe failed to re-connect\n");
  589. continue;
  590. }
  591. }
  592. /* Next check the number of resets in the database connection, if this
  593. * number is higher than the number we keep in my_cmd structure in
  594. * last_reset variable then the connection was reset and we need to
  595. * upload the command again to the server before executing it, because
  596. * the server recycles all server side information upon disconnect.
  597. */
  598. if (mcon->resets > mcmd->last_reset) {
  599. INFO("mysql: Connection reset detected, uploading command to server\n");
  600. err = upload_cmd(cmd);
  601. if (err < 0) {
  602. INFO("mysql: Error while uploading command\n");
  603. continue;
  604. } else if (err > 0) {
  605. /* DB API error, this is a serious problem such as memory
  606. * allocation failure, bail out
  607. */
  608. return 1;
  609. }
  610. }
  611. set_mysql_params(cmd);
  612. err = mysql_stmt_execute(mcmd->st);
  613. if (err == 0) {
  614. /* The command was executed successfully, now fetch all data to
  615. * the client if it was requested by the user */
  616. if (mcmd->flags & MY_FETCH_ALL) {
  617. err = mysql_stmt_store_result(mcmd->st);
  618. if (err) {
  619. INFO("mysql: Error while fetching data to client.\n");
  620. goto error;
  621. }
  622. }
  623. return 0;
  624. }
  625. error:
  626. /* Command execution failed, log a message and try to reconnect */
  627. INFO("mysql: libmysql: %d, %s\n", mysql_stmt_errno(mcmd->st),
  628. mysql_stmt_error(mcmd->st));
  629. INFO("mysql: Error while executing command on server, trying to reconnect\n");
  630. my_con_disconnect(con);
  631. if (my_con_connect(con)) {
  632. INFO("mysql: Failed to reconnect server\n");
  633. } else {
  634. INFO("mysql: Successfully reconnected server\n");
  635. }
  636. }
  637. INFO("mysql: Failed to execute command, giving up\n");
  638. return -1;
  639. }
  640. int my_cmd_exec(db_res_t* res, db_cmd_t* cmd)
  641. {
  642. struct my_cmd* mcmd;
  643. mcmd = DB_GET_PAYLOAD(cmd);
  644. mcmd->next_flag = -1;
  645. return exec_cmd_safe(cmd);
  646. }
  647. /**
  648. * Set MYSQL_BIND item.
  649. * @param bind destination
  650. * @param fld source
  651. */
  652. static void set_field(MYSQL_BIND *bind, db_fld_t* fld)
  653. {
  654. struct my_fld* f;
  655. f = DB_GET_PAYLOAD(fld);
  656. bind->is_null = &f->is_null;
  657. /* We can do it for all the types here, mysql will ignore it
  658. * for fixed-size types such as MYSQL_TYPE_LONG
  659. */
  660. bind->length = &f->length;
  661. switch(fld->type) {
  662. case DB_INT:
  663. case DB_BITMAP:
  664. bind->buffer_type = MYSQL_TYPE_LONG;
  665. bind->buffer = &fld->v.int4;
  666. break;
  667. case DB_FLOAT:
  668. bind->buffer_type = MYSQL_TYPE_FLOAT;
  669. bind->buffer = &fld->v.flt;
  670. break;
  671. case DB_DOUBLE:
  672. bind->buffer_type = MYSQL_TYPE_DOUBLE;
  673. bind->buffer = &fld->v.dbl;
  674. break;
  675. case DB_DATETIME:
  676. bind->buffer_type = MYSQL_TYPE_DATETIME;
  677. bind->buffer = &f->time;
  678. break;
  679. case DB_STR:
  680. case DB_CSTR:
  681. bind->buffer_type = MYSQL_TYPE_VAR_STRING;
  682. bind->buffer = ""; /* Updated on runtime */
  683. break;
  684. case DB_BLOB:
  685. bind->buffer_type = MYSQL_TYPE_BLOB;
  686. bind->buffer = ""; /* Updated on runtime */
  687. break;
  688. case DB_NONE:
  689. /* Eliminates gcc warning */
  690. break;
  691. }
  692. }
  693. /**
  694. * Bind params, give real values into prepared statement.
  695. * Up to two sets of parameters are provided.
  696. * Both of them are used in UPDATE command, params1 as colspecs and values and
  697. * params2 as WHERE clause. In other cases one set could be enough because values
  698. * or match (WHERE clause) is needed.
  699. * @param st MySQL command statement
  700. * @param params1 first set of params
  701. * @param params2 second set of params
  702. * @return 0 if OK, <0 on MySQL error, >0 on DB API error
  703. * @see update_params
  704. */
  705. static int bind_mysql_params(MYSQL_STMT* st, db_fld_t* params1, db_fld_t* params2)
  706. {
  707. int my_idx, fld_idx;
  708. int count1, count2;
  709. MYSQL_BIND* my_params;
  710. int err = 0;
  711. /* Calculate the number of parameters */
  712. for(count1 = 0; !DB_FLD_EMPTY(params1) && !DB_FLD_LAST(params1[count1]); count1++);
  713. for(count2 = 0; !DB_FLD_EMPTY(params2) && !DB_FLD_LAST(params2[count2]); count2++);
  714. if (st->param_count != count1 + count2) {
  715. BUG("mysql: Number of parameters in SQL command does not match number of DB API parameters\n");
  716. return 1;
  717. }
  718. my_params = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * (count1 + count2));
  719. if (my_params == NULL) {
  720. ERR("mysql: No memory left\n");
  721. return -1;
  722. }
  723. memset(my_params, '\0', sizeof(MYSQL_BIND) * (count1 + count2));
  724. /* params1 */
  725. my_idx = 0;
  726. for (fld_idx = 0; fld_idx < count1; fld_idx++, my_idx++) {
  727. set_field(&my_params[my_idx], params1 + fld_idx);
  728. }
  729. /* params2 */
  730. for (fld_idx = 0; fld_idx < count2; fld_idx++, my_idx++) {
  731. set_field(&my_params[my_idx], params2 + fld_idx);
  732. }
  733. err = mysql_stmt_bind_param(st, my_params);
  734. if (err) {
  735. ERR("mysql: libmysqlclient: %d, %s\n",
  736. mysql_stmt_errno(st), mysql_stmt_error(st));
  737. goto error;
  738. }
  739. /* We do not need the array of MYSQL_BIND anymore, mysql_stmt_bind_param
  740. * creates a copy in the statement and we will update it there
  741. */
  742. pkg_free(my_params);
  743. return err;
  744. error:
  745. if (my_params) pkg_free(my_params);
  746. return err;
  747. }
  748. /*
  749. * FIXME: This function will only work if we have one db connection
  750. * in every context, otherwise it would initialize the result set
  751. * from the first connection in the context.
  752. */
  753. static int check_result(db_cmd_t* cmd, struct my_cmd* payload)
  754. {
  755. int i, n;
  756. MYSQL_FIELD *fld;
  757. MYSQL_RES *meta = NULL;
  758. meta = mysql_stmt_result_metadata(payload->st);
  759. if (meta == NULL) {
  760. /* No error means no result set to be checked */
  761. if (mysql_stmt_errno(payload->st) == 0) return 0;
  762. ERR("mysql: Error while getting metadata of SQL command: %d, %s\n",
  763. mysql_stmt_errno(payload->st), mysql_stmt_error(payload->st));
  764. return -1;
  765. }
  766. n = mysql_num_fields(meta);
  767. if (cmd->result == NULL) {
  768. /* The result set parameter of db_cmd function was empty, that
  769. * means the command is select * and we have to create the array
  770. * of result fields in the cmd structure manually.
  771. */
  772. cmd->result = db_fld(n + 1);
  773. cmd->result_count = n;
  774. for(i = 0; i < cmd->result_count; i++) {
  775. struct my_fld *f;
  776. if (my_fld(cmd->result + i, cmd->table.s) < 0) goto error;
  777. f = DB_GET_PAYLOAD(cmd->result + i);
  778. fld = mysql_fetch_field_direct(meta, i);
  779. f->name = pkg_malloc(strlen(fld->name)+1);
  780. if (f->name == NULL) {
  781. ERR("mysql: Out of private memory\n");
  782. goto error;
  783. }
  784. strcpy(f->name, fld->name);
  785. cmd->result[i].name = f->name;
  786. }
  787. } else {
  788. if (cmd->result_count != n) {
  789. BUG("mysql: Number of fields in MySQL result does not match number of parameters in DB API\n");
  790. goto error;
  791. }
  792. }
  793. /* Now iterate through all the columns in the result set and replace
  794. * any occurrence of DB_UNKNOWN type with the type of the column
  795. * retrieved from the database and if no column name was provided then
  796. * update it from the database as well.
  797. */
  798. for(i = 0; i < cmd->result_count; i++) {
  799. fld = mysql_fetch_field_direct(meta, i);
  800. if (cmd->result[i].type != DB_NONE) continue;
  801. switch(fld->type) {
  802. case MYSQL_TYPE_TINY:
  803. case MYSQL_TYPE_SHORT:
  804. case MYSQL_TYPE_INT24:
  805. case MYSQL_TYPE_LONG:
  806. cmd->result[i].type = DB_INT;
  807. break;
  808. case MYSQL_TYPE_FLOAT:
  809. cmd->result[i].type = DB_FLOAT;
  810. break;
  811. case MYSQL_TYPE_DOUBLE:
  812. cmd->result[i].type = DB_DOUBLE;
  813. break;
  814. case MYSQL_TYPE_TIMESTAMP:
  815. case MYSQL_TYPE_DATETIME:
  816. cmd->result[i].type = DB_DATETIME;
  817. break;
  818. case MYSQL_TYPE_STRING:
  819. case MYSQL_TYPE_VAR_STRING:
  820. cmd->result[i].type = DB_STR;
  821. break;
  822. default:
  823. ERR("mysql: Unsupported MySQL column type: %d, table: %s, column: %s\n",
  824. fld->type, cmd->table.s, fld->name);
  825. goto error;
  826. }
  827. }
  828. if (meta) mysql_free_result(meta);
  829. return 0;
  830. error:
  831. if (meta) mysql_free_result(meta);
  832. return 1;
  833. }
  834. /* FIXME: Add support for DB_NONE, in this case the function should determine
  835. * the type of the column in the database and set the field type appropriately.
  836. * This function must be called after check_result.
  837. */
  838. static int bind_result(MYSQL_STMT* st, db_fld_t* fld)
  839. {
  840. int i, n, err = 0;
  841. struct my_fld* f;
  842. MYSQL_BIND* result;
  843. /* Calculate the number of fields in the result */
  844. for(n = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[n]); n++);
  845. /* Return immediately if there are no fields in the result set */
  846. if (n == 0) return 0;
  847. result = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * n);
  848. if (result == NULL) {
  849. ERR("mysql: No memory left\n");
  850. return 1;
  851. }
  852. memset(result, '\0', sizeof(MYSQL_BIND) * n);
  853. for(i = 0; i < n; i++) {
  854. f = DB_GET_PAYLOAD(fld + i);
  855. result[i].is_null = &f->is_null;
  856. /* We can do it for all the types here, mysql will ignore it
  857. * for fixed-size types such as MYSQL_TYPE_LONG
  858. */
  859. result[i].length = &f->length;
  860. switch(fld[i].type) {
  861. case DB_INT:
  862. case DB_BITMAP:
  863. result[i].buffer_type = MYSQL_TYPE_LONG;
  864. result[i].buffer = &fld[i].v.int4;
  865. break;
  866. case DB_FLOAT:
  867. result[i].buffer_type = MYSQL_TYPE_FLOAT;
  868. result[i].buffer = &fld[i].v.flt;
  869. break;
  870. case DB_DOUBLE:
  871. result[i].buffer_type = MYSQL_TYPE_DOUBLE;
  872. result[i].buffer = &fld[i].v.dbl;
  873. break;
  874. case DB_DATETIME:
  875. result[i].buffer_type = MYSQL_TYPE_DATETIME;
  876. result[i].buffer = &f->time;
  877. break;
  878. case DB_STR:
  879. result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
  880. if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
  881. if (f->buf.s == NULL) {
  882. ERR("mysql: No memory left\n");
  883. err = 1;
  884. goto error;
  885. }
  886. result[i].buffer = f->buf.s;
  887. fld[i].v.lstr.s = f->buf.s;
  888. result[i].buffer_length = STR_BUF_SIZE - 1;
  889. break;
  890. case DB_CSTR:
  891. result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
  892. if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
  893. if (f->buf.s == NULL) {
  894. ERR("mysql: No memory left\n");
  895. err = 1;
  896. goto error;
  897. }
  898. result[i].buffer = f->buf.s;
  899. fld[i].v.cstr = f->buf.s;
  900. result[i].buffer_length = STR_BUF_SIZE - 1;
  901. break;
  902. case DB_BLOB:
  903. result[i].buffer_type = MYSQL_TYPE_BLOB;
  904. if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
  905. if (f->buf.s == NULL) {
  906. ERR("mysql: No memory left\n");
  907. err = 1;
  908. goto error;
  909. }
  910. result[i].buffer = f->buf.s;
  911. fld[i].v.blob.s = f->buf.s;
  912. result[i].buffer_length = STR_BUF_SIZE - 1;
  913. break;
  914. case DB_NONE:
  915. /* Eliminates gcc warning */
  916. break;
  917. }
  918. }
  919. err = mysql_stmt_bind_result(st, result);
  920. if (err) {
  921. ERR("mysql: Error while binding result: %s\n", mysql_stmt_error(st));
  922. goto error;
  923. }
  924. /* We do not need the array of MYSQL_BIND anymore, mysql_stmt_bind_param
  925. * creates a copy in the statement and we will update it there
  926. */
  927. if (result) pkg_free(result);
  928. return 0;
  929. error:
  930. if (result) pkg_free(result);
  931. return err;
  932. }
  933. /**
  934. * Upload database command to the server
  935. * @param cmd Command to be uploaded
  936. * @return 0 if OK, >0 on DB API errors, <0 on MySQL errors
  937. */
  938. static int upload_cmd(db_cmd_t* cmd)
  939. {
  940. struct my_cmd* res;
  941. struct my_con* mcon;
  942. int err = 0;
  943. res = DB_GET_PAYLOAD(cmd);
  944. /* FIXME: The function should take the connection as one of parameters */
  945. mcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  946. /* Do not upload the command if the connection is not connected */
  947. if ((mcon->flags & MY_CONNECTED) == 0) {
  948. err = 1;
  949. goto error;
  950. }
  951. /* If there is a previous pre-compiled statement, close it first */
  952. if (res->st) mysql_stmt_close(res->st);
  953. res->st = NULL;
  954. /* Create a new pre-compiled statement data structure */
  955. res->st = mysql_stmt_init(mcon->con);
  956. if (res->st == NULL) {
  957. ERR("mysql: Error while creating new MySQL_STMT data structure (no memory left)\n");
  958. err = 1;
  959. goto error;
  960. }
  961. /* Try to upload the command to the server */
  962. if (mysql_stmt_prepare(res->st, res->sql_cmd.s, res->sql_cmd.len)) {
  963. err = mysql_stmt_errno(res->st);
  964. ERR("mysql: libmysql: %d, %s\n", err, mysql_stmt_error(res->st));
  965. ERR("mysql: An error occurred while uploading command to server\n");
  966. }
  967. if (err == CR_SERVER_LOST ||
  968. err == CR_SERVER_GONE_ERROR) {
  969. /* Connection to the server was lost, mark the connection as
  970. * disconnected. In this case mysql_stmt_prepare invalidates the
  971. * connection internally and calling another mysql function on that
  972. * connection would crash. To make sure that no other mysql function
  973. * gets called unless the connection is reconnected we disconnect it
  974. * explicitly here. This is a workaround for mysql bug #33384. */
  975. my_con_disconnect(cmd->ctx->con[db_payload_idx]);
  976. }
  977. if (err) {
  978. /* Report mysql error to the caller */
  979. err = -1;
  980. goto error;
  981. }
  982. err = bind_mysql_params(res->st, cmd->vals, cmd->match);
  983. if (err) goto error;
  984. if (cmd->type == DB_GET || cmd->type == DB_SQL) {
  985. err = check_result(cmd, res);
  986. if (err) goto error;
  987. err = bind_result(res->st, cmd->result);
  988. if (err) goto error;
  989. }
  990. res->last_reset = mcon->resets;
  991. return 0;
  992. error:
  993. if (res->st) {
  994. mysql_stmt_close(res->st);
  995. res->st = NULL;
  996. }
  997. return err;
  998. }
  999. int my_cmd(db_cmd_t* cmd)
  1000. {
  1001. struct my_cmd* res;
  1002. res = (struct my_cmd*)pkg_malloc(sizeof(struct my_cmd));
  1003. if (res == NULL) {
  1004. ERR("mysql: No memory left\n");
  1005. goto error;
  1006. }
  1007. memset(res, '\0', sizeof(struct my_cmd));
  1008. /* Fetch all data to client at once by default */
  1009. res->flags |= MY_FETCH_ALL;
  1010. if (db_drv_init(&res->gen, my_cmd_free) < 0) goto error;
  1011. switch(cmd->type) {
  1012. case DB_PUT:
  1013. if (DB_FLD_EMPTY(cmd->vals)) {
  1014. BUG("mysql: No parameters provided for DB_PUT in context '%.*s'\n",
  1015. cmd->ctx->id.len, ZSW(cmd->ctx->id.s));
  1016. goto error;
  1017. }
  1018. if (build_replace_cmd(&res->sql_cmd, cmd) < 0) goto error;
  1019. break;
  1020. case DB_DEL:
  1021. if (build_delete_cmd(&res->sql_cmd, cmd) < 0) goto error;
  1022. break;
  1023. case DB_GET:
  1024. if (build_select_cmd(&res->sql_cmd, cmd) < 0) goto error;
  1025. break;
  1026. case DB_UPD:
  1027. if (build_update_cmd(&res->sql_cmd, cmd) < 0) goto error;
  1028. break;
  1029. case DB_SQL:
  1030. res->sql_cmd.s = (char*)pkg_malloc(cmd->table.len);
  1031. if (res->sql_cmd.s == NULL) {
  1032. ERR("mysql: Out of private memory\n");
  1033. goto error;
  1034. }
  1035. memcpy(res->sql_cmd.s,cmd->table.s, cmd->table.len);
  1036. res->sql_cmd.len = cmd->table.len;
  1037. break;
  1038. }
  1039. DB_SET_PAYLOAD(cmd, res);
  1040. /* In order to check all the parameters and results, we need to upload
  1041. * the command to the server. We need to do that here before we report
  1042. * back that the command was created successfully. Hence, this
  1043. * function requires the corresponding connection be established. We
  1044. * would not be able to check parameters if we don't do that there and
  1045. * that could result in repeated execution failures at runtime.
  1046. */
  1047. if (upload_cmd(cmd)) goto error;
  1048. return 0;
  1049. error:
  1050. if (res) {
  1051. DB_SET_PAYLOAD(cmd, NULL);
  1052. db_drv_free(&res->gen);
  1053. if (res->sql_cmd.s) pkg_free(res->sql_cmd.s);
  1054. pkg_free(res);
  1055. }
  1056. return -1;
  1057. }
  1058. int my_cmd_first(db_res_t* res) {
  1059. struct my_cmd* mcmd;
  1060. mcmd = DB_GET_PAYLOAD(res->cmd);
  1061. switch (mcmd->next_flag) {
  1062. case -2: /* table is empty */
  1063. return 1;
  1064. case 0: /* cursor position is 0 */
  1065. return 0;
  1066. case 1: /* next row */
  1067. case 2: /* EOF */
  1068. ERR("mysql: Unbuffered queries do not support cursor reset.\n");
  1069. return -1;
  1070. default:
  1071. return my_cmd_next(res);
  1072. }
  1073. }
  1074. int my_cmd_next(db_res_t* res)
  1075. {
  1076. int ret;
  1077. struct my_cmd* mcmd;
  1078. mcmd = DB_GET_PAYLOAD(res->cmd);
  1079. if (mcmd->next_flag == 2 || mcmd->next_flag == -2) return 1;
  1080. if (mcmd->st == NULL) {
  1081. ERR("mysql: Prepared statement not found\n");
  1082. return -1;
  1083. }
  1084. ret = mysql_stmt_fetch(mcmd->st);
  1085. if (ret == MYSQL_NO_DATA) {
  1086. mcmd->next_flag = mcmd->next_flag<0?-2:2;
  1087. return 1;
  1088. }
  1089. /* MYSQL_DATA_TRUNCATED is only defined in mysql >= 5.0 */
  1090. #if defined MYSQL_DATA_TRUNCATED
  1091. if (ret == MYSQL_DATA_TRUNCATED) {
  1092. int i;
  1093. ERR("mysql: mysql_stmt_fetch, data truncated, fields: %d\n", res->cmd->result_count);
  1094. for (i = 0; i < res->cmd->result_count; i++) {
  1095. if (mcmd->st->bind[i].error /*&& mcmd->st->bind[i].buffer_length*/) {
  1096. ERR("mysql: truncation, bind %d, length: %lu, buffer_length: %lu\n",
  1097. i, *(mcmd->st->bind[i].length), mcmd->st->bind[i].buffer_length);
  1098. }
  1099. }
  1100. ret = 0;
  1101. }
  1102. #endif
  1103. if (mcmd->next_flag <= 0) {
  1104. mcmd->next_flag++;
  1105. }
  1106. if (ret != 0) {
  1107. ERR("mysql: Error in mysql_stmt_fetch (ret=%d): %s\n", ret, mysql_stmt_error(mcmd->st));
  1108. return -1;
  1109. }
  1110. if (update_result(res->cmd->result, mcmd->st) < 0) {
  1111. mysql_stmt_free_result(mcmd->st);
  1112. return -1;
  1113. }
  1114. res->cur_rec->fld = res->cmd->result;
  1115. return 0;
  1116. }
  1117. int my_getopt(db_cmd_t* cmd, char* optname, va_list ap)
  1118. {
  1119. struct my_cmd* mcmd;
  1120. long long* id;
  1121. int* val;
  1122. mcmd = (struct my_cmd*)DB_GET_PAYLOAD(cmd);
  1123. if (!strcasecmp("last_id", optname)) {
  1124. id = va_arg(ap, long long*);
  1125. if (id == NULL) {
  1126. BUG("mysql: NULL pointer passed to 'last_id' option\n");
  1127. goto error;
  1128. }
  1129. if (mcmd->st->last_errno != 0) {
  1130. BUG("mysql: Option 'last_id' called but previous command failed, "
  1131. "check your code\n");
  1132. return -1;
  1133. }
  1134. *id = mysql_stmt_insert_id(mcmd->st);
  1135. if ((*id) == 0) {
  1136. BUG("mysql: Option 'last_id' called but there is no auto-increment"
  1137. " column in table, SQL command: %.*s\n", STR_FMT(&mcmd->sql_cmd));
  1138. return -1;
  1139. }
  1140. } else if (!strcasecmp("fetch_all", optname)) {
  1141. val = va_arg(ap, int*);
  1142. if (val == NULL) {
  1143. BUG("mysql: NULL pointer passed to 'fetch_all' DB option\n");
  1144. goto error;
  1145. }
  1146. *val = mcmd->flags;
  1147. } else {
  1148. return 1;
  1149. }
  1150. return 0;
  1151. error:
  1152. return -1;
  1153. }
  1154. int my_setopt(db_cmd_t* cmd, char* optname, va_list ap)
  1155. {
  1156. struct my_cmd* mcmd;
  1157. int* val;
  1158. mcmd = (struct my_cmd*)DB_GET_PAYLOAD(cmd);
  1159. if (!strcasecmp("fetch_all", optname)) {
  1160. val = va_arg(ap, int*);
  1161. if (val != 0) {
  1162. mcmd->flags |= MY_FETCH_ALL;
  1163. } else {
  1164. mcmd->flags &= ~MY_FETCH_ALL;
  1165. }
  1166. } else {
  1167. return 1;
  1168. }
  1169. return 0;
  1170. }
  1171. /** @} */