db.c

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 /* CP_HAS_DLFCN_H */
00012 
00013 #ifdef CP_HAS_SYS_TIME_H
00014 #include <sys/time.h>
00015 #endif /* CP_HAS_SYS_TIME_H */
00016 
00017 #include "db.h"
00018 /* ----------------------------------------------------------------------- */
00019 /*                                                                         */
00020 /*                           general framework setup                       */
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 /* CP_HAS_DLFCN_H */
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 /*                          cp_timestampz functions                        */
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 /* CP_HAS_LOCALTIME_R */
00110 }
00111 
00112 void cp_timestampz_destroy(cp_timestampz *tm)
00113 {
00114     free(tm);
00115 }
00116 
00117 
00118 /* ----------------------------------------------------------------------- */
00119 /*                                                                         */
00120 /*                      connection parameter functions                     */
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 /*                           connection functions                          */
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                                      size_t 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                                      size_t src_len, 
00209                                      size_t *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 //          cp_field_type *type;
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 /* CP_HAS_LONG_LONG */
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 /* CP_HAS_LONG_LONG */
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(cp_string));
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 /* CP_HAS_LONG_LONG */
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 /*                       prepared statement functions                      */
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 /*                           result set functions                          */
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 /*                            db_actions functions                         */
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 /*                           data source functions                         */
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 /* unix */
00815 #ifdef _WINDOWS
00816 #define SO_EXT "dll"
00817 #endif /* _WINDOWS */
00818 #endif /* SO_EXT */
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 /* CP_HAS_SNPRINTF */
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 /* CP_HAS_SNPRINTF */
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 /* CP_HAS_SNPRINTF */
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 /* CP_HAS_SNPRINTF */
00894         init_fn = dlsym(lib, buf);
00895         if (init_fn) (*init_fn)();
00896 
00897         cp_hashtable_put(drivers, name, desc);
00898     }
00899 
00900     /* return 0 on success, non-zero otherwise */
00901     return desc == NULL; 
00902 }
00903 #endif /* CP_HAS_DLFCN_H */
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 /* CP_HAS_DLFCN_H */
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         //~~ refcount?
00972 //      if (data_source->act)
00973 //          cp_db_actions_destroy(data_source->act);
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 /*                         connection pool functions                       */
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) //~~ better than nothing
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     /* signal shutdown */
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) //~~ set some kind of limit - eg wait up to 30 sec
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 

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