/********************************************************************************* * Package : c-sqld-psql.c * Author : Hans Oesterholt-Dijkema. * Copyright : HOD 2004/2005. * License : The Elemental Programming Artistic License. * CVS : $Id: c-sqld-psql.c,v 1.9 2006/01/05 00:35:33 HansOesterholt Exp $ *********************************************************************************/ #include "sqlid.h" #include #include #include #ifdef Darwin #include #else #include #endif #ifdef BIGLOO #include #endif #ifdef MZSCHEME # include # include "c-threads.c" #endif /* =head1 Name SQLD-PSQL - C Part =head1 Author Hans Oesterholt-Dijkema =head1 Copyright/License (c) 2004 Hans Oesterholt-Dijkema, L. =head1 Version $Id: c-sqld-psql.c,v 1.9 2006/01/05 00:35:33 HansOesterholt Exp $ =head1 Literate Description In this source file, the interfacing between bigloo and PostgreSQL is described. This interface to PostgreSQL is based on PostgreSQL E=7.4. =head2 Query Handle The interfacing between bigloo and PostgreSQL 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; #ifdef MZSCHEME Scheme_Object ***rows; Scheme_Object *errorMsg; #else char ***rows; char *errorMsg; #endif } psql_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 is an array of C, that holds pointers to rows. C is a field that will hold the last reported error by PostgreSQL. =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 static char *gc_strdup(char *s) { char *n=(char *) GC_MALLOC(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 =cut */ /************BIGLOO***************************************************/ #ifdef BIGLOO /* =head2 Interfacing with bigloo This section is dedicated to the interfacing between bigloo and PostgreSQL. With F, an PostgreSQL database is opened. There's no error handling for this function, because PostgreSQL will detect an invalid handle itself. =verbatim c,8 */ void *c_psql_open(char *connection_string) { PGconn *conn; conn=PQconnectdb(connection_string); return (void *) conn; } /*=verbatim The F function closes a previously opened PostgreSQL database. =verbatim c,8 */ void *c_psql_close(void *conn) { PQfinish((PGconn *) conn); return NULL; } /*=verbatim With the F function, SQL queries are done. It returns a handle of struct psql_query_t. =verbatim c,8 */ void *c_psql_query(void *db,char *query) { PGresult *res; PGconn *conn=(PGconn *) db; psql_query_t *Q=(psql_query_t *) GC_MALLOC(sizeof(psql_query_t)); Q->ncols=0; Q->nrows=0; Q->rows=NULL; Q->errorMsg=NULL; res=PQexec(conn,query); if (PQresultStatus(res) != PGRES_COMMAND_OK) { Q->errorMsg=gc_strdup(PQerrorMessage(conn)); } else { Q->errorMsg=gc_strdup(""); } Q->ncols=PQnfields(res); Q->nrows=PQntuples(res); { int r,c,R=Q->nrows,C=Q->ncols; char **row; Q->rows=(char ***) GC_MALLOC(sizeof(char **)*R); for(r=0;rrows[r]=row; } } PQclear(res); 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_psql_nrows(void *q) { psql_query_t *Q=(psql_query_t *) q; return Q->nrows; } int c_psql_ncols(void *q) { psql_query_t *Q=(psql_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 psql_query_t struct. =verbatim c,8 */ char *c_psql_cell(void *q,int row,int col) { psql_query_t *Q=(psql_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_psql_lasterr(void *q) { psql_query_t *Q=(psql_query_t *) q; if (Q->errorMsg==NULL) { Q->errorMsg=gc_strdup(""); } return Q->errorMsg; } /*=verbatim The F function returns the major, middle and minor version of the PostgreSQL library that is used. They are composed in an integer, as follows: major*100+middle*10+minor. =verbatim c,8 */ int c_psql_version(void) { int maj=0,min1=0,min2=0; char *s=PG_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; } /*=verbatim The F function escapes a given string, using the standard libpq escape function. =verbatim c,8 */ char *c_psql_string2db(char *s) { int len=strlen(s); char *to=(char *) GC_MALLOC((2*len+1)*sizeof(char)); PQescapeString(to,s,len); return to; } #endif /*=verbatim =cut */ /************MZSCHEME***************************************************/ /* =head2 mzscheme support =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 *PGconn_Type=NULL; static Scheme_Object *Query_Type=NULL; #else static char *PGconn_Type="PGconn_t"; static char *Query_type="psql_query_t"; #endif static Scheme_Object *c_psql_version(int argc,Scheme_Object **argv) { int maj=0,min1=0,min2=0; char *s=PG_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 scheme_make_integer(maj*100+min1*10+min2); } static Scheme_Object *c_psql_open(int argc,Scheme_Object **argv) { PGconn *conn; char *connection_string; #ifdef MZSCHEME3 if (PGconn_Type==NULL) { PGconn_Type=scheme_make_byte_string("PGconn_t"); } #endif if (!IS_STRINGP(argv[0])) { scheme_wrong_type("c-psql-open","string",0,argc,argv); } connection_string=SCHEME_STR_VAL(argv[0]); conn=PQconnectdb(connection_string); return scheme_make_cptr(conn,PGconn_Type); } static Scheme_Object *c_psql_close(int argc,Scheme_Object **argv) { PGconn *conn; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-close","PGconn",0,argc,argv); } else if (!EQ_CTYPE(argv[0],PGconn_Type)) { scheme_wrong_type("c-psql-close","PGconn",0,argc,argv); } conn=SCHEME_CPTR_VAL(argv[0]); PQfinish(conn); return scheme_void; } typedef struct { PGconn *conn; char *query; PGresult *result; int ready; } t_exec_query; static int execute_query_ready(Scheme_Object *data) { t_exec_query *Q=(t_exec_query *) data; return Q->ready; } static int execute_query(void *data) { t_exec_query *Q=(t_exec_query *) data; Q->result=PQexec(Q->conn,Q->query); Q->ready=1; return 0; } static Scheme_Object *c_psql_query(int argc,Scheme_Object **argv) { PGresult *res; PGconn *conn; char *query; psql_query_t *Q=(psql_query_t *) scheme_malloc(sizeof(psql_query_t)); Q->ncols=0; Q->nrows=0; Q->rows=NULL; Q->errorMsg=NULL; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-query","PGconn",0,argc,argv); } else if (!EQ_CTYPE(argv[0],PGconn_Type)) { scheme_wrong_type("c-psql-query","PGconn",0,argc,argv); } if (!IS_STRINGP(argv[1])) { scheme_wrong_type("c-psql-query","string",1,argc,argv); } conn=SCHEME_CPTR_VAL(argv[0]); query=SCHEME_STR_VAL(argv[1]); { t_exec_query *Q = (t_exec_query *) malloc(sizeof(t_exec_query)); t_c_thread_id id; Q->conn=conn; Q->query=query; Q->result=NULL; Q->ready=0; //{ conn, query, NULL, 0 }; //printf("sql query in thread, creating for query %s\n",query); id = c_thread_create(execute_query,(void *) Q); //printf("sql query in thread, thread created\n"); //printf("block scheme until query ready\n"); scheme_block_until(execute_query_ready,NULL,(Scheme_Object *) Q,-1); //printf("wait for thread join\n"); c_thread_join(id); res=Q->result; free(Q); //printf("result is there\n"); } if (PQresultStatus(res) != PGRES_COMMAND_OK) { Q->errorMsg=gc_strdup(PQerrorMessage(conn)); } else { Q->errorMsg=gc_strdup(""); } Q->ncols=PQnfields(res); Q->nrows=PQntuples(res); { int r,c,R=Q->nrows,C=Q->ncols; Scheme_Object **row; Q->rows=(Scheme_Object ***) scheme_malloc(sizeof(Scheme_Object **)*R); for(r=0;rrows[r]=row; } } PQclear(res); return scheme_make_cptr(Q,Query_Type); } static Scheme_Object *c_psql_nrows(int argc,Scheme_Object **argv) { psql_query_t *Q; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-nrows","psql_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-psql-nrows","psql_query_t",0,argc,argv); } Q=SCHEME_CPTR_VAL(argv[0]); return scheme_make_integer(Q->nrows); } static Scheme_Object *c_psql_ncols(int argc,Scheme_Object **argv) { psql_query_t *Q; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-ncols","psql_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-psql-ncols","psql_query_t",0,argc,argv); } Q=SCHEME_CPTR_VAL(argv[0]); return scheme_make_integer(Q->ncols); } static Scheme_Object *c_psql_cell(int argc,Scheme_Object **argv) //void *q,int row,int col) { psql_query_t *Q; int row,col; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-cell","psql_query_t",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type)) { scheme_wrong_type("c-psql-cell","psql_query_t",0,argc,argv); } if (!SCHEME_INTP(argv[1])) { scheme_wrong_type("c-psql-cell","integer",1,argc,argv); } if (!SCHEME_INTP(argv[2])) { scheme_wrong_type("c-psql-cell","integer",2,argc,argv); } Q=SCHEME_CPTR_VAL(argv[0]); row=SCHEME_INT_VAL(argv[1]); col=SCHEME_INT_VAL(argv[2]); return Q->rows[row][col]; } static Scheme_Object *c_psql_lasterr(int argc,Scheme_Object **argv) { psql_query_t *Q; if (!SCHEME_CPTRP(argv[0])) { scheme_wrong_type("c-psql-lasterr","psql_query_t|PGconn",0,argc,argv); } else if (!EQ_CTYPE(argv[0],Query_Type) && !EQ_CTYPE(argv[0],PGconn_Type)) { scheme_wrong_type("c-psql-lasterr","psql_query_t|PGconn",0,argc,argv); } if (EQ_CTYPE(argv[0],Query_Type)) { Q=SCHEME_CPTR_VAL(argv[0]); if (Q->errorMsg==NULL) { Q->errorMsg=gc_strdup(""); } return Q->errorMsg; } else { PGconn *conn; conn=SCHEME_CPTR_VAL(argv[0]); if (PQstatus(conn) != CONNECTION_OK) { return gc_strdup(PQerrorMessage(conn)); } else { return gc_strdup(""); } } } static Scheme_Object *c_psql_string2db(int argc,Scheme_Object **argv) { char *s; int len; char *to; if (!IS_STRINGP(argv[0])) { scheme_wrong_type("c-psql-open","string",0,argc,argv); } s=SCHEME_STR_VAL(argv[0]); len=strlen(s); to=(char *) scheme_malloc_atomic((2*len+1)*sizeof(char)); PQescapeString(to,s,len); return gc_strdup(to); } Scheme_Object *scheme_reload(Scheme_Env *env) { Scheme_Env *menv; Scheme_Object *proc; menv = scheme_primitive_module(scheme_intern_symbol("c-sqld-psql"),env); proc = scheme_make_prim_w_arity(c_psql_version, "c-psql-version", 0, 0); scheme_add_global("c-psql-version", proc, menv); proc = scheme_make_prim_w_arity(c_psql_open, "c-psql-open", 1, 1); scheme_add_global("c-psql-open", proc, menv); proc = scheme_make_prim_w_arity(c_psql_close, "c-psql-close", 1, 1); scheme_add_global("c-psql-close", proc, menv); proc = scheme_make_prim_w_arity(c_psql_query, "c-psql-query", 2, 2); scheme_add_global("c-psql-query", proc, menv); proc = scheme_make_prim_w_arity(c_psql_nrows, "c-psql-nrows", 1, 1); scheme_add_global("c-psql-nrows", proc, menv); proc = scheme_make_prim_w_arity(c_psql_ncols, "c-psql-ncols", 1, 1); scheme_add_global("c-psql-ncols", proc, menv); proc = scheme_make_prim_w_arity(c_psql_cell, "c-psql-cell", 3, 3); scheme_add_global("c-psql-cell", proc, menv); proc = scheme_make_prim_w_arity(c_psql_lasterr, "c-psql-lasterr", 1, 1); scheme_add_global("c-psql-lasterr", proc, menv); proc = scheme_make_prim_w_arity(c_psql_string2db, "c-psql-string2db", 1, 1); scheme_add_global("c-psql-string2db", 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-psql"); } #endif /*=verbatim =cut */