diff options
Diffstat (limited to 'ipl/packs/icondb/mysqldb.c')
-rw-r--r-- | ipl/packs/icondb/mysqldb.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/ipl/packs/icondb/mysqldb.c b/ipl/packs/icondb/mysqldb.c new file mode 100644 index 0000000..06dc179 --- /dev/null +++ b/ipl/packs/icondb/mysqldb.c @@ -0,0 +1,289 @@ + +/*-----------------3/27/2007 11:23AM----------------- + * loadable C function mysqldb for icon access to + * a mySQL database from linux, by Carl Sturtivant. + * (This also built on solaris.) + * + * This should be Garbage Collection safe except + * under very extreme memory shortages. + * + * Requires a mySQL installation to build. + * I used the following from bash: + +CFG=/usr/bin/mysql_config +sh -c "gcc -shared -o mysqldb.so -fPIC `$CFG --cflags` mysqldb.c `$CFG --libs`" + + * for details about calling mysqldb, see below. + * --------------------------------------------------*/ + +#include <stdio.h> +#include <string.h> + +/* http://dev.mysql.com/doc/refman/5.0/en/c.html */ +/* #include "/usr/include/mysql/mysql.h" */ +#include <mysql.h> + + +#include "icall.h" + + +/* macros obtained by modifying some from icall.h */ + +#define Mkinteger(i, dp) \ +do { (dp)->dword = D_Integer; (dp)->vword = (i); } while(0) + +#define Mkstring(s, dp) \ +do { word n = strlen(s); \ +(dp)->dword = n; (dp)->vword = (word)alcstr(s,n); } while(0) + +/* ensure that return to icon removes our tended descriptors from the list */ +#define ReturnDescriptor(d) do { gcu_aware_pop(); return ( argv[0] = (d), 0 ); } while(0) +#define ReturnError(d, n) do { gcu_aware_pop(); return ( argv[0] = (d), n ); } while(0) + + +/****************start of Garbage Collection Utilities****************/ + +/* Structure for chaining descriptors to be tended properly by GC (rstructs.h) */ +struct tend_desc { + struct tend_desc *previous; + int num; + descriptor d[1]; /* actual size is in num */ +}; +typedef struct tend_desc gcu_tended; + +/* global chain of such structures used by iconx (rinit.r) */ +extern gcu_tended *tend; + +/* int parameter to pass to gcu_initialize */ +#define gcu_max(vars) ( (sizeof(vars) - sizeof(gcu_tended) )/sizeof(descriptor) ) + +/* initialize all descriptors to &null and assign the number */ +static void gcu_initialize(int maxindex, void *descriptors) { + int i; + gcu_tended *desc = (gcu_tended *)descriptors; + desc->num = maxindex+1; + for( i = 0; i <= maxindex; ++i ) (desc->d)[i] = nulldesc; +} + +/* add descriptors in a gcu_tended structure to the tended list */ +static void gcu_aware_push(void *descriptors) { + gcu_tended *desc = (gcu_tended *)descriptors; + desc->previous = tend; + tend = descriptors; +} + +/* remove descriptors in a gcu_tended structure from the tended list */ +static void gcu_aware_pop() { + tend = tend->previous; +} + +/****************end of Garbage Collection utilities****************/ + + +/****************start of list utilities****************/ + +int Zlist(descriptor argv[]); /* resolved in iconx: icon function list(i,X):L */ +int Osubsc(descriptor argv[]); /* resolved in iconx: icon operator L[i]:v */ +int Oasgn(descriptor argv[]); /* resolved in iconx: icon operator v:=X */ + +typedef int (*iconfunction)(descriptor argv[]); + +/* safely call an icon built-in function or operator with two arguments from C. */ +static descriptor iconcall2(iconfunction F, descriptor x1, descriptor x2) { + struct { /* structure like struct tend_desc with extra descriptors at the bottom */ + gcu_tended other; /* vital: used to chain onto the tend list */ + descriptor stack[3]; /* GC is aware of these once this struct is pushed onto the tend list */ + } tended; + gcu_initialize( gcu_max(tended), &tended ); /* vital: call before icon may be made aware of this */ + gcu_aware_push( &tended ); /* GC is now aware of tended.stack */ + tended.stack[0] = nulldesc; + tended.stack[1] = x1; + tended.stack[2] = x2; + F(tended.stack); /* No error handling for the uses below */ + gcu_aware_pop(); /* vital: GC is now unaware of tended.stack */ + return tended.stack[0]; +} + +/* returns list(n, &null) --- allocates memory */ +static descriptor newlist(int length) { + descriptor len; + Mkinteger(length, &len); + return iconcall2( &Zlist, len, nulldesc ); +} + +/* returns list[index] := value */ +static descriptor assign(descriptor list, int index, descriptor value) { + descriptor i; + Mkinteger(index, &i); + return iconcall2( &Oasgn, iconcall2(&Osubsc, list, i), value ); +} + +/* returns .list[index] */ +static descriptor subscript(descriptor list, int index) { + descriptor i, result; + Mkinteger(index, &i); + result = iconcall2(&Osubsc, list, i); + /* result of an icon subscripting operation is a variable */ + deref(&result, &result); /* deref resolved in iconx */ + return result; +} + +/****************end of list utilities****************/ + + +/* make icon list of mysql error information */ +static descriptor error_info(int mysqlNumber, const char * mysqlError) { + descriptor number; + struct { + gcu_tended other; + descriptor text, ls; + } tended; + gcu_initialize( gcu_max(tended), &tended ); + gcu_aware_push( &tended ); + tended.ls = newlist(2); + Mkinteger(mysqlNumber, &number); + Mkstring((char *)mysqlError, &tended.text); + assign( tended.ls, 1, number ); + assign( tended.ls, 2, tended.text ); + gcu_aware_pop(); + return tended.ls; +} + +/* make mySQL row retrieved from query results into icon list */ +static descriptor convertrow(MYSQL_ROW row, int numfields) { + int i; + struct { + gcu_tended other; + descriptor x, ls; + } tended; + gcu_initialize( gcu_max(tended), &tended ); + gcu_aware_push( &tended ); + tended.ls = newlist(numfields); + for( i = 1; i <= numfields; ++i ) { + if( row[i-1] ) Mkstring( row[i-1], &tended.x ); + else tended.x = nulldesc; + assign( tended.ls, i, tended.x ); + } + gcu_aware_pop(); + return tended.ls; +} + +/*-------------------------------------------------- + * Called with a list, mysqldb attempts to connect. + * Only one database can be connected to at a time. + * Needs the database name, username, password, + * and optionally the host, and if so optionally + * the port number, all passed in a list. The host + * defaults to localhost, and the port number to + * the default port number for mySQL. + * + * Called with a string, mysqldb attempts to + * execute that string as a mySQL query. + * + * Called with no parameters, mysqldb closes + * the connection if it is open. + * + * Returns a list of lists for a SELECT query, or + * the number of rows affected for other queries. + * Otherwise, fails if everything works, returns + * error information if not, except if incorrect + * argument types are supplied, in which case the + * result is an error. + * --------------------------------------------------*/ +int mysqldb(int argc, descriptor argv[]) { + static MYSQL dbh; /* connection sticks around between calls */ + static int connected = 0; + + MYSQL_RES *result; + MYSQL_ROW row; + char *querystring, *hoststring, + *databasestring, *userstring, *passwordstring; + int i, len, rowsize, portnum; + struct { + gcu_tended other; + descriptor ls, host, port, database, user, password, answer; + } tended; + gcu_initialize( gcu_max(tended), &tended ); + gcu_aware_push( &tended ); + + + if( argc == 0 ) { /* close connection */ + if( connected ) mysql_close(&dbh); + connected = 0; + gcu_aware_pop(); + Fail; + } /* end close connection */ + + if( argc >= 1 && IconType(argv[1]) == 'L' ) { /* connect to MySQL */ + if( connected ) + ReturnDescriptor( error_info(-1, "mysqldb: already connected") ); + if( !mysql_init(&dbh) ) + ReturnDescriptor( error_info(-1, "mysqldb: cannot initialize mySQL!") ); + + tended.ls = argv[1]; + hoststring = "localhost"; /* host defaults to localhost */ + portnum = 0; /* port defaults to 0 giving the mySQL default */ + + switch( ListLen(tended.ls) ) { + default: + ReturnDescriptor( error_info(-1, "mysqldb: list of dbname, user, pwd, [host, [port]] expected") ); + case 5 : + tended.port = subscript(tended.ls, 5); + if( !cnv_int(&tended.port,&tended.port) ) ReturnError(tended.port,101); + portnum = IntegerVal(tended.port); + case 4 : + tended.host = subscript(tended.ls, 4); + if ( !cnv_str(&tended.host,&tended.host) ) ReturnError(tended.host,103); + hoststring = StringVal(tended.host); + case 3 : + tended.password = subscript(tended.ls, 3); + if ( !cnv_str(&tended.password,&tended.password) ) ReturnError(tended.password,103); + passwordstring = StringVal(tended.password); + tended.user = subscript(tended.ls, 2); + if ( !cnv_str(&tended.user,&tended.user) ) ReturnError(tended.user,103); + userstring = StringVal(tended.user); + tended.database = subscript(tended.ls, 1); + if ( !cnv_str(&tended.database,&tended.database) ) ReturnError(tended.database,103); + databasestring = StringVal(tended.database); + } + + if( mysql_real_connect(&dbh, hoststring, userstring, + passwordstring, databasestring, portnum, NULL, 0) ) { + connected = 1; + gcu_aware_pop(); + Fail; + } + else ReturnDescriptor( error_info(mysql_errno(&dbh), mysql_error(&dbh)) ); + } /* end connect to MySQL */ + + if( argc >= 1 && IconType(argv[1]) == 's' ) { /* execute a query */ + if( !connected ) + ReturnDescriptor( error_info(-1, "mysqldb: not connected") ); + querystring = StringVal(argv[1]); + + if( mysql_query(&dbh, querystring) ) + ReturnDescriptor( error_info(mysql_errno(&dbh), mysql_error(&dbh)) ); + + result = mysql_store_result(&dbh); + + if( !result ) /* not a SELECT query or some sort of error */ + if( mysql_field_count(&dbh) != 0 ) + ReturnDescriptor( error_info(mysql_errno(&dbh), mysql_error(&dbh)) ); + else { /* not a SELECT query */ + gcu_aware_pop(); + RetInteger( mysql_affected_rows(&dbh) ); + } + + /* SELECT query */ + tended.answer = newlist( mysql_num_rows(result) ); + rowsize = mysql_num_fields(result); + i = 0; + while( row = mysql_fetch_row(result) ) + assign( tended.answer, ++i, convertrow(row, rowsize) ); + mysql_free_result(result); + ReturnDescriptor(tended.answer); + } /* end execute a query */ + + /* wrong argument type to mysqldb */ + ReturnError(argv[1], 110); /* list or string expected */ +} |