123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732 |
- /*
- * $Id$
- *
- * Copyright (C) 2001-2003 FhG Fokus
- * Copyright (C) 2006-2007 iptelorg GmbH
- *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #define _XOPEN_SOURCE 4 /* bsd */
- #define _XOPEN_SOURCE_EXTENDED 1 /* solaris */
- #define _SVID_SOURCE 1 /* timegm */
- #include <strings.h>
- #include <stdio.h>
- #include <time.h> /*strptime, XOPEN issue must be >=4 */
- #include <string.h>
- #include "../../mem/mem.h"
- #include "../../str.h"
- #include "../../db/db_cmd.h"
- #include "../../ut.h"
- #include "my_con.h"
- #include "my_fld.h"
- #include "my_cmd.h"
- #define STR_BUF_SIZE 256
- enum {
- STR_DELETE,
- STR_INSERT,
- STR_UPDATE,
- STR_SELECT,
- STR_REPLACE,
- STR_WHERE,
- STR_IS,
- STR_AND,
- STR_OR,
- STR_ESC,
- STR_OP_EQ,
- STR_OP_LT,
- STR_OP_GT,
- STR_OP_LEQ,
- STR_OP_GEQ,
- STR_VALUES,
- STR_FROM
- };
- static str strings[] = {
- STR_STATIC_INIT("delete from "),
- STR_STATIC_INIT("insert into "),
- STR_STATIC_INIT("update "),
- STR_STATIC_INIT("select "),
- STR_STATIC_INIT("replace "),
- STR_STATIC_INIT(" where "),
- STR_STATIC_INIT(" is "),
- STR_STATIC_INIT(" and "),
- STR_STATIC_INIT(" or "),
- STR_STATIC_INIT("?"),
- STR_STATIC_INIT("="),
- STR_STATIC_INIT("<"),
- STR_STATIC_INIT(">"),
- STR_STATIC_INIT("<="),
- STR_STATIC_INIT(">="),
- STR_STATIC_INIT(") values ("),
- STR_STATIC_INIT(" from ")
- };
- #define APPEND_STR(p, str) do { \
- memcpy((p), (str).s, (str).len); \
- (p) += (str).len; \
- } while(0)
- #define APPEND_CSTR(p, cstr) do { \
- int _len = strlen(cstr); \
- memcpy((p), (cstr), _len); \
- (p) += _len; \
- } while(0)
- static int my_cmd_free(db_cmd_t* cmd, struct my_cmd* payload)
- {
- db_drv_free(&payload->gen);
- if (payload->query.s) pkg_free(payload->query.s);
- if (payload->st) mysql_stmt_close(payload->st);
- pkg_free(payload);
- }
- static int build_delete_query(str* query, db_cmd_t* cmd)
- {
- db_fld_t* fld;
- int i;
- char* p;
- query->len = strings[STR_DELETE].len;
- query->len += cmd->table.len;
- if (!DB_FLD_EMPTY(cmd->params)) {
- query->len += strings[STR_WHERE].len;
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- query->len += strlen(fld[i].name);
- switch(fld[i].op) {
- case DB_EQ: query->len += strings[STR_OP_EQ].len; break;
- case DB_LT: query->len += strings[STR_OP_LT].len; break;
- case DB_GT: query->len += strings[STR_OP_GT].len; break;
- case DB_LEQ: query->len += strings[STR_OP_LEQ].len; break;
- case DB_GEQ: query->len += strings[STR_OP_GEQ].len; break;
- default:
- ERR("Unsupported db_fld operator %d\n", fld[i].op);
- return -1;
- }
- query->len += strings[STR_ESC].len;
-
- if (!DB_FLD_LAST(fld[i + 1])) query->len += strings[STR_AND].len;
- }
- }
- query->s = pkg_malloc(query->len + 1);
- if (query->s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- p = query->s;
-
- APPEND_STR(p, strings[STR_DELETE]);
- APPEND_STR(p, cmd->table);
- if (!DB_FLD_EMPTY(cmd->params)) {
- APPEND_STR(p, strings[STR_WHERE]);
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- APPEND_CSTR(p, fld[i].name);
- switch(fld[i].op) {
- case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
- case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
- case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
- case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
- case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
- }
-
- APPEND_STR(p, strings[STR_ESC]);
- if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
- }
- }
-
- *p = '\0';
- return 0;
- }
- static int build_select_query(str* query, db_cmd_t* cmd)
- {
- db_fld_t* fld;
- int i;
- char* p;
- query->len = strings[STR_SELECT].len;
- if (DB_FLD_EMPTY(cmd->result)) {
- query->len += 1; /* "*" */
- } else {
- for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
- query->len += strlen(fld[i].name);
- if (!DB_FLD_LAST(fld[i + 1])) query->len += 1; /* , */
- }
- }
- query->len += strings[STR_FROM].len;
- query->len += cmd->table.len;
- if (!DB_FLD_EMPTY(cmd->params)) {
- query->len += strings[STR_WHERE].len;
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- query->len += strlen(fld[i].name);
- switch(fld[i].op) {
- case DB_EQ: query->len += strings[STR_OP_EQ].len; break;
- case DB_LT: query->len += strings[STR_OP_LT].len; break;
- case DB_GT: query->len += strings[STR_OP_GT].len; break;
- case DB_LEQ: query->len += strings[STR_OP_LEQ].len; break;
- case DB_GEQ: query->len += strings[STR_OP_GEQ].len; break;
- default:
- ERR("Unsupported db_fld operator %d\n", fld[i].op);
- return -1;
- }
- query->len += strings[STR_ESC].len;
-
- if (!DB_FLD_LAST(fld[i + 1])) query->len += strings[STR_AND].len;
- }
- }
- query->s = pkg_malloc(query->len + 1);
- if (query->s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- p = query->s;
-
- APPEND_STR(p, strings[STR_SELECT]);
- if (DB_FLD_EMPTY(cmd->result)) {
- *p++ = '*';
- } else {
- for(i = 0, fld = cmd->result; !DB_FLD_LAST(fld[i]); i++) {
- APPEND_CSTR(p, fld[i].name);
- if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
- }
- }
- APPEND_STR(p, strings[STR_FROM]);
- APPEND_STR(p, cmd->table);
- if (!DB_FLD_EMPTY(cmd->params)) {
- APPEND_STR(p, strings[STR_WHERE]);
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- APPEND_CSTR(p, fld[i].name);
- switch(fld[i].op) {
- case DB_EQ: APPEND_STR(p, strings[STR_OP_EQ]); break;
- case DB_LT: APPEND_STR(p, strings[STR_OP_LT]); break;
- case DB_GT: APPEND_STR(p, strings[STR_OP_GT]); break;
- case DB_LEQ: APPEND_STR(p, strings[STR_OP_LEQ]); break;
- case DB_GEQ: APPEND_STR(p, strings[STR_OP_GEQ]); break;
- }
-
- APPEND_STR(p, strings[STR_ESC]);
- if (!DB_FLD_LAST(fld[i + 1])) APPEND_STR(p, strings[STR_AND]);
- }
- }
-
- *p = '\0';
- return 0;
- }
- static int build_replace_query(str* query, db_cmd_t* cmd)
- {
- db_fld_t* fld;
- int i;
- char* p;
- query->len = strings[STR_REPLACE].len;
- query->len += cmd->table.len;
- query->len += 2; /* " (" */
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- query->len += strlen(fld[i].name);
- query->len += strings[STR_ESC].len;
- if (!DB_FLD_LAST(fld[i + 1])) query->len += 2; /* , twice */
- }
- query->len += strings[STR_VALUES].len;
- query->len += 1; /* ) */
- query->s = pkg_malloc(query->len + 1);
- if (query->s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- p = query->s;
-
- APPEND_STR(p, strings[STR_REPLACE]);
- APPEND_STR(p, cmd->table);
- *p++ = ' ';
- *p++ = '(';
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- APPEND_CSTR(p, fld[i].name);
- if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
- }
- APPEND_STR(p, strings[STR_VALUES]);
- for(i = 0, fld = cmd->params; !DB_FLD_LAST(fld[i]); i++) {
- APPEND_STR(p, strings[STR_ESC]);
- if (!DB_FLD_LAST(fld[i + 1])) *p++ = ',';
- }
- *p++ = ')';
- *p = '\0';
- return 0;
- }
- static inline int update_params(MYSQL_STMT* st, db_fld_t* params)
- {
- int i;
- struct db_fld* f; /* Current field */
- struct my_fld* fp; /* Current field payload */
- struct tm* t;
- /* Iterate through all the query parameters and update
- * their values if needed
- */
- /* FIXME: We are updating internals of the prepared statement here,
- * this is probably not nice but I could not find another way of
- * updating the pointer to the buffer without the need to run
- * mysql_stmt_bind_param again (which would be innefficient
- */
- for(i = 0; i < st->param_count; i++) {
- fp = DB_GET_PAYLOAD(params + i);
- fp->is_null = params[i].flags & DB_NULL;
- if (fp->is_null) continue;
- switch(params[i].type) {
- case DB_STR:
- st->params[i].buffer = params[i].v.str.s;
- fp->length = params[i].v.str.len;
- break;
- case DB_BLOB:
- st->params[i].buffer = params[i].v.blob.s;
- fp->length = params[i].v.blob.len;
- break;
- case DB_CSTR:
- st->params[i].buffer = (char*)params[i].v.cstr;
- fp->length = strlen(params[i].v.cstr);
- break;
- case DB_DATETIME:
- t = gmtime(¶ms[i].v.time);
- fp->time.second = t->tm_sec;
- fp->time.minute = t->tm_min;
- fp->time.hour = t->tm_hour;
- fp->time.day = t->tm_mday;
- fp->time.month = t->tm_mon + 1;
- fp->time.year = t->tm_year + 1900;
- break;
- }
- }
- return 0;
- }
- static inline int update_result(db_fld_t* result, MYSQL_STMT* st)
- {
- int i;
- struct db_fld* r; /* Current field in the result */
- struct my_fld* rp; /* Payload of the current field in result */
- struct tm t;
- /* Iterate through all the query parameters and update
- * their values if needed
- */
- for(i = 0; i < st->field_count; i++) {
- rp = DB_GET_PAYLOAD(result + i);
- if (rp->is_null) {
- result[i].flags |= DB_NULL;
- continue;
- } else {
- result[i].flags &= ~DB_NULL;
- }
- switch(result[i].type) {
- case DB_STR:
- result[i].v.str.len = rp->length;
- break;
- case DB_BLOB:
- result[i].v.blob.len = rp->length;
- break;
- case DB_CSTR:
- result[i].v.cstr[rp->length] = '\0';
- break;
- case DB_DATETIME:
- memset(&t, '\0', sizeof(struct tm));
- t.tm_sec = rp->time.second;
- t.tm_min = rp->time.minute;
- t.tm_hour = rp->time.hour;
- t.tm_mday = rp->time.day;
- t.tm_mon = rp->time.month - 1;
- t.tm_year = rp->time.year - 1900;;
- /* Daylight saving information got lost in the database
- * so let timegm to guess it. This eliminates the bug when
- * contacts reloaded from the database have different time
- * of expiration by one hour when daylight saving is used
- */
- t.tm_isdst = -1;
- #ifdef HAVE_TIMEGM
- result[i].v.time = timegm(&t);
- #else
- result[i].v.time = _timegm(&t);
- #endif /* HAVE_TIMEGM */
- break;
- }
- }
- return 0;
- }
- int my_cmd_write(db_res_t* res, db_cmd_t* cmd)
- {
- struct my_cmd* mcmd;
- mcmd = DB_GET_PAYLOAD(cmd);
- if (mcmd->st->param_count && update_params(mcmd->st, cmd->params) < 0) return -1;
- if (mysql_stmt_execute(mcmd->st)) {
- ERR("Error while executing query: %s\n", mysql_stmt_error(mcmd->st));
- return -1;
- }
- return 0;
- }
- int my_cmd_read(db_res_t* res, db_cmd_t* cmd)
- {
- db_res_t* r;
- struct my_cmd* mcmd;
-
- mcmd = DB_GET_PAYLOAD(cmd);
- if (mcmd->st->param_count && update_params(mcmd->st, cmd->params) < 0) return -1;
- if (mysql_stmt_execute(mcmd->st)) {
- ERR("Error while executing query: %s\n", mysql_stmt_error(mcmd->st));
- return -1;
- }
- return 0;
- }
- static int bind_params(MYSQL_STMT* st, db_fld_t* fld)
- {
- int i, n;
- struct my_fld* f;
- MYSQL_BIND* params;
- /* Calculate the number of parameters */
- for(n = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[n]); n++);
- params = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * n);
- if (params == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- memset(params, '\0', sizeof(MYSQL_BIND) * n);
-
- for(i = 0; i < n; i++) {
- f = DB_GET_PAYLOAD(fld + i);
- params[i].is_null = &f->is_null;
- /* We can do it for all the types here, mysql will ignore it
- * for fixed-size types such as MYSQL_TYPE_LONG
- */
- params[i].length = &f->length;
- switch(fld[i].type) {
- case DB_INT:
- case DB_BITMAP:
- params[i].buffer_type = MYSQL_TYPE_LONG;
- params[i].buffer = &fld[i].v.int4;
- break;
- case DB_FLOAT:
- params[i].buffer_type = MYSQL_TYPE_FLOAT;
- params[i].buffer = &fld[i].v.flt;
- break;
-
- case DB_DOUBLE:
- params[i].buffer_type = MYSQL_TYPE_DOUBLE;
- params[i].buffer = &fld[i].v.dbl;
- break;
- case DB_DATETIME:
- params[i].buffer_type = MYSQL_TYPE_DATETIME;
- params[i].buffer = &f->time;
- break;
- case DB_STR:
- case DB_CSTR:
- params[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- params[i].buffer = ""; /* Updated on runtime */
- break;
- case DB_BLOB:
- params[i].buffer_type = MYSQL_TYPE_BLOB;
- params[i].buffer = ""; /* Updated on runtime */
- break;
- }
- }
- if (mysql_stmt_bind_param(st, params)) {
- ERR("Error while binding parameters: %s\n", mysql_stmt_error(st));
- goto error;
- }
- /* We do not need the array of MYSQL_BIND anymore, mysql_stmt_bind_param
- * creates a copy in the statement and we will update it there
- */
- pkg_free(params);
- return 0;
-
- error:
- if (params) pkg_free(params);
- return -1;
- }
- static int bind_result(MYSQL_STMT* st, db_fld_t* fld)
- {
- int i, n;
- struct my_fld* f;
- MYSQL_BIND* result;
- /* Calculate the number of fields in the result */
- for(n = 0; !DB_FLD_EMPTY(fld) && !DB_FLD_LAST(fld[n]); n++);
- result = (MYSQL_BIND*)pkg_malloc(sizeof(MYSQL_BIND) * n);
- if (result == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- memset(result, '\0', sizeof(MYSQL_BIND) * n);
-
- for(i = 0; i < n; i++) {
- f = DB_GET_PAYLOAD(fld + i);
- result[i].is_null = &f->is_null;
- /* We can do it for all the types here, mysql will ignore it
- * for fixed-size types such as MYSQL_TYPE_LONG
- */
- result[i].length = &f->length;
- switch(fld[i].type) {
- case DB_INT:
- case DB_BITMAP:
- result[i].buffer_type = MYSQL_TYPE_LONG;
- result[i].buffer = &fld[i].v.int4;
- break;
- case DB_FLOAT:
- result[i].buffer_type = MYSQL_TYPE_FLOAT;
- result[i].buffer = &fld[i].v.flt;
- break;
-
- case DB_DOUBLE:
- result[i].buffer_type = MYSQL_TYPE_DOUBLE;
- result[i].buffer = &fld[i].v.dbl;
- break;
- case DB_DATETIME:
- result[i].buffer_type = MYSQL_TYPE_DATETIME;
- result[i].buffer = &f->time;
- break;
- case DB_STR:
- result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- f->buf.s = pkg_malloc(STR_BUF_SIZE);
- if (f->buf.s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- result[i].buffer = f->buf.s;
- fld[i].v.str.s = f->buf.s;
- result[i].buffer_length = STR_BUF_SIZE - 1;
- break;
- case DB_CSTR:
- result[i].buffer_type = MYSQL_TYPE_VAR_STRING;
- f->buf.s = pkg_malloc(STR_BUF_SIZE);
- if (f->buf.s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- result[i].buffer = f->buf.s;
- fld[i].v.cstr = f->buf.s;
- result[i].buffer_length = STR_BUF_SIZE - 1;
- break;
- case DB_BLOB:
- result[i].buffer_type = MYSQL_TYPE_BLOB;
- f->buf.s = pkg_malloc(STR_BUF_SIZE);
- if (f->buf.s == NULL) {
- ERR("No memory left\n");
- return -1;
- }
- result[i].buffer = f->buf.s;
- fld[i].v.blob.s = f->buf.s;
- result[i].buffer_length = STR_BUF_SIZE - 1;
- break;
- }
- }
- if (mysql_stmt_bind_result(st, result)) {
- ERR("Error while binding result: %s\n", mysql_stmt_error(st));
- goto error;
- }
- /* We do not need the array of MYSQL_BIND anymore, mysql_stmt_bind_param
- * creates a copy in the statement and we will update it there
- */
- pkg_free(result);
- return 0;
-
- error:
- if (result) pkg_free(result);
- return -1;
- }
- int my_cmd(db_cmd_t* cmd)
- {
- struct my_cmd* res;
- struct my_con* mcon;
- res = (struct my_cmd*)pkg_malloc(sizeof(struct my_cmd));
- if (res == NULL) {
- ERR("No memory left\n");
- goto error;
- }
- memset(res, '\0', sizeof(struct my_cmd));
- if (db_drv_init(&res->gen, my_cmd_free) < 0) goto error;
- /* FIXME */
- mcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
- res->st = mysql_stmt_init(mcon->con);
- if (res->st == NULL) {
- ERR("No memory left\n");
- goto error;
- }
- switch(cmd->type) {
- case DB_PUT:
- if (DB_FLD_EMPTY(cmd->params)) {
- ERR("BUG: No parameters provided for DB_PUT in context '%.*s'\n",
- cmd->ctx->id.len, ZSW(cmd->ctx->id.s));
- goto error;
- }
- if (build_replace_query(&res->query, cmd) < 0) goto error;
- if (mysql_stmt_prepare(res->st, res->query.s, res->query.len)) {
- ERR("Error while preparing replace query: %s\n",
- mysql_stmt_error(res->st));
- goto error;
- }
- if (bind_params(res->st, cmd->params) < 0) goto error;
- break;
- case DB_DEL:
- if (build_delete_query(&res->query, cmd) < 0) goto error;
- if (mysql_stmt_prepare(res->st, res->query.s, res->query.len)) {
- ERR("Error while preparing delete query: %s\n",
- mysql_stmt_error(res->st));
- goto error;
- }
- if (!DB_FLD_EMPTY(cmd->params)) {
- if (bind_params(res->st, cmd->params) < 0) goto error;
- }
- break;
- case DB_GET:
- if (build_select_query(&res->query, cmd) < 0) goto error;
- if (mysql_stmt_prepare(res->st, res->query.s, res->query.len)) {
- ERR("Error while preparing select query: %s\n",
- mysql_stmt_error(res->st));
- goto error;
- }
- if (!DB_FLD_EMPTY(cmd->params)) {
- if (bind_params(res->st, cmd->params) < 0) goto error;
- }
- if (bind_result(res->st, cmd->result) < 0) goto error;
- break;
- }
- DB_SET_PAYLOAD(cmd, res);
- return 0;
- error:
- if (res) {
- db_drv_free(&res->gen);
- if (res->query.s) pkg_free(res->query.s);
- if (res->st) mysql_stmt_close(res->st);
- pkg_free(res);
- }
- return -1;
- }
- int my_cmd_next(db_res_t* res)
- {
- int ret;
- struct my_cmd* mcmd;
- mcmd = DB_GET_PAYLOAD(res->cmd);
- ret = mysql_stmt_fetch(mcmd->st);
-
- if (ret == MYSQL_NO_DATA) return 1;
- if (ret != 0) {
- ERR("Error in mysql_stmt_fetch: %s\n", mysql_stmt_error(mcmd->st));
- return -1;
- }
- if (update_result(res->cmd->result, mcmd->st) < 0) {
- mysql_stmt_free_result(mcmd->st);
- return -1;
- }
- res->cur_rec->fld = res->cmd->result;
- return 0;
- }
|