00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00033 #define _XOPEN_SOURCE 4
00034 #define _XOPEN_SOURCE_EXTENDED 1
00035 #define _SVID_SOURCE 1
00036
00037 #include "my_cmd.h"
00038
00039 #include "my_con.h"
00040 #include "mysql_mod.h"
00041 #include "my_fld.h"
00042
00043 #include "../../mem/mem.h"
00044 #include "../../str.h"
00045 #include "../../db/db_cmd.h"
00046 #include "../../ut.h"
00047
00048 #include <stdlib.h>
00049 #include <strings.h>
00050 #include <stdio.h>
00051 #include <time.h>
00052 #include <string.h>
00053 #include <mysql/errmsg.h>
00054 #include <mysql/mysqld_error.h>
00055
00056 #define STR_BUF_SIZE 1024
00057
00058 #ifdef MYSQL_FAKE_NULL
00059
00060 #define FAKE_NULL_STRING "[~NULL~]"
00061 static str FAKE_NULL_STR = STR_STATIC_INIT(FAKE_NULL_STRING);
00062
00063
00064 #define FAKE_NULL_INT (-2147483647 - 1)
00065 #endif
00066
00067 enum {
00068 STR_DELETE,
00069 STR_INSERT,
00070 STR_UPDATE,
00071 STR_SELECT,
00072 STR_REPLACE,
00073 STR_SET,
00074 STR_WHERE,
00075 STR_IS,
00076 STR_AND,
00077 STR_OR,
00078 STR_ESC,
00079 STR_OP_EQ,
00080 STR_OP_NE,
00081 STR_OP_LT,
00082 STR_OP_GT,
00083 STR_OP_LEQ,
00084 STR_OP_GEQ,
00085 STR_VALUES,
00086 STR_FROM
00087 };
00088
00089 static str strings[] = {
00090 STR_STATIC_INIT("delete from "),
00091 STR_STATIC_INIT("insert into "),
00092 STR_STATIC_INIT("update "),
00093 STR_STATIC_INIT("select "),
00094 STR_STATIC_INIT("replace "),
00095 STR_STATIC_INIT(" set "),
00096 STR_STATIC_INIT(" where "),
00097 STR_STATIC_INIT(" is "),
00098 STR_STATIC_INIT(" and "),
00099 STR_STATIC_INIT(" or "),
00100 STR_STATIC_INIT("?"),
00101 STR_STATIC_INIT("="),
00102 STR_STATIC_INIT("!="),
00103 STR_STATIC_INIT("<"),
00104 STR_STATIC_INIT(">"),
00105 STR_STATIC_INIT("<="),
00106 STR_STATIC_INIT(">="),
00107 STR_STATIC_INIT(") values ("),
00108 STR_STATIC_INIT(" from ")
00109 };
00110
00111
00112 #define APPEND_STR(p, str) do { \
00113 memcpy((p), (str).s, (str).len); \
00114 (p) += (str).len; \
00115 } while(0)
00116
00117
00118 #define APPEND_CSTR(p, cstr) do { \
00119 int _len = strlen(cstr); \
00120 memcpy((p), (cstr), _len); \
00121 (p) += _len; \
00122 } while(0)
00123
00124
00125 static int upload_cmd(db_cmd_t* cmd);
00126
00127
00128 static void my_cmd_free(db_cmd_t* cmd, struct my_cmd* payload)
00129 {
00130 db_drv_free(&payload->gen);
00131 if (payload->sql_cmd.s) pkg_free(payload->sql_cmd.s);
00132 if (payload->st) mysql_stmt_close(payload->st);
00133 pkg_free(payload);
00134 }
00135
00136
00142 static int build_delete_cmd(str* sql_cmd, db_cmd_t* cmd)
00143 {
00144 db_fld_t* fld;
00145 int i;
00146 char* p;
00147
00148 sql_cmd->len = strings[STR_DELETE].len;
00149 sql_cmd->len += cmd->table.len;
00150
00151 if (!DB_FLD_EMPTY(cmd->match)) {
00152 sql_cmd->len += strings[STR_WHERE].len;
00153
00154 for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
00155 sql_cmd->len += strlen(fld[i].name);
00156
00157 switch(fld[i].op) {
00158 case DB_EQ: sql_cmd->len += strings[STR_OP_EQ].len; break;
00159 case DB_NE: sql_cmd->len += strings[STR_OP_NE].len; break;
00160 case DB_LT: sql_cmd->len += strings[STR_OP_LT].len; break;
00161 case DB_GT: sql_cmd->len += strings[STR_OP_GT].len; break;
00162 case DB_LEQ: sql_cmd->len += strings[STR_OP_LEQ].len; break;
00163 case DB_GEQ: sql_cmd->len += strings[STR_OP_GEQ].len; break;
00164 default:
00165 ERR("mysql: Unsupported db_fld operator %d\n", fld[i].op);
00166 return -1;
00167 }
00168
00169 sql_cmd->len += strings[STR_ESC].len;
00170
00171 if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += strings[STR_AND].len;
00172 }
00173 }
00174
00175 sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
00176 if (sql_cmd->s == NULL) {
00177 ERR("mysql: No memory left\n");
00178 return -1;
00179 }
00180 p = sql_cmd->s;
00181
00182 APPEND_STR(p, strings[STR_DELETE]);
00183 APPEND_STR(p, cmd->table);
00184
00185 if (!DB_FLD_EMPTY(cmd->match)) {
00186 APPEND_STR(p, strings[STR_WHERE]);
00187
00188 for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
00189 APPEND_CSTR(p, fld[i].name);
00190
00191 switch(fld[i].op) {
00192 case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
00193 case DB_NE: APPEND_STR(p, strings[STR_OP_NE]); break;
00194 case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
00195 case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
00196 case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
00197 case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
00198 }
00199
00200 APPEND_STR(p, strings[STR_ESC]);
00201 if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
00202 }
00203 }
00204
00205 *p = '\0';
00206 return 0;
00207 }
00208
00209
00216 static int build_select_cmd(str* sql_cmd, db_cmd_t* cmd)
00217 {
00218 db_fld_t* fld;
00219 int i;
00220 char* p;
00221
00222 sql_cmd->len = strings[STR_SELECT].len;
00223
00224 if (DB_FLD_EMPTY(cmd->result)) {
00225 sql_cmd->len += 1;
00226 } else {
00227 for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
00228 sql_cmd->len += strlen(fld[i].name);
00229 if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += 1;
00230 }
00231 }
00232 sql_cmd->len += strings[STR_FROM].len;
00233 sql_cmd->len += cmd->table.len;
00234
00235 if (!DB_FLD_EMPTY(cmd->match)) {
00236 sql_cmd->len += strings[STR_WHERE].len;
00237
00238 for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
00239 sql_cmd->len += strlen(fld[i].name);
00240
00241 switch(fld[i].op) {
00242 case DB_EQ: sql_cmd->len += strings[STR_OP_EQ].len; break;
00243 case DB_NE: sql_cmd->len += strings[STR_OP_NE].len; break;
00244 case DB_LT: sql_cmd->len += strings[STR_OP_LT].len; break;
00245 case DB_GT: sql_cmd->len += strings[STR_OP_GT].len; break;
00246 case DB_LEQ: sql_cmd->len += strings[STR_OP_LEQ].len; break;
00247 case DB_GEQ: sql_cmd->len += strings[STR_OP_GEQ].len; break;
00248 default:
00249 ERR("mysql: Unsupported db_fld operator %d\n", fld[i].op);
00250 return -1;
00251 }
00252
00253 sql_cmd->len += strings[STR_ESC].len;
00254
00255 if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += strings[STR_AND].len;
00256 }
00257 }
00258
00259 sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
00260 if (sql_cmd->s == NULL) {
00261 ERR("mysql: No memory left\n");
00262 return -1;
00263 }
00264 p = sql_cmd->s;
00265
00266 APPEND_STR(p, strings[STR_SELECT]);
00267 if (DB_FLD_EMPTY(cmd->result)) {
00268 *p++ = '*';
00269 } else {
00270 for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
00271 APPEND_CSTR(p, fld[i].name);
00272 if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
00273 }
00274 }
00275 APPEND_STR(p, strings[STR_FROM]);
00276 APPEND_STR(p, cmd->table);
00277
00278 if (!DB_FLD_EMPTY(cmd->match)) {
00279 APPEND_STR(p, strings[STR_WHERE]);
00280
00281 for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
00282 APPEND_CSTR(p, fld[i].name);
00283
00284 switch(fld[i].op) {
00285 case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
00286 case DB_NE: APPEND_STR(p, strings[STR_OP_NE]); break;
00287 case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
00288 case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
00289 case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
00290 case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
00291 }
00292
00293 APPEND_STR(p, strings[STR_ESC]);
00294 if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
00295 }
00296 }
00297
00298 *p = '\0';
00299 return 0;
00300 }
00301
00302
00308 static int build_replace_cmd(str* sql_cmd, db_cmd_t* cmd)
00309 {
00310 db_fld_t* fld;
00311 int i;
00312 char* p;
00313
00314 sql_cmd->len = strings[STR_REPLACE].len;
00315 sql_cmd->len += cmd->table.len;
00316 sql_cmd->len += 2;
00317
00318 for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
00319 sql_cmd->len += strlen(fld[i].name);
00320 sql_cmd->len += strings[STR_ESC].len;
00321 if (!DB_FLD_LAST(fld[i + 1])) sql_cmd->len += 2;
00322 }
00323 sql_cmd->len += strings[STR_VALUES].len;
00324 sql_cmd->len += 1;
00325
00326 sql_cmd->s = pkg_malloc(sql_cmd->len + 1);
00327 if (sql_cmd->s == NULL) {
00328 ERR("mysql: No memory left\n");
00329 return -1;
00330 }
00331 p = sql_cmd->s;
00332
00333 APPEND_STR(p, strings[STR_REPLACE]);
00334 APPEND_STR(p, cmd->table);
00335 *p++ = ' ';
00336 *p++ = '(';
00337
00338 for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
00339 APPEND_CSTR(p, fld[i].name);
00340 if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
00341 }
00342 APPEND_STR(p, strings[STR_VALUES]);
00343
00344 for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
00345 APPEND_STR(p, strings[STR_ESC]);
00346 if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
00347 }
00348 *p++ = ')';
00349 *p = '\0';
00350 return 0;
00351 }
00352
00353
00357 struct string_buffer {
00358 char *s;
00359 int len;
00360 int size;
00361 int increment;
00362 };
00363
00364
00371 static inline int sb_add(struct string_buffer *sb, str *nstr)
00372 {
00373 int new_size = 0;
00374 int rsize = sb->len + nstr->len;
00375 int asize;
00376 char *newp;
00377
00378 if ( rsize > sb->size ) {
00379 asize = rsize - sb->size;
00380 new_size = sb->size + (asize / sb->increment + (asize % sb->increment > 0)) * sb->increment;
00381 newp = pkg_malloc(new_size);
00382 if (!newp) {
00383 ERR("mysql: No memory left\n");
00384 return -1;
00385 }
00386 if (sb->s) {
00387 memcpy(newp, sb->s, sb->len);
00388 pkg_free(sb->s);
00389 }
00390 sb->s = newp;
00391 sb->size = new_size;
00392 }
00393 memcpy(sb->s + sb->len, nstr->s, nstr->len);
00394 sb->len += nstr->len;
00395 return 0;
00396 }
00397
00398
00403 static inline str* set_str(str *str, const char *s)
00404 {
00405 str->s = (char *)s;
00406 str->len = strlen(s);
00407 return str;
00408 }
00409
00410
00417 static int build_update_cmd(str* sql_cmd, db_cmd_t* cmd)
00418 {
00419 struct string_buffer sql_buf = {.s = NULL, .len = 0, .size = 0, .increment = 128};
00420 db_fld_t* fld;
00421 int i;
00422 int rv = 0;
00423 str tmpstr;
00424
00425 rv = sb_add(&sql_buf, &strings[STR_UPDATE]);
00426 rv |= sb_add(&sql_buf, &cmd->table);
00427 rv |= sb_add(&sql_buf, &strings[STR_SET]);
00428
00429
00430 for(i = 0, fld = cmd->vals; !DB_FLD_LAST(fld[i]); i++) {
00431 rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
00432 rv |= sb_add(&sql_buf, set_str(&tmpstr, " = "));
00433 rv |= sb_add(&sql_buf, &strings[STR_ESC]);
00434 if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, set_str(&tmpstr, ", "));
00435 }
00436 if (rv) {
00437 goto err;
00438 }
00439
00440 if (!DB_FLD_EMPTY(cmd->match)) {
00441 rv |= sb_add(&sql_buf, &strings[STR_WHERE]);
00442
00443 for(i = 0, fld = cmd->match; !DB_FLD_LAST(fld[i]); i++) {
00444 rv |= sb_add(&sql_buf, set_str(&tmpstr, fld[i].name));
00445
00446 switch(fld[i].op) {
00447 case DB_EQ: rv |= sb_add(&sql_buf, &strings[STR_OP_EQ]); break;
00448 case DB_NE: rv |= sb_add(&sql_buf, &strings[STR_OP_NE]); break;
00449 case DB_LT: rv |= sb_add(&sql_buf, &strings[STR_OP_LT]); break;
00450 case DB_GT: rv |= sb_add(&sql_buf, &strings[STR_OP_GT]); break;
00451 case DB_LEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_LEQ]); break;
00452 case DB_GEQ: rv |= sb_add(&sql_buf, &strings[STR_OP_GEQ]); break;
00453 }
00454
00455 rv |= sb_add(&sql_buf, &strings[STR_ESC]);
00456 if (!DB_FLD_LAST(fld[i + 1])) rv |= sb_add(&sql_buf, &strings[STR_AND]);
00457 }
00458 }
00459 rv |= sb_add(&sql_buf, set_str(&tmpstr, "\0"));
00460 if (rv) {
00461 goto err;
00462 }
00463 sql_cmd->s = sql_buf.s;
00464 sql_cmd->len = sql_buf.len;
00465 return 0;
00466
00467 err:
00468 if (sql_buf.s) pkg_free(sql_buf.s);
00469 return -1;
00470 }
00471
00472
00473 static inline void update_field(MYSQL_BIND *param, db_fld_t* fld)
00474 {
00475 struct my_fld* fp;
00476 struct tm* t;
00477
00478 fp = DB_GET_PAYLOAD(fld);
00479
00480 #ifndef MYSQL_FAKE_NULL
00481 fp->is_null = fld->flags & DB_NULL;
00482 if (fp->is_null) return;
00483 #else
00484 if (fld->flags & DB_NULL) {
00485 switch(fld->type) {
00486 case DB_STR:
00487 case DB_CSTR:
00488 param->buffer = FAKE_NULL_STR.s;
00489 fp->length = FAKE_NULL_STR.len;
00490 break;
00491 case DB_INT:
00492 *(int*)param->buffer = FAKE_NULL_INT;
00493 break;
00494 case DB_BLOB:
00495 case DB_DATETIME:
00496 case DB_NONE:
00497 case DB_FLOAT:
00498 case DB_DOUBLE:
00499 case DB_BITMAP:
00500
00501 fp->is_null = DB_NULL;
00502 break;
00503 }
00504 return;
00505 }
00506 #endif
00507 switch(fld->type) {
00508 case DB_STR:
00509 param->buffer = fld->v.lstr.s;
00510 fp->length = fld->v.lstr.len;
00511 break;
00512
00513 case DB_BLOB:
00514 param->buffer = fld->v.blob.s;
00515 fp->length = fld->v.blob.len;
00516 break;
00517
00518 case DB_CSTR:
00519 param->buffer = (char*)fld->v.cstr;
00520 fp->length = strlen(fld->v.cstr);
00521 break;
00522
00523 case DB_DATETIME:
00524 t = gmtime(&fld->v.time);
00525 fp->time.second = t->tm_sec;
00526 fp->time.minute = t->tm_min;
00527 fp->time.hour = t->tm_hour;
00528 fp->time.day = t->tm_mday;
00529 fp->time.month = t->tm_mon + 1;
00530 fp->time.year = t->tm_year + 1900;
00531 break;
00532
00533 case DB_NONE:
00534 case DB_INT:
00535 case DB_FLOAT:
00536 case DB_DOUBLE:
00537 case DB_BITMAP:
00538
00539 break;
00540
00541 }
00542 }
00543
00544
00551 static inline void set_mysql_params(db_cmd_t* cmd)
00552 {
00553 struct my_cmd* mcmd;
00554 int i;
00555
00556 mcmd = DB_GET_PAYLOAD(cmd);
00557
00558
00559
00560
00561
00562
00563 for(i = 0; i < cmd->vals_count; i++) {
00564 update_field(mcmd->st->params + i, cmd->vals + i);
00565 }
00566
00567 for(i = 0; i < cmd->match_count; i++) {
00568 update_field(mcmd->st->params + cmd->vals_count + i, cmd->match + i);
00569 }
00570 }
00571
00572
00573 static inline int update_result(db_fld_t* result, MYSQL_STMT* st)
00574 {
00575 int i;
00576 struct my_fld* rp;
00577 struct tm t;
00578
00579
00580
00581
00582
00583 for(i = 0; i < st->field_count; i++) {
00584 rp = DB_GET_PAYLOAD(result + i);
00585
00586 if (rp->is_null) {
00587 result[i].flags |= DB_NULL;
00588 continue;
00589 } else {
00590 result[i].flags &= ~DB_NULL;
00591 }
00592
00593 switch(result[i].type) {
00594 case DB_STR:
00595 result[i].v.lstr.len = rp->length;
00596 #ifdef MYSQL_FAKE_NULL
00597 if (STR_EQ(FAKE_NULL_STR,result[i].v.lstr)) {
00598 result[i].flags |= DB_NULL;
00599 }
00600 #endif
00601 break;
00602
00603 case DB_BLOB:
00604 result[i].v.blob.len = rp->length;
00605 break;
00606
00607 case DB_CSTR:
00608 if (rp->length < STR_BUF_SIZE) {
00609 result[i].v.cstr[rp->length] = '\0';
00610 } else {
00611
00612
00613
00614 result[i].v.cstr[STR_BUF_SIZE - 1] = '\0';
00615 }
00616 #ifdef MYSQL_FAKE_NULL
00617 if (strcmp(FAKE_NULL_STR.s,result[i].v.cstr)==0) {
00618 result[i].flags |= DB_NULL;
00619 }
00620 #endif
00621 break;
00622
00623 case DB_DATETIME:
00624 memset(&t, '\0', sizeof(struct tm));
00625 t.tm_sec = rp->time.second;
00626 t.tm_min = rp->time.minute;
00627 t.tm_hour = rp->time.hour;
00628 t.tm_mday = rp->time.day;
00629 t.tm_mon = rp->time.month - 1;
00630 t.tm_year = rp->time.year - 1900;
00631
00632
00633
00634
00635
00636
00637 t.tm_isdst = -1;
00638 #ifdef HAVE_TIMEGM
00639 result[i].v.time = timegm(&t);
00640 #else
00641 result[i].v.time = _timegm(&t);
00642 #endif
00643 break;
00644
00645 case DB_INT:
00646 #ifdef MYSQL_FAKE_NULL
00647 if (FAKE_NULL_INT==result[i].v.int4) {
00648 result[i].flags |= DB_NULL;
00649 }
00650 break;
00651 #endif
00652 case DB_NONE:
00653 case DB_FLOAT:
00654 case DB_DOUBLE:
00655 case DB_BITMAP:
00656
00657 break;
00658 }
00659 }
00660
00661 return 0;
00662 }
00663
00664
00672 static int exec_cmd_safe(db_cmd_t* cmd)
00673 {
00674 int i, err;
00675 db_con_t* con;
00676 struct my_cmd* mcmd;
00677 struct my_con* mcon;
00678
00679
00680
00681
00682
00683 mcmd = DB_GET_PAYLOAD(cmd);
00684 con = cmd->ctx->con[db_payload_idx];
00685 mcon = DB_GET_PAYLOAD(con);
00686
00687 for(i = 0; i <= my_retries; i++) {
00688
00689
00690
00691
00692
00693
00694
00695 if (mcon->resets > mcmd->last_reset) {
00696 INFO("mysql: Connection reset detected, uploading command to server\n");
00697 err = upload_cmd(cmd);
00698 if (err < 0) {
00699 INFO("mysql: Error while uploading command\n");
00700
00701 continue;
00702 } else if (err > 0) {
00703
00704
00705
00706 return 1;
00707 }
00708 }
00709
00710 set_mysql_params(cmd);
00711 err = mysql_stmt_execute(mcmd->st);
00712 if (err == 0) {
00713
00714
00715 if (mcmd->flags & MY_FETCH_ALL) {
00716 err = mysql_stmt_store_result(mcmd->st);
00717 if (err) {
00718 INFO("mysql: Error while fetching data to client.\n");
00719 goto error;
00720 }
00721 }
00722 return 0;
00723 }
00724
00725 error:
00726
00727 INFO("mysql: libmysql: %d, %s\n", mysql_stmt_errno(mcmd->st),
00728 mysql_stmt_error(mcmd->st));
00729 INFO("mysql: Error while executing command on server, trying to reconnect\n");
00730 my_con_disconnect(con);
00731 if (my_con_connect(con)) {
00732 INFO("mysql: Failed to reconnect server\n");
00733 } else {
00734 INFO("mysql: Successfully reconnected server\n");
00735 }
00736 }
00737
00738 INFO("mysql: Failed to execute command, giving up\n");
00739 return -1;
00740 }
00741
00742
00743 int my_cmd_exec(db_res_t* res, db_cmd_t* cmd)
00744 {
00745 struct my_cmd* mcmd;
00746
00747 mcmd = DB_GET_PAYLOAD(cmd);
00748
00749 mcmd->next_flag = -1;
00750 return exec_cmd_safe(cmd);
00751 }
00752
00753
00759 static void set_field(MYSQL_BIND *bind, db_fld_t* fld)
00760 {
00761 struct my_fld* f;
00762
00763 f = DB_GET_PAYLOAD(fld);
00764 bind->is_null = &f->is_null;
00765
00766
00767
00768 bind->length = &f->length;
00769 switch(fld->type) {
00770 case DB_INT:
00771 case DB_BITMAP:
00772 bind->buffer_type = MYSQL_TYPE_LONG;
00773 bind->buffer = &fld->v.int4;
00774 break;
00775
00776 case DB_FLOAT:
00777 bind->buffer_type = MYSQL_TYPE_FLOAT;
00778 bind->buffer = &fld->v.flt;
00779 break;
00780
00781 case DB_DOUBLE:
00782 bind->buffer_type = MYSQL_TYPE_DOUBLE;
00783 bind->buffer = &fld->v.dbl;
00784 break;
00785
00786 case DB_DATETIME:
00787 bind->buffer_type = MYSQL_TYPE_DATETIME;
00788 bind->buffer = &f->time;
00789 break;
00790
00791 case DB_STR:
00792 case DB_CSTR:
00793 bind->buffer_type = MYSQL_TYPE_VAR_STRING;
00794 bind->buffer = "";
00795 break;
00796
00797 case DB_BLOB:
00798 bind->buffer_type = MYSQL_TYPE_BLOB;
00799 bind->buffer = "";
00800 break;
00801
00802 case DB_NONE:
00803
00804 break;
00805
00806 }
00807 }
00808
00809
00822 static int bind_mysql_params(MYSQL_STMT* st, db_fld_t* params1, db_fld_t* params2)
00823 {
00824 int my_idx, fld_idx;
00825 int count1, count2;
00826 MYSQL_BIND* my_params;
00827 int err = 0;
00828
00829
00830 for(count1 = 0; !DB_FLD_EMPTY(params1) && !DB_FLD_LAST(params1[count1]); count1++);
00831 for(count2 = 0; !DB_FLD_EMPTY(params2) && !DB_FLD_LAST(params2[count2]); count2++);
00832 if (st->param_count != count1 + count2) {
00833 BUG("mysql: Number of parameters in SQL command does not match number of DB API parameters\n");
00834 return 1;
00835 }
00836
00837 my_params = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * (count1 + count2));
00838 if (my_params == NULL) {
00839 ERR("mysql: No memory left\n");
00840 return 1;
00841 }
00842 memset(my_params, '\0', sizeof(MYSQL_BIND) * (count1 + count2));
00843
00844
00845 my_idx = 0;
00846 for (fld_idx = 0; fld_idx < count1; fld_idx++, my_idx++) {
00847 set_field(&my_params[my_idx], params1 + fld_idx);
00848 }
00849
00850 for (fld_idx = 0; fld_idx < count2; fld_idx++, my_idx++) {
00851 set_field(&my_params[my_idx], params2 + fld_idx);
00852 }
00853
00854 err = mysql_stmt_bind_param(st, my_params);
00855 if (err) {
00856 ERR("mysql: libmysqlclient: %d, %s\n",
00857 mysql_stmt_errno(st), mysql_stmt_error(st));
00858 err = -abs(err);
00859 goto error;
00860 }
00861
00862
00863
00864
00865 pkg_free(my_params);
00866 return err;
00867
00868 error:
00869 if (my_params) pkg_free(my_params);
00870 return err;
00871 }
00872
00873
00874
00875
00876
00877
00878
00879 static int check_result(db_cmd_t* cmd, struct my_cmd* payload)
00880 {
00881 int i, n;
00882 MYSQL_FIELD *fld;
00883 MYSQL_RES *meta = NULL;
00884
00885 meta = mysql_stmt_result_metadata(payload->st);
00886 if (meta == NULL) {
00887
00888 if (mysql_stmt_errno(payload->st) == 0) return 0;
00889 ERR("mysql: Error while getting metadata of SQL command: %d, %s\n",
00890 mysql_stmt_errno(payload->st), mysql_stmt_error(payload->st));
00891 return -1;
00892 }
00893 n = mysql_num_fields(meta);
00894 if (cmd->result == NULL) {
00895
00896
00897
00898
00899 cmd->result = db_fld(n + 1);
00900 cmd->result_count = n;
00901 for(i = 0; i < cmd->result_count; i++) {
00902 struct my_fld *f;
00903 if (my_fld(cmd->result + i, cmd->table.s) < 0) goto error;
00904 f = DB_GET_PAYLOAD(cmd->result + i);
00905 fld = mysql_fetch_field_direct(meta, i);
00906 f->name = pkg_malloc(strlen(fld->name)+1);
00907 if (f->name == NULL) {
00908 ERR("mysql: Out of private memory\n");
00909 goto error;
00910 }
00911 strcpy(f->name, fld->name);
00912 cmd->result[i].name = f->name;
00913 }
00914 } else {
00915 if (cmd->result_count != n) {
00916 BUG("mysql: Number of fields in MySQL result does not match number of parameters in DB API\n");
00917 goto error;
00918 }
00919 }
00920
00921
00922
00923
00924
00925
00926 for(i = 0; i < cmd->result_count; i++) {
00927 fld = mysql_fetch_field_direct(meta, i);
00928 if (cmd->result[i].type != DB_NONE) continue;
00929 switch(fld->type) {
00930 case MYSQL_TYPE_TINY:
00931 case MYSQL_TYPE_SHORT:
00932 case MYSQL_TYPE_INT24:
00933 case MYSQL_TYPE_LONG:
00934 cmd->result[i].type = DB_INT;
00935 break;
00936
00937 case MYSQL_TYPE_FLOAT:
00938 cmd->result[i].type = DB_FLOAT;
00939 break;
00940
00941 case MYSQL_TYPE_DOUBLE:
00942 cmd->result[i].type = DB_DOUBLE;
00943 break;
00944
00945 case MYSQL_TYPE_TIMESTAMP:
00946 case MYSQL_TYPE_DATETIME:
00947 cmd->result[i].type = DB_DATETIME;
00948 break;
00949
00950 case MYSQL_TYPE_STRING:
00951 case MYSQL_TYPE_VAR_STRING:
00952 cmd->result[i].type = DB_STR;
00953 break;
00954
00955 default:
00956 ERR("mysql: Unsupported MySQL column type: %d, table: %s, column: %s\n",
00957 fld->type, cmd->table.s, fld->name);
00958 goto error;
00959 }
00960 }
00961
00962 if (meta) mysql_free_result(meta);
00963 return 0;
00964
00965 error:
00966 if (meta) mysql_free_result(meta);
00967 return 1;
00968 }
00969
00970
00971
00972
00973
00974
00975 static int bind_result(MYSQL_STMT* st, db_fld_t* fld)
00976 {
00977 int i, n, err = 0;
00978 struct my_fld* f;
00979 MYSQL_BIND* result;
00980
00981
00982 for(n = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[n]); n++);
00983
00984 if (n == 0) return 0;
00985
00986 result = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * n);
00987 if (result == NULL) {
00988 ERR("mysql: No memory left\n");
00989 return 1;
00990 }
00991 memset(result, '\0', sizeof(MYSQL_BIND) * n);
00992
00993 for(i = 0; i < n; i++) {
00994 f = DB_GET_PAYLOAD(fld + i);
00995 result[i].is_null = &f->is_null;
00996
00997
00998
00999 result[i].length = &f->length;
01000 switch(fld[i].type) {
01001 case DB_INT:
01002 case DB_BITMAP:
01003 result[i].buffer_type = MYSQL_TYPE_LONG;
01004 result[i].buffer = &fld[i].v.int4;
01005 break;
01006
01007 case DB_FLOAT:
01008 result[i].buffer_type = MYSQL_TYPE_FLOAT;
01009 result[i].buffer = &fld[i].v.flt;
01010 break;
01011
01012 case DB_DOUBLE:
01013 result[i].buffer_type = MYSQL_TYPE_DOUBLE;
01014 result[i].buffer = &fld[i].v.dbl;
01015 break;
01016
01017 case DB_DATETIME:
01018 result[i].buffer_type = MYSQL_TYPE_DATETIME;
01019 result[i].buffer = &f->time;
01020 break;
01021
01022 case DB_STR:
01023 result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
01024 if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
01025 if (f->buf.s == NULL) {
01026 ERR("mysql: No memory left\n");
01027 err = 1;
01028 goto error;
01029 }
01030 result[i].buffer = f->buf.s;
01031 fld[i].v.lstr.s = f->buf.s;
01032 result[i].buffer_length = STR_BUF_SIZE - 1;
01033 break;
01034
01035 case DB_CSTR:
01036 result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
01037 if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
01038 if (f->buf.s == NULL) {
01039 ERR("mysql: No memory left\n");
01040 err = 1;
01041 goto error;
01042 }
01043 result[i].buffer = f->buf.s;
01044 fld[i].v.cstr = f->buf.s;
01045 result[i].buffer_length = STR_BUF_SIZE - 1;
01046 break;
01047
01048 case DB_BLOB:
01049 result[i].buffer_type = MYSQL_TYPE_BLOB;
01050 if (!f->buf.s) f->buf.s = pkg_malloc(STR_BUF_SIZE);
01051 if (f->buf.s == NULL) {
01052 ERR("mysql: No memory left\n");
01053 err = 1;
01054 goto error;
01055 }
01056 result[i].buffer = f->buf.s;
01057 fld[i].v.blob.s = f->buf.s;
01058 result[i].buffer_length = STR_BUF_SIZE - 1;
01059 break;
01060
01061 case DB_NONE:
01062
01063 break;
01064
01065 }
01066 }
01067
01068 err = mysql_stmt_bind_result(st, result);
01069 if (err) {
01070 ERR("mysql: Error while binding result: %s\n", mysql_stmt_error(st));
01071 err = -abs(err);
01072 goto error;
01073 }
01074
01075
01076
01077
01078 if (result) pkg_free(result);
01079 return 0;
01080
01081 error:
01082 if (result) pkg_free(result);
01083 return err;
01084 }
01085
01086
01092 static int upload_cmd(db_cmd_t* cmd)
01093 {
01094 struct my_cmd* res;
01095 struct my_con* mcon;
01096 int err = 0;
01097
01098 res = DB_GET_PAYLOAD(cmd);
01099
01100
01101 mcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
01102
01103
01104 if (res->st) mysql_stmt_close(res->st);
01105 res->st = NULL;
01106
01107
01108 res->st = mysql_stmt_init(mcon->con);
01109 if (res->st == NULL) {
01110 ERR("mysql: Error while creating new MySQL_STMT data structure (no memory left)\n");
01111 err = -1;
01112 goto error;
01113 }
01114
01115
01116 err = mysql_stmt_prepare(res->st, res->sql_cmd.s, res->sql_cmd.len);
01117 if (err) {
01118 ERR("mysql: libmysql: %d, %s\n", mysql_stmt_errno(res->st),
01119 mysql_stmt_error(res->st));
01120 ERR("mysql: An error occurred while uploading a command to MySQL server\n");
01121 err = -abs(err);
01122 goto error;
01123 }
01124
01125 err = bind_mysql_params(res->st, cmd->vals, cmd->match);
01126 if (err) goto error;
01127
01128 if (cmd->type == DB_GET || cmd->type == DB_SQL) {
01129 err = check_result(cmd, res);
01130 if (err) goto error;
01131 err = bind_result(res->st, cmd->result);
01132 if (err) goto error;
01133 }
01134
01135 res->last_reset = mcon->resets;
01136 return 0;
01137
01138 error:
01139 if (res->st) {
01140 ERR("mysql: libmysqlclient: %d, %s\n",
01141 mysql_stmt_errno(res->st),
01142 mysql_stmt_error(res->st));
01143 mysql_stmt_close(res->st);
01144 res->st = NULL;
01145 }
01146 return err;
01147 }
01148
01149
01150 int my_cmd(db_cmd_t* cmd)
01151 {
01152 struct my_cmd* res;
01153
01154 res = (struct my_cmd*)pkg_malloc(sizeof(struct my_cmd));
01155 if (res == NULL) {
01156 ERR("mysql: No memory left\n");
01157 goto error;
01158 }
01159 memset(res, '\0', sizeof(struct my_cmd));
01160
01161 res->flags |= MY_FETCH_ALL;
01162 if (db_drv_init(&res->gen, my_cmd_free) < 0) goto error;
01163
01164 switch(cmd->type) {
01165 case DB_PUT:
01166 if (DB_FLD_EMPTY(cmd->vals)) {
01167 BUG("mysql: No parameters provided for DB_PUT in context '%.*s'\n",
01168 cmd->ctx->id.len, ZSW(cmd->ctx->id.s));
01169 goto error;
01170 }
01171 if (build_replace_cmd(&res->sql_cmd, cmd) < 0) goto error;
01172 break;
01173
01174 case DB_DEL:
01175 if (build_delete_cmd(&res->sql_cmd, cmd) < 0) goto error;
01176 break;
01177
01178 case DB_GET:
01179 if (build_select_cmd(&res->sql_cmd, cmd) < 0) goto error;
01180 break;
01181
01182 case DB_UPD:
01183 if (build_update_cmd(&res->sql_cmd, cmd) < 0) goto error;
01184 break;
01185
01186 case DB_SQL:
01187 res->sql_cmd.s = (char*)pkg_malloc(cmd->table.len);
01188 if (res->sql_cmd.s == NULL) {
01189 ERR("mysql: Out of private memory\n");
01190 goto error;
01191 }
01192 memcpy(res->sql_cmd.s,cmd->table.s, cmd->table.len);
01193 res->sql_cmd.len = cmd->table.len;
01194 break;
01195 }
01196
01197 DB_SET_PAYLOAD(cmd, res);
01198 if (upload_cmd(cmd) != 0) goto error;
01199 return 0;
01200
01201 error:
01202 if (res) {
01203 DB_SET_PAYLOAD(cmd, NULL);
01204 db_drv_free(&res->gen);
01205 if (res->sql_cmd.s) pkg_free(res->sql_cmd.s);
01206 pkg_free(res);
01207 }
01208 return -1;
01209 }
01210
01211
01212 int my_cmd_first(db_res_t* res) {
01213 struct my_cmd* mcmd;
01214
01215 mcmd = DB_GET_PAYLOAD(res->cmd);
01216 switch (mcmd->next_flag) {
01217 case -2:
01218 return 1;
01219 case 0:
01220 return 0;
01221 case 1:
01222 case 2:
01223 ERR("mysql: Unbuffered queries do not support cursor reset.\n");
01224 return -1;
01225 default:
01226 return my_cmd_next(res);
01227 }
01228 }
01229
01230
01231 int my_cmd_next(db_res_t* res)
01232 {
01233 int ret;
01234 struct my_cmd* mcmd;
01235
01236 mcmd = DB_GET_PAYLOAD(res->cmd);
01237 if (mcmd->next_flag == 2 || mcmd->next_flag == -2) return 1;
01238
01239 if (mcmd->st == NULL) {
01240 ERR("mysql: Prepared statement not found\n");
01241 return -1;
01242 }
01243
01244 ret = mysql_stmt_fetch(mcmd->st);
01245
01246 if (ret == MYSQL_NO_DATA) {
01247 mcmd->next_flag = mcmd->next_flag<0?-2:2;
01248 return 1;
01249 }
01250
01251 #if defined MYSQL_DATA_TRUNCATED
01252 if (ret == MYSQL_DATA_TRUNCATED) {
01253 int i;
01254 ERR("mysql: mysql_stmt_fetch, data truncated, fields: %d\n", res->cmd->result_count);
01255 for (i = 0; i < res->cmd->result_count; i++) {
01256 if (mcmd->st->bind[i].error ) {
01257 ERR("mysql: truncation, bind %d, length: %lu, buffer_length: %lu\n",
01258 i, *(mcmd->st->bind[i].length), mcmd->st->bind[i].buffer_length);
01259 }
01260 }
01261 ret = 0;
01262 }
01263 #endif
01264 if (mcmd->next_flag <= 0) {
01265 mcmd->next_flag++;
01266 }
01267 if (ret != 0) {
01268 ERR("mysql: Error in mysql_stmt_fetch (ret=%d): %s\n", ret, mysql_stmt_error(mcmd->st));
01269 return -1;
01270 }
01271
01272 if (update_result(res->cmd->result, mcmd->st) < 0) {
01273 mysql_stmt_free_result(mcmd->st);
01274 return -1;
01275 }
01276
01277 res->cur_rec->fld = res->cmd->result;
01278 return 0;
01279 }
01280
01281
01282 int my_getopt(db_cmd_t* cmd, char* optname, va_list ap)
01283 {
01284 struct my_cmd* mcmd;
01285 long long* id;
01286 int* val;
01287
01288 mcmd = (struct my_cmd*)DB_GET_PAYLOAD(cmd);
01289
01290 if (!strcasecmp("last_id", optname)) {
01291 id = va_arg(ap, long long*);
01292 if (id == NULL) {
01293 BUG("mysql: NULL pointer passed to 'last_id' option\n");
01294 goto error;
01295 }
01296
01297 if (mcmd->st->last_errno != 0) {
01298 BUG("mysql: Option 'last_id' called but previous command failed, "
01299 "check your code\n");
01300 return -1;
01301 }
01302
01303 *id = mysql_stmt_insert_id(mcmd->st);
01304 if ((*id) == 0) {
01305 BUG("mysql: Option 'last_id' called but there is no auto-increment"
01306 " column in table, SQL command: %.*s\n", STR_FMT(&mcmd->sql_cmd));
01307 return -1;
01308 }
01309 } else if (!strcasecmp("fetch_all", optname)) {
01310 val = va_arg(ap, int*);
01311 if (val == NULL) {
01312 BUG("mysql: NULL pointer passed to 'fetch_all' DB option\n");
01313 goto error;
01314 }
01315 *val = mcmd->flags;
01316 } else {
01317 return 1;
01318 }
01319 return 0;
01320
01321 error:
01322 return -1;
01323 }
01324
01325
01326 int my_setopt(db_cmd_t* cmd, char* optname, va_list ap)
01327 {
01328 struct my_cmd* mcmd;
01329 int* val;
01330
01331 mcmd = (struct my_cmd*)DB_GET_PAYLOAD(cmd);
01332 if (!strcasecmp("fetch_all", optname)) {
01333 val = va_arg(ap, int*);
01334 if (val != 0) {
01335 mcmd->flags |= MY_FETCH_ALL;
01336 } else {
01337 mcmd->flags &= ~MY_FETCH_ALL;
01338 }
01339 } else {
01340 return 1;
01341 }
01342 return 0;
01343 }
01344