/********************************************************************************* * Package : c-sqld-sqlite.c * Author : Hans Oesterholt-Dijkema. * Copyright : HOD 2004/2005. * License : The Elemental Programming Artistic License. * CVS : $Id: c-sqld-sqlite.c,v 1.25 2006/01/05 00:19:24 HansOesterholt Exp $ *********************************************************************************/ #include "sqlid.h" #include #include #include /* #ifdef Darwin #include #else #include #endif */ #ifdef MZSCHEME # include # include "c-threads.c" #endif #ifdef BIGLOO #include #endif /* =head1 Name SQLD-SQLite - C Part =head1 Author Hans Oesterholt-Dijkema =head1 Copyright/License (c) 2004 Hans Oesterholt-Dijkema, L. =head1 Version $Id: c-sqld-sqlite.c,v 1.25 2006/01/05 00:19:24 HansOesterholt Exp $ =head1 Literate Description In this source file, the interfacing between bigloo and SQLite is described. This interface to SQLite is based on SQLite 3. =head2 Query Handle The interfacing between bigloo or mzscheme and SQLite for queries is done, using a an opaque (void *) handle, that is wrapped using functions. This handle is a C struct that has following fields: =verbatim c,8 */ typedef struct { int ncols; int nrows; int reserved_rows; #ifdef BIGLOO char ***rows; char *errorMsg; #else Scheme_Object ***rows; Scheme_Object *errorMsg; #endif } sqlite_query_t; /*=verbatim C is the number of rows that the query returned. C is the number of columns per row returned by the query. C represents the number of row pointers that have been allocated to use for the C field. C is an array of C, that holds pointers to rows. C is a field that will hold the last reported error by SQLite. */ static int cc_sqlite_version(void) { int maj=0,min1=0,min2=0; char *s=SQLITE_VERSION; maj=atoi(s); for(;s[0]!='.' && s[0]!='\0';s++); if (s[0]=='.') { min1=atoi(++s); } for(;s[0]!='.' && s[0]!='\0';s++); if (s[0]=='.') { min2=atoi(++s); } return maj*100+min1*10+min2; } /* =head2 Supportive functions The next functions are support functions for use in this library. They are made static. The first function F is a function to copy a string in the GC domain. =verbatim c,8 */ #ifdef BIGLOO #define scheme_malloc_atomic(a) GC_MALLOC(a) #endif #ifdef BIGLOO static char *gc_strdup(char *s) { char *n; if (s==NULL) { s=""; } n=scheme_malloc_atomic(sizeof(char)*(strlen(s)+1)); strcpy(n,s); return n; } #else /* mzscheme */ static Scheme_Object *gc_strdup(char *s) /* utf8 s */ { if (s==NULL) { s=""; } return scheme_make_utf8_string(s); } #endif /*=verbatim The next function is a function that is used by SQLite. It is a callback function that is used to put the results of a query in the query structure. bigloo.h doesn't know about GC_REALLOC nor GC_FREE, so we don't use them, because we don't want to depend on an external gc. This function is not used anymore in the MZSCHEME variant, instead, sqlite3_get_table() is used, because MZSCHEME uses threads now to keep queries from blocking mzscheme. =verbatim c,8 */ #ifdef BIGLOO static int callback(void *query,int cols,char **values,char **colnames) { sqlite_query_t *Q=(sqlite_query_t *) query; Q->ncols=cols; if (Q->nrows>=Q->reserved_rows) { int ns; char ***R; ns=(Q->reserved_rows+1)*2; R=(char ***) GC_MALLOC(ns*sizeof(char **)); memcpy(R,Q->rows,sizeof(char**)*Q->reserved_rows); Q->rows=R; Q->reserved_rows=ns; } { int i; char **R; Q->rows[Q->nrows]=(char **) GC_MALLOC(cols*sizeof(char *)); R=Q->rows[Q->nrows]; Q->nrows+=1; for(i=0;i, an SQLite database is opened. There's no error handling for this function, because SQLite will detect an invalid handle itself. =verbatim c,8 */ void *c_sqlite_open(char *dbn) { sqlite3 *db; sqlite3_open(dbn,&db); return (void *) db; } /*=verbatim The F function closes a previously opened SQLite database. =verbatim c,8 */ void *c_sqlite_close(void *db) { sqlite3_close((sqlite3 *) db); return NULL; } /*=verbatim With the F function, SQL queries are done. It returns a handle of struct sqlite_query_t. This function uses the previously defined callback function to handle the results of the query. The struct is preinitialized (see code). =verbatim c,8 */ void *c_sqlite_query(void *db,char *query) { char *err=NULL; sqlite_query_t *Q=(sqlite_query_t *) GC_MALLOC(sizeof(sqlite_query_t)); Q->ncols=0; Q->nrows=0; Q->reserved_rows=32; Q->rows=(char ***) GC_MALLOC(sizeof(char**)*Q->reserved_rows); Q->errorMsg=NULL; sqlite3_exec((sqlite3 *) db,query,callback,Q,&err); if (err) { Q->errorMsg=gc_strdup(err); sqlite3_free(err); } else { Q->errorMsg=gc_strdup(""); } return (void *) Q; } /*=verbatim F and F can be used to get the number of rows and columns that a query resulted in. =verbatim c,8 */ int c_sqlite_nrows(void *q) { sqlite_query_t *Q=(sqlite_query_t *) q; return Q->nrows; } int c_sqlite_ncols(void *q) { sqlite_query_t *Q=(sqlite_query_t *) q; return Q->ncols; } /*=verbatim F can be used to get a cell from the returned query. The C and C parameters must be less then the C and C fields of the sqlite_query_t struct. =verbatim c,8 */ char *c_sqlite_cell(void *q,int row,int col) { sqlite_query_t *Q=(sqlite_query_t *) q; return Q->rows[row][col]; } /*=verbatim With the F function one can get the last reported error for a query. It returns a string containing the error message. If this errormessage is the empty string, no error has been reported. =verbatim c,8 */ char *c_sqlite_lasterr(void *q) { sqlite_query_t *Q=(sqlite_query_t *) q; if (Q->errorMsg==NULL) { Q->errorMsg=gc_strdup(""); } return Q->errorMsg; } /*=verbatim The F function returns the major and minor version of the SQLite library that is used. They are composed in an integer, as follows: major*100+minor. =verbatim c,8 */ int c_sqlite_version(void) { return cc_sqlite_version(); } #endif /*=verbatim =cut */ /************MZSCHEME***************************************************/ /* =head2 Interfacing with mzscheme =verbatim c,8 */ #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 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 IS_STRINGP(obj) SCHEME_STRINGP(obj) #endif #ifdef MZSCHEME3 static Scheme_Object *SQLite_Type=NULL; static Scheme_Object *Query_Type=NULL; #else static char *SQLite_Type="SQLite_t"; static char *Query_Type="sqlite_query_t"; #endif typedef struct { sqlite3 *db; char *errmsg; } mz_sqlite3_t; static Scheme_Object *c_sqlite_open(int argc, Scheme_Object **argv) { mz_sqlite3_t *db=(mz_sqlite3_t *) scheme_malloc(sizeof(mz_sqlite3_t)); char *dbn; Scheme_Object *obj; #ifdef MZSCHEME3 if (SQLite_Type==NULL) { SQLite_Type=scheme_make_byte_string("SQLite"); } #endif if (!IS_STRINGP(argv[0])) { scheme_wrong_type("c-sqlite-open","string",0,argc,argv); } dbn=SCHEME_STR_VAL(argv[0]); /*printf("%s\n",dbn);*/ if (sqlite3_open(dbn,&db->db)==SQLITE_OK) { db->errmsg=scheme_malloc(sizeof("")); strcpy(db->errmsg,""); } else { db->errmsg=scheme_malloc((strlen(sqlite3_errmsg(db->db))+1)*sizeof(char)); strcpy(db->errmsg,sqlite3_errmsg(db->db)); } obj=scheme_make_cptr(db,SQLite_Type); return obj; } static Scheme_Object *c_sqlite_close(int argc, Scheme_Object **argv) { mz_sqlite3_t *db; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-close","SQLite_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],SQLite_Type)) { scheme_wrong_type("c-sqlite-close","SQLite_t",0,argc,argv); } db=(mz_sqlite3_t *) SCHEME_CPTR_VAL(argv[0]); if (sqlite3_close(db->db)!=SQLITE_OK) { db->errmsg=scheme_malloc(sizeof("Unexpected: Error while closing sqlite3 handle")); strcpy(db->errmsg,"Unexpected: Error while closing sqlite3 handle"); } return scheme_void; } typedef struct { sqlite3 *handle; char *query; int nrows; int ncols; char **results; int ready; char *errormsg; int query_result; } sqlite_exec_t; static int query(void *data) { sqlite_exec_t *H=(sqlite_exec_t *) data; H->nrows=0; H->ncols=0; H->results=NULL; H->errormsg=NULL; H->query_result=sqlite3_get_table(H->handle,H->query,&H->results,&H->nrows,&H->ncols,&H->errormsg); /*fprintf(stdout,"query: %s, %d, %d, %s, %p\n",H->query,H->nrows, H->ncols, H->errormsg, H->results); fseek(stdout,0,SEEK_END);*/ H->ready=1; return 0; } static int query_ready(Scheme_Object *data) { sqlite_exec_t *H=(sqlite_exec_t *) data; return H->ready; } static Scheme_Object *c_sqlite_query(int argc, Scheme_Object **argv) //void *db,char *query) { char *err=NULL; sqlite_query_t *Q=(sqlite_query_t *) scheme_malloc(sizeof(sqlite_query_t)); Scheme_Object *obj; #ifdef MZSCHEME3 if (Query_Type==NULL) { Query_Type=scheme_make_byte_string("sqlite_query_t"); } #endif if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-query","SQLite_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],SQLite_Type)) { scheme_wrong_type("c-sqlite-query","SQLite_t",0,argc,argv); } if (!IS_STRINGP(argv[1])) { scheme_wrong_type("c-sqlite-query","string",0,argc,argv); } { sqlite_exec_t *H=(sqlite_exec_t *) malloc(sizeof(sqlite_exec_t)); mz_sqlite3_t *db=SCHEME_CPTR_VAL(argv[0]); H->handle=db->db; H->query=SCHEME_STR_VAL(argv[1]); H->ready=0; /*fprintf(stdout,"1\n");fseek(stdout,0,SEEK_END);*/ { t_c_thread_id id; id=c_thread_create(query,(void *) H); scheme_block_until(query_ready,NULL,(Scheme_Object *) H,-1); c_thread_join(id); } if (H->errormsg) { Q->errorMsg=gc_strdup(H->errormsg); sqlite3_free(H->errormsg); /*free(err); CAUSES SIGSEGV ON WINDOWS */ } else { Q->errorMsg=gc_strdup(""); } /*fprintf(stdout,"2\n");fseek(stdout,0,SEEK_END);*/ Q->ncols=H->ncols; Q->nrows=H->nrows; Q->reserved_rows=H->nrows; Q->rows=(Scheme_Object ***) scheme_malloc(sizeof(Scheme_Object **)*Q->reserved_rows); { int nrows=H->nrows; int ncols=H->ncols; char **results=H->results; int r,c,i; /*fprintf(stdout,"3\n");fseek(stdout,0,SEEK_END);*/ for(i=ncols,r=0;rrows[r]=row; for(c=0;cresults); free(H); } /*fprintf(stdout,"5\n");fseek(stdout,0,SEEK_END);*/ obj=scheme_make_cptr(Q,Query_Type); return obj; } static Scheme_Object *c_sqlite_nrows(int argc,Scheme_Object **argv) //void *q) { sqlite_query_t *Q; Scheme_Object *obj; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-nrows","sqlite_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-sqlite-nrows","sqlite_query_t",0,argc,argv); } Q=SCHEME_CPTR_VAL(argv[0]); obj=scheme_make_integer(Q->nrows); return obj; } static Scheme_Object *c_sqlite_ncols(int argc,Scheme_Object **argv) //void *q) { sqlite_query_t *Q; Scheme_Object *obj; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-ncols","sqlite_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-sqlite-ncols","sqlite_query_t",0,argc,argv); } Q=SCHEME_CPTR_VAL(argv[0]); obj=scheme_make_integer(Q->ncols); return obj; } static Scheme_Object *c_sqlite_cell(int argc,Scheme_Object **argv) //void *q,int row,int col) { sqlite_query_t *Q; int row,col; Scheme_Object *obj; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-cell","sqlite_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-sqlite-cell","sqlite_query_t",0,argc,argv); } if (!SCHEME_INTP(argv[1])) { scheme_wrong_type("c-sqlite-cell","integer",1,argc,argv); } if (!SCHEME_INTP(argv[2])) { scheme_wrong_type("c-sqlite-cell","integer",2,argc,argv); } Q=(sqlite_query_t *) SCHEME_CPTR_VAL(argv[0]); row=SCHEME_INT_VAL(argv[1]); col=SCHEME_INT_VAL(argv[2]); if (row>=Q->nrows) { scheme_signal_error("c_sqlite_cell: row out of bound"); } if (col>=Q->ncols) { scheme_signal_error("c_sqlite_cell: column out of bound"); } obj=Q->rows[row][col]; return obj; } static Scheme_Object *c_sqlite_lasterr(int argc, Scheme_Object **argv) { sqlite_query_t *Q; mz_sqlite3_t *db; Scheme_Object *obj; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-sqlite-lasterr","sqlite_query_t|SQLite_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type) && !EQ_CTYPE(argv[0],SQLite_Type)) { scheme_wrong_type("c-sqlite-lasterr","sqlite_query_t|SQLite_t",0,argc,argv); } if (EQ_CTYPE(argv[0],Query_Type)) { Q=(sqlite_query_t *) SCHEME_CPTR_VAL(argv[0]); if (Q->errorMsg==NULL) { Q->errorMsg=gc_strdup(""); } obj=Q->errorMsg; } else { db=(mz_sqlite3_t *) SCHEME_CPTR_VAL(argv[0]); obj=gc_strdup(db->errmsg); } return obj; } static Scheme_Object *c_sqlite_version(int argc, Scheme_Object **argv) { return scheme_make_integer(cc_sqlite_version()); } Scheme_Object *scheme_reload(Scheme_Env *env) { Scheme_Env *menv; Scheme_Object *proc; menv = scheme_primitive_module(scheme_intern_symbol("c-sqld-sqlite"),env); proc = scheme_make_prim_w_arity(c_sqlite_version, "c-sqlite-version", 0, 0); scheme_add_global("c-sqlite-version", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_open, "c-sqlite-open", 1, 1); scheme_add_global("c-sqlite-open", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_close, "c-sqlite-close", 1, 1); scheme_add_global("c-sqlite-close", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_query, "c-sqlite-query", 2, 2); scheme_add_global("c-sqlite-query", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_nrows, "c-sqlite-nrows", 1, 1); scheme_add_global("c-sqlite-nrows", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_ncols, "c-sqlite-ncols", 1, 1); scheme_add_global("c-sqlite-ncols", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_cell, "c-sqlite-cell", 3, 3); scheme_add_global("c-sqlite-cell", proc, menv); proc = scheme_make_prim_w_arity(c_sqlite_lasterr, "c-sqlite-lasterr", 1, 1); scheme_add_global("c-sqlite-lasterr", proc, menv); scheme_finish_primitive_module(menv); return scheme_void; } Scheme_Object *scheme_initialize(Scheme_Env *env) { return scheme_reload(env); } Scheme_Object *scheme_module_name(void) { return scheme_intern_symbol("c-sqld-sqlite"); } #endif /*=verbatim =cut */