00001 #include <stdlib.h>
00002 #include <string.h>
00003 #include <stdarg.h>
00004 #include <time.h>
00005
00006 #include "config.h"
00007 #ifdef CP_HAS_DLFCN_H
00008 #include <dlfcn.h>
00009 #else
00010 #include "util.h"
00011 #endif
00012
00013 #ifdef CP_HAS_SYS_TIME_H
00014 #include <sys/time.h>
00015 #endif
00016
00017 #include "db.h"
00018
00019
00020
00021
00022
00023
00024 volatile static int initialized = 0;
00025 static cp_list *shutdown_hook = NULL;
00026 static cp_hashtable *drivers = NULL;
00027
00028 int cp_db_init()
00029 {
00030 if (initialized) return 1;
00031 initialized = 1;
00032
00033 shutdown_hook = cp_list_create();
00034 return 0;
00035 }
00036
00037 int cp_db_register_dbms(char *name, void (*shutdown_call)())
00038 {
00039 cp_list_append(shutdown_hook, shutdown_call);
00040
00041 return cp_list_item_count(shutdown_hook);
00042 }
00043
00044 static void invoke_shutdown_call(void *fn)
00045 {
00046 void (*call)() = fn;
00047 (*call)();
00048 }
00049
00050 void cp_dbms_driver_descriptor_destroy(void *descriptor)
00051 {
00052 cp_dbms_driver_descriptor *desc = descriptor;
00053 #if defined(CP_HAS_DLFCN_H) || defined (_WINDOWS)
00054 if (desc->lib)
00055 dlclose(desc->lib);
00056 #endif
00057 free(desc);
00058 }
00059
00060 int cp_db_shutdown()
00061 {
00062 if (!initialized) return 1;
00063 initialized = 0;
00064
00065 cp_list_destroy_custom(shutdown_hook, invoke_shutdown_call);
00066 if (drivers) cp_hashtable_destroy(drivers);
00067
00068 return 0;
00069 }
00070
00071
00072
00073
00074
00075
00076
00077 cp_timestampz *cp_timestampz_create(struct timeval *tm, int minuteswest)
00078 {
00079 cp_timestampz *tmz = calloc(1, sizeof(cp_timestampz));
00080 if (tmz == NULL) return NULL;
00081
00082 tmz->tm.tv_sec = tm->tv_sec;
00083 tmz->tm.tv_usec = tm->tv_usec;
00084 tmz->tz_minuteswest = minuteswest;
00085
00086 return tmz;
00087 }
00088
00089 cp_timestampz *
00090 cp_timestampz_create_prm(int sec_since_epoch, int microsec, int minwest)
00091 {
00092 cp_timestampz *tmz = calloc(1, sizeof(cp_timestampz));
00093 if (tmz == NULL) return NULL;
00094
00095 tmz->tm.tv_sec = sec_since_epoch;
00096 tmz->tm.tv_usec = microsec;
00097 tmz->tz_minuteswest = minwest;
00098
00099 return tmz;
00100 }
00101
00102 struct tm *cp_timestampz_localtime(cp_timestampz *tm, struct tm *res)
00103 {
00104 #ifdef CP_HAS_LOCALTIME_R
00105 return localtime_r((time_t *) &tm->tm.tv_sec, res);
00106 #else
00107 res = localtime((time_t *) &tm->tm.tv_sec);
00108 return res;
00109 #endif
00110 }
00111
00112 void cp_timestampz_destroy(cp_timestampz *tm)
00113 {
00114 free(tm);
00115 }
00116
00117
00118
00119
00120
00121
00122
00123
00124 void cp_db_connection_parameters_destroy(cp_db_connection_parameters *prm)
00125 {
00126 if (prm->impl_dtr)
00127 (*prm->impl_dtr)(prm->impl);
00128 else
00129 free(prm->impl);
00130
00131 free(prm);
00132 }
00133
00134
00135
00136
00137
00138
00139
00140 cp_result_set *
00141 cp_db_connection_select(cp_db_connection *connection, char *query)
00142 {
00143 return (*connection->data_source->act->select)(connection, query);
00144 }
00145
00146 int cp_db_connection_update(cp_db_connection *connection, char *query)
00147 {
00148 return (*connection->data_source->act->update)(connection, query);
00149 }
00150
00151 int cp_db_connection_close(cp_db_connection *connection)
00152 {
00153 return (*connection->data_source->act->close)(connection);
00154 }
00155
00156 void cp_db_connection_destroy(cp_db_connection *connection)
00157 {
00158 free(connection);
00159 }
00160
00161 void
00162 cp_db_connection_set_fetch_metadata(cp_db_connection *connection, int mode)
00163 {
00164 connection->autofetch_query_metadata = mode;
00165 }
00166
00167 void cp_db_connection_set_read_result_set_at_once(cp_db_connection *connection,
00168 int mode)
00169 {
00170 if (mode == 0 && connection->data_source->act->fetch_next == NULL)
00171 {
00172 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00173 "%s: can\'t unset read at once mode - method fetch_next not "
00174 "implemented", connection->data_source->act->dbms_lit);
00175 return;
00176 }
00177 connection->read_result_set_at_once = mode;
00178 }
00179
00180 void cp_db_connection_set_fetch_size(cp_db_connection *connection, int fetch_size)
00181 {
00182 if (connection->data_source->act->fetch_next == NULL)
00183 {
00184 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00185 "%s: can\'t set fetch size - method fetch_next not implemented",
00186 connection->data_source->act->dbms_lit);
00187 return;
00188 }
00189 connection->fetch_size = fetch_size;
00190 }
00191
00192 char *cp_db_connection_escape_string(cp_db_connection *connection,
00193 char *src,
00194 int len)
00195 {
00196 if (connection->data_source->act->escape_string)
00197 return (*connection->data_source->act->escape_string)(connection, src, len);
00198
00199 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00200 "%s driver does not implement escape_string",
00201 connection->data_source->act->dbms_lit);
00202
00203 return strdup(src);
00204 }
00205
00206 char *cp_db_connection_escape_binary(cp_db_connection *connection,
00207 char *src,
00208 int src_len,
00209 int *res_len)
00210 {
00211 char *res;
00212 if (connection->data_source->act->escape_binary)
00213 return (*connection->data_source->act->escape_binary)(connection, src,
00214 src_len, res_len);
00215
00216 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00217 "%s driver does not implement escape_binary",
00218 connection->data_source->act->dbms_lit);
00219
00220 *res_len = src_len;
00221 res = malloc(src_len);
00222 memcpy(res, src, src_len);
00223 return res;
00224 }
00225
00226 cp_string *cp_db_connection_unescape_binary(cp_db_connection *connection,
00227 char *src)
00228 {
00229 if (connection->data_source->act->unescape_binary)
00230 return (*connection->data_source->act->unescape_binary)(connection, src);
00231
00232 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00233 "%s driver does not implement unescape_binary",
00234 connection->data_source->act->dbms_lit);
00235
00236
00237 return cp_string_create(src, strlen(src));
00238 }
00239
00240 cp_db_statement *
00241 cp_db_connection_prepare_statement(cp_db_connection *connection,
00242 int prm_count,
00243 cp_field_type *prm_types,
00244 char *query)
00245 {
00246 if (connection->data_source->act->prepare_statement)
00247 return (*connection->data_source->act->prepare_statement)(connection,
00248 prm_count,
00249 prm_types,
00250 query);
00251
00252 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00253 "%s driver does not implement prepare_statement",
00254 connection->data_source->act->dbms_lit);
00255
00256 return NULL;
00257 }
00258
00260 int cp_db_connection_execute_statement(cp_db_connection *connection,
00261 cp_db_statement *statement,
00262 cp_vector *prm,
00263 cp_result_set **results)
00264 {
00265 if (connection->data_source->act->execute_statement)
00266 {
00267 int rc;
00268 int *lengths = NULL;
00269 void **sprm = NULL;
00270 char *cstr;
00271 if (statement->prm_count)
00272 {
00273 int i;
00274
00275 cp_string *str;
00276 lengths = calloc(statement->prm_count, sizeof(int));
00277 sprm = calloc(statement->prm_count, sizeof(void *));
00278 for (i = 0; i < statement->prm_count; i++)
00279 {
00280 sprm[i] = cp_vector_element_at(prm, i);
00281 switch (statement->types[i])
00282 {
00283 case CP_FIELD_TYPE_BOOLEAN:
00284 case CP_FIELD_TYPE_SHORT:
00285 lengths[i] = sizeof(short); break;
00286 case CP_FIELD_TYPE_INT:
00287 lengths[i] = sizeof(int); break;
00288 case CP_FIELD_TYPE_LONG:
00289 lengths[i] = sizeof(long); break;
00290 case CP_FIELD_TYPE_LONG_LONG:
00291 #ifdef CP_HAS_LONG_LONG
00292 lengths[i] = sizeof(long long); break;
00293 #else
00294 lengths[i] = sizeof(__int64); break;
00295 #endif
00296 case CP_FIELD_TYPE_FLOAT:
00297 lengths[i] = sizeof(float); break;
00298 case CP_FIELD_TYPE_DOUBLE:
00299 lengths[i] = sizeof(double); break;
00300 case CP_FIELD_TYPE_CHAR:
00301 case CP_FIELD_TYPE_VARCHAR:
00302 cstr = cp_vector_element_at(prm, i);
00303 lengths[i] = strlen(cstr);
00304 break;
00305 case CP_FIELD_TYPE_BLOB:
00306 str = cp_vector_element_at(prm, i);
00307 lengths[i] = str->len;
00308 sprm[i] = str->data;
00309 break;
00310 case CP_FIELD_TYPE_DATE:
00311 case CP_FIELD_TYPE_TIME:
00312 case CP_FIELD_TYPE_TIMESTAMP:
00313 default:
00314 ;
00315 }
00316 }
00317 }
00318 rc = (*connection->data_source->act->execute_statement)(connection,
00319 statement,
00320 results,
00321 lengths,
00322 sprm);
00323 if (statement->prm_count)
00324 {
00325 free(lengths);
00326 free(sprm);
00327 }
00328
00329 return rc;
00330 }
00331
00332 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00333 "%s driver does not implement execute_statement",
00334 connection->data_source->act->dbms_lit);
00335
00336 return -1;
00337 }
00338
00339 int cp_db_connection_execute_statement_args(cp_db_connection *connection,
00340 cp_db_statement *statement,
00341 cp_result_set **results, ...)
00342 {
00343 if (connection->data_source->act->execute_statement)
00344 {
00345 int i;
00346 int rc;
00347 va_list argp;
00348 int p_int;
00349 long p_long;
00350 #ifdef CP_HAS_LONG_LONG
00351 long long p_longlong;
00352 #else
00353 __int64 p_longlong;
00354 #endif
00355 double p_double;
00356 cp_timestampz p_timestamp;
00357 cp_string p_str;
00358 cp_vector *tmp = NULL;
00359 void *ptr;
00360 int *lengths = NULL;
00361
00362 void **prm = NULL;
00363
00364 if (statement->prm_count)
00365 {
00366 prm = calloc(statement->prm_count, sizeof(void *));
00367 lengths = calloc(statement->prm_count, sizeof(int));
00368 }
00369
00370 va_start(argp, results);
00371 for (i = 0; i < statement->prm_count; i++)
00372 {
00373 tmp = cp_vector_create(statement->prm_count);
00374 switch (statement->types[i])
00375 {
00376 case CP_FIELD_TYPE_CHAR:
00377 case CP_FIELD_TYPE_VARCHAR:
00378 prm[i] = va_arg(argp, char *);
00379 lengths[i] = strlen(prm[i]);
00380 break;
00381
00382 case CP_FIELD_TYPE_BLOB:
00383 ptr = malloc(sizeof(int));
00384 p_str = va_arg(argp, cp_string);
00385 memcpy(ptr, &p_str, sizeof(cp_string));
00386 prm[i] = ptr;
00387 lengths[i] = p_str.len;
00388 break;
00389
00390 case CP_FIELD_TYPE_BOOLEAN:
00391 case CP_FIELD_TYPE_SHORT:
00392 case CP_FIELD_TYPE_INT:
00393 ptr = malloc(sizeof(int));
00394 p_int = va_arg(argp, int);
00395 memcpy(ptr, &p_int, sizeof(int));
00396 cp_vector_add_element(tmp, ptr);
00397 prm[i] = ptr;
00398 lengths[i] = sizeof(int);
00399 break;
00400
00401 case CP_FIELD_TYPE_LONG:
00402 ptr = malloc(sizeof(long));
00403 p_long = va_arg(argp, long);
00404 memcpy(ptr, &p_long, sizeof(long));
00405 cp_vector_add_element(tmp, ptr);
00406 prm[i] = ptr;
00407 lengths[i] = sizeof(long);
00408 break;
00409
00410 case CP_FIELD_TYPE_LONG_LONG:
00411 #ifdef CP_HAS_LONG_LONG
00412 ptr = malloc(sizeof(long long));
00413 p_longlong = va_arg(argp, long long);
00414 memcpy(ptr, &p_longlong, sizeof(long long));
00415 lengths[i] = sizeof(long long);
00416 #else
00417 ptr = malloc(sizeof(__int64));
00418 p_longlong = va_arg(argp, __int64);
00419 memcpy(ptr, &p_longlong, sizeof(__int64));
00420 lengths[i] = sizeof(__int64);
00421 #endif
00422 cp_vector_add_element(tmp, ptr);
00423 prm[i] = ptr;
00424 break;
00425
00426 case CP_FIELD_TYPE_FLOAT:
00427 case CP_FIELD_TYPE_DOUBLE:
00428 ptr = malloc(sizeof(double));
00429 p_double = va_arg(argp, double);
00430 memcpy(ptr, &p_double, sizeof(double));
00431 cp_vector_add_element(tmp, ptr);
00432 prm[i] = ptr;
00433 lengths[i] = sizeof(double);
00434 break;
00435
00436 case CP_FIELD_TYPE_DATE:
00437 case CP_FIELD_TYPE_TIME:
00438 case CP_FIELD_TYPE_TIMESTAMP:
00439 ptr = malloc(sizeof(cp_timestampz));
00440 p_timestamp = va_arg(argp, cp_timestampz);
00441 memcpy(ptr, &p_timestamp, sizeof(cp_timestampz));
00442 cp_vector_add_element(tmp, ptr);
00443 prm[i] = ptr;
00444 break;
00445 }
00446 }
00447
00448 rc = (*connection->data_source->act->execute_statement)(connection,
00449 statement,
00450 results,
00451 lengths,
00452 prm);
00453 va_end(argp);
00454 if (statement->prm_count)
00455 {
00456 free(prm);
00457 free(lengths);
00458 }
00459 if (prm) free(prm);
00460 if (tmp)
00461 cp_vector_destroy_custom(tmp, (cp_destructor_fn) free);
00462 return rc;
00463 }
00464
00465 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00466 "%s driver does not implement execute_statement",
00467 connection->data_source->act->dbms_lit);
00468
00469 return -1;
00470 }
00471
00472 void cp_db_connection_close_statement(cp_db_connection *connection,
00473 cp_db_statement *statement)
00474 {
00475 if (connection->data_source->act->release_statement)
00476 (*connection->data_source->act->release_statement)(statement);
00477 else
00478 {
00479 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00480 "%s driver does not implement release_statement",
00481 connection->data_source->act->dbms_lit);
00482 }
00483
00484 cp_db_statement_destroy(statement);
00485 }
00486
00487 void cp_db_connection_set_autocommit(cp_db_connection *connection, int state)
00488 {
00489 connection->autocommit = state;
00490 if (connection->data_source->act->set_autocommit)
00491 (*connection->data_source->act->set_autocommit)(connection, state);
00492 else
00493 {
00494 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00495 "%s driver does not implement set_autocommit",
00496 connection->data_source->act->dbms_lit);
00497 }
00498 }
00499
00500 int cp_db_connection_commit(cp_db_connection *connection)
00501 {
00502 if (connection->data_source->act->commit)
00503 return (*connection->data_source->act->commit)(connection);
00504
00505 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00506 "%s driver does not implement commit",
00507 connection->data_source->act->dbms_lit);
00508
00509 return -1;
00510 }
00511
00512 int cp_db_connection_rollback(cp_db_connection *connection)
00513 {
00514 if (connection->data_source->act->rollback)
00515 return (*connection->data_source->act->rollback)(connection);
00516
00517 cp_error(CP_METHOD_NOT_IMPLEMENTED,
00518 "%s driver does not implement rollback",
00519 connection->data_source->act->dbms_lit);
00520
00521 return -1;
00522 }
00523
00524
00525
00526
00527
00528
00529
00530
00531 cp_db_statement *cp_db_statement_instantiate(cp_db_connection *connection,
00532 int prm_count,
00533 cp_field_type *types,
00534 void *source)
00535 {
00536 cp_db_statement *stmt = calloc(1, sizeof(cp_db_statement));
00537 if (stmt == NULL) goto DONE;
00538
00539 stmt->connection = connection;
00540 stmt->prm_count = prm_count;
00541 stmt->types = malloc(prm_count * sizeof(cp_field_type));
00542 if (stmt->types == NULL) goto DONE;
00543 memcpy(stmt->types, types, prm_count * sizeof(cp_field_type));
00544 stmt->source = source;
00545
00546 DONE:
00547 if (stmt && stmt->types == NULL)
00548 {
00549 free(stmt);
00550 stmt = NULL;
00551 }
00552
00553 return stmt;
00554 }
00555
00556 void cp_db_statement_set_binary(cp_db_statement *stmt, int binary)
00557 {
00558 stmt->binary = binary;
00559 }
00560
00561 void cp_db_statement_destroy(cp_db_statement *statement)
00562 {
00563 if (statement)
00564 {
00565 if (statement->types) free(statement->types);
00566 free(statement);
00567 }
00568 }
00569
00570
00571
00572
00573
00574
00575
00576 cp_vector *cp_result_set_next(cp_result_set *result_set)
00577 {
00578 cp_vector *row = cp_list_remove_head(result_set->results);
00579 if (row == NULL && !result_set->fetch_complete)
00580 {
00581 if (result_set->connection)
00582 {
00583 if (result_set->connection->data_source->act->fetch_next)
00584 (*result_set->connection->data_source->act->fetch_next)(result_set);
00585 else
00586 cp_error(CP_METHOD_NOT_IMPLEMENTED, "%s: fetch_next",
00587 result_set->connection->data_source->act->dbms_lit);
00588
00589 row = cp_list_remove_head(result_set->results);
00590 }
00591 }
00592
00593 if (row)
00594 {
00595 result_set->position++;
00596 if (result_set->dispose)
00597 cp_list_append(result_set->dispose_list, row);
00598 }
00599 else
00600 result_set->fetch_complete = 1;
00601
00602 return row;
00603 }
00604
00605 void cp_result_set_destroy(cp_result_set *result_set)
00606 {
00607 cp_vector *row;
00608
00609 while ((row = cp_list_remove_head(result_set->results)))
00610 cp_result_set_release_row(result_set, row);
00611
00612 if (result_set->dispose)
00613 while ((row = cp_list_remove_head(result_set->dispose_list)))
00614 cp_result_set_release_row(result_set, row);
00615
00616 if (result_set->dispose_list)
00617 cp_list_destroy(result_set->dispose_list);
00618
00619 if (result_set->fetch_complete == 0 &&
00620 result_set->connection->data_source->act->release_result_set)
00621 (*result_set->connection->data_source->act->release_result_set)(result_set);
00622
00623 cp_list_destroy(result_set->results);
00624
00625 if (result_set->field_headers)
00626 cp_vector_destroy(result_set->field_headers);
00627
00628 if (result_set->field_types)
00629 cp_vector_destroy(result_set->field_types);
00630
00631 free(result_set);
00632 }
00633
00634 cp_vector *cp_result_set_get_headers(cp_result_set *result_set)
00635 {
00636 if (result_set->field_headers == NULL &&
00637 result_set->connection != NULL)
00638 {
00639 if (result_set->connection->data_source->act->fetch_metadata != NULL)
00640 (*result_set->connection->data_source->act->fetch_metadata)(result_set);
00641 else
00642 cp_error(CP_METHOD_NOT_IMPLEMENTED, "%s: fetch_metadata",
00643 result_set->connection->data_source->act->dbms_lit);
00644 }
00645
00646 return result_set->field_headers;
00647 }
00648
00649 cp_vector *cp_result_set_get_field_types(cp_result_set *result_set)
00650 {
00651 if (result_set->field_types == NULL &&
00652 result_set->connection != NULL)
00653 {
00654 if (result_set->connection->data_source->act->fetch_metadata != NULL)
00655 (*result_set->connection->data_source->act->fetch_metadata)(result_set);
00656 else
00657 cp_error(CP_METHOD_NOT_IMPLEMENTED, "%s: fetch_metadata",
00658 result_set->connection->data_source->act->dbms_lit);
00659 }
00660
00661 return result_set->field_types;
00662 }
00663
00664 int cp_result_set_field_count(cp_result_set *result_set)
00665 {
00666 return result_set->field_count;
00667 }
00668
00669 int cp_result_set_row_count(cp_result_set *result_set)
00670 {
00671 return result_set->row_count;
00672 }
00673
00674 char *cp_result_set_get_header(cp_result_set *rs, int column)
00675 {
00676 cp_vector *field_headers;
00677 field_headers = cp_result_set_get_headers(rs);
00678 if (rs->field_headers == NULL) return NULL;
00679
00680 return cp_vector_element_at(field_headers, column);
00681 }
00682
00683 cp_field_type cp_result_set_get_field_type(cp_result_set *rs, int column)
00684 {
00685 cp_vector *field_types;
00686 cp_field_type *type;
00687 field_types = cp_result_set_get_field_types(rs);
00688 if (rs->field_types == NULL) return -1;
00689
00690 type = cp_vector_element_at(rs->field_types, column);
00691 if (type == NULL) return -1;
00692
00693 return *type;
00694 }
00695
00696 int cp_result_set_is_binary(cp_result_set *result_set)
00697 {
00698 return result_set->binary;
00699 }
00700
00701 void cp_result_set_autodispose(cp_result_set *rs, int mode)
00702 {
00703 rs->dispose = mode;
00704 if (mode && rs->dispose_list == NULL)
00705 rs->dispose_list = cp_list_create();
00706 }
00707
00708 void cp_result_set_release_row(cp_result_set *result_set, cp_vector *row)
00709 {
00710 if (result_set->binary)
00711 {
00712 int i;
00713 int n = cp_vector_size(row);
00714 cp_field_type *type;
00715 void *item;
00716
00717 for (i = 0; i < n; i++)
00718 {
00719 if ((item = cp_vector_element_at(row, i)) == NULL) continue;
00720 type = cp_vector_element_at(result_set->field_types, i);
00721 if (*type == CP_FIELD_TYPE_VARCHAR ||
00722 *type == CP_FIELD_TYPE_CHAR ||
00723 *type == CP_FIELD_TYPE_BLOB)
00724 {
00725 cp_string_destroy(item);
00726 cp_vector_set_element(row, i, NULL);
00727 }
00728 else
00729 free(item);
00730 }
00731
00732 cp_vector_destroy(row);
00733 }
00734 else
00735 cp_vector_destroy_custom(row, (cp_destructor_fn) cp_string_destroy);
00736 }
00737
00738
00739
00740
00741
00742
00743
00744 cp_db_actions *
00745 cp_db_actions_create(int dbms,
00746 char *dbms_lit,
00747 cp_db_open_fn open,
00748 cp_db_select_fn select,
00749 cp_db_fetch_metadata_fn fetch_metadata,
00750 cp_db_fetch_next_fn fetch_next,
00751 cp_db_release_result_set_fn release_result_set,
00752 cp_db_update_fn update,
00753 cp_db_close_fn close,
00754 cp_db_escape_string_fn escape_string,
00755 cp_db_escape_binary_fn escape_binary,
00756 cp_db_unescape_binary_fn unescape_binary,
00757 cp_db_prepare_statement_fn prepare_statement,
00758 cp_db_execute_statement_fn execute_statement,
00759 cp_db_release_statement_fn release_statement,
00760 cp_db_set_autocommit_fn set_autocommit,
00761 cp_db_commit_fn commit,
00762 cp_db_rollback_fn rollback)
00763 {
00764 cp_db_actions *actions = calloc(1, sizeof(cp_db_actions));
00765 if (actions == NULL) return NULL;
00766
00767 actions->dbms = dbms;
00768 actions->dbms_lit = strdup(dbms_lit);
00769 if (actions->dbms_lit == NULL)
00770 {
00771 free(actions);
00772 return NULL;
00773 }
00774
00775 actions->open = open;
00776 actions->select = select;
00777 actions->fetch_metadata = fetch_metadata;
00778 actions->fetch_next = fetch_next;
00779 actions->release_result_set = release_result_set;
00780 actions->update = update;
00781 actions->close = close;
00782 actions->escape_string = escape_string;
00783 actions->escape_binary = escape_binary;
00784 actions->unescape_binary = unescape_binary;
00785 actions->prepare_statement = prepare_statement;
00786 actions->execute_statement = execute_statement;
00787 actions->release_statement = release_statement;
00788 actions->set_autocommit = set_autocommit;
00789 actions->commit = commit;
00790 actions->rollback = rollback;
00791
00792 return actions;
00793 }
00794
00795 void cp_db_actions_destroy(cp_db_actions *actions)
00796 {
00797 if (actions)
00798 {
00799 if (actions->dbms_lit) free(actions->dbms_lit);
00800 free(actions);
00801 }
00802 }
00803
00804
00805
00806
00807
00808
00809
00810 #if defined(CP_HAS_DLFCN_H) || defined(_WINDOWS)
00811 #ifndef SO_EXT
00812 #if defined(unix) || defined(__unix__) || defined(__MACH__)
00813 #define SO_EXT "so"
00814 #endif
00815 #ifdef _WINDOWS
00816 #define SO_EXT "dll"
00817 #endif
00818 #endif
00819
00820 int cp_dbms_load_driver(char *name)
00821 {
00822 cp_dbms_driver_descriptor *desc;
00823
00824 if (drivers == NULL)
00825 drivers =
00826 cp_hashtable_create_by_option(COLLECTION_MODE_DEEP |
00827 COLLECTION_MODE_COPY,
00828 1,
00829 cp_hash_string,
00830 cp_hash_compare_string,
00831 (cp_copy_fn) strdup,
00832 free, NULL,
00833 cp_dbms_driver_descriptor_destroy);
00834
00835 desc = cp_hashtable_get(drivers, name);
00836 if (desc == NULL)
00837 {
00838 char buf[0x100];
00839
00840 void *lib;
00841 void *ds_fn;
00842 void (*init_fn)();
00843
00844 #ifdef CP_HAS_SNPRINTF
00845 snprintf(buf, 0x100, "libcp_dbms_%s.%s", name, SO_EXT);
00846 #else
00847 sprintf(buf, "libcp_dbms_%s.%s", name, SO_EXT);
00848 #endif
00849 lib = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
00850 if (lib == NULL)
00851 {
00852 cp_error(CP_LOADLIB_FAILED, "can\'t load %s dbms driver from %s",
00853 name, buf);
00854 return -1;
00855 }
00856
00857 #ifdef CP_HAS_SNPRINTF
00858 snprintf(buf, 0x100, "cp_dbms_%s_get_data_source", name);
00859 #else
00860 sprintf(buf, "cp_dbms_%s_get_data_source", name);
00861 #endif
00862 ds_fn = dlsym(lib, buf);
00863 if (ds_fn == NULL)
00864 {
00865 cp_error(CP_LOADFN_FAILED,
00866 "can\'t find data source factory function %s", buf);
00867 dlclose(lib);
00868 return -1;
00869 }
00870
00871 desc = calloc(1, sizeof(cp_dbms_driver_descriptor));
00872 if (desc == NULL)
00873 {
00874 cp_error(CP_MEMORY_ALLOCATION_FAILURE, "can\'t allocate "
00875 "descriptor function for %s", name);
00876 dlclose(lib);
00877 return -1;
00878 }
00879 desc->lib = lib;
00880 desc->get_data_source = ds_fn;
00881
00882 #ifdef CP_HAS_SNPRINTF
00883 snprintf(buf, 0x100, "cp_dbms_%s_get_data_source_prm", name);
00884 #else
00885 sprintf(buf, "cp_dbms_%s_get_data_source_prm", name);
00886 #endif
00887 desc->get_data_source_prm = dlsym(lib, buf);
00888
00889 #ifdef CP_HAS_SNPRINTF
00890 snprintf(buf, 0x100, "cp_dbms_%s_init", name);
00891 #else
00892 sprintf(buf, "cp_dbms_%s_init", name);
00893 #endif
00894 init_fn = dlsym(lib, buf);
00895 if (init_fn) (*init_fn)();
00896
00897 cp_hashtable_put(drivers, name, desc);
00898 }
00899
00900
00901 return desc == NULL;
00902 }
00903 #endif
00904
00905 static cp_dbms_driver_descriptor *fetch_driver(char *driver)
00906 {
00907 cp_dbms_driver_descriptor *desc;
00908
00909 if (drivers == NULL)
00910 {
00911 cp_error(CP_DBMS_NO_DRIVER, "no drivers loaded");
00912 return NULL;
00913 }
00914
00915 desc = cp_hashtable_get(drivers, driver);
00916
00917 #if defined(CP_HAS_DLFCN_H) || defined(_WINDOWS)
00918 if (desc == NULL)
00919 {
00920 if (cp_dbms_load_driver(driver) == 0)
00921 desc = cp_hashtable_get(drivers, driver);
00922 }
00923 #endif
00924
00925 return desc;
00926 }
00927
00928 cp_data_source *
00929 cp_dbms_get_data_source(char *driver, char *host, int port, char *user,
00930 char *passwd, char *dbname)
00931 {
00932 cp_dbms_driver_descriptor *desc = fetch_driver(driver);
00933
00934 if (desc == NULL)
00935 {
00936 cp_error(CP_DBMS_NO_DRIVER, "can\'t find driver for %s", driver);
00937 return NULL;
00938 }
00939
00940 return (desc->get_data_source)(host, port, user, passwd, dbname);
00941 }
00942
00943 cp_data_source *
00944 cp_dbms_get_data_source_prm(char *driver, char *host, int port,
00945 char *user, char *passwd, char *dbname, cp_hashtable *prm)
00946 {
00947 cp_dbms_driver_descriptor *desc = fetch_driver(driver);
00948
00949 if (desc == NULL)
00950 {
00951 cp_error(CP_DBMS_NO_DRIVER, "can\'t find driver for %s", driver);
00952 return NULL;
00953 }
00954
00955 if (desc->get_data_source_prm == NULL)
00956 {
00957 cp_error(CP_METHOD_NOT_IMPLEMENTED, "%s driver does not implement "
00958 "get_data_source_prm()", driver);
00959 return NULL;
00960 }
00961
00962 return (desc->get_data_source_prm)(host, port, user, passwd, dbname, prm);
00963 }
00964
00965 void cp_data_source_destroy(cp_data_source *data_source)
00966 {
00967 if (data_source)
00968 {
00969 if (data_source->prm)
00970 cp_db_connection_parameters_destroy(data_source->prm);
00971
00972
00973
00974 free(data_source);
00975 }
00976 }
00977
00978 cp_db_connection *
00979 cp_data_source_get_connection(cp_data_source *data_source)
00980 {
00981 return (*data_source->act->open)(data_source);
00982 }
00983
00984
00985
00986
00987
00988
00989
00990
00991 cp_db_connection_pool *
00992 cp_db_connection_pool_create(cp_data_source *data_source,
00993 int min_size,
00994 int max_size,
00995 int initial_size)
00996 {
00997 int rc;
00998 cp_db_connection_pool *pool = calloc(1, sizeof(cp_db_connection_pool));
00999 if (pool == NULL) return NULL;
01000
01001 pool->min_size = min_size;
01002 pool->max_size = max_size;
01003
01004 pool->data_source = data_source;
01005
01006 pool->lock = malloc(sizeof(cp_mutex));
01007 if (pool->lock == NULL)
01008 {
01009 cp_db_connection_pool_destroy(pool);
01010 return NULL;
01011 }
01012
01013 if ((rc = cp_mutex_init(pool->lock, NULL)))
01014 {
01015 free(pool->lock);
01016 pool->lock = NULL;
01017 cp_db_connection_pool_destroy(pool);
01018 return NULL;
01019 }
01020
01021 pool->cond = malloc(sizeof(cp_cond));
01022 if (pool->cond == NULL)
01023 {
01024 cp_db_connection_pool_destroy(pool);
01025 return NULL;
01026 }
01027 cp_cond_init(pool->cond, NULL);
01028
01029 pool->free_list =
01030 cp_list_create_list(COLLECTION_MODE_NOSYNC |
01031 COLLECTION_MODE_MULTIPLE_VALUES,
01032 NULL, NULL, NULL);
01033 if (pool->free_list == NULL)
01034 {
01035 cp_db_connection_pool_destroy(pool);
01036 return NULL;
01037 }
01038
01039 pool->running = 1;
01040
01041 for (pool->size = 0; pool->size < initial_size; pool->size++)
01042 {
01043 cp_db_connection *conn =
01044 (*pool->data_source->act->open)(pool->data_source);
01045 if (conn == NULL)
01046 {
01047 if (pool->size > 0)
01048 {
01049 cp_error(CP_DBMS_CONNECTION_FAILURE,
01050 "creating connection pool: managed %d connections",
01051 pool->size);
01052 break;
01053 }
01054 else
01055 {
01056 cp_error(CP_DBMS_CONNECTION_FAILURE,
01057 "can\'t open connection pool");
01058 cp_db_connection_pool_destroy(pool);
01059 pool = NULL;
01060 break;
01061 }
01062 }
01063 cp_list_append(pool->free_list, conn);
01064 }
01065
01066 return pool;
01067 }
01068
01069 int cp_db_connection_pool_shutdown(cp_db_connection_pool *pool)
01070 {
01071 cp_db_connection *conn;
01072
01073
01074 cp_mutex_lock(pool->lock);
01075 pool->running = 0;
01076 cp_cond_broadcast(pool->cond);
01077 cp_mutex_unlock(pool->lock);
01078
01079 while (pool->size > 0)
01080 {
01081 cp_mutex_lock(pool->lock);
01082 while ((conn = cp_list_remove_head(pool->free_list)) != NULL)
01083 {
01084 (*pool->data_source->act->close)(conn);
01085 pool->size--;
01086 }
01087 cp_mutex_unlock(pool->lock);
01088
01089 if (pool->size > 0)
01090 {
01091 #ifdef __TRACE__
01092 DEBUGMSG("waiting on %d db connections", pool->size);
01093 #endif
01094 cp_mutex_lock(pool->lock);
01095 while (cp_list_item_count(pool->free_list) == 0)
01096 cp_cond_wait(pool->cond, pool->lock);
01097 cp_mutex_unlock(pool->lock);
01098 }
01099 }
01100
01101 return 0;
01102 }
01103
01104 void cp_db_connection_pool_destroy(cp_db_connection_pool *pool)
01105 {
01106 if (pool)
01107 {
01108 if (pool->free_list) cp_list_destroy(pool->free_list);
01109 if (pool->lock)
01110 {
01111 cp_mutex_destroy(pool->lock);
01112 free(pool->lock);
01113 }
01114 if (pool->cond)
01115 {
01116 cp_cond_destroy(pool->cond);
01117 free(pool->cond);
01118 }
01119 free(pool);
01120 }
01121 }
01122
01123 void cp_db_connection_pool_set_blocking(cp_db_connection_pool *pool, int block)
01124 {
01125 pool->block = block;
01126 }
01127
01128 cp_db_connection *
01129 cp_db_connection_pool_get_connection(cp_db_connection_pool *pool)
01130 {
01131 cp_db_connection *conn;
01132
01133 cp_mutex_lock(pool->lock);
01134 conn = cp_list_remove_head(pool->free_list);
01135 if (conn == NULL)
01136 {
01137 if (pool->size < pool->max_size)
01138 conn = (*pool->data_source->act->open)(pool->data_source);
01139 else if (pool->block)
01140 {
01141 do
01142 {
01143 cp_cond_wait(pool->cond, pool->lock);
01144 } while (pool->running && ((conn = cp_list_remove_head(pool->free_list)) == NULL));
01145 }
01146 }
01147 cp_mutex_unlock(pool->lock);
01148
01149 return conn;
01150 }
01151
01152 void cp_db_connection_pool_release_connection(cp_db_connection_pool *pool,
01153 cp_db_connection *connection)
01154 {
01155 cp_mutex_lock(pool->lock);
01156 cp_list_insert(pool->free_list, connection);
01157 cp_cond_signal(pool->cond);
01158 cp_mutex_unlock(pool->lock);
01159 }
01160