Return-Path: Delivered-To: apmail-apr-commits-archive@www.apache.org Received: (qmail 89657 invoked from network); 17 Feb 2007 02:50:42 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 17 Feb 2007 02:50:42 -0000 Received: (qmail 96462 invoked by uid 500); 17 Feb 2007 02:50:49 -0000 Delivered-To: apmail-apr-commits-archive@apr.apache.org Received: (qmail 96392 invoked by uid 500); 17 Feb 2007 02:50:49 -0000 Mailing-List: contact commits-help@apr.apache.org; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: Reply-To: dev@apr.apache.org List-Id: Delivered-To: mailing list commits@apr.apache.org Received: (qmail 96368 invoked by uid 99); 17 Feb 2007 02:50:49 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Feb 2007 18:50:48 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Feb 2007 18:50:38 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id BB8311A981A; Fri, 16 Feb 2007 18:50:18 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r508700 [1/2] - in /apr/apr-util/trunk: CHANGES STATUS dbd/apr_dbd.c dbd/apr_dbd_oracle.c dbd/apr_dbd_pgsql.c dbd/apr_dbd_sqlite2.c dbd/apr_dbd_sqlite3.c include/apr_dbd.h include/private/apr_dbd_internal.h Date: Sat, 17 Feb 2007 02:50:18 -0000 To: commits@apr.apache.org From: bojan@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070217025018.BB8311A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: bojan Date: Fri Feb 16 18:50:17 2007 New Revision: 508700 URL: http://svn.apache.org/viewvc?view=rev&rev=508700 Log: Unify parsing of prepared statements in DBD Add binary argument functions to DBD Modified: apr/apr-util/trunk/CHANGES apr/apr-util/trunk/STATUS apr/apr-util/trunk/dbd/apr_dbd.c apr/apr-util/trunk/dbd/apr_dbd_oracle.c apr/apr-util/trunk/dbd/apr_dbd_pgsql.c apr/apr-util/trunk/dbd/apr_dbd_sqlite2.c apr/apr-util/trunk/dbd/apr_dbd_sqlite3.c apr/apr-util/trunk/include/apr_dbd.h apr/apr-util/trunk/include/private/apr_dbd_internal.h Modified: apr/apr-util/trunk/CHANGES URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/CHANGES?view=diff&rev=508700&r1=508699&r2=508700 ============================================================================== --- apr/apr-util/trunk/CHANGES (original) +++ apr/apr-util/trunk/CHANGES Fri Feb 16 18:50:17 2007 @@ -1,5 +1,8 @@ Changes with APR-util 1.3.0 + *) Unify parsing of prepared statements and add binary argument functions + to DBD [Bojan Smojver with help from many on the APR list] + *) Support building DBD drivers as DSOs; use --enable-dbd-dso flag to configure. [Joe Orton, Bojan Smojver] Modified: apr/apr-util/trunk/STATUS URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/STATUS?view=diff&rev=508700&r1=508699&r2=508700 ============================================================================== --- apr/apr-util/trunk/STATUS (original) +++ apr/apr-util/trunk/STATUS Fri Feb 16 18:50:17 2007 @@ -69,14 +69,6 @@ Justin says: Do we still want to do this? testdate is now in test. Status: Greg +1 (volunteers) - * unify parsing of prepared statements in DBD - Status: patches exist, awaiting review - format of arguments (i.e. %s, %d etc.) needs to be agreed upon - - * add binary argument functions to DBD - Status: patches exist, awaiting review - API needs to be agreed upon - Documentation that needs writing: * API documentation @@ -90,3 +82,10 @@ Open Issues: + * unify parsing of prepared statements in DBD + Status: committed, awaiting review + format of arguments (i.e. %s, %d etc.) needs to be agreed upon + + * add binary argument functions to DBD + Status: committed, awaiting review + API needs to be agreed upon Modified: apr/apr-util/trunk/dbd/apr_dbd.c URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd.c?view=diff&rev=508700&r1=508699&r2=508700 ============================================================================== --- apr/apr-util/trunk/dbd/apr_dbd.c (original) +++ apr/apr-util/trunk/dbd/apr_dbd.c Fri Feb 16 18:50:17 2007 @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include "apu_config.h" @@ -303,21 +304,123 @@ const char *label, apr_dbd_prepared_t **statement) { - return driver->prepare(pool,handle,query,label,statement); + size_t qlen; + int i, nargs = 0, nvals = 0; + char *p, *pq; + const char *q; + apr_dbd_type_e *t; + + if (!driver->pformat) { + return APR_ENOTIMPL; + } + + /* find the number of parameters in the query */ + for (q = query; *q; q++) { + if (q[0] == '%') { + if (isalpha(q[1])) { + nargs++; + } else if (q[1] == '%') { + q++; + } + } + } + nvals = nargs; + + qlen = strlen(query) + + nargs * (strlen(driver->pformat) + sizeof(nargs) * 3 + 2) + 1; + pq = apr_palloc(pool, qlen); + t = apr_pcalloc(pool, sizeof(*t) * nargs); + + for (p = pq, q = query, i = 0; *q; q++) { + if (q[0] == '%') { + if (isalpha(q[1])) { + switch (q[1]) { + case 'd': t[i] = APR_DBD_TYPE_INT; break; + case 'u': t[i] = APR_DBD_TYPE_UINT; break; + case 'f': t[i] = APR_DBD_TYPE_FLOAT; break; + case 'h': + switch (q[2]) { + case 'h': + switch (q[3]){ + case 'd': t[i] = APR_DBD_TYPE_TINY; q += 2; break; + case 'u': t[i] = APR_DBD_TYPE_UTINY; q += 2; break; + } + break; + case 'd': t[i] = APR_DBD_TYPE_SHORT; q++; break; + case 'u': t[i] = APR_DBD_TYPE_USHORT; q++; break; + } + break; + case 'l': + switch (q[2]) { + case 'l': + switch (q[3]){ + case 'd': t[i] = APR_DBD_TYPE_LONGLONG; q += 2; break; + case 'u': t[i] = APR_DBD_TYPE_ULONGLONG; q += 2; break; + } + break; + case 'd': t[i] = APR_DBD_TYPE_LONG; q++; break; + case 'u': t[i] = APR_DBD_TYPE_ULONG; q++; break; + case 'f': t[i] = APR_DBD_TYPE_DOUBLE; q++; break; + } + break; + case 'p': + if (q[2] == 'D') { + switch (q[3]) { + case 't': t[i] = APR_DBD_TYPE_TEXT; q += 2; break; + case 'i': t[i] = APR_DBD_TYPE_TIME; q += 2; break; + case 'd': t[i] = APR_DBD_TYPE_DATE; q += 2; break; + case 'a': t[i] = APR_DBD_TYPE_DATETIME; q += 2; break; + case 's': t[i] = APR_DBD_TYPE_TIMESTAMP; q += 2; break; + case 'z': t[i] = APR_DBD_TYPE_ZTIMESTAMP; q += 2; break; + case 'b': t[i] = APR_DBD_TYPE_BLOB; q += 2; break; + case 'c': t[i] = APR_DBD_TYPE_CLOB; q += 2; break; + case 'n': t[i] = APR_DBD_TYPE_NULL; q += 2; break; + } + } + break; + } + q++; + + switch (t[i]) { + case APR_DBD_TYPE_NONE: /* by default, we expect strings */ + t[i] = APR_DBD_TYPE_STRING; + break; + case APR_DBD_TYPE_BLOB: + case APR_DBD_TYPE_CLOB: /* three (3) more values passed in */ + nvals += 3; + break; + default: + break; + } + + /* insert database specific parameter reference */ + p += apr_snprintf(p, qlen - (p - pq), driver->pformat, ++i); + } else if (q[1] == '%') { /* reduce %% to % */ + *p++ = *q++; + } else { + *p++ = *q; + } + } else { + *p++ = *q; + } + } + *p = '\0'; + + return driver->prepare(pool,handle,pq,label,nargs,nvals,t,statement); } APU_DECLARE(int) apr_dbd_pquery(const apr_dbd_driver_t *driver, apr_pool_t *pool, apr_dbd_t *handle, int *nrows, - apr_dbd_prepared_t *statement, int nargs, - const char **args) + apr_dbd_prepared_t *statement, + int nargs, const char **args) { - return driver->pquery(pool,handle,nrows,statement,nargs,args); + return driver->pquery(pool,handle,nrows,statement,args); } APU_DECLARE(int) apr_dbd_pselect(const apr_dbd_driver_t *driver, apr_pool_t *pool, apr_dbd_t *handle, apr_dbd_results_t **res, apr_dbd_prepared_t *statement, int random, int nargs, const char **args) { - return driver->pselect(pool,handle,res,statement,random,nargs,args); + return driver->pselect(pool,handle,res,statement,random,args); } APU_DECLARE(int) apr_dbd_pvquery(const apr_dbd_driver_t *driver, apr_pool_t *pool, apr_dbd_t *handle, int *nrows, @@ -340,4 +443,51 @@ ret = driver->pvselect(pool,handle,res,statement,random,args); va_end(args); return ret; +} +APU_DECLARE(int) apr_dbd_pbquery(const apr_dbd_driver_t *driver, + apr_pool_t *pool, + apr_dbd_t *handle, int *nrows, + apr_dbd_prepared_t *statement, + const void **args) +{ + return driver->pbquery(pool,handle,nrows,statement,args); +} +APU_DECLARE(int) apr_dbd_pbselect(const apr_dbd_driver_t *driver, + apr_pool_t *pool, + apr_dbd_t *handle, apr_dbd_results_t **res, + apr_dbd_prepared_t *statement, int random, + const void **args) +{ + return driver->pbselect(pool,handle,res,statement,random,args); +} +APU_DECLARE(int) apr_dbd_pvbquery(const apr_dbd_driver_t *driver, + apr_pool_t *pool, + apr_dbd_t *handle, int *nrows, + apr_dbd_prepared_t *statement,...) +{ + int ret; + va_list args; + va_start(args, statement); + ret = driver->pvbquery(pool,handle,nrows,statement,args); + va_end(args); + return ret; +} +APU_DECLARE(int) apr_dbd_pvbselect(const apr_dbd_driver_t *driver, + apr_pool_t *pool, + apr_dbd_t *handle, apr_dbd_results_t **res, + apr_dbd_prepared_t *statement, + int random,...) +{ + int ret; + va_list args; + va_start(args, random); + ret = driver->pvbselect(pool,handle,res,statement,random,args); + va_end(args); + return ret; +} +APU_DECLARE(apr_status_t) apr_dbd_datum_get(const apr_dbd_driver_t *driver, + apr_dbd_row_t *row, int col, + apr_dbd_type_e type, void *data) +{ + return driver->datum_get(row,col,type,data); } Modified: apr/apr-util/trunk/dbd/apr_dbd_oracle.c URL: http://svn.apache.org/viewvc/apr/apr-util/trunk/dbd/apr_dbd_oracle.c?view=diff&rev=508700&r1=508699&r2=508700 ============================================================================== --- apr/apr-util/trunk/dbd/apr_dbd_oracle.c (original) +++ apr/apr-util/trunk/dbd/apr_dbd_oracle.c Fri Feb 16 18:50:17 2007 @@ -78,6 +78,7 @@ #include "apr_strings.h" #include "apr_time.h" #include "apr_hash.h" +#include "apr_buckets.h" #define TRANS_TIMEOUT 30 #define MAX_ARG_LEN 256 /* in line with other apr_dbd drivers. We alloc this @@ -88,17 +89,6 @@ #define DBD_ORACLE_MAX_COLUMNS 256 #define NUMERIC_FIELD_SIZE 32 -typedef enum { - APR_DBD_ORACLE_NULL, - APR_DBD_ORACLE_STRING, - APR_DBD_ORACLE_INT, - APR_DBD_ORACLE_FLOAT, - APR_DBD_ORACLE_LOB, - /* don't work */ - APR_DBD_ORACLE_BLOB, - APR_DBD_ORACLE_CLOB, -} dbd_field_type; - #ifdef DEBUG #include #endif @@ -109,15 +99,16 @@ static const char *dbd_oracle_error(apr_dbd_t *sql, int n); static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql, const char *query, const char *label, + int nargs, int nvals, apr_dbd_type_e *types, apr_dbd_prepared_t **statement); static int outputParams(apr_dbd_t*, apr_dbd_prepared_t*); static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql, apr_dbd_results_t **results, apr_dbd_prepared_t *statement, - int seek, int nargs, const char **values); + int seek, const char **values); static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows, apr_dbd_prepared_t *statement, - int nargs, const char **values); + const char **values); static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql, apr_dbd_transaction_t **trans); static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans); @@ -132,6 +123,7 @@ }; struct apr_dbd_results_t { + apr_pool_t *pool; apr_dbd_t* handle; unsigned int rownum; int seek; @@ -158,15 +150,16 @@ }; typedef struct { - dbd_field_type type; + apr_dbd_type_e type; sb2 ind; sb4 len; OCIBind *bind; union { void *raw; - char *stringval; - int *ival; - double *floatval; + char *sval; + int ival; + unsigned int uval; + double fval; OCILobLocator *lobval; } value; } bind_arg; @@ -179,7 +172,7 @@ apr_size_t sz; /* length of buf for output */ union { void *raw; - char *stringval; + char *sval; OCILobLocator *lobval; } buf; const char *name; @@ -188,6 +181,7 @@ struct apr_dbd_prepared_t { OCIStmt *stmt; int nargs; + int nvals; bind_arg *args; int nout; define_arg *out; @@ -205,12 +199,156 @@ * OK, forget about using APR pools here, until we figure out * the right way to do it (if such a thing exists). */ -static ub4 null = 0; static OCIEnv *dbd_oracle_env = NULL; #ifdef GLOBAL_PREPARED_STATEMENTS static apr_hash_t *oracle_statements = NULL; #endif +/* Oracle specific bucket for BLOB/CLOB types */ +typedef struct apr_bucket_lob apr_bucket_lob; +/** + * A bucket referring to a Oracle BLOB/CLOB + */ +struct apr_bucket_lob { + /** Number of buckets using this memory */ + apr_bucket_refcount refcount; + /** The row this bucket refers to */ + const apr_dbd_row_t *row; + /** The column this bucket refers to */ + int col; + /** The pool into which any needed structures should + * be created while reading from this bucket */ + apr_pool_t *readpool; +}; + +static void lob_bucket_destroy(void *data); +static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, + apr_size_t *len, apr_read_type_e block); +static apr_bucket *apr_bucket_lob_make(apr_bucket *b, + const apr_dbd_row_t *row, int col, + apr_off_t offset, apr_size_t len, + apr_pool_t *p); +static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, + apr_off_t offset, + apr_size_t len, apr_pool_t *p, + apr_bucket_alloc_t *list); + +static const apr_bucket_type_t apr_bucket_type_lob = { + "LOB", 5, APR_BUCKET_DATA, + lob_bucket_destroy, + lob_bucket_read, + apr_bucket_setaside_notimpl, + apr_bucket_shared_split, + apr_bucket_shared_copy +}; + +static void lob_bucket_destroy(void *data) +{ + apr_bucket_lob *f = data; + + if (apr_bucket_shared_destroy(f)) { + /* no need to destroy database objects here; it will get + * done automatically when the pool gets cleaned up */ + apr_bucket_free(f); + } +} + +static apr_status_t lob_bucket_read(apr_bucket *e, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + apr_bucket_lob *a = e->data; + const apr_dbd_row_t *row = a->row; + apr_dbd_results_t *res = row->res; + int col = a->col; + apr_bucket *b = NULL; + apr_size_t blength = e->length; /* bytes remaining in file past offset */ + apr_off_t boffset = e->start; + define_arg *val = &res->statement->out[col]; + apr_dbd_t *sql = res->handle; +/* Only with 10g, unfortunately + oraub8 length = APR_BUCKET_BUFF_SIZE; +*/ + ub4 length = APR_BUCKET_BUFF_SIZE; + char *buf = NULL; + + *str = NULL; /* in case we die prematurely */ + + /* fetch from offset if not at the beginning */ + buf = apr_palloc(row->pool, APR_BUCKET_BUFF_SIZE); + sql->status = OCILobRead(sql->svc, sql->err, val->buf.lobval, + &length, 1 + boffset, + (dvoid*) buf, APR_BUCKET_BUFF_SIZE, + NULL, NULL, 0, SQLCS_IMPLICIT); +/* Only with 10g, unfortunately + sql->status = OCILobRead2(sql->svc, sql->err, val->buf.lobval, + &length, NULL, 1 + boffset, + (dvoid*) buf, APR_BUCKET_BUFF_SIZE, + OCI_ONE_PIECE, NULL, NULL, 0, SQLCS_IMPLICIT); +*/ + if (sql->status != OCI_SUCCESS) { + return APR_EGENERAL; + } + blength -= length; + *len = length; + *str = buf; + + /* + * Change the current bucket to refer to what we read, + * even if we read nothing because we hit EOF. + */ + apr_bucket_pool_make(e, *str, *len, res->pool); + + /* If we have more to read from the field, then create another bucket */ + if (blength > 0) { + /* for efficiency, we can just build a new apr_bucket struct + * to wrap around the existing LOB bucket */ + b = apr_bucket_alloc(sizeof(*b), e->list); + b->start = boffset + *len; + b->length = blength; + b->data = a; + b->type = &apr_bucket_type_lob; + b->free = apr_bucket_free; + b->list = e->list; + APR_BUCKET_INSERT_AFTER(e, b); + } + else { + lob_bucket_destroy(a); + } + + return APR_SUCCESS; +} + +static apr_bucket *apr_bucket_lob_make(apr_bucket *b, + const apr_dbd_row_t *row, int col, + apr_off_t offset, apr_size_t len, + apr_pool_t *p) +{ + apr_bucket_lob *f; + + f = apr_bucket_alloc(sizeof(*f), b->list); + f->row = row; + f->col = col; + f->readpool = p; + + b = apr_bucket_shared_make(b, f, offset, len); + b->type = &apr_bucket_type_lob; + + return b; +} + +static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col, + apr_off_t offset, + apr_size_t len, apr_pool_t *p, + apr_bucket_alloc_t *list) +{ + apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); + + APR_BUCKET_INIT(b); + b->free = apr_bucket_free; + b->list = list; + return apr_bucket_lob_make(b, row, col, offset, len, p); +} + static apr_status_t dbd_free_lobdesc(void *lob) { switch (OCIDescriptorFree(lob, OCI_DTYPE_LOB)) { @@ -278,8 +416,13 @@ * various Oracle bugs. See, for example, Oracle MetaLink bug 2972890 * and PHP bug http://bugs.php.net/bug.php?id=23733 */ - OCIEnvCreate(&dbd_oracle_env, OCI_THREADED, NULL, - NULL, NULL, NULL, 0, NULL); +#ifdef OCI_NEW_LENGTH_SEMANTICS + OCIEnvCreate(&dbd_oracle_env, OCI_THREADED|OCI_NEW_LENGTH_SEMANTICS, + NULL, NULL, NULL, NULL, 0, NULL); +#else + OCIEnvCreate(&dbd_oracle_env, OCI_THREADED, + NULL, NULL, NULL, NULL, 0, NULL); +#endif } #ifdef GLOBAL_PREPARED_STATEMENTS if (oracle_statements == NULL) { @@ -351,7 +494,7 @@ } ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->err, - OCI_HTYPE_ERROR, 0, NULL); + OCI_HTYPE_ERROR, 0, NULL); switch (ret->status) { default: #ifdef DEBUG @@ -365,7 +508,7 @@ } ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svr, - OCI_HTYPE_SERVER, 0, NULL); + OCI_HTYPE_SERVER, 0, NULL); switch (ret->status) { default: #ifdef DEBUG @@ -381,7 +524,7 @@ } ret->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**)&ret->svc, - OCI_HTYPE_SVCCTX, 0, NULL); + OCI_HTYPE_SVCCTX, 0, NULL); switch (ret->status) { default: #ifdef DEBUG @@ -416,7 +559,7 @@ break; } #else - ret->status = OCIServerAttach(ret->svr, ret->err, fields[3].value, + ret->status = OCIServerAttach(ret->svr, ret->err, (text*) fields[3].value, strlen(fields[3].value), OCI_DEFAULT); switch (ret->status) { default: @@ -621,7 +764,7 @@ } switch (OCIErrorGet(sql->err, 1, NULL, &errorcode, - sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) { + (text*) sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR)) { case OCI_SUCCESS: return sql->buf; default: @@ -663,12 +806,12 @@ int ret = 0; apr_dbd_prepared_t *statement = NULL; - ret = dbd_oracle_prepare(pool, sql, query, NULL, &statement); + ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement); if (ret != 0) { return ret; } - ret = dbd_oracle_pselect(pool, sql, results, statement, seek, 0, NULL); + ret = dbd_oracle_pselect(pool, sql, results, statement, seek, NULL); if (ret != 0) { return ret; } @@ -681,7 +824,6 @@ int ret = 0; apr_pool_t *pool; apr_dbd_prepared_t *statement = NULL; - sword status; if (sql->trans && sql->trans->status == TRANS_ERROR) { return 1; @@ -689,13 +831,13 @@ /* make our own pool so that APR allocations don't linger and so that * both Stmt and LOB handles are cleaned up (LOB handles may be - * allocated when preparing APR_DBD_ORACLE_CLOB/BLOBs) + * allocated when preparing APR_DBD_TYPE_CLOB/BLOBs) */ apr_pool_create(&pool, sql->pool); - ret = dbd_oracle_prepare(pool, sql, query, NULL, &statement); + ret = dbd_oracle_prepare(pool, sql, query, NULL, 0, 0, NULL, &statement); if (ret == 0) { - ret = dbd_oracle_pquery(pool, sql, nrows, statement, 0, NULL); + ret = dbd_oracle_pquery(pool, sql, nrows, statement, NULL); if (ret == 0) { sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0, OCI_ATTR_ROW_COUNT, @@ -716,15 +858,11 @@ static int dbd_oracle_prepare(apr_pool_t *pool, apr_dbd_t *sql, const char *query, const char *label, + int nargs, int nvals, apr_dbd_type_e *types, apr_dbd_prepared_t **statement) { int ret = 0; - size_t length; - size_t bindlen = 0; int i; - char *sqlptr; - char *orastr; - char *oraptr; apr_dbd_prepared_t *stmt ; /* prepared statements in a global lookup table would be nice, @@ -746,6 +884,8 @@ stmt = *statement; stmt->handle = sql; stmt->pool = pool; + stmt->nargs = nargs; + stmt->nvals = nvals; /* If we have a label, we're going to cache it globally. * Check first if we already have it. If not, prepare the @@ -769,91 +909,25 @@ } } #endif - /* translate from apr_dbd to native query format */ - stmt->nargs = 0; - for (sqlptr = (char*)query; *sqlptr; ++sqlptr) { - if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) { - ++stmt->nargs; - } - else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) { - /* ignore %% */ - ++sqlptr; - } - } - length = strlen(query) + 1; - for (i = stmt->nargs; i > 0; i /= 10) { - ++bindlen; - } - length += (2 + bindlen) * stmt->nargs; /* replace "%x" with ":aprN" */ - - oraptr = orastr = apr_palloc(pool, length); - if (stmt->nargs > 0) { - stmt->args = apr_pcalloc(pool, stmt->nargs*sizeof(bind_arg)); - for (i=0; inargs; ++i) { - stmt->args[i].type = APR_DBD_ORACLE_STRING; + /* populate our own args, if any */ + if (nargs > 0) { + stmt->args = apr_pcalloc(pool, nargs*sizeof(bind_arg)); + for (i = 0; i < nargs; i++) { + stmt->args[i].type = types[i]; } } - i = 0; - for (sqlptr = (char*)query; *sqlptr; ++sqlptr) { - if ((sqlptr[0] == '%') && isalnum(sqlptr[1])) { - while (isdigit(*++sqlptr)) { - stmt->args[i].len *= 10; - stmt->args[i].len += (*sqlptr - '0'); - } - oraptr += apr_snprintf(oraptr, length - (oraptr - orastr), - ":apr%d", i + 1); - switch (*sqlptr) { - case 'd': - stmt->args[i].type = APR_DBD_ORACLE_INT; - break; - case 'f': - stmt->args[i].type = APR_DBD_ORACLE_FLOAT; - break; - case 'L': - stmt->args[i].type = APR_DBD_ORACLE_LOB; - break; - /* BLOB and CLOB won't work - use LOB instead */ - case 'C': - stmt->args[i].type = APR_DBD_ORACLE_CLOB; - break; - case 'B': - stmt->args[i].type = APR_DBD_ORACLE_BLOB; - break; - /* default is STRING, but we already set that */ - default: - stmt->args[i].type = APR_DBD_ORACLE_STRING; - break; - } - ++i; - } - else if ((sqlptr[0] == '%') && (sqlptr[1] == '%')) { - /* reduce %% to % */ - *oraptr++ = *sqlptr++; - } - else { - *oraptr++ = *sqlptr; - } - } - *oraptr = '\0'; - sql->status = OCIHandleAlloc(dbd_oracle_env, (dvoid**) &stmt->stmt, OCI_HTYPE_STMT, 0, NULL); - switch (sql->status) { - case OCI_SUCCESS: - break; - default: + if (sql->status != OCI_SUCCESS) { apr_dbd_mutex_unlock(); return 1; } - sql->status = OCIStmtPrepare(stmt->stmt, sql->err, orastr, - strlen(orastr), OCI_NTV_SYNTAX, OCI_DEFAULT); - switch (sql->status) { - case OCI_SUCCESS: - break; - default: + sql->status = OCIStmtPrepare(stmt->stmt, sql->err, (text*) query, + strlen(query), OCI_NTV_SYNTAX, OCI_DEFAULT); + if (sql->status != OCI_SUCCESS) { OCIHandleFree(stmt->stmt, OCI_HTYPE_STMT); apr_dbd_mutex_unlock(); return 1; @@ -865,10 +939,7 @@ /* Perl gets statement type here */ sql->status = OCIAttrGet(stmt->stmt, OCI_HTYPE_STMT, &stmt->type, 0, OCI_ATTR_STMT_TYPE, sql->err); - switch (sql->status) { - case OCI_SUCCESS: - break; - default: + if (sql->status != OCI_SUCCESS) { apr_dbd_mutex_unlock(); return 1; } @@ -878,119 +949,14 @@ sql->status = OCIAttrSet(stmt->stmt, OCI_HTYPE_STMT, &prefetch_size, sizeof(prefetch_size), OCI_ATTR_PREFETCH_MEMORY, sql->err); - switch (sql->status) { - case OCI_SUCCESS: - break; - default: + if (sql->status != OCI_SUCCESS) { apr_dbd_mutex_unlock(); return 1; } #endif - sql->status = OCI_SUCCESS; - for (i = 0; i < stmt->nargs; ++i) { - switch (stmt->args[i].type) { - default: - case APR_DBD_ORACLE_STRING: - if (stmt->args[i].len == 0) { - stmt->args[i].len = MAX_ARG_LEN; - } - stmt->args[i].value.stringval = apr_palloc(pool, stmt->args[i].len); - sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind, - sql->err, i+1, - stmt->args[i].value.stringval, - stmt->args[i].len, - SQLT_STR, - &stmt->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - case APR_DBD_ORACLE_FLOAT: - stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE); - sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind, - sql->err, i+1, - (void*)&stmt->args[i].value.floatval, - sizeof(stmt->args[i].value.floatval), - SQLT_FLT, - &stmt->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - case APR_DBD_ORACLE_INT: - stmt->args[i].value.raw = apr_palloc(pool, NUMERIC_FIELD_SIZE); - sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind, - sql->err, i+1, - (void*)stmt->args[i].value.ival, - sizeof(*stmt->args[i].value.ival), - SQLT_INT, - &stmt->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - /* lots of examples in the docs use this for LOB - * but it relies on knowing the size in advance and - * holding it in memory - */ - case APR_DBD_ORACLE_LOB: - break; /* bind LOBs at write-time */ - /* This is also cited in the docs for LOB, if we don't - * want the whole thing in memory - */ - case APR_DBD_ORACLE_BLOB: - sql->status = OCIDescriptorAlloc(dbd_oracle_env, - (dvoid**)&stmt->args[i].value.lobval, - OCI_DTYPE_LOB, 0, NULL); - apr_pool_cleanup_register(pool, stmt->args[i].value.lobval, - dbd_free_lobdesc, - apr_pool_cleanup_null); - sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind, - sql->err, i+1, - (void*) &stmt->args[i].value.lobval, - -1, - SQLT_BLOB, - &stmt->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - /* This is also cited in the docs for LOB, if we don't - * want the whole thing in memory - */ - case APR_DBD_ORACLE_CLOB: - sql->status = OCIDescriptorAlloc(dbd_oracle_env, - (dvoid**)&stmt->args[i].value.lobval, - OCI_DTYPE_LOB, 0, NULL); - apr_pool_cleanup_register(pool, stmt->args[i].value.lobval, - dbd_free_lobdesc, - apr_pool_cleanup_null); - sql->status = OCIBindByPos(stmt->stmt, &stmt->args[i].bind, - sql->err, i+1, - (dvoid*) &stmt->args[i].value.lobval, - -1, - SQLT_CLOB, - &stmt->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - } - switch (sql->status) { - case OCI_SUCCESS: - break; - default: - apr_dbd_mutex_unlock(); - return 1; - } - } - switch (stmt->type) { - case OCI_STMT_SELECT: + if (stmt->type == OCI_STMT_SELECT) { ret = outputParams(sql, stmt); - break; - default: - break; } #ifdef GLOBAL_PREPARED_STATEMENTS if (label != NULL) { @@ -1001,6 +967,80 @@ return ret; } +static void dbd_oracle_bind(apr_dbd_prepared_t *statement, const char **values) +{ + OCIStmt *stmt = statement->stmt; + apr_dbd_t *sql = statement->handle; + int i, j; + sb2 null_ind = -1; + + for (i = 0, j = 0; i < statement->nargs; i++, j++) { + if (values[j] == NULL) { + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + NULL, 0, SQLT_STR, + &null_ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + } + else { + switch (statement->args[i].type) { + case APR_DBD_TYPE_BLOB: + { + char *data = (char *)values[j]; + int size = atoi((char*)values[++j]); + + /* skip table and column for now */ + j += 2; + + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + data, size, SQLT_LBI, + &statement->args[i].ind, + NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + } + break; + case APR_DBD_TYPE_CLOB: + { + char *data = (char *)values[j]; + int size = atoi((char*)values[++j]); + + /* skip table and column for now */ + j += 2; + + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + data, size, SQLT_LNG, + &statement->args[i].ind, + NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + } + break; + default: + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + (dvoid*) values[j], + strlen(values[j]) + 1, + SQLT_STR, + &statement->args[i].ind, + NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + } + } + + if (sql->status != OCI_SUCCESS) { + return; + } + } + + return; +} + static int outputParams(apr_dbd_t *sql, apr_dbd_prepared_t *stmt) { OCIParam *parms; @@ -1097,7 +1137,7 @@ stmt->out[i].buf.raw = apr_palloc(stmt->pool, stmt->out[i].sz); sql->status = OCIDefineByPos(stmt->stmt, &stmt->out[i].defn, sql->err, i+1, - stmt->out[i].buf.stringval, + stmt->out[i].buf.sval, stmt->out[i].sz, SQLT_STR, &stmt->out[i].ind, &stmt->out[i].len, 0, OCI_DEFAULT); @@ -1149,17 +1189,15 @@ return 0; } -static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql, - int *nrows, apr_dbd_prepared_t *statement, - va_list args) +static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql, + int *nrows, apr_dbd_prepared_t *statement, + const char **values) { - const char *arg = NULL; + int_errorcode; OCISnapshot *oldsnapshot = NULL; OCISnapshot *newsnapshot = NULL; apr_dbd_transaction_t* trans = sql->trans; - int i; int exec_mode; - int_errorcode; if (trans) { switch (trans->status) { @@ -1185,51 +1223,7 @@ exec_mode = OCI_COMMIT_ON_SUCCESS; } - /* we've bound these vars, so now we just copy data in to them */ - for (i=0; inargs; ++i) { - switch (statement->args[i].type) { - case APR_DBD_ORACLE_INT: - arg = va_arg(args, char*); - sscanf(arg, "%d", statement->args[i].value.ival); - break; - case APR_DBD_ORACLE_FLOAT: - arg = va_arg(args, char*); - sscanf(arg, "%lf", statement->args[i].value.floatval); - break; - case APR_DBD_ORACLE_BLOB: - case APR_DBD_ORACLE_CLOB: - sql->status = OCIAttrSet(statement->args[i].value.lobval, - OCI_DTYPE_LOB, &null, 0, - OCI_ATTR_LOBEMPTY, sql->err); - break; - case APR_DBD_ORACLE_LOB: - /* requires strlen() over large data, which may fail for binary */ - statement->args[i].value.raw = va_arg(args, char*); - statement->args[i].len = - strlen(statement->args[i].value.stringval); - sql->status = OCIBindByPos(statement->stmt, - &statement->args[i].bind, - sql->err, i+1, - (void*)statement->args[i].value.raw, - statement->args[i].len, SQLT_LNG, - &statement->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - case APR_DBD_ORACLE_STRING: - default: - arg = va_arg(args, char*); - if (strlen(arg) >= statement->args[i].len) { - strncpy(statement->args[i].value.stringval, arg, - statement->args[i].len-1); - } - else { - strcpy(statement->args[i].value.stringval, arg); - } - break; - } - } + dbd_oracle_bind(statement, values); sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0, oldsnapshot, newsnapshot, exec_mode); @@ -1255,21 +1249,40 @@ return 0; } -static int dbd_oracle_pquery(apr_pool_t *pool, apr_dbd_t *sql, - int *nrows, apr_dbd_prepared_t *statement, - int nargs, const char **values) +static int dbd_oracle_pvquery(apr_pool_t *pool, apr_dbd_t *sql, + int *nrows, apr_dbd_prepared_t *statement, + va_list args) { - int_errorcode; + const char **values; + int i; + + if (sql->trans && sql->trans->status == TRANS_ERROR) { + return -1; + } + + values = apr_palloc(pool, sizeof(*values) * statement->nvals); + + for (i = 0; i < statement->nvals; i++) { + values[i] = va_arg(args, const char*); + } + + return dbd_oracle_pquery(pool, sql, nrows, statement, values); +} + +static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql, + apr_dbd_results_t **results, + apr_dbd_prepared_t *statement, + int seek, const char **values) +{ + int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; OCISnapshot *oldsnapshot = NULL; OCISnapshot *newsnapshot = NULL; apr_dbd_transaction_t* trans = sql->trans; - int i; - int exec_mode; if (trans) { switch (trans->status) { case TRANS_ERROR: - return -1; + return 1; case TRANS_NONE: trans = NULL; break; @@ -1284,68 +1297,21 @@ trans->status = TRANS_1; break; } - exec_mode = OCI_DEFAULT; - } - else { - exec_mode = OCI_COMMIT_ON_SUCCESS; } - /* we've bound these vars, so now we just copy data in to them */ - if (nargs > statement->nargs) { - nargs = statement->nargs; - } - for (i=0; iargs[i].type) { - case APR_DBD_ORACLE_INT: - sscanf(values[i], "%d", statement->args[i].value.ival); - break; - case APR_DBD_ORACLE_FLOAT: - sscanf(values[i], "%lf", statement->args[i].value.floatval); - break; - case APR_DBD_ORACLE_BLOB: - case APR_DBD_ORACLE_CLOB: - sql->status = OCIAttrSet(statement->args[i].value.lobval, - OCI_DTYPE_LOB, &null, 0, - OCI_ATTR_LOBEMPTY, sql->err); - break; - case APR_DBD_ORACLE_LOB: - /* requires strlen() over large data, which may fail for binary */ - statement->args[i].value.raw = (char*)values[i]; - statement->args[i].len = - strlen(statement->args[i].value.stringval); - sql->status = OCIBindByPos(statement->stmt, - &statement->args[i].bind, - sql->err, i+1, - (void*)statement->args[i].value.raw, - statement->args[i].len, SQLT_LNG, - &statement->args[i].ind, - NULL, - (ub2) 0, (ub4) 0, - (ub4 *) 0, OCI_DEFAULT); - break; - case APR_DBD_ORACLE_STRING: - default: - if (strlen(values[i]) >= statement->args[i].len) { - strncpy(statement->args[i].value.stringval, values[i], - statement->args[i].len-1); - } - else { - strcpy(statement->args[i].value.stringval, values[i]); - } - break; - } - } + dbd_oracle_bind(statement, values); - sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0, + sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0, oldsnapshot, newsnapshot, exec_mode); switch (sql->status) { + int_errorcode; case OCI_SUCCESS: break; case OCI_ERROR: #ifdef DEBUG OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); - printf("Execute error %d: %s\n", sql->status, sql->buf); + printf("Executing prepared statement: %s\n", sql->buf); #endif /* fallthrough */ default: @@ -1355,8 +1321,15 @@ return 1; } - sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0, - OCI_ATTR_ROW_COUNT, sql->err); + if (!*results) { + *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); + } + (*results)->handle = sql; + (*results)->statement = statement; + (*results)->seek = seek; + (*results)->rownum = seek ? 0 : -1; + (*results)->pool = pool; + return 0; } @@ -1365,18 +1338,258 @@ apr_dbd_prepared_t *statement, int seek, va_list args) { + const char **values; int i; - char *arg; - int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; + + if (sql->trans && sql->trans->status == TRANS_ERROR) { + return -1; + } + + values = apr_palloc(pool, sizeof(*values) * statement->nvals); + + for (i = 0; i < statement->nvals; i++) { + values[i] = va_arg(args, const char*); + } + + return dbd_oracle_pselect(pool, sql, results, statement, seek, values); +} + +static void dbd_oracle_bbind(apr_dbd_prepared_t * statement, + const void **values) +{ + OCIStmt *stmt = statement->stmt; + apr_dbd_t *sql = statement->handle; + int i, j; + sb2 null_ind = -1; + apr_dbd_type_e type; + + for (i = 0, j = 0; i < statement->nargs; i++, j++) { + type = (values[j] == NULL ? APR_DBD_TYPE_NULL + : statement->args[i].type); + + switch (type) { + case APR_DBD_TYPE_TINY: + statement->args[i].value.ival = *(char*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.ival, + sizeof(statement->args[i].value.ival), + SQLT_INT, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_UTINY: + statement->args[i].value.uval = *(unsigned char*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.uval, + sizeof(statement->args[i].value.uval), + SQLT_UIN, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_SHORT: + statement->args[i].value.ival = *(short*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.ival, + sizeof(statement->args[i].value.ival), + SQLT_INT, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_USHORT: + statement->args[i].value.uval = *(unsigned short*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.uval, + sizeof(statement->args[i].value.uval), + SQLT_UIN, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_INT: + statement->args[i].value.ival = *(int*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.ival, + sizeof(statement->args[i].value.ival), + SQLT_INT, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_UINT: + statement->args[i].value.uval = *(unsigned int*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.uval, + sizeof(statement->args[i].value.uval), + SQLT_UIN, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_LONG: + statement->args[i].value.sval = + apr_psprintf(statement->pool, "%ld", *(long*)values[j]); + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + statement->args[i].value.sval, + strlen(statement->args[i].value.sval)+1, + SQLT_STR, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_ULONG: + statement->args[i].value.sval = + apr_psprintf(statement->pool, "%lu", + *(unsigned long*)values[j]); + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + statement->args[i].value.sval, + strlen(statement->args[i].value.sval)+1, + SQLT_STR, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_LONGLONG: + statement->args[i].value.sval = + apr_psprintf(statement->pool, "%" APR_INT64_T_FMT, + *(apr_int64_t*)values[j]); + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + statement->args[i].value.sval, + strlen(statement->args[i].value.sval)+1, + SQLT_STR, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_ULONGLONG: + statement->args[i].value.sval = + apr_psprintf(statement->pool, "%" APR_UINT64_T_FMT, + *(apr_uint64_t*)values[j]); + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + statement->args[i].value.sval, + strlen(statement->args[i].value.sval)+1, + SQLT_UIN, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_FLOAT: + statement->args[i].value.fval = *(float*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.fval, + sizeof(statement->args[i].value.fval), + SQLT_FLT, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_DOUBLE: + statement->args[i].value.fval = *(double*)values[j]; + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + &statement->args[i].value.fval, + sizeof(statement->args[i].value.fval), + SQLT_FLT, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_STRING: + case APR_DBD_TYPE_TEXT: + case APR_DBD_TYPE_TIME: + case APR_DBD_TYPE_DATE: + case APR_DBD_TYPE_DATETIME: + case APR_DBD_TYPE_TIMESTAMP: + case APR_DBD_TYPE_ZTIMESTAMP: + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + (dvoid*) values[j], + strlen(values[j]) + 1, + SQLT_STR, + &statement->args[i].ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + case APR_DBD_TYPE_BLOB: + { + char *data = (char *)values[j]; + apr_size_t size = *(apr_size_t*)values[++j]; + + /* skip table and column for now */ + j += 2; + + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + data, size, SQLT_LBI, + &statement->args[i].ind, + NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + } + break; + case APR_DBD_TYPE_CLOB: + { + char *data = (char *)values[j]; + apr_size_t size = *(apr_size_t*)values[++j]; + + /* skip table and column for now */ + j += 2; + + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + data, size, SQLT_LNG, + &statement->args[i].ind, + NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + } + break; + case APR_DBD_TYPE_NULL: + default: + sql->status = OCIBindByPos(stmt, &statement->args[i].bind, + sql->err, i + 1, + NULL, 0, SQLT_STR, + &null_ind, NULL, + (ub2) 0, (ub4) 0, + (ub4 *) 0, OCI_DEFAULT); + break; + } + + if (sql->status != OCI_SUCCESS) { + return; + } + } + + return; +} + +static int dbd_oracle_pbquery(apr_pool_t * pool, apr_dbd_t * sql, + int *nrows, apr_dbd_prepared_t * statement, + const void **values) +{ + int_errorcode; OCISnapshot *oldsnapshot = NULL; OCISnapshot *newsnapshot = NULL; apr_dbd_transaction_t* trans = sql->trans; - int_errorcode; + int exec_mode; if (trans) { switch (trans->status) { case TRANS_ERROR: - return 1; + return -1; case TRANS_NONE: trans = NULL; break; @@ -1391,47 +1604,15 @@ trans->status = TRANS_1; break; } + exec_mode = OCI_DEFAULT; } - - /* we've bound these vars, so now we just copy data in to them */ - for (i=0; inargs; ++i) { - int len; - switch (statement->args[i].type) { - case APR_DBD_ORACLE_INT: - arg = va_arg(args, char*); - sscanf(arg, "%d", statement->args[i].value.ival); - break; - case APR_DBD_ORACLE_FLOAT: - arg = va_arg(args, char*); - sscanf(arg, "%lf", statement->args[i].value.floatval); - break; - case APR_DBD_ORACLE_BLOB: - case APR_DBD_ORACLE_CLOB: - sql->status = OCIAttrSet(statement->args[i].value.lobval, - OCI_DTYPE_LOB, &null, 0, - OCI_ATTR_LOBEMPTY, sql->err); - break; - case APR_DBD_ORACLE_STRING: - default: - arg = va_arg(args, char*); - len = strlen(arg); - if (len >= statement->args[i].len) { - len = statement->args[i].len - 1; - strncpy(statement->args[i].value.stringval, arg, len); - statement->args[i].value.stringval[len] = '\0'; - } - else { - strcpy(statement->args[i].value.stringval, arg); - } - ++len; - sql->status = OCIAttrSet(statement->args[i].bind, - OCI_DTYPE_PARAM, &len, 0, - OCI_ATTR_DATA_SIZE, sql->err); - break; - } + else { + exec_mode = OCI_COMMIT_ON_SUCCESS; } - sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0, + dbd_oracle_bbind(statement, values); + + sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 1, 0, oldsnapshot, newsnapshot, exec_mode); switch (sql->status) { case OCI_SUCCESS: @@ -1440,7 +1621,7 @@ #ifdef DEBUG OCIErrorGet(sql->err, 1, NULL, &errorcode, sql->buf, sizeof(sql->buf), OCI_HTYPE_ERROR); - printf("Executing prepared statement: %s\n", sql->buf); + printf("Execute error %d: %s\n", sql->status, sql->buf); #endif /* fallthrough */ default: @@ -1450,23 +1631,36 @@ return 1; } - if (!*results) { - *results = apr_palloc(pool, sizeof(apr_dbd_results_t)); - } - (*results)->handle = sql; - (*results)->statement = statement; - (*results)->seek = seek; - (*results)->rownum = seek ? 0 : -1; - + sql->status = OCIAttrGet(statement->stmt, OCI_HTYPE_STMT, nrows, 0, + OCI_ATTR_ROW_COUNT, sql->err); return 0; } -static int dbd_oracle_pselect(apr_pool_t *pool, apr_dbd_t *sql, - apr_dbd_results_t **results, - apr_dbd_prepared_t *statement, - int seek, int nargs, const char **values) +static int dbd_oracle_pvbquery(apr_pool_t * pool, apr_dbd_t * sql, + int *nrows, apr_dbd_prepared_t * statement, + va_list args) { + const void **values; int i; + + if (sql->trans && sql->trans->status == TRANS_ERROR) { + return -1; + } + + values = apr_palloc(pool, sizeof(*values) * statement->nvals); + + for (i = 0; i < statement->nvals; i++) { + values[i] = va_arg(args, const void*); + } + + return dbd_oracle_pbquery(pool, sql, nrows, statement, values); +} + +static int dbd_oracle_pbselect(apr_pool_t * pool, apr_dbd_t * sql, + apr_dbd_results_t ** results, + apr_dbd_prepared_t * statement, + int seek, const void **values) +{ int exec_mode = seek ? OCI_STMT_SCROLLABLE_READONLY : OCI_DEFAULT; OCISnapshot *oldsnapshot = NULL; OCISnapshot *newsnapshot = NULL; @@ -1492,36 +1686,7 @@ } } - /* we've bound these vars, so now we just copy data in to them */ - if (nargs > statement->nargs) { - nargs = statement->nargs; - } - for (i=0; iargs[i].type) { - case APR_DBD_ORACLE_INT: - sscanf(values[i], "%d", statement->args[i].value.ival); - break; - case APR_DBD_ORACLE_FLOAT: - sscanf(values[i], "%lf", statement->args[i].value.floatval); - break; - case APR_DBD_ORACLE_BLOB: - case APR_DBD_ORACLE_CLOB: - sql->status = OCIAttrSet(statement->args[i].value.lobval, - OCI_DTYPE_LOB, &null, 0, - OCI_ATTR_LOBEMPTY, sql->err); - break; - case APR_DBD_ORACLE_STRING: - default: - if (strlen(values[i]) >= MAX_ARG_LEN) { - strncpy(statement->args[i].value.stringval, values[i], - MAX_ARG_LEN-1); - } - else { - strcpy(statement->args[i].value.stringval, values[i]); - } - break; - } - } + dbd_oracle_bbind(statement, values); sql->status = OCIStmtExecute(sql->svc, statement->stmt, sql->err, 0, 0, oldsnapshot, newsnapshot, exec_mode); @@ -1550,10 +1715,32 @@ (*results)->statement = statement; (*results)->seek = seek; (*results)->rownum = seek ? 0 : -1; + (*results)->pool = pool; return 0; } +static int dbd_oracle_pvbselect(apr_pool_t * pool, apr_dbd_t * sql, + apr_dbd_results_t ** results, + apr_dbd_prepared_t * statement, int seek, + va_list args) +{ + const void **values; + int i; + + if (sql->trans && sql->trans->status == TRANS_ERROR) { + return -1; + } + + values = apr_palloc(pool, sizeof(*values) * statement->nvals); + + for (i = 0; i < statement->nvals; i++) { + values[i] = va_arg(args, const void*); + } + + return dbd_oracle_pbselect(pool, sql, results, statement, seek, values); +} + static int dbd_oracle_start_transaction(apr_pool_t *pool, apr_dbd_t *sql, apr_dbd_transaction_t **trans) { @@ -1715,8 +1902,7 @@ break; } - if (val->type == APR_DBD_ORACLE_CLOB) { - + if (val->type == APR_DBD_TYPE_CLOB) { #if 1 /* Is this necessary, or can it be defaulted? */ sql->status = OCILobCharSetForm(dbd_oracle_env, sql->err, @@ -1783,15 +1969,178 @@ case SQLT_LBI: /* raw is struct { ub4 len; char *buf; } */ len = *(ub4*) val->buf.raw; - buf = apr_pstrndup(row->pool, val->buf.stringval + sizeof(ub4), len); + buf = apr_pstrndup(row->pool, val->buf.sval + sizeof(ub4), len); break; default: - buf = apr_pstrndup(row->pool, val->buf.stringval, val->len); + buf = apr_pstrndup(row->pool, val->buf.sval, val->len); break; } return (const char*) buf; } +/* XXX Should this use Oracle proper API instead of calling get_entry()? */ +static apr_status_t dbd_oracle_datum_get(const apr_dbd_row_t *row, int n, + apr_dbd_type_e type, void *data) +{ + define_arg *val = &row->res->statement->out[n]; + const char *entry; + + if ((n < 0) || (n >= row->res->statement->nout)) { + return APR_EGENERAL; + } + + if(val->ind == -1) { + return APR_ENOENT; + } + + switch (type) { + case APR_DBD_TYPE_TINY: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(char*)data = atoi(entry); + break; + case APR_DBD_TYPE_UTINY: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(unsigned char*)data = atoi(entry); + break; + case APR_DBD_TYPE_SHORT: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(short*)data = atoi(entry); + break; + case APR_DBD_TYPE_USHORT: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(unsigned short*)data = atoi(entry); + break; + case APR_DBD_TYPE_INT: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(int*)data = atoi(entry); + break; + case APR_DBD_TYPE_UINT: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(unsigned int*)data = atoi(entry); + break; + case APR_DBD_TYPE_LONG: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(long*)data = atol(entry); + break; + case APR_DBD_TYPE_ULONG: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(unsigned long*)data = atol(entry); + break; + case APR_DBD_TYPE_LONGLONG: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(apr_int64_t*)data = apr_atoi64(entry); + break; + case APR_DBD_TYPE_ULONGLONG: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(apr_uint64_t*)data = apr_atoi64(entry); + break; + case APR_DBD_TYPE_FLOAT: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(float*)data = atof(entry); + break; + case APR_DBD_TYPE_DOUBLE: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(double*)data = atof(entry); + break; + case APR_DBD_TYPE_STRING: + case APR_DBD_TYPE_TEXT: + case APR_DBD_TYPE_TIME: + case APR_DBD_TYPE_DATE: + case APR_DBD_TYPE_DATETIME: + case APR_DBD_TYPE_TIMESTAMP: + case APR_DBD_TYPE_ZTIMESTAMP: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + *(char**)data = (char*)entry; + break; + case APR_DBD_TYPE_BLOB: + case APR_DBD_TYPE_CLOB: + { + apr_bucket *e; + apr_bucket_brigade *b = (apr_bucket_brigade*)data; + apr_dbd_t *sql = row->res->handle; + ub4 len = 0; + + switch (val->type) { + case SQLT_BLOB: + case SQLT_CLOB: + sql->status = OCILobGetLength(sql->svc, sql->err, + val->buf.lobval, &len); + switch(sql->status) { + case OCI_SUCCESS: + case OCI_SUCCESS_WITH_INFO: + if (len == 0) { + e = apr_bucket_eos_create(b->bucket_alloc); + } + else { + e = apr_bucket_lob_create(row, n, 0, len, + row->pool, b->bucket_alloc); + } + break; + default: + return APR_ENOENT; + } + break; + default: + entry = dbd_oracle_get_entry(row, n); + if (entry == NULL) { + return APR_ENOENT; + } + e = apr_bucket_pool_create(entry, strlen(entry), + row->pool, b->bucket_alloc); + break; + } + APR_BRIGADE_INSERT_TAIL(b, e); + } + break; + case APR_DBD_TYPE_NULL: + *(void**)data = NULL; + break; + default: + return APR_EGENERAL; + } + + return APR_SUCCESS; +} + static apr_status_t dbd_oracle_close(apr_dbd_t *handle) { /* FIXME: none of the oracle docs/examples say anything about @@ -1897,6 +2246,12 @@ dbd_oracle_pselect, dbd_oracle_get_name, dbd_oracle_transaction_mode_get, - dbd_oracle_transaction_mode_set + dbd_oracle_transaction_mode_set, + ":apr%d", + dbd_oracle_pvbquery, + dbd_oracle_pvbselect, + dbd_oracle_pbquery, + dbd_oracle_pbselect, + dbd_oracle_datum_get }; #endif