/* * $Id: oracle.c,v 1.27 2000/03/29 22:43:22 hubbe Exp $ * * Pike interface to Oracle databases. * * original design by Marcus Comstedt * re-written for Oracle 8.x by Fredrik Hubinette * */ /* * Includes */ #include "global.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "svalue.h" #include "object.h" #include "array.h" #include "stralloc.h" #include "interpret.h" #include "pike_types.h" #include "pike_memory.h" #include "threads.h" #include "module_support.h" #include "mapping.h" #include "multiset.h" #include "builtin_functions.h" #include "opcodes.h" #include "pike_macros.h" #ifdef HAVE_ORACLE /* VERY VERY UGLY */ #define MOTIF #include <oci.h> #include <math.h> RCSID("$Id: oracle.c,v 1.27 2000/03/29 22:43:22 hubbe Exp $"); #define BLOB_FETCH_CHUNK 16384 /* #define ORACLE_DEBUG */ #define ORACLE_USE_THREADS #define SERIALIZE_CONNECT #ifndef ORACLE_USE_THREADS #undef THREADS_ALLOW #define THREADS_ALLOW() #undef THREADS_DISALLOW #define THREADS_DISALLOW() #endif #define BLOCKSIZE 2048 #if defined(SERIALIZE_CONNECT) DEFINE_MUTEX(oracle_serialization_mutex); #endif #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 ADD_STORAGE(X) add_storage(sizeof(X)) #define tNone "" #define tInt "int" #define tStr "string" #define tFlt "float" #define tObj "object" #define tVoid "void" #define tMix "mixed" #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 ")" #define tComma "," #define string_builder dynamic_buffer_s #define init_string_builder(X,Y) initialize_buf(X) #define string_builder_allocate(X,Y,Z) low_make_buf_space(Y,X); #define finish_string_builder(X) low_free_buf(X) #define free_string_builder(X) toss_buffer(X) #define STRING_BUILDER_STR(X) ((X).s.str) #define STRING_BUILDER_LEN(X) ((X).s.len) #include "dynamic_buffer.h" #else #define STRING_BUILDER_STR(X) ((X).s) #define STRING_BUILDER_LEN(X) ((X).s->len) #define tComma "" #endif #ifndef CURRENT_STORAGE #define CURRENT_STORAGE (fp->current_storage) #endif #ifdef DEBUG_MALLOC #define THISOBJ dmalloc_touch(struct pike_frame *,fp)->current_object #else #define THISOBJ (fp->current_object) #endif #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; }; 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); }; 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; }; void init_dbquery_struct(struct object *o) { #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; }; static void init_dbresult_struct(struct object *o) { #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) { #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 ); } } #ifdef ORACLE_DEBUG else { fprintf(stderr,"exit_dbresult_struct %p %p\n", PARENTOF(THISOBJ), PARENTOF(THISOBJ)?PARENTOF(THISOBJ)->prog:0); } #endif } /****** dbresultinfo ******/ struct dbresultinfo { INT_TYPE length; INT_TYPE decimals; INT_TYPE real_type; struct pike_string *name; struct pike_string *type; OCIDefine *define_handle; struct inout data; }; static void init_dbresultinfo_struct(struct object *o) { #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif THIS_RESULTINFO->define_handle=0; init_inout(& THIS_RESULTINFO->data); } 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 protect_dbresultinfo(INT32 args) { error("You may not change variables in dbresultinfo objects.\n"); } /****** dbdate ******/ struct dbdate { OCIDate date; }; static void init_dbdate_struct(struct object *o) {} static void exit_dbdate_struct(struct object *o) {} /****** dbnull ******/ struct dbnull { struct svalue type; }; static void init_dbnull_struct(struct object *o) {} static void exit_dbnull_struct(struct object *o) {} /************/ 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; #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 OCIError *global_error_handle=0;; OCIError *get_global_error_handle(void) { sword rc; 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; } static void f_num_fields(INT32 args) { struct dbresult *dbresult = THIS_RESULT; struct dbquery *dbquery = THIS_RESULT_QUERY; struct dbcon *dbcon = THIS_RESULT_DBCON; #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif if(dbquery->cols == -2) { sword rc; ub4 columns; THREADS_ALLOW(); /* LOCK(dbcon->lock); */ rc=OCIAttrGet(dbquery->statement, OCI_HTYPE_STMT, &columns, 0, OCI_ATTR_PARAM_COUNT, dbcon->error_handle); /* <- FIXME */ THREADS_DISALLOW(); /* UNLOCK(dbcon->lock); */ if(rc != OCI_SUCCESS) ora_error_handler(dbcon->error_handle, rc,"OCIAttrGet"); 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; switch(inout->ftype) { case SQLT_CHR: case SQLT_STR: case SQLT_LBI: case SQLT_LNG: if(!STRING_BUILDER_STR(inout->output)) { init_string_builder(& inout->output,0); }else{ STRING_BUILDER_LEN(inout->output)+=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; } } static sb4 define_callback(void *dbresultinfo, OCIDefine *def, ub4 iter, void **bufpp, ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcodep) { #ifdef ORACLE_DEBUG /* fprintf(stderr,"%s ..",__FUNCTION__); */ #endif return output_callback( &((struct dbresultinfo *)dbresultinfo)->data, iter, bufpp, alenpp, piecep, indpp, rcodep); } 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; #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif pop_n_elems(args); if(!dbquery->field_info) { /* Get the number of rows */ if(dbquery->cols == -2) { f_num_fields(0); pop_stack(); } check_stack(dbquery->cols); 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; case SQLT_NUM: case SQLT_FLT: type_name="float"; data_size=sizeof(info->data.u.f); break; 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; case SQLT_RID: type_name="rowid"; data_size=-1; type=SQLT_LNG; break; case SQLT_DAT: case SQLT_ODT: type_name="date"; data_size=sizeof(info->data.u.date); type=SQLT_ODT; break; case SQLT_BIN: /* raw */ case SQLT_VBI: /* varraw */ case SQLT_LBI: /* long raw */ type_name="raw"; data_size=-1; type=SQLT_LBI; /*** dynamic ****/ break; case SQLT_LAB: type_name="mslabel"; type=SQLT_LBI; data_size=-1; break; default: type_name="unknown"; type=SQLT_LBI; data_size=-1; break; } 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); } } static void push_inout_value(struct inout *inout) { #ifdef ORACLE_DEBUG /* fprintf(stderr,"%s ..",__FUNCTION__); */ #endif 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; } return; } switch(inout->ftype) { case SQLT_BIN: case SQLT_LBI: case SQLT_AFC: case SQLT_LAB: case SQLT_LNG: case SQLT_CHR: case SQLT_STR: STRING_BUILDER_LEN(inout->output)+=inout->xlen-BLOCKSIZE; if(inout->ftype == SQLT_STR) STRING_BUILDER_LEN(inout->output)--; inout->xlen=0; push_string(finish_string_builder(& inout->output)); STRING_BUILDER_STR(inout->output)=0;; break; 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; } free_inout(inout); } static void init_inout(struct inout *i) { STRING_BUILDER_STR(i->output)=0; i->has_output=0; i->xlen=0; i->len=0; i->indicator=0; } static void free_inout(struct inout *i) { if(STRING_BUILDER_STR(i->output)) { free_string_builder(& i->output); init_inout(i); } } static void f_fetch_row(INT32 args) { int i; sword rc; struct dbresult *dbresult = THIS_RESULT; struct dbquery *dbquery = THIS_RESULT_QUERY; struct dbcon *dbcon = THIS_RESULT_DBCON; #ifdef ORACLE_DEBUG /* fprintf(stderr,"%s ..",__FUNCTION__); */ #endif pop_n_elems(args); if(dbquery->cols==-2) { f_fetch_fields(0); pop_stack(); } THREADS_ALLOW(); rc=OCIStmtFetch(dbquery->statement, dbcon->error_handle, 1, OCI_FETCH_NEXT, OCI_DEFAULT); THREADS_DISALLOW(); if(rc==OCI_NO_DATA) { push_int(0); return; } if(rc != OCI_SUCCESS) ora_error_handler(dbcon->error_handle, rc, 0); 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); /* Extract data from 'info' */ push_inout_value(& info->data); } } f_aggregate(dbquery->cols); } static void f_oracle_create(INT32 args) { char *err=0; struct dbcon *dbcon = THIS_DBCON; struct pike_string *uid, *passwd, *host, *database; sword rc; check_all_args("Oracle.oracle->create", args, BIT_STRING|BIT_INT, BIT_STRING|BIT_INT, BIT_STRING, BIT_STRING|BIT_VOID|BIT_INT, 0); host = (sp[-args].type == T_STRING? sp[-args].u.string : NULL); database = (sp[1-args].type == T_STRING? sp[1-args].u.string : NULL); uid = (sp[2-args].type == T_STRING? sp[2-args].u.string : NULL); if(args >= 4) passwd = (sp[3-args].type == T_STRING? sp[3-args].u.string : NULL); else passwd = NULL; THREADS_ALLOW(); LOCK(dbcon->lock); LOCK(oracle_serialization_mutex); do { rc=OCIHandleAlloc(get_oracle_environment(), (void **)& dbcon->error_handle, OCI_HTYPE_ERROR, 0, 0); if(rc != OCI_SUCCESS) break; #ifdef ORACLE_DEBUG fprintf(stderr,"OCIHandleAlloc -> %p\n",dbcon->error_handle); #endif #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 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); 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_compile_query_create(INT32 args) { int rc; struct pike_string *query; struct dbquery *dbquery=THIS_QUERY; struct dbcon *dbcon=THIS_QUERY_DBCON; char *errfunc=0; #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif 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); if(rc != OCI_SUCCESS) ora_error_handler(dbcon->error_handle, rc, 0); pop_n_elems(args); push_int(0); } struct bind { OCIBind *bind; struct svalue ind; /* The name of the input/output variable */ struct svalue val; /* The input value */ void *addr; int len; sb2 indicator; struct inout data; }; #define MAX_NUMBER_OF_BINDINGS 256 struct bind_block { struct bind bind[MAX_NUMBER_OF_BINDINGS]; int bindnum; }; 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; return OCI_CONTINUE; } 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 output_callback( &bind->data, iter, bufpp, alenpp, piecep, indpp, rcodepp); return OCI_CONTINUE; } static void free_bind_block(struct bind_block *bind) { #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--; } } /* * 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); switch(args) { default: new_parent=sp[2-args].u.object; case 2: autocommit=sp[1-args].u.integer; case 1: if(sp[-args].type == T_MAPPING) bnds=sp[-args].u.mapping; case 0: break; } if(bnds && m_sizeof(bnds) > MAX_NUMBER_OF_BINDINGS) error("Too many variables.\n"); destruct_objects_to_destruct(); /* Optimize me with trylock! */ THREADS_ALLOW(); LOCK(dbquery->lock); dbresult->dbquery_lock=1; THREADS_DISALLOW(); /* 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 ); } SET_ONERROR(err, free_bind_block, &bind); 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); if(rc) { UNLOCK(dbcon->lock); ora_error_handler(dbcon->error_handle, rc, "OCiBindDynamic"); } } } } THREADS_ALLOW(); LOCK(dbcon->lock); dbresult->dbcon_lock=1; #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(); if(rc) ora_error_handler(dbcon->error_handle, rc, 0); pop_n_elems(args); for(num=i=0;i<=bind.bindnum;i++) if(bind.bind[i].data.has_output) num++; 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(); } } } free_bind_block(&bind); UNSET_ONERROR(err); } void dbdate_create(INT32 args) { struct tm *tm; time_t t; sword rc; 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; 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); } void dbnull_create(INT32 args) { if(args<1) error("Too few arguments to Oracle.NULL->create\n"); assign_svalue(& THIS_DBNULL->type, sp-args); } 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; } } void pike_module_init(void) { long offset; #ifdef ORACLE_HOME if(getenv("ORACLE_HOME")==NULL) putenv("ORACLE_HOME="ORACLE_HOME); #endif #ifdef ORACLE_SID if(getenv("ORACLE_SID")==NULL) putenv("ORACLE_SID="ORACLE_SID); #endif #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif if(OCIInitialize( OCI_OBJECT | #ifdef ORACLE_USE_THREADS 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"); 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)) tComma tOr(tVoid,tInt) tComma 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 tComma tMix,tVoid),0); ADD_FUNCTION("`[]=",protect_dbresultinfo,tFunc(tStr tComma tMix,tVoid),0); MY_END_CLASS(dbresultinfo); } MY_END_CLASS(compile_query); #ifdef PROGRAM_USES_PARENT compile_query_program->flags|=PROGRAM_USES_PARENT; #endif } ADD_FUNCTION("create", f_oracle_create,tFunc(tOr(tStr,tVoid) tComma tOr(tStr,tVoid) tComma tOr(tStr,tVoid) tComma tOr(tStr,tVoid),tVoid), ID_PUBLIC); MY_END_CLASS(oracle); } 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); 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); push_text(""); add_object_constant("NULLstring",nullstring_object=clone_object(NULL_program,1),0); push_int(0); add_object_constant("NULLint",nullint_object=clone_object(NULL_program,1),0); 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 ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif if(global_error_handle) { OCIHandleFree(global_error_handle, OCI_HTYPE_ERROR); global_error_handle=0; } #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(); } #ifdef DYNAMIC_MODULE static int atexit_cnt=0; static void (*atexit_fnc[32])(void); int atexit(void (*func)(void)) { if(atexit_cnt==32) return -1; atexit_fnc[atexit_cnt++]=func; return 0; } static void call_atexits(void) { #ifdef ORACLE_DEBUG fprintf(stderr,"%s\n",__FUNCTION__); #endif while(atexit_cnt) (*atexit_fnc[--atexit_cnt])(); } #else /* DYNAMIC_MODULE */ static void call_atexits(void) { } #endif /* DYNAMIC_MODULE */ #else /* HAVE_ORACLE */ void pike_module_init(void) {} void pike_module_exit(void) {} #endif