Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (c) 2005, 2006 Tilghman Lesher
* Copyright (c) 2008, 2009 Digium, Inc.
*
* Tilghman Lesher <func_odbc__200508@the-tilghman.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
/*!
* \file
*
* \brief ODBC lookups
*
* \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
Kevin P. Fleming
committed
/*** MODULEINFO
<depend>res_odbc</depend>
<support_level>core</support_level>
Kevin P. Fleming
committed
***/
Kevin P. Fleming
committed
#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/res_odbc.h"
#include "asterisk/res_odbc_transaction.h"
#include "asterisk/app.h"
#include "asterisk/strings.h"
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*** DOCUMENTATION
<function name="ODBC_FETCH" language="en_US">
<synopsis>
Fetch a row from a multirow query.
</synopsis>
<syntax>
<parameter name="result-id" required="true" />
</syntax>
<description>
<para>For queries which are marked as mode=multirow, the original
query returns a <replaceable>result-id</replaceable> from which results
may be fetched. This function implements the actual fetch of the results.</para>
<para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
<variablelist>
<variable name="ODBC_FETCH_STATUS">
<value name="SUCESS">
If rows are available.
</value>
<value name="FAILURE">
If no rows are available.
</value>
</variable>
</variablelist>
</description>
</function>
<application name="ODBCFinish" language="en_US">
<synopsis>
Clear the resultset of a sucessful multirow query.
</synopsis>
<syntax>
<parameter name="result-id" required="true" />
</syntax>
<description>
<para>For queries which are marked as mode=multirow, this will clear
any remaining rows of the specified resultset.</para>
</description>
</application>
<function name="SQL_ESC" language="en_US">
<synopsis>
Escapes single ticks for use in SQL statements.
</synopsis>
<syntax>
<parameter name="string" required="true" />
</syntax>
<description>
<para>Used in SQL templates to escape data which may contain single ticks
<literal>'</literal> which are otherwise used to delimit data.</para>
<para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'</para>
</description>
</function>
***/
static char *config = "func_odbc.conf";
#define DEFAULT_SINGLE_DB_CONNECTION 0
static int single_db_connection;
AST_RWLOCK_DEFINE_STATIC(single_db_connection_lock);
enum odbc_option_flags {
OPT_ESCAPECOMMAS = (1 << 0),
struct acf_odbc_query {
Tilghman Lesher
committed
AST_RWLIST_ENTRY(acf_odbc_query) list;
char readhandle[5][30];
char writehandle[5][30];
Joshua Colp
committed
char *sql_read;
char *sql_write;
char *sql_insert;
unsigned int flags;
struct ast_custom_function *acf;
};
static void odbc_datastore_free(void *data);
static const struct ast_datastore_info odbc_info = {
.type = "FUNC_ODBC",
.destroy = odbc_datastore_free,
};
/* For storing each result row */
struct odbc_datastore_row {
AST_LIST_ENTRY(odbc_datastore_row) list;
char data[0];
};
/* For storing each result set */
struct odbc_datastore {
AST_LIST_HEAD(, odbc_datastore_row);
char names[0];
};
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/* \brief Data source name
*
* This holds data that pertains to a DSN
*/
struct dsn {
/*! A connection to the database */
struct odbc_obj *connection;
/*! The name of the DSN as defined in res_odbc.conf */
char name[0];
};
#define DSN_BUCKETS 37
struct ao2_container *dsns;
static int dsn_hash(const void *obj, const int flags)
{
const struct dsn *object;
const char *key;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_KEY:
key = obj;
break;
case OBJ_SEARCH_OBJECT:
object = obj;
key = object->name;
break;
default:
ast_assert(0);
return 0;
}
return ast_str_hash(key);
}
static int dsn_cmp(void *obj, void *arg, int flags)
{
const struct dsn *object_left = obj;
const struct dsn *object_right = arg;
const char *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
right_key = object_right->name;
/* Fall through */
case OBJ_SEARCH_KEY:
cmp = strcmp(object_left->name, right_key);
break;
case OBJ_SEARCH_PARTIAL_KEY:
cmp = strncmp(object_left->name, right_key, strlen(right_key));
break;
default:
cmp = 0;
break;
}
if (cmp) {
return 0;
}
return CMP_MATCH;
}
static void dsn_destructor(void *obj)
{
struct dsn *dsn = obj;
if (dsn->connection) {
ast_odbc_release_obj(dsn->connection);
}
}
/*!
* \brief Create a DSN and connect to the database
*
* \param name The name of the DSN as found in res_odbc.conf
* \retval NULL Fail
* \retval non-NULL The newly-created structure
*/
static struct dsn *create_dsn(const char *name)
{
struct dsn *dsn;
if (!dsns) {
return NULL;
}
dsn = ao2_alloc(sizeof(*dsn) + strlen(name) + 1, dsn_destructor);
if (!dsn) {
return NULL;
}
/* Safe */
strcpy(dsn->name, name);
dsn->connection = ast_odbc_request_obj(name, 0);
if (!dsn->connection) {
ao2_ref(dsn, -1);
return NULL;
}
if (!ao2_link_flags(dsns, dsn, OBJ_NOLOCK)) {
ao2_ref(dsn, -1);
return NULL;
}
return dsn;
}
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data);
/*!
* \brief Determine if the connection has died.
*
* \param connection The connection to check
* \retval 1 Yep, it's dead
* \retval 0 It's alive and well
*/
static int connection_dead(struct odbc_obj *connection)
{
SQLINTEGER dead;
SQLRETURN res;
SQLHSTMT stmt;
if (!connection) {
return 1;
}
res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
if (SQL_SUCCEEDED(res)) {
return dead == SQL_CD_TRUE ? 1 : 0;
}
/* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a direct
* execute of a probing statement and see if that succeeds instead
*/
stmt = ast_odbc_direct_execute(connection, silent_execute, "SELECT 1");
if (!stmt) {
return 1;
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
return 0;
}
/*!
* \brief Retrieve a DSN, or create it if it does not exist.
*
* The created DSN is returned locked. This should be inconsequential
* to callers in most cases.
*
* When finished with the returned structure, the caller must call
* \ref release_dsn
*
* \param name Name of the DSN as found in res_odbc.conf
* \retval NULL Unable to retrieve or create the DSN
* \retval non-NULL The retrieved/created locked DSN
*/
static struct dsn *get_dsn(const char *name)
{
struct dsn *dsn;
if (!dsns) {
return NULL;
}
ao2_lock(dsns);
dsn = ao2_find(dsns, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
if (!dsn) {
dsn = create_dsn(name);
}
ao2_unlock(dsns);
if (!dsn) {
return NULL;
}
ao2_lock(dsn);
if (!dsn->connection) {
dsn->connection = ast_odbc_request_obj(name, 0);
if (!dsn->connection) {
ao2_unlock(dsn);
ao2_ref(dsn, -1);
return NULL;
}
return dsn;
}
if (connection_dead(dsn->connection)) {
ast_odbc_release_obj(dsn->connection);
dsn->connection = ast_odbc_request_obj(name, 0);
if (!dsn->connection) {
ao2_unlock(dsn);
ao2_ref(dsn, -1);
return NULL;
}
}
return dsn;
}
/*!
* \brief Get a DB handle via a DSN or directly
* If single db connection then get the DB handle via DSN
* else by requesting a connection directly
*
* \param dsn_name Name of the DSN as found in res_odbc.conf
* \param dsn The pointer to the DSN
* \retval NULL Unable to retrieve the DB handle
* \retval non-NULL The retrieved DB handle
static struct odbc_obj *get_odbc_obj(const char *dsn_name, struct dsn **dsn)
struct odbc_obj *obj = NULL;
ast_rwlock_rdlock(&single_db_connection_lock);
if (single_db_connection) {
if (dsn) {
*dsn = get_dsn(dsn_name);
if (*dsn) {
obj = (*dsn)->connection;
}
}
} else {
obj = ast_odbc_request_obj(dsn_name, 0);
ast_rwlock_unlock(&single_db_connection_lock);
return obj;
}
/*!
* \brief Release an ODBC obj or a DSN
*
* If single db connection then unlock and unreference the DSN
* else release the ODBC obj
*
* \param obj The pointer to the ODBC obj to release
* \param dsn The pointer to the dsn to unlock and unreference
*/
static inline void release_obj_or_dsn(struct odbc_obj **obj, struct dsn **dsn)
{
if (dsn && *dsn) {
/* If multiple connections are not enabled then the guarantee
* of a single connection already exists and holding on to the
* connection would prevent any other user from acquiring it
* indefinitely.
*/
if (ast_odbc_get_max_connections((*dsn)->name) < 2) {
ast_odbc_release_obj((*dsn)->connection);
(*dsn)->connection = NULL;
}
ao2_unlock(*dsn);
ao2_ref(*dsn, -1);
*dsn = NULL;
/* Some callers may provide both an obj and dsn. To ensure that
* the connection is not released twice we set it to NULL here if
* present.
*/
if (obj) {
*obj = NULL;
}
} else if (obj && *obj) {
ast_odbc_release_obj(*obj);
*obj = NULL;
}
static AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
static int resultcount = 0;
Tilghman Lesher
committed
AST_THREADSTORAGE(sql2_buf);
AST_THREADSTORAGE(coldata_buf);
AST_THREADSTORAGE(colnames_buf);
static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
static void odbc_datastore_free(void *data)
{
struct odbc_datastore *result = data;
struct odbc_datastore_row *row;
AST_LIST_LOCK(result);
while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
ast_free(row);
}
AST_LIST_UNLOCK(result);
AST_LIST_HEAD_DESTROY(result);
ast_free(result);
}
/*!
* \brief Common execution function for SQL queries.
*
* \param obj DB connection
* \param data The query to execute
* \param silent If true, do not print warnings on failure
* \retval NULL Failed to execute query
* \retval non-NULL The executed statement
*/
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Tilghman Lesher
committed
int res;
char *sql = data;
SQLHSTMT stmt;
res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
Tilghman Lesher
committed
ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
Tilghman Lesher
committed
return NULL;
}
res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
if (res == SQL_ERROR && !silent) {
Tilghman Lesher
committed
int i;
SQLINTEGER nativeerror=0, numfields=0;
SQLSMALLINT diagbytes=0;
unsigned char state[10], diagnostic[256];
SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
for (i = 0; i < numfields; i++) {
SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
if (i > 10) {
ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
break;
}
}
}
if (!silent) {
ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
}
Tilghman Lesher
committed
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
Tilghman Lesher
committed
return NULL;
}
return stmt;
static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
{
return execute(obj, data, 0);
}
static SQLHSTMT silent_execute(struct odbc_obj *obj, void *data)
{
return execute(obj, data, 1);
}
/*
* Master control routine
*/
static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
struct odbc_obj *obj = NULL;
struct acf_odbc_query *query;
Tilghman Lesher
committed
char *t, varname[15];
AST_DECLARE_APP_ARGS(values,
AST_APP_ARG(field)[100];
);
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field)[100];
);
struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
Tilghman Lesher
committed
struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
const char *status = "FAILURE";
Tilghman Lesher
committed
return -1;
}
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strcmp(query->acf->name, cmd)) {
break;
}
}
if (!query) {
ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
if (chan) {
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
}
if (!(chan = ast_dummy_channel_alloc())) {
AST_RWLIST_UNLOCK(&queries);
return -1;
}
bogus_chan = 1;
if (!bogus_chan) {
ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
/* We only get here if sql_write is set. sql_insert is optional however. */
if (query->sql_insert) {
ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
}
Tilghman Lesher
committed
/* Parse our arguments */
Russell Bryant
committed
if (!s || !t) {
ast_log(LOG_ERROR, "Out of memory\n");
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
if (!bogus_chan) {
Tilghman Lesher
committed
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
Tilghman Lesher
committed
}
Russell Bryant
committed
}
AST_STANDARD_APP_ARGS(args, s);
for (i = 0; i < args.argc; i++) {
snprintf(varname, sizeof(varname), "ARG%d", i + 1);
pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
}
/* Parse values, just like arguments */
Tilghman Lesher
committed
AST_STANDARD_APP_ARGS(values, t);
for (i = 0; i < values.argc; i++) {
snprintf(varname, sizeof(varname), "VAL%d", i + 1);
pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
/* Additionally set the value as a whole (but push an empty string if value is NULL) */
pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
if (query->sql_insert) {
ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
}
if (bogus_chan) {
chan = ast_channel_unref(chan);
} else {
/* Restore prior values */
for (i = 0; i < args.argc; i++) {
snprintf(varname, sizeof(varname), "ARG%d", i + 1);
pbx_builtin_setvar_helper(chan, varname, NULL);
for (i = 0; i < values.argc; i++) {
snprintf(varname, sizeof(varname), "VAL%d", i + 1);
pbx_builtin_setvar_helper(chan, varname, NULL);
}
pbx_builtin_setvar_helper(chan, "VALUE", NULL);
/*!\note
* Okay, this part is confusing. Transactions belong to a single database
* handle. Therefore, when working with transactions, we CANNOT failover
* to multiple DSNs. We MUST have a single handle all the way through the
* transaction, or else we CANNOT enforce atomicity.
*/
for (dsn_num = 0; dsn_num < 5; dsn_num++) {
if (!ast_strlen_zero(query->writehandle[dsn_num])) {
if (transactional) {
/* This can only happen second time through or greater. */
ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
transactional = 1;
} else {
obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
transactional = 0;
}
if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
break;
if (!transactional) {
release_obj_or_dsn (&obj, &dsn);
}
Tilghman Lesher
committed
}
}
if (stmt) {
SQLRowCount(stmt, &rows);
Tilghman Lesher
committed
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
if (rows != 0) {
status = "SUCCESS";
} else if (query->sql_insert) {
if (!transactional) {
release_obj_or_dsn (&obj, &dsn);
}
for (transactional = 0, dsn_num = 0; dsn_num < 5; dsn_num++) {
if (!ast_strlen_zero(query->writehandle[dsn_num])) {
if (transactional) {
/* This can only happen second time through or greater. */
ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
} else {
release_obj_or_dsn (&obj, &dsn);
if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn_num]))) {
transactional = 1;
} else {
obj = get_odbc_obj(query->writehandle[dsn_num], &dsn);
transactional = 0;
}
if (obj) {
stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
}
if (stmt) {
status = "FAILOVER";
SQLRowCount(stmt, &rows);
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
break;
Tilghman Lesher
committed
}
}
}
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
/* Output the affected rows, for all cases. In the event of failure, we
* flag this as -1 rows. Note that this is different from 0 affected rows
* which would be the case if we succeeded in our query, but the values did
* not change. */
if (!bogus_chan) {
snprintf(varname, sizeof(varname), "%d", (int)rows);
pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
}
if (!transactional) {
release_obj_or_dsn (&obj, &dsn);
}
if (!bogus_chan) {
static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
struct odbc_obj *obj = NULL;
struct acf_odbc_query *query;
char varname[15], rowcount[12] = "-1";
struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn_num, bogus_chan = 0;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field)[100];
);
SQLSMALLINT colcount=0;
SQLSMALLINT collength;
struct odbc_datastore *resultset = NULL;
struct odbc_datastore_row *row = NULL;
struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
Tilghman Lesher
committed
const char *status = "FAILURE";
Tilghman Lesher
committed
if (chan) {
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
}
Tilghman Lesher
committed
return -1;
}
ast_str_reset(colnames);
Tilghman Lesher
committed
AST_RWLIST_RDLOCK(&queries);
AST_RWLIST_TRAVERSE(&queries, query, list) {
if (!strcmp(query->acf->name, cmd)) {
break;
}
}
if (!query) {
ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
if (chan) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
}
if (!(chan = ast_dummy_channel_alloc())) {
AST_RWLIST_UNLOCK(&queries);
return -1;
if (!bogus_chan) {
AST_STANDARD_APP_ARGS(args, s);
for (x = 0; x < args.argc; x++) {
snprintf(varname, sizeof(varname), "ARG%d", x + 1);
pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
if (bogus_chan) {
chan = ast_channel_unref(chan);
} else {
/* Restore prior values */
for (x = 0; x < args.argc; x++) {
snprintf(varname, sizeof(varname), "ARG%d", x + 1);
pbx_builtin_setvar_helper(chan, varname, NULL);
}
/* Save these flags, so we can release the lock */
escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
ast_autoservice_stop(chan);
return -1;
}
AST_LIST_HEAD_INIT(resultset);
if (query->rowlimit) {
rowlimit = query->rowlimit;
} else {
multirow = 1;
} else if (!bogus_chan) {
if (query->rowlimit > 1) {
rowlimit = query->rowlimit;
if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
ast_autoservice_stop(chan);
return -1;
}
AST_LIST_HEAD_INIT(resultset);
}
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&queries);
for (dsn_num = 0; dsn_num < 5; dsn_num++) {
if (!ast_strlen_zero(query->readhandle[dsn_num])) {
obj = get_odbc_obj(query->readhandle[dsn_num], &dsn);
if (!obj) {
stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
}
if (stmt) {
break;
release_obj_or_dsn (&obj, &dsn);
}
Tilghman Lesher
committed
if (!stmt) {
ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
release_obj_or_dsn (&obj, &dsn);
if (!bogus_chan) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
}
res = SQLNumResultCols(stmt, &colcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (!bogus_chan) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
Tilghman Lesher
committed
int res1 = -1;
if (res == SQL_NO_DATA) {
ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
Tilghman Lesher
committed
res1 = 0;
ast_copy_string(rowcount, "0", sizeof(rowcount));
Tilghman Lesher
committed
status = "NODATA";
ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
Tilghman Lesher
committed
status = "FETCHERROR";
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (!bogus_chan) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
Tilghman Lesher
committed
return res1;
Tilghman Lesher
committed
status = "SUCCESS";
for (y = 0; y < rowlimit; y++) {
buf[0] = '\0';
for (x = 0; x < colcount; x++) {
int i;
struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
char *ptrcoldata;
SQLCloseCursor(stmt);
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (!bogus_chan) {
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
ast_autoservice_stop(chan);
}
return -1;
}
if (y == 0) {
char colname[256];
res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
snprintf(colname, sizeof(colname), "field%d", x);
}
ast_str_make_space(&coldata, maxcol + 1);
if (ast_str_strlen(colnames)) {
ast_str_append(&colnames, 0, ",");
}
ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
if (!tmp) {
ast_log(LOG_ERROR, "No space for a new resultset?\n");
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
release_obj_or_dsn (&obj, &dsn);
if (!bogus_chan) {
pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
ast_autoservice_stop(chan);
}
return -1;
}
resultset = tmp;
strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
Kevin P. Fleming
committed
res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
if (indicator == SQL_NULL_DATA) {
ast_debug(3, "Got NULL data\n");
ast_str_reset(coldata);
}
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
}
ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
buf[buflen++] = ',';
}
/* Copy data, encoding '\' and ',' for the argument parser */
ptrcoldata = ast_str_buffer(coldata);
for (i = 0; i < ast_str_strlen(coldata); i++) {
if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
buf[buflen++] = '\\';
}
buf[buflen++] = ptrcoldata[i];
if (buflen >= len - 2) {
}
if (ptrcoldata[i] == '\0') {
}
}
ast_debug(2, "buf is now set to '%s'\n", buf);
ast_debug(2, "buf is now set to '%s'\n", buf);
row = ast_calloc(1, sizeof(*row) + buflen + 1);
if (!row) {
ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
Tilghman Lesher
committed
status = "MEMERROR";
goto end_acf_read;
}
strcpy((char *)row + sizeof(*row), buf);
AST_LIST_INSERT_TAIL(resultset, row, list);
/* Get next row */
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
if (res != SQL_NO_DATA) {
ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
}
/* Number of rows in the resultset */