/********************************************************************************* * Package : c-sqld-oracle.c * Author : Hans Oesterholt-Dijkema. * Copyright : HOD 2004/2005. * License : The Elemental Programming Artistic License. * CVS : $Id: c-sqld-oracle.c,v 1.11 2006/01/02 20:50:35 HansOesterholt Exp $ *********************************************************************************/ /* We're not yet changing the Oracle Driver to use threads with mzscheme * It looks like this driver will handle mzscheme threads just fine. * The query/fetch model is completely different from postgresql or sqlite. */ #include "sqlid.h" #ifdef MZSCHEME #include #include "c-threads.c" #endif #include #include #include #ifdef BIGLOO #include #endif #define DEBUG 0 #if (DEBUG==1) #include static void dbg(const char *format,...) { va_list ap; va_start(ap,format); vprintf(format,ap); va_end(ap); fflush(stdout); } #define LOG(a) dbg("%s:%s[%d]",__FILE__,__FUNCTION__,__LINE__);a;dbg("\n"); #else #define LOG(a) #endif /*************************************************************** * Some contants ***************************************************************/ #define MAX_OPEN_DB_CONNECTIONS 1024 #define MAX_OPEN_CURSORS_PER_CONNECTION 1024 #define STRIP_RESULT_STRINGS 1 #define TYPE_CONNECTION 1 #define TYPE_CONNECT TYPE_CONNECTION #define TYPE_QUERY 2 /*************************************************************** * typedefs ***************************************************************/ typedef struct { int type; sqlo_stmt_handle_t sth; char **errorMsg; unsigned int prows; char **values; unsigned short *lens; int fetch_result; int columns; } oracle_query_t; typedef struct { int type; sqlo_db_handle_t dbh; char *errorMsg; oracle_query_t *current_associated_query; char *empty_string; } oracle_connect_t; /*************************************************************** * supportive functions ***************************************************************/ #ifndef MZSCHEME static void *scheme_malloc(int size) { return GC_MALLOC(size); } #endif static char *gc_strdup(const char *s) { char *r; r=(char *) scheme_malloc(strlen(s)+1); strcpy(r,s); return r; } /*************************************************************** * Library functions for this driver ***************************************************************/ static int fetch(oracle_query_t *q) { q->fetch_result=sqlo_fetch(q->sth,1); if (q->fetch_result<0 || q->fetch_result==SQLO_NO_DATA) { q->values=NULL; q->lens=NULL; } else if (sqlo_ncols(q->sth,0)<=0) { q->values=NULL; q->lens=NULL; q->fetch_result=SQLO_NO_DATA; } else { const char **values; const unsigned short *lens; values=sqlo_values(q->sth,&q->columns,STRIP_RESULT_STRINGS); lens=sqlo_value_lens(q->sth,NULL); { int i,N=q->columns; q->values=scheme_malloc(sizeof(char *)*N); q->lens=scheme_malloc(sizeof(unsigned short)*N); for(i=0;ivalues[i]=buf; q->lens[i]=lens[i]; } } } return q->fetch_result; } static oracle_connect_t *oracle_open(const char *connection_string) { oracle_connect_t *h=scheme_malloc(sizeof(oracle_connect_t)); LOG(dbg("h=%p",h)); h->type=TYPE_CONNECTION; h->empty_string=gc_strdup(""); if (strlen(connection_string)==0) { connection_string="_"; } if (SQLO_SUCCESS!=sqlo_connect(&h->dbh,connection_string)) { char *msg=scheme_malloc(strlen(connection_string)+100); sprintf(msg, "c-sqld-oracle: cannot connect and/or login with '%s'", connection_string ); h->errorMsg=msg; } else { h->errorMsg=h->empty_string; } LOG(dbg("h->dbh=%d, h->errorMsg=%s",h->dbh,h->errorMsg)); h->current_associated_query=NULL; LOG(dbg("h->current_associated_query=%p",h->current_associated_query)); return h; } static void oracle_close(oracle_connect_t *h) { if (h->current_associated_query!=NULL) { oracle_query_t *q=h->current_associated_query; sqlo_close(q->sth); h->current_associated_query=NULL; } sqlo_finish(h->dbh); } #define allocate scheme_malloc #define a_strdup(a,s) a=gc_strdup(s) static oracle_query_t *oracle_query(oracle_connect_t *h,char *_query) { oracle_query_t *q; int ok,end,i,N,in_string,k,dotcomma; char *query=strdup(_query); LOG(dbg("1")); if (h->current_associated_query!=NULL) { q=h->current_associated_query; sqlo_close(q->sth); h->current_associated_query=NULL; } LOG(dbg("2")); q=(oracle_query_t *) allocate(sizeof(oracle_query_t)); q->type=TYPE_QUERY; q->sth=SQLO_STH_INIT; q->errorMsg=&h->errorMsg; /* We're able to process ';' in queries? Yes, but not between single quotes! */ LOG(dbg("3")); end=(1==0);ok=(1==1);k=i=0;in_string=(1==0); for(N=strlen(query)-1;N>=0 && (isspace(query[N]) || query[N]==';');N--); N+=1; if (N<=0) { end=(1==1); ok=(1==0); a_strdup(h->errorMsg,"Empty query given"); q->fetch_result=-1; } LOG(dbg("4 (N=%d,i=%d,ok=%d,end=%d)",N,i,ok,end)); while (ok && !end) { for(;isth,h->dbh,&query[k],0,NULL)<0) { a_strdup(h->errorMsg,sqlo_geterror(h->dbh)); ok=(1==0); } else { h->errorMsg=h->empty_string; } LOG(dbg("6")); if (ok) { if (fetch(q)<0) { a_strdup(h->errorMsg,sqlo_geterror(h->dbh)); ok=(1==0); } } LOG(dbg("7")); if (!end) { if (dotcomma) { query[i]=';'; } if (ok) { sqlo_close(q->sth); } i+=1; } k=i; } LOG(dbg("8")); if (ok) { q->prows=sqlo_prows(q->sth); h->current_associated_query=q; } else { q->prows=-1; h->current_associated_query=NULL; } LOG(dbg("9")); free(query); return q; } static int oracle_prows(oracle_query_t *q) { return q->prows; } static int oracle_eoq(oracle_query_t *q) { return q->fetch_result!=SQLO_SUCCESS; } static char *oracle_dbh_lasterr(oracle_connect_t *h) { return h->errorMsg; } static char *oracle_sth_lasterr(oracle_query_t *q) { return q->errorMsg[0]; } static int oracle_columns(oracle_query_t *q) { return q->columns; } static oracle_query_t * oracle_fetch(oracle_query_t *q) { if (q->fetch_result==SQLO_SUCCESS) { fetch(q); } return q; } static char *oracle_field(oracle_query_t *q,int c) { int N=q->columns; if (c>=N) { return NULL; } else { return q->values[c]; } } static int oracle_autocommit(oracle_connect_t *h,int on) { int result; LOG(dbg("h=%p,on=%d",h,on)); if (on) { result=sqlo_set_autocommit(h->dbh,SQLO_ON); } else { result=sqlo_set_autocommit(h->dbh,SQLO_OFF); } LOG(dbg("result=%d",result)); if (result<0) { h->errorMsg=gc_strdup(sqlo_geterror(h->dbh)); } else { h->errorMsg=h->empty_string; } LOG(dbg("msg=%s",h->errorMsg)); return result==SQLO_SUCCESS; } static char *oracle_escape(char *s) { char *S; int N,extend,i,k; /* Determine needed length */ for(i=0,N=strlen(s),extend=2;idbh,buf,1023); return buf; } static int handle_type(void *handle) { return ((oracle_connect_t *) handle)->type; } /*************************************************************** * BIGLOO implementation ***************************************************************/ #ifdef BIGLOO void *c_oracle_open(char *cs) { static int do_init=1; /* This is not thread safe! */ if (do_init) { do_init=0; sqlo_init(SQLO_ON,MAX_OPEN_DB_CONNECTIONS,MAX_OPEN_CURSORS_PER_CONNECTION); } return (void *) oracle_open(cs); } void *c_oracle_close(void *c) { oracle_close((oracle_connect_t *) c); return NULL; } void *c_oracle_query(void *c,char *q) { return (void *) oracle_query((oracle_connect_t *) c,q); } int c_oracle_prows(void *q) { return oracle_prows((oracle_query_t *) q); } char *c_oracle_lasterr(void *q) { if (handle_type(q)==TYPE_CONNECT) { LOG(dbg("c_oracle_lasterr: oracle_dbh_lasterr, h->errorMsg=%s", ((oracle_connect_t *) q)->errorMsg)); return oracle_dbh_lasterr(q); } else { LOG(dbg("c_oracle_lasterr: oracle_sth_lasterr, h->errorMsg=%s", ((oracle_query_t *) q)->errorMsg)); return oracle_sth_lasterr(q); } } int c_oracle_eoq(void *q) { return oracle_eoq((oracle_query_t *) q); } int c_oracle_columns(void *q) { return oracle_columns((oracle_query_t *) q); } void *c_oracle_fetch(void *q) { return (void *) oracle_fetch((oracle_query_t *) q); } char *c_oracle_field(void *q,int c) { return oracle_field((oracle_query_t *) q,c); } int c_oracle_autocommit_on(void *h) { return oracle_autocommit((oracle_connect_t *) h,1); } int c_oracle_autocommit_off(void *h) { return oracle_autocommit((oracle_connect_t *) h,0); } char *c_oracle_escape(char *s) { return oracle_escape(s); } int c_oracle_version(void) { return oracle_version(); } char *c_oracle_server_version(void *h) { return oracle_server_version((oracle_connect_t *) h); } #endif /*************************************************************** * MZScheme implementation ***************************************************************/ #ifdef MZSCHEME #ifndef SCHEME_STR_VAL /* This is mzscheme 30x */ # define MZSCHEME3 # define SCHEME_STR_VAL(obj) SCHEME_BYTE_STR_VAL(scheme_char_string_to_byte_string(obj)) # define EQ_CTYPE(cobj,type) (SCHEME_CPTR_TYPE(cobj)==type) # define NEQ_CTYPE(cobj,type) !(EQ_CTYPE(cobj,type)) # define IS_STRINGP(obj) SCHEME_CHAR_STRINGP(obj) #else /* this is mzscheme 20x */ # define EQ_CTYPE(cobj,type) strcmp(SCHEME_CPTR_TYPE(cobj),type)==0 # define NEQ_CTYPE(cobj,type) !(EQ_CTYPE(cobj,type)) # define IS_STRINGP(obj) SCHEME_STRINGP(obj) #endif #ifdef MZSCHEME3 static Scheme_Object *oracle_connect_ct=NULL; static Scheme_Object *oracle_query_ct=NULL; #else static char *oracle_connect_ct="oracle_connect_t"; static char *oracle_query_ct="oracle_query_t"; #endif static Scheme_Object *c_oracle_open(int argc,Scheme_Object **argv) { #ifdef MZSCHEME3 if (oracle_connect_ct==NULL) { oracle_connect_ct=scheme_make_byte_string("oracle_connect_t"); oracle_query_ct=scheme_make_byte_string("oracle_query_t"); } #endif if (!IS_STRINGP(argv[0])) { scheme_wrong_type("c-oracle-open","string",0,argc,argv); } { Scheme_Object *obj=scheme_make_cptr(oracle_open(SCHEME_STR_VAL(argv[0])), oracle_connect_ct); /*LOG(dbg("obj=%p,%s",obj,SCHEME_CPTR_TYPE(obj)));*/ return obj; } } static Scheme_Object *c_oracle_close(int argc,Scheme_Object **argv) { if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-close","oracle_connect_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_connect_ct)) { scheme_wrong_type("c-oracle-close","oracle_connect_t",0,argc,argv); } oracle_close(SCHEME_CPTR_VAL(argv[0])); return scheme_void; } static Scheme_Object *c_oracle_query(int argc,Scheme_Object **argv) { oracle_query_t *res; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-query","oracle_connect_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_connect_ct)) { scheme_wrong_type("c-oracle-query","oracle_connect_t",0,argc,argv); } if (!IS_STRINGP(argv[1])) { scheme_wrong_type("c-oracle-query","string",1,argc,argv); } { oracle_query_t *res=oracle_query(SCHEME_CPTR_VAL(argv[0]),SCHEME_STR_VAL(argv[1])); return scheme_make_cptr(res,oracle_query_ct); } } static Scheme_Object *c_oracle_prows(int argc,Scheme_Object **argv) { if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-prows","oracle_query_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct)) { scheme_wrong_type("c-oracle-prows","oracle_query_t",0,argc,argv); } return scheme_make_integer(oracle_prows(SCHEME_CPTR_VAL(argv[0]))); } static Scheme_Object *c_oracle_lasterr(int argc,Scheme_Object **argv) { char *msg; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-lasterr","oracle_query_t/oracle_connect_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct) && NEQ_CTYPE(argv[0],oracle_connect_ct)) { scheme_wrong_type("c-oracle-lasterr","oracle_query_t/oracle_connect_t",0,argc,argv); } if (EQ_CTYPE(argv[0],oracle_query_ct)) { msg=oracle_sth_lasterr(SCHEME_CPTR_VAL(argv[0])); } else { msg=oracle_dbh_lasterr(SCHEME_CPTR_VAL(argv[0])); } LOG(dbg("msg=%s",msg)); #ifdef MZSCHEME3 return scheme_make_utf8_string(msg); #else return scheme_make_string_without_copying(msg); #endif } static Scheme_Object *c_oracle_eoq(int argc, Scheme_Object **argv) { if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-eoq","oracle_query_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct)) { scheme_wrong_type("c-oracle-eoq","oracle_query_t",0,argc,argv); } if (oracle_eoq(SCHEME_CPTR_VAL(argv[0]))) { return scheme_true; } else { return scheme_false; } } static Scheme_Object *c_oracle_columns(int argc, Scheme_Object **argv) { if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-columns","oracle_query_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct)) { scheme_wrong_type("c-oracle-columns","oracle_query_t",0,argc,argv); } return scheme_make_integer(oracle_columns(SCHEME_CPTR_VAL(argv[0]))); } static Scheme_Object *c_oracle_fetch(int argc, Scheme_Object **argv) { if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-fetch","oracle_query_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct)) { scheme_wrong_type("c-oracle-fetch","oracle_query_t",0,argc,argv); } oracle_fetch(SCHEME_CPTR_VAL(argv[0])); return argv[0]; } static Scheme_Object *c_oracle_fields(int argc, Scheme_Object **argv) { oracle_query_t *q; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-oracle-field","oracle_query_t",0,argc,argv); } else if (NEQ_CTYPE(argv[0],oracle_query_ct)) { scheme_wrong_type("c-oracle-field","oracle_query_t",0,argc,argv); } q=SCHEME_CPTR_VAL(argv[0]); if (oracle_eoq(q)) { return scheme_make_symbol("eoq"); } else { int i,N=oracle_columns(q); Scheme_Object **list=scheme_malloc(sizeof(Scheme_Object *)*N); for(i=0;i