From dcfeece6a6b410ad5e80ceb5e4d9a0935c0444a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=BCbinette=20=28Hubbe=29?= <hubbe@hubbe.net> Date: Thu, 23 Mar 2000 17:22:17 -0800 Subject: [PATCH] new oracle module (oracle 8.x) Rev: src/modules/Oracle/configure.in:1.15 Rev: src/modules/Oracle/oracle.c:1.22 --- src/modules/Oracle/configure.in | 10 +- src/modules/Oracle/oracle.c | 1895 ++++++++++++++++++++++--------- 2 files changed, 1368 insertions(+), 537 deletions(-) diff --git a/src/modules/Oracle/configure.in b/src/modules/Oracle/configure.in index 814cedad5a..8486c0a174 100755 --- a/src/modules/Oracle/configure.in +++ b/src/modules/Oracle/configure.in @@ -1,5 +1,5 @@ # -# $Id: configure.in,v 1.14 1999/06/01 21:09:03 marcus Exp $ +# $Id: configure.in,v 1.15 2000/03/24 01:22:17 hubbe Exp $ # # Configure script for the oracle module # @@ -107,14 +107,14 @@ if test x$with_oracle = xyes; then if test x"$pike_cv_oracle_oracle_home" != xno -a -f "$pike_cv_oracle_oracle_home/rdbms/demo/ocidfn.h"; then - echo "$pike_cv_oracle_oracle_home/rdbms/demo" > conftest + echo "$pike_cv_oracle_oracle_home" > conftest else if test x"$pike_cv_oracle_oratab_file" = xno; then :; else sed -e '/^#/d' < $pike_cv_oracle_oratab_file | while IFS=":" read sid dir bootstart; do if test -d "$dir/." -a -f "$dir/rdbms/demo/ocidfn.h"; then - echo "$dir/rdbms/demo" > conftest + echo "$dir" > conftest break else : @@ -125,7 +125,7 @@ if test x$with_oracle = xyes; then oracle_include_dir="`cat conftest`" - AC_MSG_RESULT($oracle_include_dir) + AC_MSG_RESULT($oracle_include_dir/rdbms/demo/) AC_MSG_CHECKING(for sysliblist) @@ -238,7 +238,7 @@ EOF if test "x$oracle_lib_dir" = xno -o "x$oracle_include_dir" = xno; then :; else - CPPFLAGS="-I$oracle_include_dir ${CPPFLAGS}" + CPPFLAGS="-I$oracle_include_dir/rdbms/demo/ -I$oracle_include_dir/network/public/ -I$oracle_include_dir/plsql/public -I$oracle_include_dir/otrace/public -I ${CPPFLAGS}" LDFLAGS="${LDFLAGS} -L$oracle_lib_dir -R$oracle_lib_dir" if test -f "$oracle_lib_dir/libcore4.a"; then diff --git a/src/modules/Oracle/oracle.c b/src/modules/Oracle/oracle.c index 811acae3cd..d3d2345850 100644 --- a/src/modules/Oracle/oracle.c +++ b/src/modules/Oracle/oracle.c @@ -1,9 +1,11 @@ /* - * $Id: oracle.c,v 1.21 2000/02/29 03:12:38 hubbe Exp $ + * $Id: oracle.c,v 1.22 2000/03/24 01:22:17 hubbe Exp $ * * Pike interface to Oracle databases. * - * Marcus Comstedt + * original design by Marcus Comstedt + * re-written for Oracle 8.x by Fredrik Hubinette + * */ /* @@ -28,18 +30,19 @@ #include "mapping.h" #include "multiset.h" #include "builtin_functions.h" +#include "opcodes.h" #ifdef HAVE_ORACLE -#include <ocidfn.h> -#include <ociapr.h> - +#include <oci.h> +#include <math.h> -RCSID("$Id: oracle.c,v 1.21 2000/02/29 03:12:38 hubbe Exp $"); +RCSID("$Id: oracle.c,v 1.22 2000/03/24 01:22:17 hubbe Exp $"); #define BLOB_FETCH_CHUNK 16384 +/* #define ORACLE_DEBUG */ #define ORACLE_USE_THREADS #define SERIALIZE_CONNECT @@ -52,427 +55,822 @@ RCSID("$Id: oracle.c,v 1.21 2000/02/29 03:12:38 hubbe Exp $"); #endif +#define BLOCKSIZE 2048 + + #if defined(SERIALIZE_CONNECT) DEFINE_MUTEX(oracle_serialization_mutex); #endif -struct program *oracle_program = NULL, *oracle_result_program = NULL; -struct dbcurs { - struct dbcurs *next; - Cda_Def cda; + +#define MY_START_CLASS(STRUCT) \ + start_new_program(); \ + offset=ADD_STORAGE(struct STRUCT); \ + set_init_callback(PIKE_CONCAT3(init_,STRUCT,_struct));\ + set_exit_callback(PIKE_CONCAT3(exit_,STRUCT,_struct)); + +#define MY_END_CLASS(NAME) \ + PIKE_CONCAT(NAME,_program) = end_program(); \ + PIKE_CONCAT(NAME,_identifier) = add_program_constant(#NAME, PIKE_CONCAT(NAME,_program), 0); + +#ifdef ORACLE_DEBUG +#define LOCK(X) do { \ + fprintf(stderr,"Locking " #X " ... from %s:%d\n",__FUNCTION__,__LINE__); \ + mt_lock( & (X) ); \ + fprintf(stderr,"Locking " #X " done from %s:%d\n",__FUNCTION__,__LINE__); \ +}while(0) + +#define UNLOCK(X) do { \ + fprintf(stderr,"unocking " #X " from %s:%d\n",__FUNCTION__,__LINE__); \ + mt_unlock( & (X) ); \ +}while(0) + +#else +#define LOCK(X) mt_lock( & (X) ); +#define UNLOCK(X) mt_unlock( & (X) ); +#endif + +#ifndef ADD_FUNCTION +#define ADD_FUNCTION add_function +#define tNone "" +#define tFunc(X,Y) +#define tInt "int" +#define tStr "string" +#define tStr "float" +#define tObj "object" +#define tVoid "void" + +#define tMap(X,Y) "mapping(" X ":" Y ")" +#define tOr(X,Y) X "|" Y +#define tArr(X) "array(" X ")" +#define tFunc(X,Y) "function(" X ":" Y ")" +#define tFuncV(X,Z,Y) "function(" X Z "...:" Y ")" +#endif + +#define THISOBJ dmalloc_touch(struct pike_frame *,fp)->current_object +#define PARENTOF(X) ((X)->parent) + +#define THIS_DBCON ((struct dbcon *)(CURRENT_STORAGE)) +#define THIS_QUERY_DBCON ((struct dbcon *)(PARENTOF( THISOBJ )->storage)) +#define THIS_RESULT_DBCON ((struct dbcon *)(PARENTOF(PARENTOF( THISOBJ ))->storage)) +#define THIS_QUERY ((struct dbquery *)(CURRENT_STORAGE)) +#define THIS_RESULT_QUERY ((struct dbquery *)(PARENTOF(THISOBJ )->storage)) +#define THIS_RESULT ((struct dbresult *)(CURRENT_STORAGE)) +#define THIS_RESULTINFO ((struct dbresultinfo *)(CURRENT_STORAGE)) +#define THIS_DBDATE ((struct dbdate *)(CURRENT_STORAGE)) +#define THIS_DBNULL ((struct dbnull *)(CURRENT_STORAGE)) + +static struct program *oracle_program = NULL; +static struct program *compile_query_program = NULL; +static struct program *big_query_program = NULL; +static struct program *dbresultinfo_program = NULL; +static struct program *Date_program = NULL; +static struct program *NULL_program = NULL; + +static int oracle_identifier; +static int compile_query_identifier; +static int big_query_identifier; +static int dbresultinfo_identifier; +static int Date_identifier; + +static struct object *nullstring_object; +static struct object *nullfloat_object; +static struct object *nullint_object; +static struct object *nulldate_object; + +static OCIEnv *oracle_environment=0; + +static OCIEnv *get_oracle_environment(void) +{ + sword rc; + if(!oracle_environment) + { + rc=OCIEnvInit(&oracle_environment, OCI_DEFAULT, 0, 0); + if(rc != OCI_SUCCESS) + error("Failed to initialize oracle environment.\n"); + } + return oracle_environment; +} + +struct inout +{ + sb2 indicator; + ub2 rcode; + ub2 len; /* not really used? */ + short has_output; + sword ftype; + + sb4 xlen; + struct string_builder output; + ub4 curlen; + + union dbunion + { + double f; + int i; + char shortstr[32]; + OCIDate date; + } u; }; -struct dbcon { - Lda_Def lda; - ub4 hda[128]; - struct dbcurs *cdas, *share_cda; +static void free_inout(struct inout *i); +static void init_inout(struct inout *i); + +/****** connection ******/ +struct dbcon +{ + OCIError *error_handle; + OCISvcCtx *context; + DEFINE_MUTEX(lock); }; -struct dbresult { - struct object *parent; - struct dbcon *dbcon; - struct dbcurs *curs; - Cda_Def *cda; - INT32 cols; +static void init_dbcon_struct(struct object *o) +{ +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + THIS_DBCON->error_handle=0; + THIS_DBCON->context=0; + mt_init( & THIS_DBCON->lock ); +} + +static void exit_dbcon_struct(struct object *o) +{ +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + OCILogoff(THIS_DBCON->context, THIS_DBCON->error_handle); + OCIHandleFree(THIS_DBCON->error_handle, OCI_HTYPE_ERROR); + mt_destroy( & THIS_DBCON->lock ); +} + +/****** query ******/ + +struct dbquery +{ + OCIStmt *statement; + INT_TYPE query_type; DEFINE_MUTEX(lock); + + INT_TYPE cols; + struct array *field_info; + struct mapping *output_variables; }; -static void ora_error_handler(struct dbcon *dbcon, sword rc) +void init_dbquery_struct(struct object *o) { - static text msgbuf[512]; - oerhms(&dbcon->lda, rc, msgbuf, sizeof(msgbuf)); - error(msgbuf); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + THIS_QUERY->cols=-2; + THIS_QUERY->statement=0; + mt_init(& THIS_QUERY->lock); } +void exit_dbquery_struct(struct object *o) +{ +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + OCIHandleFree(THIS_QUERY->statement, OCI_HTYPE_STMT); + mt_destroy(& THIS_QUERY->lock); +} + + +/****** dbresult ******/ + +struct dbresult +{ + char dbcon_lock; + char dbquery_lock; +}; -#define THIS ((struct dbresult *)(fp->current_storage)) static void init_dbresult_struct(struct object *o) { - memset(THIS, 0, sizeof(*THIS)); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + THIS_RESULT->dbcon_lock=0; + THIS_RESULT->dbquery_lock=0; } static void exit_dbresult_struct(struct object *o) { - struct dbresult *r = THIS; - - if(r->curs) { - ocan(&r->curs->cda); - r->curs->next = r->dbcon->cdas; - r->dbcon->cdas = r->curs; +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + /* Variables are freed automatically */ + if(PARENTOF(THISOBJ) && + PARENTOF(THISOBJ)->prog && + THIS_RESULT->dbquery_lock) + { + struct dbquery *dbquery=THIS_RESULT_QUERY; + UNLOCK( dbquery->lock ); + if(PARENTOF(PARENTOF(THISOBJ)) && + PARENTOF(PARENTOF(THISOBJ)) -> prog && + THIS_RESULT->dbcon_lock) + { + struct dbcon *dbcon=THIS_RESULT_DBCON; + UNLOCK( dbcon->lock ); + } } - -/* fprintf(stderr,"Unlocking dbcon\n"); */ - mt_unlock(& r->dbcon->lock); - - if(r->parent) - free_object(r->parent); +#ifdef ORACLE_DEBUG + else + { + fprintf(stderr,"exit_dbresult_struct %p %p\n", + PARENTOF(THISOBJ), + PARENTOF(THISOBJ)?PARENTOF(THISOBJ)->prog:0); + } +#endif } -static void f_result_create(INT32 args) +/****** dbresultinfo ******/ + +struct dbresultinfo { - struct object *p; - struct dbcon *dbcon; - struct dbcurs *curs; - struct dbresult *r = THIS; - INT32 i; + INT_TYPE length; + INT_TYPE decimals; + INT_TYPE real_type; + struct pike_string *name; + struct pike_string *type; - get_all_args("Oracle.oracle_result->create", args, "%o", &p); + OCIDefine *define_handle; - if(p && !p->prog) - error("Bad argument 1 to Oracle.oracle_result->create(), destructd object!\n"); + struct inout data; +}; - if(!(dbcon = (struct dbcon *)get_storage(p, oracle_program))) - { -#if 0 - describe(p); - dump_program_desc(p->prog); - describe(oracle_program); - dump_program_desc(oracle_program); +static void init_dbresultinfo_struct(struct object *o) +{ +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); #endif - error("Bad argument 1 to Oracle.oracle_result->create(), not an oracle object\n"); - } - - if(!(curs = dbcon->share_cda)) - error("Unititialized object as argument 1 to Oracle.oracle_result->create()\n"); - - r->curs = curs; - dbcon->share_cda = NULL; - - add_ref(r->parent = p); - r->dbcon = dbcon; - r->cda = &curs->cda; + THIS_RESULTINFO->define_handle=0; + init_inout(& THIS_RESULTINFO->data); +} - r->cols = 0; +static void exit_dbresultinfo_struct(struct object *o) +{ +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + OCIHandleFree(THIS_RESULTINFO->define_handle, OCI_HTYPE_DEFINE); + free_inout( & THIS_RESULTINFO->data); } -static void f_num_fields(INT32 args) +static void protect_dbresultinfo(INT32 args) { - struct dbresult *r = THIS; + error("You may not change variables in dbresultinfo objects.\n"); +} - if(THIS->cols == 0) { - sword rc; - INT32 i; - sb4 siz; +/****** dbdate ******/ - THREADS_ALLOW(); +struct dbdate +{ + OCIDate date; +}; - mt_lock(& r->lock); +static void init_dbdate_struct(struct object *o) {} +static void exit_dbdate_struct(struct object *o) {} - for(i=11; ; i+=10) - if((rc = odescr(r->cda, i, &siz, NULL, NULL, NULL, NULL, - NULL, NULL, NULL))) - break; +/****** dbnull ******/ - if(r->cda->rc == 1007) - for(i-=10; ; i++) - if((rc = odescr(r->cda, i, &siz, NULL, NULL, NULL, NULL, - NULL, NULL, NULL))) - break; +struct dbnull +{ + struct svalue type; +}; - THREADS_DISALLOW(); +static void init_dbnull_struct(struct object *o) {} +static void exit_dbnull_struct(struct object *o) {} - mt_unlock(& r->lock); +/************/ - if(r->cda->rc != 1007) - ora_error_handler(r->dbcon, r->cda->rc); +static void ora_error_handler(OCIError *err, sword rc, char *func) +{ + /* FIXME: we might need to do switch(rc) */ + static text msgbuf[512]; + ub4 errcode; - THIS->cols = i-1; - } - pop_n_elems(args); - push_int(THIS->cols); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + + OCIErrorGet(err,1,0,&errcode,msgbuf,sizeof(msgbuf),OCI_HTYPE_ERROR); + if(func) + error("%s:code=%d:%s",func,rc,msgbuf); + else + error("Oracle:code=%d:%s",rc,msgbuf); } -static void f_fetch_fields(INT32 args) + +static OCIError *global_error_handle=0;; + +OCIError *get_global_error_handle(void) { - struct dbresult *r = THIS; - INT32 i, nambufsz=64; sword rc; - text *nambuf=xalloc(nambufsz+1); + rc=OCIHandleAlloc(get_oracle_environment(), + (void **)& global_error_handle, + OCI_HTYPE_ERROR, + 0, + 0); + + if(rc != OCI_SUCCESS) + error("Failed to allocate error handle.\n"); + + return global_error_handle; +} - pop_n_elems(args); - for(i=0; i<r->cols || r->cols == 0; i++) { - sb4 siz, cbufl, dispsz; - sb2 typ, scale; +static void f_num_fields(INT32 args) +{ + struct dbresult *dbresult = THIS_RESULT; + struct dbquery *dbquery = THIS_RESULT_QUERY; + struct dbcon *dbcon = THIS_RESULT_DBCON; - THREADS_ALLOW(); - - mt_lock( & r->lock ); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif - for(;;) { + if(dbquery->cols == -2) + { + sword rc; + ub4 columns; - cbufl = nambufsz; + THREADS_ALLOW(); - rc = odescr(r->cda, i+1, &siz, &typ, nambuf, &cbufl, &dispsz, - NULL, &scale, NULL); - +/* LOCK(dbcon->lock); */ - if(rc || cbufl < nambufsz) break; + rc=OCIAttrGet(dbquery->statement, + OCI_HTYPE_STMT, + &columns, + 0, + OCI_ATTR_PARAM_COUNT, + dbcon->error_handle); // <- FIXME - free(nambuf); - nambuf = xalloc((nambufsz <<= 1)+1); - } THREADS_DISALLOW(); +/* UNLOCK(dbcon->lock); */ - mt_unlock(& r->lock); + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc,"OCIAttrGet"); - if(rc) { - if(r->cda->rc == 1007) - break; - free(nambuf); - ora_error_handler(r->dbcon, r->cda->rc); - } + dbquery->cols = columns; /* -1 ? */ + } + pop_n_elems(args); + push_int(dbquery->cols); +} + +static sb4 output_callback(struct inout *inout, + ub4 index, + void **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) +{ + inout->has_output=1; + *indpp = (dvoid *) &inout->indicator; + *rcodepp=&inout->rcode; + *alenpp=&inout->xlen; - push_text("name"); - push_string(make_shared_binary_string(nambuf, cbufl)); - push_text("type"); - switch(typ) { + switch(inout->ftype) + { case SQLT_CHR: - push_text("varchar2"); - break; - case SQLT_NUM: - push_text("number"); - break; - case SQLT_LNG: - push_text("long"); - break; - case SQLT_RID: - push_text("rowid"); - break; - case SQLT_DAT: - push_text("date"); - break; - case SQLT_BIN: - push_text("raw"); - break; + case SQLT_STR: case SQLT_LBI: - push_text("long raw"); - break; - case SQLT_AFC: - push_text("char"); - break; - case SQLT_LAB: - push_text("mslabel"); - break; - default: - push_int(0); - break; - } - push_text("length"); - push_int(dispsz); - push_text("decimals"); - push_int(scale); - f_aggregate_mapping(8); + case SQLT_LNG: + if(!inout->output.s) + { + init_string_builder(& inout->output,0); + }else{ + inout->output.s->len+=inout->xlen-BLOCKSIZE; + inout->xlen=0; + } + + inout->xlen=BLOCKSIZE; + *bufpp=string_builder_allocate(& inout->output, inout->xlen, 0); + *piecep = OCI_NEXT_PIECE; +#ifdef ORACLE_DEBUG + MEMSET(*bufpp, '#', inout->xlen); + ((char *)*bufpp)[inout->xlen-1]=0; +#endif + return OCI_CONTINUE; + + case SQLT_FLT: + *bufpp=&inout->u.f; + inout->xlen=sizeof(inout->u.f); + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; + + case SQLT_INT: + *bufpp=&inout->u.i; + inout->xlen=sizeof(inout->u.i); + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; + + case SQLT_ODT: + *bufpp=&inout->u.date; + inout->xlen=sizeof(inout->u.date); + *piecep = OCI_ONE_PIECE; + return OCI_CONTINUE; + + default: return 0; } - free(nambuf); - - if(r->cols == 0) - r->cols = i; - - f_aggregate(r->cols); } - -static void f_fetch_row(INT32 args) + + +static sb4 define_callback(void *dbresultinfo, + OCIDefine *def, + ub4 iter, + void **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodep) { - struct fetchslot { - struct fetchslot *next; - INT32 siz; - ub2 rsiz, rcode; - sb2 indp; - sword typ; - char data[1]; - } *s, *s2, *slots = NULL; - struct dbresult *r = THIS; - sword rc; - INT32 i; - - pop_n_elems(args); - - THREADS_ALLOW(); - - mt_lock( & r->lock ); - - THREADS_DISALLOW(); +#ifdef ORACLE_DEBUG +/* fprintf(stderr,"%s ..",__FUNCTION__); */ +#endif + return + output_callback( &((struct dbresultinfo *)dbresultinfo)->data, + iter, + bufpp, + alenpp, + piecep, + indpp, + rcodep); +} - for(i=0; i<r->cols || r->cols == 0; i++) { - sb4 siz, dsiz; - sb2 typ; - THREADS_ALLOW(); - - rc = odescr(r->cda, i+1, &siz, &typ, NULL, NULL, &dsiz, - NULL, NULL, NULL); +// FIXME: Threading... +static void f_fetch_fields(INT32 args) +{ + struct dbresult *dbresult = THIS_RESULT; + struct dbquery *dbquery=THIS_RESULT_QUERY; + struct dbcon *dbcon=THIS_RESULT_DBCON; + INT32 i; + sword rc; - THREADS_DISALLOW(); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif - if(rc) { - if(r->cda->rc == 1007) - break; - while((s=slots)) { - slots=s->next; - free(s); - } + pop_n_elems(args); - mt_unlock(& r->lock); - ora_error_handler(r->dbcon, r->cda->rc); + if(!dbquery->field_info) + { + /* Get the number of rows */ + if(dbquery->cols == -2) + { + f_num_fields(0); + pop_stack(); } + check_stack(dbquery->cols); - s = (struct fetchslot *)xalloc(sizeof(struct fetchslot)+dsiz+4); + for(i=0; i<dbquery->cols; i++) + { + char *errfunc=0; + OCIParam *column_parameter; + ub2 type; + ub4 size; + sb1 scale; + char *name; + ub4 namelen; + struct object *o; + struct dbresultinfo *info; + char *type_name; + int data_size; + +/* pike_gdb_breakpoint(); */ + THREADS_ALLOW(); +/* LOCK(dbcon->lock); */ + + do { + rc=OCIParamGet(dbquery->statement,OCI_HTYPE_STMT, + dbcon->error_handle, + (void **)&column_parameter, + i+1); + + if(rc != OCI_SUCCESS) { errfunc="OciParamGet"; break; } + + rc=OCIAttrGet((void *)column_parameter, + OCI_DTYPE_PARAM, + &type, + (ub4*)0, + OCI_ATTR_DATA_TYPE, + dbcon->error_handle); + + if(rc != OCI_SUCCESS) { errfunc="OCIAttrGet, OCI_ATTR_DATA_TYPE"; break;} + + rc=OCIAttrGet((void *)column_parameter, + OCI_DTYPE_PARAM, + &size, + (ub4*)0, + OCI_ATTR_DATA_SIZE, + dbcon->error_handle); + + if(rc != OCI_SUCCESS) { errfunc="OCIAttrGet, OCI_ATTR_DATA_SIZE"; break;} + + rc=OCIAttrGet((void *)column_parameter, + OCI_DTYPE_PARAM, + &scale, + (ub4*)0, + OCI_ATTR_SCALE, + dbcon->error_handle); + + if(rc != OCI_SUCCESS) { errfunc="OCIAttrGet, OCI_ATTR_SCALE"; break;} + + rc=OCIAttrGet((void *)column_parameter, + OCI_DTYPE_PARAM, + &name, + &namelen, + OCI_ATTR_NAME, + dbcon->error_handle); + + if(rc != OCI_SUCCESS) { errfunc="OCIAttrGet, OCI_ATTR_NAME"; break;} + + }while(0); + + THREADS_DISALLOW(); +/* UNLOCK(dbcon->lock); */ + + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, errfunc); + + push_object( o=clone_object(dbresultinfo_program,0) ); + info= (struct dbresultinfo *)o->storage; + + info->name=make_shared_binary_string(name, namelen); + info->length=size; + info->decimals=scale; + info->real_type=type; + + data_size=0; + + switch(type) + { + case SQLT_INT: + type_name="int"; + data_size=sizeof(info->data.u.i); + type=SQLT_INT; + break; - s->next = slots; - s->siz = dsiz+4; - slots = s; + case SQLT_NUM: + case SQLT_FLT: + type_name="float"; + data_size=sizeof(info->data.u.f); + break; - THREADS_ALLOW(); + case SQLT_STR: /* string */ + case SQLT_AFC: /* char */ + case SQLT_AVC: /* charz */ + + case SQLT_CHR: /* varchar2 */ + case SQLT_VCS: /* varchar */ + case SQLT_LNG: /* long */ + case SQLT_LVC: /* long varchar */ + type_name="char"; + data_size=-1; + type=SQLT_LNG; + break; - s->rcode = 0; - s->indp = -1; + case SQLT_RID: + type_name="rowid"; + data_size=-1; + type=SQLT_LNG; + break; - rc = odefin(r->cda, i+1, s->data, s->siz, - s->typ=((typ==SQLT_LNG || typ==SQLT_BIN || typ==SQLT_LBI)? - typ : SQLT_STR), - -1, &s->indp, NULL, -1, -1, &s->rsiz, &s->rcode); + case SQLT_DAT: + case SQLT_ODT: + type_name="date"; + data_size=sizeof(info->data.u.date); + type=SQLT_ODT; + break; - THREADS_DISALLOW(); + case SQLT_BIN: /* raw */ + case SQLT_VBI: /* varraw */ + case SQLT_LBI: /* long raw */ + type_name="raw"; + data_size=-1; + type=SQLT_LBI; + /*** dynamic ****/ + break; - if(rc) { - while((s=slots)) { - slots=s->next; - free(s); - } + case SQLT_LAB: + type_name="mslabel"; + type=SQLT_LBI; + data_size=-1; + break; - mt_unlock(& r->lock); - ora_error_handler(r->dbcon, r->cda->rc); - } - } - if(r->cols == 0) - r->cols = i; + default: + type_name="unknown"; + type=SQLT_LBI; + data_size=-1; + break; + } - /* Do link reversal */ - for(s=NULL; slots; slots=s2) { - s2 = slots->next; - slots->next = s; - s = slots; + info->data.ftype=type; + + if(type_name) + info->type=make_shared_string(type_name); + + rc=OCIDefineByPos(dbquery->statement, + &info->define_handle, + dbcon->error_handle, + i+1, + & info->data.u, + data_size<0? (0x7fffffff) :data_size, + type, + & info->data.indicator, + & info->data.len, + & info->data.rcode, + OCI_DYNAMIC_FETCH); + + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, 0); + + if(data_size < 0) + { + rc=OCIDefineDynamic(info->define_handle, + dbcon->error_handle, + info, + define_callback); + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, 0); + } + } + f_aggregate(dbquery->cols); + add_ref( dbquery->field_info=sp[-1].u.array ); + }else{ + ref_push_array( dbquery->field_info); } - slots = s; - - THREADS_ALLOW(); - - rc = ofetch(r->cda); - - THREADS_DISALLOW(); +} - /* no more threaded operations... */ - mt_unlock( & r->lock ); +static void push_inout_value(struct inout *inout) +{ +#ifdef ORACLE_DEBUG +/* fprintf(stderr,"%s ..",__FUNCTION__); */ +#endif - if(rc) { - while((s=slots)) { - slots=s->next; - free(s); + if(inout->indicator) + { + switch(inout->ftype) + { + case SQLT_BIN: + case SQLT_LBI: + case SQLT_AFC: + case SQLT_LAB: + case SQLT_LNG: + case SQLT_CHR: + case SQLT_STR: + ref_push_object(nullstring_object); + break; + + case SQLT_ODT: + case SQLT_DAT: + ref_push_object(nulldate_object); + push_object(low_clone(Date_program)); + ((struct dbdate *)sp[-1].u.object->storage)->date = inout->u.date; + break; + + case SQLT_INT: + ref_push_object(nullint_object); + break; + + case SQLT_FLT: + ref_push_object(nullfloat_object); + break; + + default: + error("Unknown data type.\n"); + break; } - - if(r->cda->rc == 1403) { - push_int(0); - return; - } else ora_error_handler(r->dbcon, r->cda->rc); + return; } - for(s=slots, i=0; i<r->cols; i++) { - if(s->indp == -1) - push_int(0); - else if(s->rcode == 1406 && (s->typ == SQLT_LNG || s->typ == SQLT_LBI)) { - sb4 retl, offs=0; - sb1 *buf = xalloc(BLOB_FETCH_CHUNK); - struct pike_string *s1=make_shared_binary_string("", 0), *s2, *s3; - for(;;) { - - retl=0; - oflng(r->cda, i+1, buf, BLOB_FETCH_CHUNK, s->typ, &retl, offs); - if(!retl) - break; + switch(inout->ftype) + { + case SQLT_BIN: + case SQLT_LBI: + case SQLT_AFC: + case SQLT_LAB: + case SQLT_LNG: + case SQLT_CHR: + case SQLT_STR: + inout->output.s->len+=inout->xlen-BLOCKSIZE; + if(inout->ftype == SQLT_STR) inout->output.s->len--; + inout->xlen=0; + push_string(finish_string_builder(& inout->output)); + inout->output.s=0;; + break; - s3 = add_shared_strings(s1, s2=make_shared_binary_string(buf, retl)); - free_string(s1); - free_string(s2); - s1 = s3; - offs += retl; - } - free(buf); - push_string(s1); - } else if(s->rcode) - push_int(0); - else - push_string(make_shared_binary_string(s->data, s->rsiz)); - s2=s->next; - free(s); - s=s2; + case SQLT_ODT: + case SQLT_DAT: + push_object(low_clone(Date_program)); + ((struct dbdate *)sp[-1].u.object->storage)->date = inout->u.date; + break; + + case SQLT_INT: + push_int(inout->u.i); + break; + + case SQLT_FLT: + push_float(inout->u.f); /* We might need to push a Matrix here */ + break; + + default: + error("Unknown data type.\n"); + break; } - - f_aggregate(r->cols); + free_inout(inout); } -#undef THIS -#define THIS ((struct dbcon *)(fp->current_storage)) - -static void init_dbcon_struct(struct object *o) +static void init_inout(struct inout *i) { - memset(THIS, 0, sizeof(*THIS)); - mt_init( & THIS->lock ); + i->output.s=0; + i->has_output=0; + i->xlen=0; + i->len=0; + i->indicator=0; } -static void exit_dbcon_struct(struct object *o) +static void free_inout(struct inout *i) { - struct dbcon *dbcon = THIS; - struct dbcurs *curs; - ologof(&dbcon->lda); - while((curs = dbcon->cdas) != NULL) { - dbcon->cdas = curs->next; - free(curs); + if(i->output.s) + { + free_string_builder(& i->output); + init_inout(i); } - mt_destroy( & THIS->lock ); } -static struct dbcurs *make_cda(struct dbcon *dbcon) + +static void f_fetch_row(INT32 args) { + int i; sword rc; - struct dbcurs *curs=(struct dbcurs *)xalloc(sizeof(struct dbcurs)); + struct dbresult *dbresult = THIS_RESULT; + struct dbquery *dbquery = THIS_RESULT_QUERY; + struct dbcon *dbcon = THIS_RESULT_DBCON; - memset(curs, 0, sizeof(*curs)); +#ifdef ORACLE_DEBUG +/* fprintf(stderr,"%s ..",__FUNCTION__); */ +#endif + + pop_n_elems(args); + + if(!dbquery->cols) + { + f_fetch_fields(0); + pop_stack(); + } THREADS_ALLOW(); + rc=OCIStmtFetch(dbquery->statement, + dbcon->error_handle, + 1, + OCI_FETCH_NEXT, + OCI_DEFAULT); + THREADS_DISALLOW(); - mt_lock( & dbcon->lock ); + if(rc==OCI_NO_DATA) + { + push_int(0); + return; + } - rc=oopen(&curs->cda, &dbcon->lda, NULL, -1, -1, NULL, -1); + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, 0); - THREADS_DISALLOW(); + check_stack(dbquery->cols); + + for(i=0;i<dbquery->cols;i++) + { + if(dbquery->field_info->item[i].type == T_OBJECT && + dbquery->field_info->item[i].u.object->prog == dbresultinfo_program) + { + struct dbresultinfo *info; + info=(struct dbresultinfo *)(dbquery->field_info->item[i].u.object->storage); - mt_unlock( & dbcon->lock ); + /* Extract data from 'info' */ - if(rc) { - rc = curs->cda.rc; - free(curs); - ora_error_handler(dbcon, rc); - } else { - curs->next = dbcon->cdas; - dbcon->cdas = curs; + push_inout_value(& info->data); + } } - return curs; + f_aggregate(dbquery->cols); } -static void f_create(INT32 args) +static void f_oracle_create(INT32 args) { - struct dbcon *dbcon = THIS; + char *err=0; + struct dbcon *dbcon = THIS_DBCON; struct pike_string *uid, *passwd, *host, *database; sword rc; @@ -490,260 +888,601 @@ static void f_create(INT32 args) THREADS_ALLOW(); - mt_lock( & dbcon->lock ); - mt_lock( & oracle_serialization_mutex ); + LOCK(dbcon->lock); + LOCK(oracle_serialization_mutex); - rc = olog(&dbcon->lda, (ub1*)dbcon->hda, uid->str, uid->len, - (passwd? passwd->str:NULL), (passwd? passwd->len:-1), - (host? host->str:NULL), (host? host->len:-1), - OCI_LM_DEF); + do { + rc=OCIHandleAlloc(get_oracle_environment(), + (void **)& dbcon->error_handle, + OCI_HTYPE_ERROR, + 0, + 0); + if(rc != OCI_SUCCESS) break; - mt_unlock( & oracle_serialization_mutex ); - mt_unlock( & dbcon->lock ); +#ifdef ORACLE_DEBUG + fprintf(stderr,"OCIHandleAlloc -> %p\n",dbcon->error_handle); +#endif - THREADS_DISALLOW(); +#if 0 + if(OCIHandleAlloc(get_oracle_environment(),&THIS_DBCON->srvhp, + OCI_HTYPE_SERVER, 0,0)!=OCI_SUCCESS) + error("Failed to allocate server handle.\n"); + + if(OCIHandleAlloc(get_oracle_environment(),&THIS_DBCON->srchp, + OCI_HTYPE_SVCCTX, 0,0)!=OCI_SUCCESS) + error("Failed to allocate service context.\n"); +#endif - if(rc) - ora_error_handler(dbcon, dbcon->lda.rc); + rc=OCILogon(get_oracle_environment(), + dbcon->error_handle, + &dbcon->context, + uid->str, uid->len, + (passwd? passwd->str:NULL), (passwd? passwd->len:-1), + (host? host->str:NULL), (host? host->len:-1)); + + }while(0); - make_cda(dbcon); + UNLOCK(oracle_serialization_mutex); + UNLOCK(dbcon->lock); + + THREADS_DISALLOW(); + + + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, 0); pop_n_elems(args); + +#ifdef ORACLE_DEBUG + fprintf(stderr,"oracle.create error_handle -> %p\n",dbcon->error_handle); +#endif + return; } - -static void f_big_query(INT32 args) +static void f_compile_query_create(INT32 args) { + int rc; struct pike_string *query; - struct dbcurs *curs; - sword rc; - struct mapping *bnds; - struct dbcon *dbcon=THIS; - /* INT32 cols=0; */ - - if(args>1) - get_all_args("Oracle.oracle->big_query", args, "%S%m", &query, &bnds); - else { - get_all_args("Oracle.oracle->big_query", args, "%S", &query); - bnds = NULL; - } + struct dbquery *dbquery=THIS_QUERY; + struct dbcon *dbcon=THIS_QUERY_DBCON; + char *errfunc=0; - if(!(curs = THIS->cdas)) - curs = make_cda(THIS); +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif - THIS->cdas = curs->next; + get_all_args("Oracle->compile_query", args, "%S", &query); + +#ifdef ORACLE_DEBUG + fprintf(stderr,"f_compile_query_create: dbquery: %p\n",dbquery); + fprintf(stderr," dbcon: %p\n",dbcon); + fprintf(stderr," error_handle: %p\n",dbcon->error_handle); +#endif THREADS_ALLOW(); + LOCK(dbcon->lock); + + rc=OCIHandleAlloc(get_oracle_environment(), + (void **)&dbquery->statement, + OCI_HTYPE_STMT, + 0,0); + + if(rc == OCI_SUCCESS) + { + rc=OCIStmtPrepare(dbquery->statement, + dbcon->error_handle, + query->str, + query->len, + OCI_NTV_SYNTAX, + OCI_DEFAULT); + + if(rc == OCI_SUCCESS) + { + ub2 query_type; + rc=OCIAttrGet(dbquery->statement, + OCI_HTYPE_STMT, + &query_type, + 0, + OCI_ATTR_STMT_TYPE, + dbcon->error_handle); + if(rc == OCI_SUCCESS) + { +#ifdef ORACLE_DEBUG + fprintf(stderr," query_type: %d\n",query_type); +#endif + dbquery->query_type = query_type; + }else{ + errfunc="OCIAttrGet"; + } + }else{ + errfunc="OCIStmtPrepare"; + } + }else{ + errfunc="OCIHandleAlloc"; + } + + THREADS_DISALLOW(); + UNLOCK(dbcon->lock); -/* fprintf(stderr,"Locking dbcon.\n"); */ - mt_lock( & dbcon->lock ); + if(rc != OCI_SUCCESS) + ora_error_handler(dbcon->error_handle, rc, 0); -/* fprintf(stderr,"ocan.\n"); */ + pop_n_elems(args); + push_int(0); +} - ocan(&curs->cda); +struct bind +{ + OCIBind *bind; + struct svalue ind; /* The name of the input/output variable */ - rc = oparse(&curs->cda, query->str, query->len, 1, 2); + struct svalue val; /* The input value */ + void *addr; + int len; + sb2 indicator; - THREADS_DISALLOW(); - - if(rc) { - curs->next = THIS->cdas; - THIS->cdas = curs; - - mt_unlock( & dbcon->lock ); - ora_error_handler(THIS, curs->cda.rc); - } else if(bnds != NULL) { - INT32 e; - struct keypair *k; - MAPPING_LOOP(bnds) { - sword rc = 0; - ub1 *addr; - sword len, fty; - switch(k->val.type) { - case T_STRING: - addr = (ub1 *)k->val.u.string->str; - len = k->val.u.string->len; - fty = SQLT_CHR; - break; - case T_FLOAT: - addr = (ub1 *)&k->val.u.float_number; - len = sizeof(k->val.u.float_number); - fty = SQLT_FLT; - break; - case T_INT: - addr = (ub1 *)&k->val.u.integer; - len = sizeof(k->val.u.integer); - fty = SQLT_INT; - break; - case T_MULTISET: - if(k->val.u.multiset->ind->size == 1 && - ITEM(k->val.u.multiset->ind)[0].type == T_STRING) { - addr = (ub1 *)ITEM(k->val.u.multiset->ind)[0].u.string->str; - len = ITEM(k->val.u.multiset->ind)[0].u.string->len; - fty = SQLT_BIN; - break; - } - default: - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; + struct inout data; +}; - mt_unlock( & dbcon->lock ); - error("Bad value type in argument 2 to " - "Oracle.oracle->big_query()\n"); - } - if(k->ind.type == T_INT) - rc = obndrn(&curs->cda, k->ind.u.integer, addr, len, fty, - -1, NULL, NULL, -1, 0); - else if(k->ind.type == T_STRING) - rc = obndrv(&curs->cda, k->ind.u.string->str, k->ind.u.string->len, - addr, len, fty, -1, NULL, NULL, -1, 0); - else { - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; - - mt_unlock( & dbcon->lock ); - error("Bad index type in argument 2 to " - "Oracle.oracle->big_query()\n"); - } - if(rc) { - rc = curs->cda.rc; - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; - - mt_unlock( & dbcon->lock ); - ora_error_handler(THIS, rc); - } - } - } +#define MAX_NUMBER_OF_BINDINGS 256 - THREADS_ALLOW(); +struct bind_block +{ + struct bind bind[MAX_NUMBER_OF_BINDINGS]; + int bindnum; +}; -/* fprintf(stderr,"oexec.\n"); */ - rc = oexec(&curs->cda); - THREADS_DISALLOW(); +static sb4 input_callback(void *vbind_struct, + OCIBind *bindp, + ub4 iter, + ub4 index, + void **bufpp, + ub4 *alenp, + ub1 *piecep, + dvoid **indpp) +{ + struct bind * bind = (struct bind *)vbind_struct; +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s %ld %ld\n",__FUNCTION__,(long)iter,(long)index); +#endif + *bufpp = bind->addr; + *alenp = bind->len; + *indpp = (dvoid *) &bind->ind; + *piecep = OCI_ONE_PIECE; - if(rc) { - rc = curs->cda.rc; - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; + return OCI_CONTINUE; +} - mt_unlock(&dbcon->lock); - ora_error_handler(THIS, rc); - } +static sb4 bind_output_callback(void *vbind_struct, + OCIBind *bindp, + ub4 iter, + ub4 index, + void **bufpp, + ub4 **alenpp, + ub1 *piecep, + dvoid **indpp, + ub2 **rcodepp) +{ + struct bind * bind = (struct bind *)vbind_struct; +#ifdef ORACLE_DEBUG + fprintf(stderr,"Output... %ld\n",(long)bind->data.xlen); +#endif - pop_n_elems(args); + output_callback( &bind->data, + iter, + bufpp, + alenpp, + piecep, + indpp, + rcodepp); + + return OCI_CONTINUE; +} - if(curs->cda.ft != 4) { - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; - push_int(0); +static void free_bind_block(struct bind_block *bind) +{ - /* NIL */ -/* fprintf(stderr,"NIL\n"); */ - mt_unlock(&dbcon->lock); - return; +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + + while(bind->bindnum>=0) + { + free_svalue( & bind->bind[bind->bindnum].ind); + free_svalue( & bind->bind[bind->bindnum].val); + free_inout(& bind->bind[bind->bindnum].data); + bind->bindnum--; } +} - push_object(this_object()); +/* + * FIXME: This function should probably lock the statement + * handle until it is freed... + */ +static void f_big_query_create(INT32 args) +{ + sword rc; + struct mapping *bnds=0; + struct dbresult *dbresult=THIS_RESULT; + struct dbquery *dbquery=THIS_RESULT_QUERY; + struct dbcon *dbcon=THIS_RESULT_DBCON; + ONERROR err; + INT32 autocommit=0; + int i,num; + struct object *new_parent=0; + + struct bind_block bind; + bind.bindnum=-1; + +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + check_all_args("Oracle.oracle.compile_query->big_query", args, + BIT_VOID | BIT_MAPPING | BIT_INT, + BIT_VOID | BIT_INT, + BIT_VOID | BIT_OBJECT, + 0); - curs->next = THIS->cdas; - THIS->cdas = curs; + switch(args) + { + default: + new_parent=sp[2-args].u.object; + case 2: + autocommit=sp[1-args].u.integer; - THIS->share_cda = curs; + case 1: + if(sp[-args].type == T_MAPPING) + bnds=sp[-args].u.mapping; - push_object(clone_object(oracle_result_program, 1)); + case 0: break; + } - THIS->cdas = curs->next; + if(bnds && m_sizeof(bnds) > MAX_NUMBER_OF_BINDINGS) + error("Too many variables.\n"); -} + destruct_objects_to_destruct(); -static void f_list_tables(INT32 args) -{ - struct pike_string *wild; - struct dbcurs *curs; - sword rc; - struct dbcon *dbcon=THIS; + /* Optimize me with trylock! */ + THREADS_ALLOW(); + LOCK(dbquery->lock); + dbresult->dbquery_lock=1; + THREADS_DISALLOW(); - if(args) - get_all_args("Oracle.oracle->list_tables", args, "%S", &wild); - else - wild = NULL; + /* Time to re-parent if required */ + if(new_parent && PARENTOF(PARENTOF(THISOBJ)) != new_parent) + { + if(new_parent->prog != oracle_program) + error("Bad argument 3 to big_query.\n"); + + /* We might need to check that there are no locks held here + * but I don't beleive that could happen, so just go with it... + */ + free_object(PARENTOF(PARENTOF(THISOBJ))); + add_ref( PARENTOF(PARENTOF(THISOBJ)) = new_parent ); + } - if(!(curs = THIS->cdas)) - curs = make_cda(THIS); + SET_ONERROR(err, free_bind_block, &bind); - THIS->cdas = curs->next; + if(bnds != NULL) + { + INT32 e; + struct keypair *k; + MAPPING_LOOP(bnds) + { + struct svalue *value=&k->val; + OCIBind *bindp; + sword rc = 0; + void *addr; + sword len, fty; + int mode=OCI_DATA_AT_EXEC; + long rlen=0x7fffffff; + bind.bindnum++; + + assign_svalue_no_free(& bind.bind[bind.bindnum].ind, & k->ind); + assign_svalue_no_free(& bind.bind[bind.bindnum].val, & k->val); + bind.bind[bind.bindnum].indicator=0; + + init_inout(& bind.bind[bind.bindnum].data); + + retry: + switch(value->type) + { + case T_OBJECT: + if(value->u.object->prog == Date_program) + { + bind.bind[bind.bindnum].data.u.date=((struct dbdate *)(value->u.object->storage))->date; + addr = &bind.bind[bind.bindnum].data.u.date; + rlen = len = sizeof(bind.bind[bind.bindnum].data.u.date); + fty=SQLT_ODT; + break; + } + if(value->u.object->prog == NULL_program) + { + bind.bind[bind.bindnum].indicator=-1; + value=& ((struct dbnull *)(value->u.object->storage))->type; + goto retry; + } + error("Bad value type in argument 2 to " + "Oracle.oracle->big_query()\n"); + break; + case T_STRING: + addr = (ub1 *)value->u.string->str; + len = value->u.string->len; + fty = SQLT_LNG; + break; + + case T_FLOAT: + addr = &value->u.float_number; + rlen = len = sizeof(value->u.float_number); + fty = SQLT_FLT; + break; + + case T_INT: + if(value->subtype) + { +#ifdef ORACLE_DEBUG + fprintf(stderr,"NULL IN\n"); +#endif + bind.bind[bind.bindnum].indicator=-1; + addr = 0; + len = 0; + fty = SQLT_LNG; + }else{ + bind.bind[bind.bindnum].data.u.i=value->u.integer; + addr = &bind.bind[bind.bindnum].data.u.i; + rlen = len = sizeof(bind.bind[bind.bindnum].data.u.i); + fty = SQLT_INT; + } + break; + + case T_MULTISET: + if(value->u.multiset->ind->size == 1 && + ITEM(value->u.multiset->ind)[0].type == T_STRING) + { + addr = (ub1 *)ITEM(value->u.multiset->ind)[0].u.string->str; + len = ITEM(value->u.multiset->ind)[0].u.string->len; + fty = SQLT_LBI; + break; + } + + default: + error("Bad value type in argument 2 to " + "Oracle.oracle->big_query()\n"); + } + + bind.bind[bind.bindnum].addr=addr; + bind.bind[bind.bindnum].len=len; + bind.bind[bind.bindnum].data.ftype=fty; + bind.bind[bind.bindnum].bind=0; + bind.bind[bind.bindnum].data.curlen=1; + +#ifdef ORACLE_DEBUG + fprintf(stderr,"BINDING... rlen=%ld\n",(long)rlen); +#endif + if(k->ind.type == T_INT) + { + rc = OCIBindByPos(dbquery->statement, + & bind.bind[bind.bindnum].bind, + dbcon->error_handle, + k->ind.u.integer, + addr, + rlen, + fty, + & bind.bind[bind.bindnum].data.indicator, + & bind.bind[bind.bindnum].data.len, + & bind.bind[bind.bindnum].data.rcode, + 0, + 0, + mode); + } + else if(k->ind.type == T_STRING) + { + rc = OCIBindByName(dbquery->statement, + & bind.bind[bind.bindnum].bind, + dbcon->error_handle, + k->ind.u.string->str, + k->ind.u.string->len, + addr, + rlen, + fty, + & bind.bind[bind.bindnum].data.indicator, + & bind.bind[bind.bindnum].data.len, + & bind.bind[bind.bindnum].data.rcode, + 0, + 0, + mode); + } + else + { + error("Bad index type in argument 2 to " + "Oracle.oracle->big_query()\n"); + } + if(rc) + { + UNLOCK(dbcon->lock); + ora_error_handler(dbcon->error_handle, rc, "OCiBindByName/Pos"); + } + + if(mode == OCI_DATA_AT_EXEC) + { + rc=OCIBindDynamic(bind.bind[bind.bindnum].bind, + dbcon->error_handle, + (void *)(bind.bind + bind.bindnum), + input_callback, + (void *)(bind.bind + bind.bindnum), + bind_output_callback); + } + } + } THREADS_ALLOW(); + LOCK(dbcon->lock); + dbresult->dbcon_lock=1; - mt_lock( & dbcon->lock ); +#ifdef ORACLE_DEBUG + fprintf(stderr,"OCIExec query_type=%d\n",dbquery->query_type); +#endif + rc = OCIStmtExecute(dbcon->context, + dbquery->statement, + dbcon->error_handle, + dbquery->query_type == OCI_STMT_SELECT ? 0 : 1, + 0, + 0,0, + autocommit?OCI_DEFAULT:OCI_COMMIT_ON_SUCCESS); + +#ifdef ORACLE_DEBUG + fprintf(stderr,"OCIExec done\n"); +#endif + THREADS_DISALLOW(); - ocan(&curs->cda); + if(rc) + ora_error_handler(dbcon->error_handle, rc, 0); - if(wild) { - rc = oparse(&curs->cda, "select tname from tab where tname like :wild", - -1, 1, 2); - if(!rc) - rc = obndrv(&curs->cda, ":wild", -1, wild->str, wild->len, SQLT_CHR, - -1, NULL, NULL, -1, 0); - } else - rc = oparse(&curs->cda, "select tname from tab", -1, 1, 2); + pop_n_elems(args); - THREADS_DISALLOW(); - - if(rc) { - curs->next = THIS->cdas; - THIS->cdas = curs; + + for(num=i=0;i<=bind.bindnum;i++) + if(bind.bind[i].data.has_output) + num++; - mt_unlock( & dbcon->lock ); - ora_error_handler(THIS, curs->cda.rc); + if(!num) + { + /* This will probably never happen, but better safe than sorry */ + if(dbquery->output_variables) + { + free_mapping(dbquery->output_variables); + dbquery->output_variables=0; + } + }else{ + if(!dbquery->output_variables) + dbquery->output_variables=allocate_mapping(num); + + for(num=i=0;i<=bind.bindnum;i++) + { + if(bind.bind[i].data.has_output) + { + push_inout_value(& bind.bind[i].data); + mapping_insert(dbquery->output_variables, & bind.bind[i].ind, sp-1); + pop_stack(); + } + } } - THREADS_ALLOW(); - - rc = oexec(&curs->cda); + CALL_AND_UNSET_ONERROR(err); +} - THREADS_DISALLOW(); +void dbdate_create(INT32 args) +{ + struct tm *tm; + time_t t; + sword rc; - if(rc) { - rc = curs->cda.rc; - ocan(&curs->cda); - curs->next = THIS->cdas; - THIS->cdas = curs; + check_all_args("Oracle.Date",args,BIT_INT|BIT_STRING,0); + switch(sp[-args].type) + { + case T_STRING: + rc=OCIDateFromText(get_global_error_handle(), + sp[-args].u.string->str, + sp[-args].u.string->len, + 0, + 0, + 0, + 0, + & THIS_DBDATE->date); + if(rc != OCI_SUCCESS) + ora_error_handler(get_global_error_handle(), rc,"OCIDateFromText"); + break; - mt_unlock( & dbcon->lock ); - ora_error_handler(THIS, rc); + case T_INT: + t=sp[-1].u.integer; + tm=localtime(&t); + OCIDateSetDate(&THIS_DBDATE->date, tm->tm_year, tm->tm_mon, tm->tm_mday); + OCIDateSetTime(&THIS_DBDATE->date, tm->tm_hour, tm->tm_min, tm->tm_sec); + break; } +} + +void dbdate_sprintf(INT32 args) +{ + char buffer[100]; + sword rc; + sb4 bsize=100; + rc=OCIDateToText(get_global_error_handle(), + &THIS_DBDATE->date, + 0, + 0, + 0, + 0, + &bsize, + buffer); + + if(rc != OCI_SUCCESS) + ora_error_handler(get_global_error_handle(), rc,"OCIDateToText"); pop_n_elems(args); + push_text(buffer); +} +void dbdate_cast(INT32 args) +{ + char *s; + get_all_args("Oracle.Date->cast",args,"%s",&s); + if(!strcmp(s,"int")) + { + struct tm *tm; + time_t t; + ub1 hour, min, sec, month,day; + sb2 year; + + extern void f_mktime(INT32 args); + + OCIDateGetDate(&THIS_DBDATE->date, &year, &month, &day); + OCIDateGetTime(&THIS_DBDATE->date, &hour, &min, &sec); + + push_int(sec); + push_int(min); + push_int(hour); + push_int(day); + push_int(month); + push_int(year); + f_mktime(6); + return; + } + if(!strcmp(s,"string")) + { + dbdate_sprintf(args); + return; + } + error("Cannot cast Oracle.Date to %s\n",s); +} - push_object(this_object()); +void dbnull_create(INT32 args) +{ + if(args<1) error("Too few arguments to Oracle.NULL->create\n"); + assign_svalue(& THIS_DBNULL->type, sp-args); +} - curs->next = THIS->cdas; - THIS->cdas = curs; - THIS->share_cda = curs; +void dbnull_sprintf(INT32 args) +{ + switch(THIS_DBNULL->type.type) + { + case T_INT: push_text("Oracle.NULLint"); break; + case T_STRING: push_text("Oracle.NULLstring"); break; + case T_FLOAT: push_text("Oracle.NULLfloat"); break; + case T_OBJECT: push_text("Oracle.NULLdate"); break; - push_object(clone_object(oracle_result_program, 1)); + } - THIS->cdas = curs->next; } -#endif void pike_module_init(void) { -#ifdef HAVE_ORACLE - + long offset; #ifdef ORACLE_HOME if(getenv("ORACLE_HOME")==NULL) putenv("ORACLE_HOME="ORACLE_HOME); @@ -753,65 +1492,146 @@ void pike_module_init(void) putenv("ORACLE_SID="ORACLE_SID); #endif +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + + if(OCIInitialize( + OCI_OBJECT | #ifdef ORACLE_USE_THREADS - opinit(OCI_EV_TSF); + OCI_DEFAULT, +#else + OCI_THREADED, #endif + 0, 0, 0, 0) != OCI_SUCCESS) + { +#ifdef ORACLE_DEBUG + fprintf(stderr,"OCIInitizlie failed\n"); +#endif + return; + } if(oracle_program) fatal("Oracle module initiated twice!\n"); - start_new_program(); - ADD_STORAGE(struct dbcon); + MY_START_CLASS(dbcon); { + + MY_START_CLASS(dbquery); { + add_function("create",f_compile_query_create,"function(string:void)",0); + map_variable("_type","int",0,offset+OFFSETOF(dbquery,query_type),T_INT); + map_variable("_cols","int",0,offset+OFFSETOF(dbquery, cols), T_INT); + map_variable("_field_info","array(object)",0, + offset+OFFSETOF(dbquery, field_info), + T_ARRAY); + map_variable("output_variables","mapping(string:mixed)",0, + offset+OFFSETOF(dbquery, output_variables), + T_MAPPING); + + +/* add_function("query_type",f_query_type,"function(:int)",0); */ + + MY_START_CLASS(dbresult); { + ADD_FUNCTION("create", f_big_query_create, + tFunc(tOr(tVoid,tMap(tStr,tMix)) tOr(tVoid,tInt) tOr(tVoid,tObj),tVoid), ID_PUBLIC); + + /* function(:int) */ + ADD_FUNCTION("num_fields", f_num_fields,tFunc(tNone,tInt), ID_PUBLIC); + + /* function(:array(mapping(string:mixed))) */ + ADD_FUNCTION("fetch_fields", f_fetch_fields,tFunc(tNone,tArr(tMap(tStr,tMix))), ID_PUBLIC); + + /* function(:int|array(string|int)) */ + ADD_FUNCTION("fetch_row", f_fetch_row,tFunc(tNone,tOr(tInt,tArr(tOr(tStr,tInt)))), ID_PUBLIC); + + MY_END_CLASS(big_query); +#ifdef PROGRAM_USES_PARENT + big_query_program->flags|=PROGRAM_USES_PARENT; +#endif + big_query_program->flags|=PROGRAM_DESTRUCT_IMMEDIATE; + } + + + MY_START_CLASS(dbresultinfo); { + map_variable("name","string",0,offset+OFFSETOF(dbresultinfo, name), T_STRING); + map_variable("type","string",0,offset+OFFSETOF(dbresultinfo, type), T_STRING); + map_variable("_type","int",0,offset+OFFSETOF(dbresultinfo, real_type), T_INT); + map_variable("length","int",0,offset+OFFSETOF(dbresultinfo, length), T_INT); + map_variable("decimals","int",0,offset+OFFSETOF(dbresultinfo, decimals), T_INT); + + ADD_FUNCTION("`->=",protect_dbresultinfo,tFunc(tStr tMix,tVoid),0); + ADD_FUNCTION("`[]=",protect_dbresultinfo,tFunc(tStr tMix,tVoid),0); + MY_END_CLASS(dbresultinfo); + } - /* function(string|void, string|void, string|void, string|void:void) */ - ADD_FUNCTION("create", f_create,tFunc(tOr(tStr,tVoid) tOr(tStr,tVoid) tOr(tStr,tVoid) tOr(tStr,tVoid),tVoid), ID_PUBLIC); - /* function(string,mapping(int|string:int|float|string|multiset(string))|void:object) */ - ADD_FUNCTION("big_query", f_big_query,tFunc(tStr tOr(tMap(tOr(tInt,tStr),tOr4(tInt,tFlt,tStr,tSet(tStr))),tVoid),tObj), ID_PUBLIC); - /* function(void|string:object) */ - ADD_FUNCTION("list_tables", f_list_tables,tFunc(tOr(tVoid,tStr),tObj), ID_PUBLIC); + MY_END_CLASS(compile_query); +#ifdef PROGRAM_USES_PARENT + compile_query_program->flags|=PROGRAM_USES_PARENT; +#endif + } - set_init_callback(init_dbcon_struct); - set_exit_callback(exit_dbcon_struct); + /* function(string|void, string|void, string|void, string|void:void) */ + ADD_FUNCTION("create", f_oracle_create,tFunc(tOr(tStr,tVoid) tOr(tStr,tVoid) tOr(tStr,tVoid) tOr(tStr,tVoid),tVoid), ID_PUBLIC); + + /* function(string,mapping(int|string:int|float|string|multiset(string))|void:object) */ + MY_END_CLASS(oracle); + } - oracle_program = end_program(); - add_program_constant("oracle", oracle_program, 0); + MY_START_CLASS(dbdate); { + ADD_FUNCTION("create",dbdate_create,tFunc(tOr(tStr,tInt),tVoid),0); + ADD_FUNCTION("cast",dbdate_cast,tFunc(tStr, tMix),0); + ADD_FUNCTION("_sprintf",dbdate_sprintf,tFunc(tInt, tStr),0); + } + MY_END_CLASS(Date); - start_new_program(); - ADD_STORAGE(struct dbresult); + MY_START_CLASS(dbnull); { + ADD_FUNCTION("create",dbnull_create,tFunc(tOr(tStr,tInt),tVoid),0); + ADD_FUNCTION("_sprintf",dbdate_sprintf,tFunc(tInt, tStr),0); + map_variable("type","mixed",0,offset+OFFSETOF(dbnull, type), T_MIXED); + } + NULL_program=end_program(); + add_program_constant("NULL", NULL_program, 0); - /* function(object, array(string|int):void) */ - ADD_FUNCTION("create", f_result_create,tFunc(tObj tArr(tOr(tStr,tInt)),tVoid), ID_PUBLIC); - /* function(:int) */ - ADD_FUNCTION("num_fields", f_num_fields,tFunc(tNone,tInt), ID_PUBLIC); - /* function(:array(mapping(string:mixed))) */ - ADD_FUNCTION("fetch_fields", f_fetch_fields,tFunc(tNone,tArr(tMap(tStr,tMix))), ID_PUBLIC); - /* function(:int|array(string|int)) */ - ADD_FUNCTION("fetch_row", f_fetch_row,tFunc(tNone,tOr(tInt,tArr(tOr(tStr,tInt)))), ID_PUBLIC); + push_text(""); + add_object_constant("NULLstring",nullstring_object=clone_object(NULL_program,1),0); - set_init_callback(init_dbresult_struct); - set_exit_callback(exit_dbresult_struct); - + push_int(0); + add_object_constant("NULLint",nullint_object=clone_object(NULL_program,1),0); - oracle_result_program = end_program(); - oracle_result_program->flags|=PROGRAM_DESTRUCT_IMMEDIATE; -#endif + push_float(0.0); + add_object_constant("NULLfloat",nullfloat_object=clone_object(NULL_program,1),0); + + push_object(low_clone(Date_program)); + add_object_constant("NULLdate",nulldate_object=clone_object(NULL_program,1),0); } static void call_atexits(void); void pike_module_exit(void) { -#ifdef HAVE_ORACLE - if(oracle_program) +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + + if(global_error_handle) { - free_program(oracle_program); - oracle_program = NULL; + OCIHandleFree(global_error_handle, OCI_HTYPE_ERROR); + global_error_handle=0; } - if (oracle_result_program) { - free_program(oracle_result_program); - oracle_result_program = NULL; - } -#endif + +#define FREE_PROG(X) if(X) { free_program(X) ; X=NULL; } +#define FREE_OBJ(X) if(X) { free_object(X) ; X=NULL; } + FREE_PROG(oracle_program); + FREE_PROG(compile_query_program); + FREE_PROG(big_query_program); + FREE_PROG(dbresultinfo_program); + FREE_PROG(Date_program); + FREE_PROG(NULL_program); + + FREE_OBJ(nullstring_object); + FREE_OBJ(nullint_object); + FREE_OBJ(nullfloat_object); + FREE_OBJ(nulldate_object); + call_atexits(); } @@ -830,6 +1650,10 @@ int atexit(void (*func)(void)) static void call_atexits(void) { +#ifdef ORACLE_DEBUG + fprintf(stderr,"%s\n",__FUNCTION__); +#endif + while(atexit_cnt) (*atexit_fnc[--atexit_cnt])(); } @@ -842,3 +1666,10 @@ static void call_atexits(void) #endif +#else /* HAVE_ORACLE */ + +void pike_modle_init(void) {} +void pike_modle_exit(void) {} + +#endif + -- GitLab