Fri Aug 24 02:27:21 2007

Asterisk developer's documentation


res_odbc.h File Reference

ODBC resource manager. More...

#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

Include dependency graph for res_odbc.h:

This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  odbc_obj

Enumerations

enum  odbc_status { ODBC_SUCCESS = 0, ODBC_FAIL = -1 }

Functions

SQLHSTMT ast_odbc_prepare_and_execute (struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
 Prepares, executes, and returns the resulting statement handle.
void ast_odbc_release_obj (struct odbc_obj *obj)
 Releases an ODBC object previously allocated by odbc_request_obj().
odbc_objast_odbc_request_obj (const char *name, int check)
 Retrieves a connected ODBC object.
int ast_odbc_sanity_check (struct odbc_obj *obj)
 Checks an ODBC object to ensure it is still connected.
int ast_odbc_smart_execute (struct odbc_obj *obj, SQLHSTMT stmt)
 Executes a prepared statement handle.


Detailed Description

ODBC resource manager.

Definition in file res_odbc.h.


Enumeration Type Documentation

enum odbc_status

Enumerator:
ODBC_SUCCESS 
ODBC_FAIL 

Definition at line 34 of file res_odbc.h.


Function Documentation

SQLHSTMT ast_odbc_prepare_and_execute ( struct odbc_obj obj,
SQLHSTMT(*)(struct odbc_obj *obj, void *data)  prepare_cb,
void *  data 
)

Prepares, executes, and returns the resulting statement handle.

Parameters:
obj The ODBC object
prepare_cb A function callback, which, when called, should return a statement handle prepared, with any necessary parameters or result columns bound.
data A parameter to be passed to the prepare_cb parameter function, indicating which statement handle is to be prepared.
Returns:
Returns a statement handle or NULL on error.

Definition at line 79 of file res_odbc.c.

References ast_log(), LOG_WARNING, odbc_obj_connect(), and odbc_obj_disconnect().

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00080 {
00081    int res = 0, i, attempt;
00082    SQLINTEGER nativeerror=0, numfields=0;
00083    SQLSMALLINT diagbytes=0;
00084    unsigned char state[10], diagnostic[256];
00085    SQLHSTMT stmt;
00086 
00087    for (attempt = 0; attempt < 2; attempt++) {
00088       /* This prepare callback may do more than just prepare -- it may also
00089        * bind parameters, bind results, etc.  The real key, here, is that
00090        * when we disconnect, all handles become invalid for most databases.
00091        * We must therefore redo everything when we establish a new
00092        * connection. */
00093       stmt = prepare_cb(obj, data);
00094 
00095       if (stmt) {
00096          res = SQLExecute(stmt);
00097          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00098             if (res == SQL_ERROR) {
00099                SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00100                for (i = 0; i < numfields; i++) {
00101                   SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00102                   ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00103                   if (i > 10) {
00104                      ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00105                      break;
00106                   }
00107                }
00108             }
00109 
00110             ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00111             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00112             stmt = NULL;
00113 
00114             obj->up = 0;
00115             /*
00116              * While this isn't the best way to try to correct an error, this won't automatically
00117              * fail when the statement handle invalidates.
00118              */
00119             /* XXX Actually, it might, if we're using a non-pooled connection. Possible race here. XXX */
00120             odbc_obj_disconnect(obj);
00121             odbc_obj_connect(obj);
00122             continue;
00123          }
00124          break;
00125       } else {
00126          ast_log(LOG_WARNING, "SQL Prepare failed.  Attempting a reconnect...\n");
00127          odbc_obj_disconnect(obj);
00128          odbc_obj_connect(obj);
00129       }
00130    }
00131 
00132    return stmt;
00133 }

void ast_odbc_release_obj ( struct odbc_obj obj  ) 

Releases an ODBC object previously allocated by odbc_request_obj().

Parameters:
obj The ODBC object

Definition at line 375 of file res_odbc.c.

References odbc_obj::used.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00376 {
00377    /* For pooled connections, this frees the connection to be
00378     * reused.  For non-pooled connections, it does nothing. */
00379    obj->used = 0;
00380 }

struct odbc_obj* ast_odbc_request_obj ( const char *  name,
int  check 
)

Retrieves a connected ODBC object.

Parameters:
name The name of the ODBC class for which a connection is needed.
check Whether to ensure that a connection is valid before returning the handle. Usually unnecessary.
Returns:
Returns an ODBC object or NULL if there is no connection available with the requested name.
Connection classes may, in fact, contain multiple connection handles. If the connection is pooled, then each connection will be dedicated to the thread which requests it. Note that all connections should be released when the thread is done by calling odbc_release_obj(), below.

Definition at line 382 of file res_odbc.c.

References ast_calloc, AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_destroy(), ast_mutex_init(), ast_odbc_sanity_check(), free, LOG_WARNING, ODBC_FAIL, odbc_obj_connect(), and odbc_obj::used.

Referenced by acf_odbc_read(), acf_odbc_write(), config_odbc(), odbc_register_class(), realtime_multi_odbc(), realtime_odbc(), and update_odbc().

