db_mysql.c

00001 #ifdef _WINDOWS
00002 #include <config-win.h>
00003 #endif /* _WINDOWS */
00004 
00005 #include <mysql.h>
00006 
00007 #include <stdlib.h>
00008 #include <string.h>
00009 
00010 #include "common.h"
00011 #include "db.h"
00012 #include "vector.h"
00013 #include "str.h"
00014 #include "hashtable.h"
00015 #include "util.h"
00016 #include "log.h"
00017 
00018 #include "db_mysql.h"
00019 
00020 // (WINVER < 0x0500)
00021 #if (_MSC_VER < 1300 && WINVER < 0x0500)
00022 /* pre VC7 */
00023 //extern "C" long _ftol( double ); //defined by VC6 C libs
00024 long _ftol2( double dblSource ) { return _ftol( dblSource ); }
00025 int _aulldvrm(void *p) { return _aulldiv(p); }
00026 void *_aligned_malloc(size_t size) { return malloc(size); }
00027 void _aligned_free(void *p) { free(p); }
00028 #endif
00029 
00030 
00031 static cp_db_connection *cp_mysql_open_conn(cp_data_source *data_source);
00032 static cp_result_set *cp_mysql_select(cp_db_connection *_conn, char *query);
00033 static int cp_mysql_fetch_metadata(cp_result_set *result_set);
00034 static int cp_mysql_fetch_next(cp_result_set *result_set);
00035 static int cp_mysql_release_result_set(cp_result_set *result_set);
00036 static int cp_mysql_update(cp_db_connection *_conn, char *query);
00037 static int cp_mysql_close_conn(cp_db_connection *connection);
00038 static char *cp_mysql_escape_string(cp_db_connection *conn, char *str, int len);
00039 static char *cp_mysql_escape_binary(cp_db_connection *conn, char *str, 
00040                                     int len, int *res_len);
00041 static cp_db_statement *
00042     cp_mysql_prepare_statement(cp_db_connection *conn, int prm_count, 
00043                                cp_field_type *prm_type, char *query);
00044 static int cp_mysql_execute_statement(cp_db_connection *conn, cp_db_statement *stmt, 
00045                                       cp_result_set **result_set, int *lengths,
00046                                       void **prm);
00047 static void cp_mysql_release_statement(cp_db_statement *statement);
00048 static void cp_mysql_set_autocommit(cp_db_connection *conn, int state);
00049 static int cp_mysql_commit(cp_db_connection *conn);
00050 static int cp_mysql_rollback(cp_db_connection *conn);
00051 
00052 volatile static int initialized = 0;
00053 static cp_db_actions *cp_mysql_db_actions = NULL;
00054 static cp_hashtable *mysql_field_type_map = NULL;
00055 
00056 #define DBNAME "mysql"
00057 
00058 typedef struct _cp_mysql_bind_desc
00059 {
00060     int len;
00061     MYSQL_BIND *bind;
00062     unsigned long *length;
00063     my_bool *is_null;
00064     char **buf;
00065 } cp_mysql_bind_desc;
00066 
00067 typedef struct _field_type_map
00068 {
00069     int mysql_field_code;
00070     int cprops_field_code;
00071 } field_type_map;
00072 
00073 field_type_map mysql2cprops[] = 
00074 {
00075     { MYSQL_TYPE_DECIMAL,      CP_FIELD_TYPE_LONG }, 
00076     { MYSQL_TYPE_TINY,         CP_FIELD_TYPE_SHORT }, 
00077     { MYSQL_TYPE_SHORT,        CP_FIELD_TYPE_SHORT}, 
00078     { MYSQL_TYPE_LONG,         CP_FIELD_TYPE_LONG }, 
00079     { MYSQL_TYPE_FLOAT,        CP_FIELD_TYPE_FLOAT }, 
00080     { MYSQL_TYPE_DOUBLE,       CP_FIELD_TYPE_DOUBLE }, 
00081 //  { MYSQL_TYPE_NULL,         CP_FIELD_TYPE_ }, 
00082     { MYSQL_TYPE_TIMESTAMP,    CP_FIELD_TYPE_TIMESTAMP }, 
00083     { MYSQL_TYPE_LONGLONG,     CP_FIELD_TYPE_LONG_LONG }, 
00084     { MYSQL_TYPE_INT24,        CP_FIELD_TYPE_LONG }, 
00085     { MYSQL_TYPE_DATE,         CP_FIELD_TYPE_DATE }, 
00086     { MYSQL_TYPE_TIME,         CP_FIELD_TYPE_TIME }, 
00087     { MYSQL_TYPE_DATETIME,     CP_FIELD_TYPE_DATE }, 
00088     { MYSQL_TYPE_YEAR,         CP_FIELD_TYPE_SHORT }, 
00089     { MYSQL_TYPE_NEWDATE,      CP_FIELD_TYPE_DATE }, 
00090     { MYSQL_TYPE_VARCHAR,      CP_FIELD_TYPE_VARCHAR }, 
00091     { MYSQL_TYPE_BIT,          CP_FIELD_TYPE_SHORT }, 
00092     { MYSQL_TYPE_NEWDECIMAL,   CP_FIELD_TYPE_LONG }, 
00093     { MYSQL_TYPE_ENUM,         CP_FIELD_TYPE_INT }, 
00094     { MYSQL_TYPE_SET,          CP_FIELD_TYPE_INT }, 
00095     { MYSQL_TYPE_TINY_BLOB,    CP_FIELD_TYPE_BLOB }, 
00096     { MYSQL_TYPE_MEDIUM_BLOB,  CP_FIELD_TYPE_BLOB }, 
00097     { MYSQL_TYPE_LONG_BLOB,    CP_FIELD_TYPE_BLOB }, 
00098     { MYSQL_TYPE_BLOB,         CP_FIELD_TYPE_BLOB }, 
00099     { MYSQL_TYPE_VAR_STRING,   CP_FIELD_TYPE_VARCHAR }, 
00100     { MYSQL_TYPE_STRING,       CP_FIELD_TYPE_VARCHAR } 
00101 //  { MYSQL_TYPE_GEOMETRY      CP_FIELD_TYPE_ }
00102 };
00103 
00104 cp_mysql_bind_desc *create_mysql_bind_desc(int len, MYSQL_BIND *bind, 
00105                                            cp_vector *types, long *lengths)
00106 {
00107     int i;
00108     cp_mysql_bind_desc *desc = calloc(1, sizeof(cp_mysql_bind_desc));
00109     desc->len = len;
00110     desc->bind = bind;
00111     desc->length = lengths;
00112     desc->is_null = calloc(len, sizeof(my_bool));
00113     desc->buf = calloc(len, sizeof(char *));
00114 
00115     for (i = 0; i < len; i++)
00116     {
00117         cp_field_type *type = cp_vector_element_at(types, i);
00118         switch(*type)
00119         {
00120             case CP_FIELD_TYPE_BOOLEAN:
00121             case CP_FIELD_TYPE_SHORT:
00122             case CP_FIELD_TYPE_INT:
00123             case CP_FIELD_TYPE_LONG:
00124                 desc->buf[i] = calloc(1, sizeof(long));
00125                 break;
00126                 
00127             case CP_FIELD_TYPE_LONG_LONG:
00128 #ifdef CP_HAS_LONG_LONG
00129                 desc->buf[i] = calloc(1, sizeof(long long));
00130 #else
00131 #ifdef _WINDOWS
00132                 desc->buf[i] = calloc(1, sizeof(ULARGE_INTEGER));
00133 #else
00134                 desc->buf[i] = calloc(1, sizeof(long));
00135 #endif /* _WINDOWS */
00136 #endif /* CP_HAS_LONG_LONG */
00137                 break;
00138                 
00139             case CP_FIELD_TYPE_FLOAT:
00140                 desc->buf[i] = calloc(1, sizeof(float));
00141                 break;
00142                 
00143             case CP_FIELD_TYPE_DOUBLE:
00144                 desc->buf[i] = calloc(1, sizeof(double));
00145                 break;
00146                 
00147             case CP_FIELD_TYPE_CHAR:
00148             case CP_FIELD_TYPE_VARCHAR:
00149                 if (lengths[i] == 0) lengths[i] = 0x400;
00150                 desc->buf[i] = calloc(1, lengths[i]);
00151                 bind[i].buffer_length = lengths[i];
00152                 break;
00153                 
00154             case CP_FIELD_TYPE_BLOB:
00155                 if (lengths[i] == 0) lengths[i] = 0x2000;
00156                 desc->buf[i] = calloc(1, lengths[i]);
00157                 bind[i].buffer_length = lengths[i];
00158                 break;
00159                 
00160             case CP_FIELD_TYPE_DATE:
00161             case CP_FIELD_TYPE_TIME:
00162             case CP_FIELD_TYPE_TIMESTAMP:
00163                 desc->buf[i] = calloc(1, sizeof(MYSQL_TIME));
00164                 break;
00165 
00166             default:
00167                 DEBUGMSG("mystery field type: %d", *type);
00168                 break;
00169         }
00170 
00171         bind[i].buffer = desc->buf[i];
00172         bind[i].length = &desc->length[i];
00173     }
00174 
00175     return desc;
00176 }
00177 
00178 void cp_mysql_bind_desc_destroy(cp_mysql_bind_desc *desc)
00179 {
00180     if (desc)
00181     {
00182         if (desc->bind) free(desc->bind);
00183         if (desc->length) free(desc->length);
00184         if (desc->is_null) free(desc->is_null);
00185         if (desc->buf) 
00186         {
00187             int i;
00188             for (i = 0; i < desc->len; i++)
00189                 if (desc->buf[i]) free(desc->buf[i]);
00190             free(desc->buf);
00191         }
00192         free(desc);
00193     }
00194 }
00195 
00196 /* ------------------------------------------------------------------------  
00197                                                                              
00198                             date conversion functions                        
00199                                                                              
00200    ------------------------------------------------------------------------ */
00201 
00202 static int is_date_type(cp_field_type type)
00203 {
00204     return type == CP_FIELD_TYPE_DATE || type == CP_FIELD_TYPE_TIME ||
00205            type == CP_FIELD_TYPE_TIMESTAMP;
00206 }
00207 
00208 static int isdst()
00209 {
00210     struct tm local;
00211     time_t t = time(NULL);
00212     localtime_r(&t, &local);
00213     return local.tm_isdst;
00214 }
00215 
00216 static cp_timestampz *mysqltime2cp_timestampz(MYSQL_TIME *mtime)
00217 {
00218     struct tm ltime;
00219     time_t caltime;
00220     
00221     memset(&ltime, 0, sizeof(struct tm));
00222     ltime.tm_sec = mtime->second;
00223     ltime.tm_min = mtime->minute;
00224     ltime.tm_hour = mtime->hour;
00225     ltime.tm_mday = mtime->day ? mtime->day : 1;
00226     ltime.tm_mon = mtime->month ? mtime->month - 1 : 0;
00227     ltime.tm_year = mtime->year ? mtime->year - 1900 : 70;
00228     ltime.tm_isdst = isdst(); //~~ MYSQL_TIME doesn't disclose timezone/dst
00229 
00230     caltime = mktime(&ltime);
00231     return cp_timestampz_create_prm(caltime, 0, 0);
00232 }
00233 
00234 static MYSQL_TIME *cp_timestampz2mysqltime(cp_timestampz *tmz)
00235 {
00236     MYSQL_TIME *res = calloc(1, sizeof(MYSQL_TIME));
00237     struct tm ltime;
00238     
00239     if (res == NULL) return NULL;
00240     
00241     localtime_r(&tmz->tm.tv_sec, &ltime);
00242 
00243     res->second = ltime.tm_sec;
00244     res->minute = ltime.tm_min;
00245     res->hour = ltime.tm_hour;
00246     res->day = ltime.tm_mday;
00247     res->month = ltime.tm_mon + 1;
00248     res->year = ltime.tm_year + 1900;
00249 
00250     return res;
00251 }
00252 
00253 cp_vector *cp_mysql_bind_desc_get_row(cp_mysql_bind_desc *desc)
00254 {
00255     int i;
00256     cp_vector *v = cp_vector_create(desc->len); //~~ NOSYNC etc
00257     void *p;
00258     size_t len;
00259     
00260     for (i = 0; i < desc->len; i++)
00261     {
00262         switch (desc->bind[i].buffer_type)
00263         {
00264             case MYSQL_TYPE_TINY: len = sizeof(short); break;
00265             case MYSQL_TYPE_SHORT: len = sizeof(short); break;
00266             case MYSQL_TYPE_LONG: len = sizeof(long); break;
00267 #ifdef CP_HAS_LONG_LONG
00268             case MYSQL_TYPE_LONGLONG: len = sizeof(long long); break;
00269 #else
00270 #ifdef _WINDOWS
00271             case MYSQL_TYPE_LONGLONG: len = sizeof(ULARGE_INTEGER); break;
00272 #else
00273             case MYSQL_TYPE_LONGLONG: len = sizeof(long); break;
00274 #endif /* _WINDOWS */
00275 #endif /* CP_HAS_LONG_LONG */
00276             case MYSQL_TYPE_FLOAT: len = sizeof(float); break;
00277             case MYSQL_TYPE_DOUBLE: len = sizeof(double); break;
00278             case MYSQL_TYPE_TIME: 
00279             case MYSQL_TYPE_DATE: 
00280             case MYSQL_TYPE_DATETIME: 
00281             case MYSQL_TYPE_TIMESTAMP:
00282                 p = mysqltime2cp_timestampz((MYSQL_TIME *) desc->buf[i]);
00283                 cp_vector_set_element(v, i, p);
00284                 len = 0; 
00285                 break;
00286             case MYSQL_TYPE_STRING:
00287             case MYSQL_TYPE_VAR_STRING:
00288             case MYSQL_TYPE_BLOB: 
00289                 p = cp_string_create(desc->buf[i], desc->length[i]);
00290                 cp_vector_set_element(v, i, p);
00291                 len = 0;
00292                 break;
00293             default: len = 0; break; //~~ err msg                                 
00294         }
00295 
00296         if (len > 0)
00297         {
00298             p = malloc(len);
00299             memcpy(p, desc->buf[i], len);
00300             cp_vector_set_element(v, i, p);
00301         }
00302     }
00303 
00304     return v;
00305 }
00306 
00307 
00308 void cp_mysql_shutdown()
00309 {
00310     if (!initialized) return;
00311     initialized = 0;
00312 
00313     if (cp_mysql_db_actions) 
00314     {
00315         cp_db_actions_destroy(cp_mysql_db_actions);
00316         cp_mysql_db_actions = NULL;
00317     }
00318 
00319     if (mysql_field_type_map) 
00320     {
00321         cp_hashtable_destroy(mysql_field_type_map);
00322         mysql_field_type_map = NULL;
00323     }
00324 }
00325 
00326     
00327 void cp_mysql_init()
00328 {
00329     int id;
00330     int i, types;
00331     if (initialized) return;
00332     initialized = 1;
00333 
00334     id = cp_db_register_dbms(DBNAME, cp_mysql_shutdown);
00335 
00336     cp_mysql_db_actions = 
00337         cp_db_actions_create(id, DBNAME, cp_mysql_open_conn, cp_mysql_select, 
00338                 cp_mysql_fetch_metadata, cp_mysql_fetch_next, 
00339                 cp_mysql_release_result_set, cp_mysql_update, 
00340                 cp_mysql_close_conn, cp_mysql_escape_string, 
00341                 cp_mysql_escape_binary, NULL, cp_mysql_prepare_statement, 
00342                 cp_mysql_execute_statement, cp_mysql_release_statement,
00343                 cp_mysql_set_autocommit, cp_mysql_commit, cp_mysql_rollback);
00344 
00345     types = sizeof(mysql2cprops) / sizeof(field_type_map);
00346     mysql_field_type_map = 
00347         cp_hashtable_create_by_mode(COLLECTION_MODE_NOSYNC, types, 
00348                                     cp_hash_int, cp_hash_compare_int);
00349     for (i = 0; i < types; i++)
00350         cp_hashtable_put(mysql_field_type_map, 
00351                          &mysql2cprops[i].mysql_field_code,
00352                          &mysql2cprops[i].cprops_field_code);
00353 }
00354 
00355 static void 
00356     cp_mysql_connection_parameters_delete(cp_mysql_connection_parameters *prm)
00357 {
00358     if (prm)
00359     {
00360         if (prm->host) free(prm->host);
00361         if (prm->login) free(prm->login);
00362         if (prm->password) free(prm->password);
00363         if (prm->db_name) free(prm->db_name);
00364         if (prm->unix_socket) free(prm->unix_socket);
00365         cp_free(prm);
00366     }
00367 }
00368 
00369 cp_data_source *
00370     cp_mysql_data_source(char *host, 
00371                          char *login, 
00372                          char *password, 
00373                          char *db_name, 
00374                          unsigned int port,
00375                          char *unix_socket, 
00376                          unsigned long client_flag)
00377 {
00378     cp_data_source *data_source;
00379     cp_mysql_connection_parameters *impl;
00380 
00381     if (!initialized) cp_mysql_init();
00382 
00383     data_source = cp_calloc(1, sizeof(cp_data_source));
00384     if (data_source == NULL) return NULL;
00385 
00386     data_source->act = cp_mysql_db_actions;
00387 
00388     data_source->prm = cp_calloc(1, sizeof(cp_db_connection_parameters));
00389     if (data_source->prm == NULL)
00390     {
00391         cp_free(data_source);
00392         return NULL;
00393     }
00394     impl = cp_calloc(1, sizeof(cp_mysql_connection_parameters));
00395     if (impl == NULL)
00396     {
00397         cp_free(data_source->prm);
00398         cp_free(data_source);
00399         return NULL;
00400     }
00401 
00402     impl->host = strdup(host);
00403     impl->port = port;
00404     impl->login = strdup(login);
00405     impl->password = password ? strdup(password) : NULL;
00406     impl->db_name = db_name ? strdup(db_name) : NULL;
00407     impl->unix_socket = unix_socket ? strdup(unix_socket) : NULL;
00408     impl->client_flag = client_flag;
00409     data_source->prm->impl = impl;
00410     data_source->prm->impl_dtr = 
00411         (cp_destructor_fn) cp_mysql_connection_parameters_delete;
00412 
00413     return data_source;
00414 }
00415 
00416 cp_data_source *
00417     cp_dbms_mysql_get_data_source(char *host, 
00418                                   int port, 
00419                                   char *login, 
00420                                   char *password,
00421                                   char *dbname)
00422 {
00423     return cp_mysql_data_source(host, login, password, dbname, port, NULL, 0);
00424 }
00425 
00426 cp_data_source *
00427     cp_dbms_mysql_get_data_source_prm(char *host,
00428                                       int port, 
00429                                       char *login, 
00430                                       char *password, 
00431                                       char *dbname, 
00432                                       cp_hashtable *prm)
00433 {
00434     int *client_flag = cp_hashtable_get(prm, "client_flag");
00435     return cp_mysql_data_source(host, login, password, dbname, port, 
00436                                    cp_hashtable_get(prm, "unix_socket"),
00437                                    client_flag ? (*client_flag) : 0);
00438 }
00439 
00440 
00441 static cp_db_connection *cp_mysql_open_conn(cp_data_source *data_source)
00442 {
00443     MYSQL *conn = NULL;
00444     cp_mysql_connection_parameters *conf = data_source->prm->impl;
00445     cp_db_connection *res;
00446 
00447     conn = malloc(sizeof(MYSQL));
00448     if (conn == NULL)
00449     {
00450         cp_error(CP_MEMORY_ALLOCATION_FAILURE, 
00451                 "can\'t allocate MYSQL connection handle");
00452         return NULL;
00453     }
00454 
00455     if (!mysql_init(conn))
00456     {
00457         cp_error(CP_DBMS_CONNECTION_FAILURE,
00458                 "can\'t initialize MYSQL connection");
00459         return NULL;
00460     }
00461 
00462     if (!mysql_real_connect(conn, conf->host, conf->login, conf->password, 
00463                             conf->db_name, conf->port, conf->unix_socket, 
00464                             conf->client_flag))
00465     {
00466         cp_error(CP_DBMS_CONNECTION_FAILURE, 
00467                 "can\'t connect to mysql dbms:%s", mysql_error(conn));
00468         free(conn);
00469         return NULL;
00470     }
00471 
00472     res = cp_calloc(1, sizeof(cp_db_connection));
00473     res->data_source = data_source;
00474     res->connection = conn;
00475     res->autocommit = 1;
00476     return res;
00477 }
00478     
00479 static cp_field_type convert_field_type(int field_type)
00480 {
00481     cp_field_type *type = cp_hashtable_get(mysql_field_type_map, &field_type);
00482     return type ? *type : field_type;
00483 }
00484 
00485 static cp_result_set *create_mysql_result_set(cp_db_connection *_conn, 
00486                                               MYSQL_RES *src, 
00487                                               int fetch_metadata)
00488 {
00489     cp_result_set *res = cp_calloc(1, sizeof(cp_result_set));
00490     if (res == NULL) return NULL;
00491 
00492     res->connection = _conn;
00493     res->source = src;
00494     res->field_count = mysql_num_fields(src);
00495 
00496     res->results = cp_list_create();
00497 
00498     if (fetch_metadata) cp_mysql_fetch_metadata(res);
00499 
00500     return res;
00501 }
00502 
00503 static int fetch_rows(cp_result_set *result_set, MYSQL_RES *src, int count)
00504 {
00505     int i, j;
00506     MYSQL_ROW row;
00507     long *lengths;
00508     cp_vector *curr;
00509 
00510     for (i = 0; i < count; i++)
00511     {
00512         if ((row = mysql_fetch_row(src)) == NULL) break;
00513         result_set->row_count++;
00514         lengths = mysql_fetch_lengths(src);
00515         curr = cp_vector_create_by_option(result_set->field_count, 
00516                     COLLECTION_MODE_DEEP, 
00517                     NULL, (cp_destructor_fn) cp_string_destroy);
00518         for (j = 0; j < result_set->field_count; j++)
00519         {
00520             if (row[j])
00521                 cp_vector_set_element(curr, j, 
00522                         cp_string_create(row[j], lengths[j]));
00523         }
00524         cp_list_append(result_set->results, curr);
00525     }
00526 
00527     return i;
00528 }
00529 
00530 static void set_fetch_complete(cp_result_set *rs)
00531 {
00532     rs->fetch_complete = 1;
00533     if (rs->store) 
00534     {
00535         cp_mysql_bind_desc_destroy(rs->store);
00536         rs->store = NULL;
00537     }
00538 }
00539 
00540 void fetch_stmt_rows(cp_result_set *rs, int count)
00541 {
00542     cp_mysql_bind_desc *desc = rs->store;
00543     cp_db_statement *stmt = rs->source;
00544     cp_vector *row;
00545     int i;
00546     int rc;
00547 
00548     for (i = 0; count == 0 || i < count; i++)
00549     {
00550         if ((rc = mysql_stmt_fetch(stmt->source))) 
00551         {
00552             set_fetch_complete(rs);
00553             break;
00554         }
00555 
00556         row = cp_mysql_bind_desc_get_row(desc);
00557         rs->row_count++;
00558         cp_list_append(rs->results, row);
00559     }
00560 }
00561 
00562 static cp_result_set *cp_mysql_select(cp_db_connection *_conn, char *query)
00563 {
00564     int rc = -1;
00565     MYSQL *conn = _conn->connection;
00566     MYSQL_RES *src;
00567     cp_result_set *res;
00568     
00569     if ((rc = mysql_query(conn, query)) != 0)
00570     {
00571         cp_error(CP_DBMS_QUERY_FAILED, "%s\n%s: %s", query, 
00572                  _conn->data_source->act->dbms_lit, mysql_error(conn));
00573         return NULL;
00574     }
00575 
00576     /* handle read-at-once */
00577     if (_conn->read_result_set_at_once || _conn->fetch_size == 0)
00578     {
00579         src = mysql_store_result(conn);
00580         if (src == NULL)
00581         {
00582             cp_error(CP_DBMS_CLIENT_ERROR, "%s: can\'t retrieve result", 
00583                      _conn->data_source->act->dbms_lit);
00584             cp_error(CP_DBMS_CLIENT_ERROR, "query: %s", query);
00585             return NULL;
00586         }
00587         res = create_mysql_result_set(_conn, src, _conn->autofetch_query_metadata);
00588         fetch_rows(res, src, mysql_num_rows(src));
00589         set_fetch_complete(res);
00590         mysql_free_result(src);
00591     }
00592     else /* perform chunked read */
00593     {
00594         src = mysql_use_result(conn);
00595         if (src == NULL) 
00596         {
00597             cp_error(CP_DBMS_CLIENT_ERROR, "%s: can\'t retrieve result", 
00598                      _conn->data_source->act->dbms_lit);
00599             cp_error(CP_DBMS_CLIENT_ERROR, "query: %s", query);
00600             return NULL;
00601         }
00602         res = create_mysql_result_set(_conn, src, _conn->autofetch_query_metadata);
00603         fetch_rows(res, src, _conn->fetch_size);
00604     }
00605     
00606     return res;
00607 }
00608 
00609 static int cp_mysql_fetch_metadata(cp_result_set *res)
00610 {
00611     int i;
00612     MYSQL_FIELD *field;
00613     MYSQL_RES *src = res->source;
00614     if (src == NULL ||
00615         res->field_headers != NULL ||
00616         res->field_types != NULL) return -1;
00617     
00618     res->field_headers = cp_vector_create(res->field_count);
00619     res->field_types = 
00620         cp_vector_create_by_option(res->field_count, 
00621                 COLLECTION_MODE_COPY | COLLECTION_MODE_DEEP | 
00622                 COLLECTION_MODE_MULTIPLE_VALUES, 
00623                 (cp_copy_fn) intdup, (cp_destructor_fn) free);
00624     for (i = 0; i < res->field_count; i++)
00625     {
00626         int type; 
00627         field = mysql_fetch_field(src);
00628         type = convert_field_type(field->type); 
00629         cp_vector_set_element(res->field_headers, i, strdup(field->name));
00630         cp_vector_set_element(res->field_types, i, &type);
00631     }
00632     
00633     return 0;
00634 }
00635 
00636 static int cp_mysql_fetch_next(cp_result_set *result_set)
00637 {
00638     MYSQL_RES *src = result_set->source;
00639     if (result_set->fetch_complete) return 0;
00640 
00641     if (result_set->position == result_set->row_count)
00642     {
00643         if (result_set->store)
00644             fetch_stmt_rows(result_set, result_set->connection->fetch_size);
00645         else
00646             fetch_rows(result_set, src, result_set->row_count);
00647     }
00648 
00649     return 0;
00650 }
00651 
00652 static int cp_mysql_release_result_set(cp_result_set *result_set)
00653 {
00654     int rc = -1;
00655 
00656     if (result_set)
00657     {
00658         if (result_set->source)
00659         {
00660             MYSQL_RES *src = result_set->source;
00661             while ((mysql_fetch_row(src)) != NULL); /* flush result buffer */
00662             mysql_free_result(src);
00663             result_set->source = NULL;
00664             rc = 0;
00665         }
00666 
00667         if (result_set->store)
00668             cp_mysql_bind_desc_destroy(result_set->store);
00669     }
00670     
00671     return rc;
00672 }
00673 
00674 static int cp_mysql_update(cp_db_connection *_conn, char *query)
00675 {
00676     int rc = -1;
00677     MYSQL *conn = _conn->connection;
00678     
00679     if (conn)
00680         rc = mysql_query(conn, query);
00681 
00682     return rc;
00683 }
00684 
00685 static int cp_mysql_close_conn(cp_db_connection *connection)
00686 {
00687     MYSQL *conn = connection->connection;
00688     if (conn == NULL) return -1;
00689     
00690     mysql_close(conn);
00691     free(conn);
00692     connection->connection = NULL;
00693     return 0;
00694 }
00695 
00696 static char *cp_mysql_escape_string(cp_db_connection *conn, char *str, int len)
00697 {
00698     MYSQL *mysql = conn->connection;
00699     char *res = malloc(2 * len + 1);
00700 
00701     if (res)
00702         mysql_real_escape_string(mysql, res, str, len);
00703     
00704     return res;
00705 }
00706 
00707 static char *cp_mysql_escape_binary(cp_db_connection *conn, char *str, 
00708                                     int len, int *res_len)
00709 {
00710     MYSQL *mysql = conn->connection;
00711     char *res = malloc(2 * len + 1);
00712 
00713     if (res)
00714         *res_len = mysql_real_escape_string(mysql, res, str, len);
00715     
00716     return res;
00717 }
00718 
00719 int cprops2mysql(cp_field_type type)
00720 {
00721     switch(type)
00722     {
00723         case CP_FIELD_TYPE_BOOLEAN: return MYSQL_TYPE_SHORT;
00724         case CP_FIELD_TYPE_CHAR: return MYSQL_TYPE_VARCHAR;
00725         case CP_FIELD_TYPE_SHORT: return MYSQL_TYPE_SHORT;
00726         case CP_FIELD_TYPE_INT: 
00727         case CP_FIELD_TYPE_LONG: return MYSQL_TYPE_LONG;
00728         case CP_FIELD_TYPE_LONG_LONG: return MYSQL_TYPE_LONGLONG;
00729         case CP_FIELD_TYPE_FLOAT: return MYSQL_TYPE_FLOAT;
00730         case CP_FIELD_TYPE_DOUBLE: return MYSQL_TYPE_DOUBLE;
00731         case CP_FIELD_TYPE_VARCHAR: return MYSQL_TYPE_VARCHAR;
00732         case CP_FIELD_TYPE_BLOB: return MYSQL_TYPE_BLOB;
00733         case CP_FIELD_TYPE_DATE: return MYSQL_TYPE_DATE;
00734         case CP_FIELD_TYPE_TIME: return MYSQL_TYPE_DATETIME;
00735         case CP_FIELD_TYPE_TIMESTAMP: return MYSQL_TYPE_DATETIME;
00736     }
00737 
00738     return MYSQL_TYPE_NULL;
00739 }
00740 
00741 static cp_db_statement *
00742     cp_mysql_prepare_statement(cp_db_connection *conn, int prm_count, 
00743                                cp_field_type *prm_type, char *query)
00744 {
00745     MYSQL_STMT *stmt; 
00746     cp_db_statement *res;
00747     stmt = mysql_stmt_init(conn->connection);
00748     if (!stmt)
00749     {
00750         cp_error(CP_MEMORY_ALLOCATION_FAILURE, 
00751                 "%s: can\'t allocate statement", conn->data_source->act->dbms_lit);
00752         return NULL;
00753     }
00754     
00755     if (mysql_stmt_prepare(stmt, query, strlen(query)))
00756     {
00757         cp_error(CP_DBMS_STATEMENT_ERROR, "%s: can\'t prepare statement: %s", 
00758                  conn->data_source->act->dbms_lit, mysql_stmt_error(stmt));
00759         mysql_stmt_close(stmt);
00760         return NULL;
00761     }
00762 
00763     res = cp_db_statement_instantiate(conn, prm_count, prm_type, stmt);
00764     if (res == NULL)
00765     {
00766         cp_error(CP_MEMORY_ALLOCATION_FAILURE, "%s: can\'t allocate statement"
00767                  " wrapper", conn->data_source->act->dbms_lit);
00768         mysql_stmt_close(stmt);
00769         return NULL;
00770     }
00771     
00772     return res;
00773 }
00774 
00775 
00776 //~~ improve: allocate bind parameter structure beforehand and reuse
00777 static int 
00778     cp_mysql_execute_statement(cp_db_connection *conn, cp_db_statement *stmt, 
00779                                cp_result_set **result_set, int *lengths,
00780                                void **prm)
00781 {
00782     int i;
00783     int rc;
00784     MYSQL_BIND *bind = NULL;
00785     MYSQL_RES *meta = NULL;
00786     cp_result_set *rs = NULL;
00787     int field_count;
00788     my_bool *is_null = NULL;
00789     long *longlengths = calloc(stmt->prm_count, sizeof(long));
00790     long *reslengths;
00791 
00792     if (lengths)
00793         for (i = 0; i < stmt->prm_count; i++)
00794             longlengths[i] = lengths[i];
00795 
00796     if (stmt->prm_count)
00797     {
00798         bind = calloc(stmt->prm_count, sizeof(MYSQL_BIND));
00799         is_null = calloc(stmt->prm_count, sizeof(my_bool));
00800     }
00801 
00802     for (i = 0; i < stmt->prm_count; i++)
00803     {
00804         bind[i].buffer_type = cprops2mysql(stmt->types[i]);
00805         if (is_date_type(stmt->types[i]))
00806             bind[i].buffer = cp_timestampz2mysqltime(prm[i]);
00807         else
00808             bind[i].buffer = prm[i];
00809         bind[i].is_null = &is_null[i];
00810         if (lengths) bind[i].length = &longlengths[i];
00811 
00812         if(mysql_stmt_bind_param(stmt->source, bind))
00813         {
00814             cp_error(CP_DBMS_STATEMENT_ERROR, "%s: can\'t bind parameter: %s", 
00815                 conn->data_source->act->dbms_lit, mysql_stmt_error(stmt->source));
00816             return -1;
00817         }
00818     }
00819 
00820     if (mysql_stmt_execute(stmt->source))
00821     {
00822         cp_error(CP_DBMS_QUERY_FAILED, "%s: prepared statement: %s", 
00823                  conn->data_source->act->dbms_lit, mysql_stmt_error(stmt->source));
00824         //~~ clean up
00825         return -1;
00826     }
00827 
00828     for (i = 0; i < stmt->prm_count; i++)
00829         if (is_date_type(stmt->types[i]))
00830             free(bind[i].buffer);
00831     if (bind) free(bind);
00832 
00833     rc = mysql_stmt_affected_rows(stmt->source);
00834 
00835     /* if stmt returns a result set, meta data should be available */
00836     meta = mysql_stmt_result_metadata(stmt->source);
00837     if (!meta) goto DONE;
00838 
00839     /* if it's a result set, fetch 'em */
00840     field_count = mysql_num_fields(meta);
00841     if (field_count) 
00842     {
00843         MYSQL_FIELD *field;
00844         bind = calloc(field_count, sizeof(MYSQL_BIND));
00845         reslengths = calloc(field_count, sizeof(long));
00846         rs = create_mysql_result_set(conn, meta, 0);
00847         rs->binary = 1; /* results for mysql prepared stmts come in binary format */
00848         rs->source = stmt;
00849 
00850         rs->field_headers = 
00851             cp_vector_create_by_option(rs->field_count, 
00852                     COLLECTION_MODE_COPY | COLLECTION_MODE_DEEP | 
00853                     COLLECTION_MODE_MULTIPLE_VALUES,
00854                     (cp_copy_fn) strdup, (cp_destructor_fn) free);
00855         rs->field_types = 
00856             cp_vector_create_by_option(rs->field_count, 
00857                     COLLECTION_MODE_COPY | COLLECTION_MODE_DEEP | 
00858                     COLLECTION_MODE_MULTIPLE_VALUES, 
00859                     (cp_copy_fn) intdup, (cp_destructor_fn) free);
00860         for (i = 0; i < rs->field_count; i++)
00861         {
00862             int type; 
00863             field = mysql_fetch_field(meta);
00864             type = convert_field_type(field->type); 
00865             cp_vector_set_element(rs->field_headers, i, field->name);
00866             cp_vector_set_element(rs->field_types, i, &type);
00867             bind[i].buffer_type = field->type;
00868             reslengths[i] = field->max_length;
00869             bind[i].length = &reslengths[i];
00870         }
00871 
00872         rs->store = 
00873             create_mysql_bind_desc(field_count, bind, rs->field_types, reslengths);
00874 
00875         if (mysql_stmt_bind_result(stmt->source, bind))
00876         {
00877             cp_error(CP_DBMS_CLIENT_ERROR, "%s: error binding prepared "
00878                      "statement results: %s", conn->data_source->act->dbms_lit,
00879                      mysql_stmt_error(stmt->source));
00880             cp_result_set_destroy(rs);
00881             rs = NULL;
00882             goto DONE;
00883         }
00884     }
00885     
00886     if (conn->read_result_set_at_once || conn->fetch_size == 0)
00887     {
00888         if (mysql_stmt_store_result(stmt->source))
00889         {
00890             cp_error(CP_DBMS_CLIENT_ERROR, "%s: error retrieving prepared "
00891                      "statement results: %s", conn->data_source->act->dbms_lit,
00892                      mysql_stmt_error(stmt->source));
00893             cp_result_set_destroy(rs);
00894             rs = NULL;
00895             goto DONE;
00896         }
00897 
00898         fetch_stmt_rows(rs, 0); 
00899         rs->fetch_complete = 1;
00900     }
00901     else
00902         fetch_stmt_rows(rs, conn->fetch_size);
00903 
00904 DONE:
00905     free(longlengths);
00906     free(is_null);
00907     mysql_free_result(meta);
00908     if (result_set)
00909         *result_set = rs;
00910     return rc;
00911 }
00912 
00913 static void cp_mysql_release_statement(cp_db_statement *statement)
00914 {
00915     if (mysql_stmt_close(statement->source))
00916     {
00917         cp_error(CP_DBMS_CLIENT_ERROR, "%s: error closing prepared statement: %s",
00918                  statement->connection->data_source->act->dbms_lit,
00919                  mysql_stmt_error(statement->source));
00920     }
00921     if (statement->store) free(statement->store);
00922 }
00923 
00924 static void cp_mysql_set_autocommit(cp_db_connection *conn, int state)
00925 {
00926     mysql_autocommit(conn->connection, state);
00927 }   
00928 
00929 static int cp_mysql_commit(cp_db_connection *conn)
00930 {
00931     return mysql_commit(conn->connection);
00932 }
00933 
00934 static int cp_mysql_rollback(cp_db_connection *conn)
00935 {
00936     return mysql_rollback(conn->connection);
00937 }
00938 

Generated on Mon Dec 5 23:00:21 2011 for cprops by  doxygen 1.4.7