00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "asterisk.h"
00037
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 414693 $")
00039
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/lock.h"
00046 #include "asterisk/res_odbc.h"
00047 #include "asterisk/utils.h"
00048 #include "asterisk/stringfields.h"
00049
00050 AST_THREADSTORAGE(sql_buf);
00051 AST_THREADSTORAGE(rowdata_buf);
00052
00053 struct custom_prepare_struct {
00054 const char *sql;
00055 const char *extra;
00056 AST_DECLARE_STRING_FIELDS(
00057 AST_STRING_FIELD(encoding)[256];
00058 );
00059 va_list ap;
00060 unsigned long long skip;
00061 };
00062
00063 #define ENCODE_CHUNK(buffer, s) \
00064 do { \
00065 char *eptr = buffer; \
00066 const char *vptr = s; \
00067 for (; *vptr && eptr < buffer + sizeof(buffer); vptr++) { \
00068 if (strchr("^;", *vptr)) { \
00069 \
00070 snprintf(eptr, buffer + sizeof(buffer) - eptr, "^%02hhX", *vptr); \
00071 eptr += 3; \
00072 } else { \
00073 *eptr++ = *vptr; \
00074 } \
00075 } \
00076 if (eptr < buffer + sizeof(buffer)) { \
00077 *eptr = '\0'; \
00078 } else { \
00079 buffer[sizeof(buffer) - 1] = '\0'; \
00080 } \
00081 } while(0)
00082
00083 static void decode_chunk(char *chunk)
00084 {
00085 for (; *chunk; chunk++) {
00086 if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
00087 sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
00088 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00089 }
00090 }
00091 }
00092
00093 static inline int is_text(const struct odbc_cache_columns *column)
00094 {
00095 return column->type == SQL_CHAR || column->type == SQL_VARCHAR || column->type == SQL_LONGVARCHAR
00096 || column->type == SQL_WCHAR || column->type == SQL_WVARCHAR || column->type == SQL_WLONGVARCHAR;
00097 }
00098
00099 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
00100 {
00101 int res, x = 1, count = 0;
00102 struct custom_prepare_struct *cps = data;
00103 const char *newparam, *newval;
00104 char encodebuf[1024];
00105 SQLHSTMT stmt;
00106 va_list ap;
00107
00108 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00109 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00110 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00111 return NULL;
00112 }
00113
00114 ast_debug(1, "Skip: %llu; SQL: %s\n", cps->skip, cps->sql);
00115
00116 res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
00117 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
00119 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00120 return NULL;
00121 }
00122
00123 va_copy(ap, cps->ap);
00124 while ((newparam = va_arg(ap, const char *))) {
00125 newval = va_arg(ap, const char *);
00126 if ((1LL << count++) & cps->skip) {
00127 ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1ULL << (count - 1), cps->skip);
00128 continue;
00129 }
00130 ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
00131 if (strchr(newval, ';') || strchr(newval, '^')) {
00132 ENCODE_CHUNK(encodebuf, newval);
00133 ast_string_field_set(cps, encoding[x], encodebuf);
00134 newval = cps->encoding[x];
00135 }
00136 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00137 }
00138 va_end(ap);
00139
00140 if (!ast_strlen_zero(cps->extra)) {
00141 const char *newval = cps->extra;
00142 if (strchr(newval, ';') || strchr(newval, '^')) {
00143 ENCODE_CHUNK(encodebuf, newval);
00144 ast_string_field_set(cps, encoding[x], encodebuf);
00145 newval = cps->encoding[x];
00146 }
00147 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00148 }
00149
00150 return stmt;
00151 }
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
00167 {
00168 struct odbc_obj *obj;
00169 SQLHSTMT stmt;
00170 char sql[1024];
00171 char coltitle[256];
00172 struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
00173 char *op;
00174 const char *newparam;
00175 char *stringp;
00176 char *chunk;
00177 SQLSMALLINT collen;
00178 int res;
00179 int x;
00180 struct ast_variable *var=NULL, *prev=NULL;
00181 SQLULEN colsize;
00182 SQLSMALLINT colcount=0;
00183 SQLSMALLINT datatype;
00184 SQLSMALLINT decimaldigits;
00185 SQLSMALLINT nullable;
00186 SQLLEN indicator;
00187 va_list aq;
00188 struct custom_prepare_struct cps = { .sql = sql };
00189 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00190
00191 if (!table) {
00192 return NULL;
00193 }
00194
00195 obj = ast_odbc_request_obj2(database, connected_flag);
00196
00197 if (!obj) {
00198 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
00199 return NULL;
00200 }
00201
00202 va_copy(aq, ap);
00203 newparam = va_arg(aq, const char *);
00204 if (!newparam) {
00205 va_end(aq);
00206 ast_odbc_release_obj(obj);
00207 return NULL;
00208 }
00209 va_arg(aq, const char *);
00210 op = !strchr(newparam, ' ') ? " =" : "";
00211 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00212 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00213 while((newparam = va_arg(aq, const char *))) {
00214 op = !strchr(newparam, ' ') ? " =" : "";
00215 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00216 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00217 va_arg(aq, const char *);
00218 }
00219 va_end(aq);
00220
00221 if (ast_string_field_init(&cps, 256)) {
00222 ast_odbc_release_obj(obj);
00223 return NULL;
00224 }
00225 va_copy(cps.ap, ap);
00226 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00227 va_end(cps.ap);
00228 ast_string_field_free_memory(&cps);
00229
00230 if (!stmt) {
00231 ast_odbc_release_obj(obj);
00232 return NULL;
00233 }
00234
00235 res = SQLNumResultCols(stmt, &colcount);
00236 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00237 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00238 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00239 ast_odbc_release_obj(obj);
00240 return NULL;
00241 }
00242
00243 res = SQLFetch(stmt);
00244 if (res == SQL_NO_DATA) {
00245 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00246 ast_odbc_release_obj(obj);
00247 return NULL;
00248 }
00249 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00250 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00251 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00252 ast_odbc_release_obj(obj);
00253 return NULL;
00254 }
00255 for (x = 0; x < colcount; x++) {
00256 colsize = 0;
00257 collen = sizeof(coltitle);
00258 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
00259 &datatype, &colsize, &decimaldigits, &nullable);
00260 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00261 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00262 if (var)
00263 ast_variables_destroy(var);
00264 ast_odbc_release_obj(obj);
00265 return NULL;
00266 }
00267
00268 ast_str_reset(rowdata);
00269 indicator = 0;
00270
00271 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
00272 ast_str_update(rowdata);
00273 if (indicator == SQL_NULL_DATA) {
00274 ast_str_reset(rowdata);
00275 } else if (!ast_str_strlen(rowdata)) {
00276
00277
00278 ast_str_set(&rowdata, -1, "%s", " ");
00279 } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
00280 if (indicator != ast_str_strlen(rowdata)) {
00281
00282 ast_str_make_space(&rowdata, indicator + 1);
00283 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
00284 ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
00285 ast_str_update(rowdata);
00286 }
00287 }
00288
00289 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00290 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00291 if (var)
00292 ast_variables_destroy(var);
00293 ast_odbc_release_obj(obj);
00294 return NULL;
00295 }
00296
00297 stringp = ast_str_buffer(rowdata);
00298 while (stringp) {
00299 chunk = strsep(&stringp, ";");
00300 if (!ast_strlen_zero(ast_strip(chunk))) {
00301 if (strchr(chunk, '^')) {
00302 decode_chunk(chunk);
00303 }
00304 if (prev) {
00305 prev->next = ast_variable_new(coltitle, chunk, "");
00306 if (prev->next) {
00307 prev = prev->next;
00308 }
00309 } else {
00310 prev = var = ast_variable_new(coltitle, chunk, "");
00311 }
00312 }
00313 }
00314 }
00315
00316 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00317 ast_odbc_release_obj(obj);
00318 return var;
00319 }
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
00336 {
00337 struct odbc_obj *obj;
00338 SQLHSTMT stmt;
00339 char sql[1024];
00340 char coltitle[256];
00341 struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128);
00342 const char *initfield;
00343 char *op;
00344 const char *newparam;
00345 char *stringp;
00346 char *chunk;
00347 SQLSMALLINT collen;
00348 int res;
00349 int x;
00350 struct ast_variable *var=NULL;
00351 struct ast_config *cfg=NULL;
00352 struct ast_category *cat=NULL;
00353 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00354 SQLULEN colsize;
00355 SQLSMALLINT colcount=0;
00356 SQLSMALLINT datatype;
00357 SQLSMALLINT decimaldigits;
00358 SQLSMALLINT nullable;
00359 SQLLEN indicator;
00360 struct custom_prepare_struct cps = { .sql = sql };
00361 va_list aq;
00362
00363 if (!table) {
00364 return NULL;
00365 }
00366
00367 obj = ast_odbc_request_obj2(database, connected_flag);
00368 if (!obj) {
00369 return NULL;
00370 }
00371
00372 va_copy(aq, ap);
00373 newparam = va_arg(aq, const char *);
00374 if (!newparam) {
00375 va_end(aq);
00376 ast_odbc_release_obj(obj);
00377 return NULL;
00378 }
00379
00380 initfield = ast_strdupa(newparam);
00381 if ((op = strchr(initfield, ' '))) {
00382 *op = '\0';
00383 }
00384
00385 va_arg(aq, const char *);
00386 op = !strchr(newparam, ' ') ? " =" : "";
00387 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00388 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00389 while((newparam = va_arg(aq, const char *))) {
00390 op = !strchr(newparam, ' ') ? " =" : "";
00391 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00392 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00393 va_arg(aq, const char *);
00394 }
00395 va_end(aq);
00396
00397 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00398
00399 if (ast_string_field_init(&cps, 256)) {
00400 ast_odbc_release_obj(obj);
00401 return NULL;
00402 }
00403 va_copy(cps.ap, ap);
00404 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00405 va_end(cps.ap);
00406 ast_string_field_free_memory(&cps);
00407
00408 if (!stmt) {
00409 ast_odbc_release_obj(obj);
00410 return NULL;
00411 }
00412
00413 res = SQLNumResultCols(stmt, &colcount);
00414 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00415 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00416 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00417 ast_odbc_release_obj(obj);
00418 return NULL;
00419 }
00420
00421 cfg = ast_config_new();
00422 if (!cfg) {
00423 ast_log(LOG_WARNING, "Out of memory!\n");
00424 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00425 ast_odbc_release_obj(obj);
00426 return NULL;
00427 }
00428
00429 while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
00430 var = NULL;
00431 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00432 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00433 continue;
00434 }
00435 cat = ast_category_new("","",99999);
00436 if (!cat) {
00437 ast_log(LOG_WARNING, "Out of memory!\n");
00438 continue;
00439 }
00440 for (x=0;x<colcount;x++) {
00441 colsize = 0;
00442 collen = sizeof(coltitle);
00443 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
00444 &datatype, &colsize, &decimaldigits, &nullable);
00445 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00446 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00447 ast_category_destroy(cat);
00448 goto next_sql_fetch;
00449 }
00450
00451 ast_str_reset(rowdata);
00452 indicator = 0;
00453
00454 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator);
00455 ast_str_update(rowdata);
00456 if (indicator == SQL_NULL_DATA) {
00457 continue;
00458 }
00459
00460 if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) {
00461 if (indicator != ast_str_strlen(rowdata)) {
00462
00463 ast_str_make_space(&rowdata, indicator + 1);
00464 res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata),
00465 ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator);
00466 ast_str_update(rowdata);
00467 }
00468 }
00469
00470 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00471 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00472 ast_category_destroy(cat);
00473 goto next_sql_fetch;
00474 }
00475 stringp = ast_str_buffer(rowdata);
00476 while (stringp) {
00477 chunk = strsep(&stringp, ";");
00478 if (!ast_strlen_zero(ast_strip(chunk))) {
00479 if (strchr(chunk, '^')) {
00480 decode_chunk(chunk);
00481 }
00482 if (!strcmp(initfield, coltitle)) {
00483 ast_category_rename(cat, chunk);
00484 }
00485 var = ast_variable_new(coltitle, chunk, "");
00486 ast_variable_append(cat, var);
00487 }
00488 }
00489 }
00490 ast_category_append(cfg, cat);
00491 next_sql_fetch:;
00492 }
00493
00494 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00495 ast_odbc_release_obj(obj);
00496 return cfg;
00497 }
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00515 {
00516 struct odbc_obj *obj;
00517 SQLHSTMT stmt;
00518 char sql[256];
00519 SQLLEN rowcount=0;
00520 const char *newparam, *newval;
00521 int res, count = 0, paramcount = 0;
00522 va_list aq;
00523 struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00524 struct odbc_cache_tables *tableptr;
00525 struct odbc_cache_columns *column = NULL;
00526 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00527
00528 if (!table || !keyfield) {
00529 return -1;
00530 }
00531
00532 tableptr = ast_odbc_find_table(database, table);
00533 if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
00534 ast_odbc_release_table(tableptr);
00535 return -1;
00536 }
00537
00538 if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
00539 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", keyfield, table, database);
00540 }
00541
00542 va_copy(aq, ap);
00543
00544 snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
00545 while((newparam = va_arg(aq, const char *))) {
00546 newval = va_arg(aq, const char *);
00547 if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count >= 64) {
00548 if (paramcount++) {
00549 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
00550 }
00551
00552 if (count < 64 && ast_strlen_zero(newval) && column->nullable && !is_text(column) && !ast_odbc_allow_empty_string_in_nontext(obj)) {
00553 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", newparam);
00554 cps.skip |= (1LL << count);
00555 } else {
00556
00557 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", newparam);
00558 }
00559 } else {
00560 cps.skip |= (1LL << count);
00561 }
00562 ++count;
00563 }
00564 va_end(aq);
00565 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
00566 ast_odbc_release_table(tableptr);
00567
00568 if (ast_string_field_init(&cps, 256)) {
00569 ast_odbc_release_obj(obj);
00570 return -1;
00571 }
00572 va_copy(cps.ap, ap);
00573 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00574 va_end(cps.ap);
00575 ast_string_field_free_memory(&cps);
00576
00577 if (!stmt) {
00578 ast_odbc_release_obj(obj);
00579 return -1;
00580 }
00581
00582 res = SQLRowCount(stmt, &rowcount);
00583 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00584 ast_odbc_release_obj(obj);
00585
00586 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00587 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00588 return -1;
00589 }
00590
00591 if (rowcount >= 0) {
00592 return (int) rowcount;
00593 }
00594
00595 return -1;
00596 }
00597
00598 struct update2_prepare_struct {
00599 const char *database;
00600 const char *table;
00601 va_list ap;
00602 };
00603
00604 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
00605 {
00606 int res, x = 1, first = 1;
00607 struct update2_prepare_struct *ups = data;
00608 const char *newparam, *newval;
00609 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00610 SQLHSTMT stmt;
00611 va_list ap;
00612 struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
00613
00614 if (!sql) {
00615 if (tableptr) {
00616 ast_odbc_release_table(tableptr);
00617 }
00618 return NULL;
00619 }
00620
00621 if (!tableptr) {
00622 ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database);
00623 return NULL;
00624 }
00625
00626 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00627 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00628 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00629 ast_odbc_release_table(tableptr);
00630 return NULL;
00631 }
00632
00633 ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
00634
00635
00636 va_copy(ap, ups->ap);
00637
00638 while ((newparam = va_arg(ap, const char *))) {
00639 newval = va_arg(ap, const char *);
00640 }
00641
00642 while ((newparam = va_arg(ap, const char *))) {
00643 newval = va_arg(ap, const char *);
00644 if (ast_odbc_find_column(tableptr, newparam)) {
00645 ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
00646 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00647 first = 0;
00648 } else {
00649 ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
00650 }
00651 }
00652 va_end(ap);
00653
00654 ast_str_append(&sql, 0, "WHERE");
00655 first = 1;
00656
00657
00658 va_copy(ap, ups->ap);
00659
00660 while ((newparam = va_arg(ap, const char *))) {
00661 newval = va_arg(ap, const char *);
00662 if (!ast_odbc_find_column(tableptr, newparam)) {
00663 va_end(ap);
00664 ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
00665 ast_odbc_release_table(tableptr);
00666 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00667 return NULL;
00668 }
00669 ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
00670 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00671 first = 0;
00672 }
00673 va_end(ap);
00674
00675
00676 ast_odbc_release_table(tableptr);
00677
00678 res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
00679 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00680 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
00681 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00682 return NULL;
00683 }
00684
00685 return stmt;
00686 }
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702 static int update2_odbc(const char *database, const char *table, va_list ap)
00703 {
00704 struct odbc_obj *obj;
00705 SQLHSTMT stmt;
00706 struct update2_prepare_struct ups = { .database = database, .table = table, };
00707 struct ast_str *sql;
00708 int res;
00709 SQLLEN rowcount = 0;
00710
00711 if (!(obj = ast_odbc_request_obj(database, 0))) {
00712 return -1;
00713 }
00714
00715 va_copy(ups.ap, ap);
00716 if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
00717 va_end(ups.ap);
00718 ast_odbc_release_obj(obj);
00719 return -1;
00720 }
00721 va_end(ups.ap);
00722
00723 res = SQLRowCount(stmt, &rowcount);
00724 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00725 ast_odbc_release_obj(obj);
00726
00727 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00728
00729 sql = ast_str_thread_get(&sql_buf, 16);
00730 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
00731 return -1;
00732 }
00733
00734 if (rowcount >= 0) {
00735 return (int)rowcount;
00736 }
00737
00738 return -1;
00739 }
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754 static int store_odbc(const char *database, const char *table, va_list ap)
00755 {
00756 struct odbc_obj *obj;
00757 SQLHSTMT stmt;
00758 char sql[256];
00759 char keys[256];
00760 char vals[256];
00761 SQLLEN rowcount=0;
00762 const char *newparam;
00763 int res;
00764 va_list aq;
00765 struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
00766 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00767
00768 if (!table) {
00769 return -1;
00770 }
00771
00772 obj = ast_odbc_request_obj2(database, connected_flag);
00773 if (!obj) {
00774 return -1;
00775 }
00776
00777 va_copy(aq, ap);
00778
00779 newparam = va_arg(aq, const char *);
00780 if (!newparam) {
00781 va_end(aq);
00782 ast_odbc_release_obj(obj);
00783 return -1;
00784 }
00785 va_arg(aq, const char *);
00786 snprintf(keys, sizeof(keys), "%s", newparam);
00787 ast_copy_string(vals, "?", sizeof(vals));
00788 while ((newparam = va_arg(aq, const char *))) {
00789 snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
00790 snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
00791 va_arg(aq, const char *);
00792 }
00793 va_end(aq);
00794 snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
00795
00796 if (ast_string_field_init(&cps, 256)) {
00797 ast_odbc_release_obj(obj);
00798 return -1;
00799 }
00800 va_copy(cps.ap, ap);
00801 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00802 va_end(cps.ap);
00803 ast_string_field_free_memory(&cps);
00804
00805 if (!stmt) {
00806 ast_odbc_release_obj(obj);
00807 return -1;
00808 }
00809
00810 res = SQLRowCount(stmt, &rowcount);
00811 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00812 ast_odbc_release_obj(obj);
00813
00814 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00815 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00816 return -1;
00817 }
00818
00819 if (rowcount >= 0)
00820 return (int)rowcount;
00821
00822 return -1;
00823 }
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00841 {
00842 struct odbc_obj *obj;
00843 SQLHSTMT stmt;
00844 char sql[256];
00845 SQLLEN rowcount=0;
00846 const char *newparam;
00847 int res;
00848 va_list aq;
00849 struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00850 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00851
00852 if (!table) {
00853 return -1;
00854 }
00855
00856 obj = ast_odbc_request_obj2(database, connected_flag);
00857 if (!obj) {
00858 return -1;
00859 }
00860
00861 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
00862
00863 va_copy(aq, ap);
00864 while((newparam = va_arg(aq, const char *))) {
00865 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
00866 va_arg(aq, const char *);
00867 }
00868 va_end(aq);
00869 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
00870
00871 if (ast_string_field_init(&cps, 256)) {
00872 ast_odbc_release_obj(obj);
00873 return -1;
00874 }
00875 va_copy(cps.ap, ap);
00876 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00877 va_end(cps.ap);
00878 ast_string_field_free_memory(&cps);
00879
00880 if (!stmt) {
00881 ast_odbc_release_obj(obj);
00882 return -1;
00883 }
00884
00885 res = SQLRowCount(stmt, &rowcount);
00886 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00887 ast_odbc_release_obj(obj);
00888
00889 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00890 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00891 return -1;
00892 }
00893
00894 if (rowcount >= 0)
00895 return (int)rowcount;
00896
00897 return -1;
00898 }
00899
00900 struct config_odbc_obj {
00901 char *sql;
00902 unsigned long cat_metric;
00903 char category[128];
00904 char var_name[128];
00905 char *var_val;
00906 unsigned long var_val_size;
00907 SQLLEN err;
00908 };
00909
00910
00911 static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data)
00912 {
00913 struct config_odbc_obj *q = data;
00914 SQLHSTMT sth;
00915 int res;
00916
00917 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00918 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00919 ast_verb(4, "Failure in AllocStatement %d\n", res);
00920 return NULL;
00921 }
00922
00923 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00924 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00925 ast_verb(4, "Error in PREPARE %d\n", res);
00926 SQLFreeHandle(SQL_HANDLE_STMT, sth);
00927 return NULL;
00928 }
00929
00930 SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err);
00931
00932 return sth;
00933 }
00934
00935 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
00936 {
00937 struct config_odbc_obj *q = data;
00938 SQLHSTMT sth;
00939 int res;
00940
00941 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00942 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00943 ast_verb(4, "Failure in AllocStatement %d\n", res);
00944 return NULL;
00945 }
00946
00947 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00948 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00949 ast_verb(4, "Error in PREPARE %d\n", res);
00950 SQLFreeHandle(SQL_HANDLE_STMT, sth);
00951 return NULL;
00952 }
00953
00954 SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
00955 SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
00956 SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
00957 SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err);
00958
00959 return sth;
00960 }
00961
00962 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
00963 {
00964 struct ast_variable *new_v;
00965 struct ast_category *cur_cat;
00966 int res = 0;
00967 struct odbc_obj *obj;
00968 char sqlbuf[1024] = "";
00969 char *sql = sqlbuf;
00970 size_t sqlleft = sizeof(sqlbuf);
00971 unsigned int last_cat_metric = 0;
00972 SQLSMALLINT rowcount = 0;
00973 SQLHSTMT stmt;
00974 char last[128] = "";
00975 struct config_odbc_obj q;
00976 struct ast_flags loader_flags = { 0 };
00977 struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
00978
00979 memset(&q, 0, sizeof(q));
00980
00981 if (!file || !strcmp (file, "res_config_odbc.conf"))
00982 return NULL;
00983
00984 obj = ast_odbc_request_obj2(database, connected_flag);
00985 if (!obj)
00986 return NULL;
00987
00988 q.sql = sqlbuf;
00989
00990 ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file);
00991
00992 stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q);
00993
00994 if (!stmt) {
00995 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
00996 ast_odbc_release_obj(obj);
00997 return NULL;
00998 }
00999
01000 res = SQLNumResultCols(stmt, &rowcount);
01001
01002 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01003 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
01004 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01005 ast_odbc_release_obj(obj);
01006 return NULL;
01007 }
01008
01009 if (!rowcount) {
01010 ast_log(LOG_NOTICE, "found nothing\n");
01011 ast_odbc_release_obj(obj);
01012 return cfg;
01013 }
01014
01015
01016 if (SQLFetch(stmt) == SQL_NO_DATA) {
01017 ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n");
01018 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01019 ast_odbc_release_obj(obj);
01020 return NULL;
01021 }
01022
01023
01024 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01025 sql = sqlbuf;
01026 sqlleft = sizeof(sqlbuf);
01027
01028 ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
01029 ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
01030 ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
01031
01032 q.var_val_size += 1;
01033 q.var_val = ast_malloc(q.var_val_size);
01034 if (!q.var_val) {
01035 ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file);
01036 ast_odbc_release_obj(obj);
01037 return NULL;
01038 }
01039
01040 stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
01041
01042 if (!stmt) {
01043 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
01044 ast_odbc_release_obj(obj);
01045 ast_free(q.var_val);
01046 return NULL;
01047 }
01048
01049 res = SQLNumResultCols(stmt, &rowcount);
01050
01051 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01052 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
01053 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01054 ast_odbc_release_obj(obj);
01055 ast_free(q.var_val);
01056 return NULL;
01057 }
01058
01059 if (!rowcount) {
01060 ast_log(LOG_NOTICE, "found nothing\n");
01061 ast_odbc_release_obj(obj);
01062 ast_free(q.var_val);
01063 return cfg;
01064 }
01065
01066 cur_cat = ast_config_get_current_category(cfg);
01067
01068 while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
01069 if (!strcmp (q.var_name, "#include")) {
01070 if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
01071 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01072 ast_odbc_release_obj(obj);
01073 ast_free(q.var_val);
01074 return NULL;
01075 }
01076 continue;
01077 }
01078 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
01079 cur_cat = ast_category_new(q.category, "", 99999);
01080 if (!cur_cat) {
01081 ast_log(LOG_WARNING, "Out of memory!\n");
01082 break;
01083 }
01084 strcpy(last, q.category);
01085 last_cat_metric = q.cat_metric;
01086 ast_category_append(cfg, cur_cat);
01087 }
01088
01089 new_v = ast_variable_new(q.var_name, q.var_val, "");
01090 ast_variable_append(cur_cat, new_v);
01091 }
01092
01093 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01094 ast_odbc_release_obj(obj);
01095 ast_free(q.var_val);
01096 return cfg;
01097 }
01098
01099 #define warn_length(col, size) ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is not long enough to contain realtime data (needs %d)\n", table, database, col->name, size)
01100 #define warn_type(col, type) ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is of the incorrect type (%d) to contain the required realtime data\n", table, database, col->name, col->type)
01101
01102 static int require_odbc(const char *database, const char *table, va_list ap)
01103 {
01104 struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
01105 struct odbc_cache_columns *col;
01106 char *elm;
01107 int type, size;
01108
01109 if (!tableptr) {
01110 return -1;
01111 }
01112
01113 while ((elm = va_arg(ap, char *))) {
01114 type = va_arg(ap, require_type);
01115 size = va_arg(ap, int);
01116
01117 AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
01118 if (strcmp(col->name, elm) == 0) {
01119
01120 switch (col->type) {
01121 case SQL_CHAR:
01122 case SQL_VARCHAR:
01123 case SQL_LONGVARCHAR:
01124 #ifdef HAVE_ODBC_WCHAR
01125 case SQL_WCHAR:
01126 case SQL_WVARCHAR:
01127 case SQL_WLONGVARCHAR:
01128 #endif
01129 case SQL_BINARY:
01130 case SQL_VARBINARY:
01131 case SQL_LONGVARBINARY:
01132 case SQL_GUID:
01133 #define CHECK_SIZE(n) \
01134 if (col->size < n) { \
01135 warn_length(col, n); \
01136 } \
01137 break;
01138 switch (type) {
01139 case RQ_UINTEGER1: CHECK_SIZE(3)
01140 case RQ_INTEGER1: CHECK_SIZE(4)
01141 case RQ_UINTEGER2: CHECK_SIZE(5)
01142 case RQ_INTEGER2: CHECK_SIZE(6)
01143 case RQ_UINTEGER3:
01144 case RQ_INTEGER3: CHECK_SIZE(8)
01145 case RQ_DATE:
01146 case RQ_UINTEGER4: CHECK_SIZE(10)
01147 case RQ_INTEGER4: CHECK_SIZE(11)
01148 case RQ_DATETIME:
01149 case RQ_UINTEGER8: CHECK_SIZE(19)
01150 case RQ_INTEGER8: CHECK_SIZE(20)
01151 case RQ_FLOAT:
01152 case RQ_CHAR: CHECK_SIZE(size)
01153 }
01154 #undef CHECK_SIZE
01155 break;
01156 case SQL_TYPE_DATE:
01157 if (type != RQ_DATE) {
01158 warn_type(col, type);
01159 }
01160 break;
01161 case SQL_TYPE_TIMESTAMP:
01162 case SQL_TIMESTAMP:
01163 if (type != RQ_DATE && type != RQ_DATETIME) {
01164 warn_type(col, type);
01165 }
01166 break;
01167 case SQL_BIT:
01168 warn_length(col, size);
01169 break;
01170 #define WARN_TYPE_OR_LENGTH(n) \
01171 if (!ast_rq_is_int(type)) { \
01172 warn_type(col, type); \
01173 } else { \
01174 warn_length(col, n); \
01175 }
01176 case SQL_TINYINT:
01177 if (type != RQ_UINTEGER1) {
01178 WARN_TYPE_OR_LENGTH(size)
01179 }
01180 break;
01181 case SQL_C_STINYINT:
01182 if (type != RQ_INTEGER1) {
01183 WARN_TYPE_OR_LENGTH(size)
01184 }
01185 break;
01186 case SQL_C_USHORT:
01187 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
01188 WARN_TYPE_OR_LENGTH(size)
01189 }
01190 break;
01191 case SQL_SMALLINT:
01192 case SQL_C_SSHORT:
01193 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
01194 WARN_TYPE_OR_LENGTH(size)
01195 }
01196 break;
01197 case SQL_C_ULONG:
01198 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01199 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01200 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01201 type != RQ_INTEGER4) {
01202 WARN_TYPE_OR_LENGTH(size)
01203 }
01204 break;
01205 case SQL_INTEGER:
01206 case SQL_C_SLONG:
01207 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01208 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01209 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01210 type != RQ_INTEGER4) {
01211 WARN_TYPE_OR_LENGTH(size)
01212 }
01213 break;
01214 case SQL_C_UBIGINT:
01215 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01216 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01217 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01218 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01219 type != RQ_INTEGER8) {
01220 WARN_TYPE_OR_LENGTH(size)
01221 }
01222 break;
01223 case SQL_BIGINT:
01224 case SQL_C_SBIGINT:
01225 if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01226 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01227 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01228 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01229 type != RQ_INTEGER8) {
01230 WARN_TYPE_OR_LENGTH(size)
01231 }
01232 break;
01233 #undef WARN_TYPE_OR_LENGTH
01234 case SQL_NUMERIC:
01235 case SQL_DECIMAL:
01236 case SQL_FLOAT:
01237 case SQL_REAL:
01238 case SQL_DOUBLE:
01239 if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01240 warn_type(col, type);
01241 }
01242 break;
01243 default:
01244 ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
01245 }
01246 break;
01247 }
01248 }
01249 if (!col) {
01250 ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
01251 }
01252 }
01253 AST_RWLIST_UNLOCK(&tableptr->columns);
01254 return 0;
01255 }
01256 #undef warn_length
01257 #undef warn_type
01258
01259 static int unload_odbc(const char *a, const char *b)
01260 {
01261 return ast_odbc_clear_cache(a, b);
01262 }
01263
01264 static struct ast_config_engine odbc_engine = {
01265 .name = "odbc",
01266 .load_func = config_odbc,
01267 .realtime_func = realtime_odbc,
01268 .realtime_multi_func = realtime_multi_odbc,
01269 .store_func = store_odbc,
01270 .destroy_func = destroy_odbc,
01271 .update_func = update_odbc,
01272 .update2_func = update2_odbc,
01273 .require_func = require_odbc,
01274 .unload_func = unload_odbc,
01275 };
01276
01277 static int unload_module (void)
01278 {
01279 ast_config_engine_deregister(&odbc_engine);
01280
01281 ast_verb(1, "res_config_odbc unloaded.\n");
01282 return 0;
01283 }
01284
01285 static int load_module (void)
01286 {
01287 ast_config_engine_register(&odbc_engine);
01288 ast_verb(1, "res_config_odbc loaded.\n");
01289 return 0;
01290 }
01291
01292 static int reload_module(void)
01293 {
01294 return 0;
01295 }
01296
01297 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime ODBC configuration",
01298 .load = load_module,
01299 .unload = unload_module,
01300 .reload = reload_module,
01301 .load_pri = AST_MODPRI_REALTIME_DRIVER,
01302 );