00383 {
00384    struct odbc_obj *obj = NULL;
00385    struct odbc_class *class;
00386 
00387    AST_LIST_LOCK(&odbc_list);
00388    AST_LIST_TRAVERSE(&odbc_list, class, list) {
00389       if (!strcmp(class->name, name))
00390          break;
00391    }
00392    AST_LIST_UNLOCK(&odbc_list);
00393 
00394    if (!class)
00395       return NULL;
00396 
00397    AST_LIST_LOCK(&class->odbc_obj);
00398    if (class->haspool) {
00399       /* Recycle connections before building another */
00400       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00401          if (! obj->used) {
00402             obj->used = 1;
00403             break;
00404          }
00405       }
00406 
00407       if (!obj && (class->count < class->limit)) {
00408          class->count++;
00409          obj = ast_calloc(1, sizeof(*obj));
00410          if (!obj) {
00411             AST_LIST_UNLOCK(&class->odbc_obj);
00412             return NULL;
00413          }
00414          ast_mutex_init(&obj->lock);
00415          obj->parent = class;
00416          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00417             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00418             ast_mutex_destroy(&obj->lock);
00419             free(obj);
00420             obj = NULL;
00421             class->count--;
00422          } else {
00423             obj->used = 1;
00424             AST_LIST_INSERT_TAIL(&class->odbc_obj, obj, list);
00425          }
00426       }
00427    } else {
00428       /* Non-pooled connection: multiple modules can use the same connection. */
00429       AST_LIST_TRAVERSE(&class->odbc_obj, obj, list) {
00430          /* Non-pooled connection: if there is an entry, return it */
00431          break;
00432       }
00433 
00434       if (!obj) {
00435          /* No entry: build one */
00436          obj = ast_calloc(1, sizeof(*obj));
00437          if (!obj) {
00438             AST_LIST_UNLOCK(&class->odbc_obj);
00439             return NULL;
00440          }
00441          ast_mutex_init(&obj->lock);
00442          obj->parent = class;
00443          if (odbc_obj_connect(obj) == ODBC_FAIL) {
00444             ast_log(LOG_WARNING, "Failed to connect to %s\n", name);
00445             ast_mutex_destroy(&obj->lock);
00446             free(obj);
00447             obj = NULL;
00448          } else {
00449             AST_LIST_INSERT_HEAD(&class->odbc_obj, obj, list);
00450          }
00451       }
00452    }
00453    AST_LIST_UNLOCK(&class->odbc_obj);
00454 
00455    if (obj && check) {
00456       ast_odbc_sanity_check(obj);
00457    }
00458    return obj;
00459 }

int ast_odbc_sanity_check ( struct odbc_obj obj  ) 

Checks an ODBC object to ensure it is still connected.

Parameters:
obj The ODBC object
Returns:
Returns 0 if connected, -1 otherwise.

Definition at line 177 of file res_odbc.c.

References ast_log(), odbc_obj::con, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.

Referenced by ast_odbc_request_obj(), and odbc_show_command().

00178 {
00179    char *test_sql = "select 1";
00180    SQLHSTMT stmt;
00181    int res = 0;
00182 
00183    if (obj->up) {
00184       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00185       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00186          obj->up = 0;
00187       } else {
00188          res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
00189          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00190             obj->up = 0;
00191          } else {
00192             res = SQLExecute(stmt);
00193             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00194                obj->up = 0;
00195             }
00196          }
00197       }
00198       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00199    }
00200 
00201    if (!obj->up) { /* Try to reconnect! */
00202       ast_log(LOG_WARNING, "Connection is down attempting to reconnect...\n");
00203       odbc_obj_disconnect(obj);
00204       odbc_obj_connect(obj);
00205    }
00206    return obj->up;
00207 }

int ast_odbc_smart_execute ( struct odbc_obj obj,
SQLHSTMT  stmt 
)

Executes a prepared statement handle.

Parameters:
obj The non-NULL result of odbc_request_obj()
stmt The prepared statement handle
Returns:
Returns 0 on success or -1 on failure
This function was originally designed simply to execute a prepared statement handle and to retry if the initial execution failed. Unfortunately, it did this by disconnecting and reconnecting the database handle which on most databases causes the statement handle to become invalid. Therefore, this method has been deprecated in favor of odbc_prepare_and_execute() which allows the statement to be prepared multiple times, if necessary, in case of a loss of connection.

This function really only ever worked with MySQL, where the statement handle is not prepared on the server. If you are not using MySQL, you should avoid it.

Definition at line 135 of file res_odbc.c.

References ast_log(), ast_mutex_lock(), ast_mutex_unlock(), odbc_obj::lock, LOG_WARNING, odbc_obj_connect(), odbc_obj_disconnect(), and odbc_obj::up.

00136 {
00137    int res = 0, i;
00138    SQLINTEGER nativeerror=0, numfields=0;
00139    SQLSMALLINT diagbytes=0;
00140    unsigned char state[10], diagnostic[256];
00141 
00142    res = SQLExecute(stmt);
00143    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00144       if (res == SQL_ERROR) {
00145          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00146          for (i = 0; i < numfields; i++) {
00147             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00148             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00149             if (i > 10) {
00150                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00151                break;
00152             }
00153          }
00154       }
00155 #if 0
00156       /* This is a really bad method of trying to correct a dead connection.  It
00157        * only ever really worked with MySQL.  It will not work with any other
00158        * database, since most databases prepare their statements on the server,
00159        * and if you disconnect, you invalidate the statement handle.  Hence, if
00160        * you disconnect, you're going to fail anyway, whether you try to execute
00161        * a second time or not.
00162        */
00163       ast_log(LOG_WARNING, "SQL Execute error %d! Attempting a reconnect...\n", res);
00164       ast_mutex_lock(&obj->lock);
00165       obj->up = 0;
00166       ast_mutex_unlock(&obj->lock);
00167       odbc_obj_disconnect(obj);
00168       odbc_obj_connect(obj);
00169       res = SQLExecute(stmt);
00170 #endif
00171    }
00172    
00173    return res;
00174 }


Generated on Fri Aug 24 02:27:21 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1