my_cmd.c 34 KB

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