00001 #ifdef _WINDOWS
00002 #include <config-win.h>
00003 #endif
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
00021 #if (_MSC_VER < 1300 && WINVER < 0x0500)
00022
00023
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
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
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
00136 #endif
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
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(<ime, 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();
00229
00230 caltime = mktime(<ime);
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, <ime);
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);
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
00275 #endif
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;
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
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
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);
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
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
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
00836 meta = mysql_stmt_result_metadata(stmt->source);
00837 if (!meta) goto DONE;
00838
00839
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;
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