diff --git a/.gitattributes b/.gitattributes index c0303b3e70ea3e9d2d7575b1a42cc6e7bf0a46b2..9d1468219b87066e9c9b33838bbb601d482ac3e6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -54,6 +54,12 @@ testfont binary /src/modules/Mysql/mysql.c foreign_ident /src/modules/Mysql/precompiled_mysql.h foreign_ident /src/modules/Mysql/result.c foreign_ident +/src/modules/Odbc/Makefile.in foreign_ident +/src/modules/Odbc/acconfig.h foreign_ident +/src/modules/Odbc/configure.in foreign_ident +/src/modules/Odbc/odbc.c foreign_ident +/src/modules/Odbc/odbc_result.c foreign_ident +/src/modules/Odbc/precompiled_odbc.h foreign_ident /src/modules/Pipe/pipe.c foreign_ident /src/modules/Regexp/acconfig.h foreign_ident /src/modules/Ssleay/ssleay.c foreign_ident diff --git a/src/modules/Odbc/.cvsignore b/src/modules/Odbc/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..3a5fcc9b7b5e80cc57e0cb8f16c49204a7e0abb6 --- /dev/null +++ b/src/modules/Odbc/.cvsignore @@ -0,0 +1,10 @@ +.pure +Makefile +config.h.in +config.log +config.status +configure +dependencies +linker_options +stamp-h +stamp-h.in diff --git a/src/modules/Odbc/.gitignore b/src/modules/Odbc/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a5317fbd105b018042f2a89481eceaf676a128c1 --- /dev/null +++ b/src/modules/Odbc/.gitignore @@ -0,0 +1,10 @@ +/.pure +/Makefile +/config.h.in +/config.log +/config.status +/configure +/dependencies +/linker_options +/stamp-h +/stamp-h.in diff --git a/src/modules/Odbc/Makefile.in b/src/modules/Odbc/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..a2ba0da840f110595934c343c77870e848b671e9 --- /dev/null +++ b/src/modules/Odbc/Makefile.in @@ -0,0 +1,12 @@ +# +# $Id: Makefile.in,v 1.1 1997/03/10 19:01:49 grubba Exp $ +# + +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@ +OBJS=odbc.o odbc_result.o +MODULE_LDFLAGS=@LDFLAGS@ @ODBC_LIBS@ + +@dynamic_module_makefile@ +@dependencies@ \ No newline at end of file diff --git a/src/modules/Odbc/acconfig.h b/src/modules/Odbc/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..ada3907ce4a43823839619fe7c862ae5d8fdde64 --- /dev/null +++ b/src/modules/Odbc/acconfig.h @@ -0,0 +1,18 @@ +/* + * $Id: acconfig.h,v 1.1 1997/03/10 19:01:50 grubba Exp $ + * + * Config-file for the Pike ODBC-module. + * + * Henrik Grubbstr�m + */ + +#ifndef PIKE_ODBC_CONFIG_H +#define PIKE_ODBC_CONFIG_H + +@TOP@ +@BOTTOM@ + +/* Define if you have ODBC */ +#undef HAVE_ODBC + +#endif /* PIKE_ODBC_CONFIG_H */ diff --git a/src/modules/Odbc/configure.in b/src/modules/Odbc/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..596801eeeeed8ccfb739ab8b9489c82cf0aa1283 --- /dev/null +++ b/src/modules/Odbc/configure.in @@ -0,0 +1,127 @@ +# +# $Id: configure.in,v 1.1 1997/03/10 19:01:51 grubba Exp $ +# +# Configure script for the odbc-module +# +# Henrik Grubbstr�m +# + +AC_INIT(odbc.c) +AC_CONFIG_HEADER(config.h) + +sinclude(../module_configure.in) + +OLD_LIBS=$LIBS +OLD_LDFLAGS=$LDFLAGS +OLD_CPPFLAGS=$CPPFLAGS +ODBC_LIBS="" + +AC_ARG_WITH(odbc, [ --without-odbc no support for the ODBC databases],[],[with_odbc=yes]) + +if test x$with_odbc = xyes; then + + AC_MSG_CHECKING(Checking for ODBC library-directory) + + AC_CACHE_VAL(pike_cv_odbc_lib_dir, [ + for pike_cv_odbc_lib_dir in ${INFORMIXDIR:+$INFORMIXDIR/cli/dlls} /opt/ISLIodbc/*/lib /usr/opt/ISLIodbc/*/lib /usr/local/lib /usr/local/odbc/lib /usr/local/lib/odbc /usr/odbc/lib /usr/lib/odbc /usr/lib /lib/odbc /lib no; do + echo $pike_cv_odbc_lib_dir + if test -d $pike_cv_odbc_lib_dir/.; then + if ls $pike_cv_odbc_lib_dir/*odbc* >/dev/null 2>&1 ; then + break + else + : + fi + else + : + fi + done + ]) + + AC_MSG_RESULT($pike_cv_odbc_lib_dir) + + if test x$pike_cv_odbc_lib_dir = xno; then :; else + echo Adding $pike_cv_odbc_lib_dir to the library search path. + LDFLAGS="-L$pike_cv_odbc_lib_dir ${LDFLAGS}" + fi + + AC_MSG_CHECKING(Checking for the ODBC include-directory) + + AC_CACHE_VAL(pike_cv_odbc_include_dir, [ + for pike_cv_odbc_include_dir in ${INFORMIXDIR:+$INFORMIXDIR/cli/include} /opt/ISLIodbc/*/include /usr/local/include /usr/local/odbc/include /usr/local/include/odbc /usr/odbc/include /usr/include/odbc /usr/include /include/odbc /include no; do + echo $pike_cv_odbc_include_dir + if test -d $pike_cv_odbc_include_dir/.; then + if ls $pike_cv_odbc_include_dir/qeodbc.h >/dev/null 2>&1; then + break + else + : + fi + else + : + fi + done + ]) + + AC_MSG_RESULT($pike_cv_odbc_include_dir) + + if test x$pike_cv_odbc_include_dir = xno; then :; else + echo Adding $pike_cv_odbc_include_dir to the include search path. + CPPFLAGS="-I$pike_cv_odbc_include_dir ${CPPFLAGS}" + fi + + # Header file + + AC_CHECK_HEADERS(qeodbc.h sql.h sqlext.h) + + # System libs which might be needed + + if echo $LIBS|grep -- -lsocket >&5 2>&5; then + : + else + AC_CHECK_LIB(socket, socket, [ + LIBS="-lsocket $LIBS" + ODBC_LIBS="-lsocket ${ODBC_LIBS}" + ], []) + fi + if echo $LIBS|grep -- -lnsl >&5 2>&5; then + : + else + AC_CHECK_LIB(nsl, gethostbyname, [ + LIBS="-lnsl $LIBS" + ODBC_LIBS="-lnsl ${ODBC_LIBS}" + ], []) + fi + if echo $LIBS|grep -- -lm >&5 2>&5; then + : + else + AC_CHECK_LIB(m, floor, [ + LIBS="-lm $LIBS" + ODBC_LIBS="-lm ${ODBC_LIBS}" + ], []) + fi + + pike_cv_odbc=no; + + AC_CHECK_LIB(odbc, SQLConnect, [ + LIBS="-lodbc $LIBS" + ODBC_LIBS="-lodbc ${ODBC_LIBS}" + pike_cv_odbc=yes; + ], []) + + if test x$pike_cv_odbc = xno; then + # Restore variables, so we don't link with unnessesary libs + + LIBS=$OLD_LIBS + CPPFLAGS=$OLD_CPPFLAGS + LDFLAGS=$OLD_LDFLAGS + ODBC_LIBS="" + else + AC_DEFINE(HAVE_ODBC) + + fi +else + : +fi + +AC_SUBST(ODBC_LIBS) + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/modules/Odbc/odbc.c b/src/modules/Odbc/odbc.c new file mode 100644 index 0000000000000000000000000000000000000000..427b31b7e85739bf11221e73bd8cdcd03877f9d7 --- /dev/null +++ b/src/modules/Odbc/odbc.c @@ -0,0 +1,334 @@ +/* + * $Id: odbc.c,v 1.1 1997/03/10 19:01:52 grubba Exp $ + * + * Pike interface to ODBC compliant databases. + * + * Henrik Grubbstr�m + */ + +/* + * Includes + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "global.h" +RCSID("$Id: odbc.c,v 1.1 1997/03/10 19:01:52 grubba Exp $"); + +#include "interpret.h" +#include "object.h" +#include "threads.h" +#include "svalue.h" +#include "stralloc.h" +#include "mapping.h" +#include "array.h" +#include "multiset.h" +#include "program.h" + +#include "precompiled_odbc.h" + +/* + * Globals + */ + +struct program *odbc_program = NULL; + +/* + * Functions + */ + +/* + * Helper functions + */ + +volatile void odbc_error(const char *fun, const char *msg, + struct precompiled_odbc *odbc, HSTMT hstmt, + RETCODE code, void (*clean)(void)); + +static INLINE volatile void odbc_check_error(const char *fun, const char *msg, + RETCODE code, void (*clean)(void)) +{ + if ((code != SQL_SUCCESS) && (code != SQL_SUCCESS_WITH_INFO)) { + odbc_error(fun, msg, PIKE_ODBC, PIKE_ODBC->hstmt, code, clean); + } +} + +volatile void odbc_error(const char *fun, const char *msg, + struct precompiled_odbc *odbc, HSTMT hstmt, + RETCODE code, void (*clean)(void)) +{ + RETCODE _code; + char errcode[256]; + char errmsg[SQL_MAX_MESSAGE_LENGTH]; + SWORD errmsg_len = 0; + SDWORD native_error; + + _code = SQLError(odbc->henv, odbc->hdbc, hstmt, errcode, &native_error, + errmsg, SQL_MAX_MESSAGE_LENGTH-1, &errmsg_len); + errmsg[errmsg_len] = '\0'; + + if (odbc) { + if (odbc->last_error) { + free_string(odbc->last_error); + } + odbc->last_error = make_shared_binary_string(errmsg, errmsg_len); + } + + if (clean) { + clean(); + } + switch(_code) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + error("%s():%s: %d:%s:%s\n", fun, msg, code, errcode, errmsg); + break; + default: + error("%s():%s: SQLError failed (%d:%d)\n", fun, msg, code, _code); + } +} + +/* + * Clean-up functions + */ + +static void clean_last_error(void) +{ + if (PIKE_ODBC->last_error) { + free_string(PIKE_ODBC->last_error); + PIKE_ODBC->last_error = NULL; + } +} + +static void clean_henv(void) +{ + HENV henv = PIKE_ODBC->henv; + PIKE_ODBC->henv = SQL_NULL_HENV; + odbc_check_error("odbc_error", "Freeing HENV", + SQLFreeEnv(henv), clean_last_error); +} + +static void clean_hdbc(void) +{ + HDBC hdbc = PIKE_ODBC->hdbc; + PIKE_ODBC->hdbc = SQL_NULL_HDBC; + odbc_check_error("odbc_error", "Disconnecting HDBC", + SQLDisconnect(hdbc), clean_henv); + odbc_check_error("odbc_error", "Freeing HDBC", + SQLFreeConnect(hdbc), clean_henv); +} + +/* + * Glue functions + */ + +static void init_odbc_struct(struct object *o) +{ + RETCODE code; + + PIKE_ODBC->henv = SQL_NULL_HENV; + PIKE_ODBC->hdbc = SQL_NULL_HDBC; + PIKE_ODBC->hstmt = SQL_NULL_HSTMT; + PIKE_ODBC->last_error = NULL; + PIKE_ODBC->affected_rows = 0; + + odbc_check_error("init_odbc_struct", "ODBC initialization failed", + SQLAllocEnv(&(PIKE_ODBC->henv)), 0); + + odbc_check_error("init_odbc_struct", "ODBC initialization failed", + SQLAllocConnect(PIKE_ODBC->henv, &(PIKE_ODBC->hdbc)), + clean_henv); +} + +static void exit_odbc_struct(struct object *o) +{ + RETCODE code; + HENV henv = PIKE_ODBC->henv; + HDBC hdbc = PIKE_ODBC->hdbc; + + PIKE_ODBC->hdbc = SQL_NULL_HDBC; + odbc_check_error("exit_odbc_struct", "ODBC disconnect failed", + SQLDisconnect(hdbc), clean_henv); + odbc_check_error("exit_odbc_struct", "Freeing ODBC connection failed", + SQLFreeConnect(hdbc), clean_henv); + PIKE_ODBC->henv = SQL_NULL_HENV; + odbc_check_error("exit_odbc_struct", "Freeing ODBC environment failed", + SQLFreeEnv(henv), clean_last_error); +} + +/* + * Pike functions + */ + +static void f_error(INT32 args) +{ + pop_n_elems(args); + + if (PIKE_ODBC->last_error) { + PIKE_ODBC->last_error->refs++; + push_string(PIKE_ODBC->last_error); + } else { + push_int(0); + } +} + +static void f_create(INT32 args) +{ + struct pike_string *server = NULL; + struct pike_string *database = NULL; + struct pike_string *user = NULL; + struct pike_string *pwd = NULL; + + get_args(sp-args, args, "%S%S%S%S", &server, &database, &user, &pwd); + + /* + * NOTE: + * + * database argument is ignored + */ + + if (!pwd) { + pwd = make_shared_string(""); + } + if (!user) { + user = make_shared_string(""); + } + if (!server) { + server = make_shared_string(""); + } + odbc_check_error("odbc->create", "Connect failed", + SQLConnect(PIKE_ODBC->hdbc, server->str, server->len, + user->str, user->len, pwd->str, pwd->len), NULL); + pop_n_elems(args); +} + +static void f_affected_rows(INT32 args) +{ + pop_n_elems(args); + push_int(PIKE_ODBC->affected_rows); +} + +static void f_insert_id(INT32 args) +{ + /**********************************************/ +} + +static void f_select_db(INT32 args) +{ + /**********************************************/ +} + +static void f_big_query(INT32 args) +{ + HSTMT hstmt = SQL_NULL_HSTMT; + struct pike_string *q = NULL; + + get_all_args("odbc->big_query", args, "%S", &q); + + odbc_check_error("odbc->big_query", "Statement allocation failed", + SQLAllocStmt(PIKE_ODBC->hdbc, &hstmt), NULL); + odbc_check_error("odbc->big_query", "Query failed", + SQLExecDirect(hstmt, q->str, q->len), NULL); + pop_n_elems(args); + + odbc_check_error("odbc->big_query", "Couldn't get the number of fields", + SQLNumResultCols(hstmt, &(PIKE_ODBC->num_fields)), NULL); + + odbc_check_error("odbc->big_query", "Couldn't get the number of rows", + SQLRowCount(hstmt, &(PIKE_ODBC->affected_rows)), NULL); + + pop_n_elems(args); + + if (PIKE_ODBC->num_fields) { + PIKE_ODBC->hstmt=hstmt; + push_object(fp->current_object); + fp->current_object->refs++; + + push_object(clone(odbc_result_program, 1)); + } else { + odbc_check_error("odbc->big_query", "Couldn't commit query", + SQLTransact(PIKE_ODBC->henv, PIKE_ODBC->hdbc, + SQL_COMMIT), NULL); + push_int(0); + } +} + +static void f_create_db(INT32 args) +{ + /**************************************************/ +} + +static void f_drop_db(INT32 args) +{ + /**************************************************/ +} + +static void f_shutdown(INT32 args) +{ + /**************************************************/ +} + +static void f_reload(INT32 args) +{ + /**************************************************/ +} + + + +/* + * Module linkage + */ + +void pike_module_init(void) +{ +#ifdef HAVE_ODBC + start_new_program(); + add_storage(sizeof(struct precompiled_odbc)); + + add_function("error", f_error, "function(void:int|string)", ID_PUBLIC); + add_function("create", f_create, "function(string|void, string|void, string|void, string|void:void)", ID_PUBLIC); + add_function("affected_rows", f_affected_rows, "function(void:int)", ID_PUBLIC); + add_function("insert_id", f_insert_id, "function(void:int)", ID_PUBLIC); + add_function("select_db", f_select_db, "function(string:void)", ID_PUBLIC); + add_function("big_query", f_big_query, "function(string:int|object)", ID_PUBLIC); + add_function("create_db", f_create_db, "function(string:void)", ID_PUBLIC); + add_function("drop_db", f_drop_db, "function(string:void)", ID_PUBLIC); + add_function("shutdown", f_shutdown, "function(void:void)", ID_PUBLIC); + add_function("reload", f_reload, "function(void:void)", ID_PUBLIC); +#if 0 + add_function("statistics", f_statistics, "function(void:string)", ID_PUBLIC); + add_function("server_info", f_server_info, "function(void:string)", ID_PUBLIC); + add_function("host_info", f_host_info, "function(void:string)", ID_PUBLIC); + add_function("protocol_info", f_protocol_info, "function(void:int)", ID_PUBLIC); + add_function("list_dbs", f_list_dbs, "function(void|string:object)", ID_PUBLIC); + add_function("list_tables", f_list_tables, "function(void|string:object)", ID_PUBLIC); + add_function("list_fields", f_list_fields, "function(string, void|string:array(int|mapping(string:mixed)))", ID_PUBLIC); + add_function("list_processes", f_list_processes, "function(void|string:object)", ID_PUBLIC); + + add_function("binary_data", f_binary_data, "function(void:int)", ID_PUBLIC); +#endif /* 0 */ + + set_init_callback(init_odbc_struct); + set_exit_callback(exit_odbc_struct); + + odbc_program = end_program(); + add_program_constant("odbc", odbc_program, 0); + + init_odbc_res_programs(); + +#endif /* HAVE_ODBC */ +} + +void pike_module_exit(void) +{ +#ifdef HAVE_ODBC + exit_odbc_res(); + + if (odbc_program) { + free_program(odbc_program); + odbc_program = NULL; + } +#endif /* HAVE_ODBC */ +} + diff --git a/src/modules/Odbc/odbc_result.c b/src/modules/Odbc/odbc_result.c new file mode 100644 index 0000000000000000000000000000000000000000..545e30b9aaee59d64b5a1f3e4c71a7e50e3c486b --- /dev/null +++ b/src/modules/Odbc/odbc_result.c @@ -0,0 +1,428 @@ +/* + * $Id: odbc_result.c,v 1.1 1997/03/10 19:01:52 grubba Exp $ + * + * Pike interface to ODBC compliant databases + * + * Henrik Grubbstr�m + */ + +/* + * Includes + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ODBC + +#include "global.h" +RCSID("$Id: odbc_result.c,v 1.1 1997/03/10 19:01:52 grubba Exp $"); + +#include "interpret.h" +#include "object.h" +#include "threads.h" +#include "svalue.h" +#include "stralloc.h" +#include "mapping.h" +#include "array.h" +#include "multiset.h" +#include "program.h" + +#include "precompiled_odbc.h" + +/* + * Globals + */ + +struct program *odbc_result_program = NULL; + +/* + * Functions + */ + +/* + * Help functions + */ + +static void clean_sql_res(void) +{ + if (PIKE_ODBC_RES->field_info) { + free(PIKE_ODBC_RES->field_info); + PIKE_ODBC_RES->field_info = NULL; + } + if (PIKE_ODBC_RES->fields) { + free_array(PIKE_ODBC_RES->fields); + PIKE_ODBC_RES->fields = NULL; + } + if (PIKE_ODBC_RES->obj) { + free_object(PIKE_ODBC_RES->obj); + PIKE_ODBC_RES->obj = NULL; + PIKE_ODBC_RES->odbc = NULL; + } + PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT; +} + +static INLINE volatile void odbc_check_error(const char *fun, const char *msg, + RETCODE code, void (*clean)(void)) +{ + if ((code != SQL_SUCCESS) && (code != SQL_SUCCESS_WITH_INFO)) { + odbc_error(fun, msg, PIKE_ODBC_RES->odbc, PIKE_ODBC_RES->hstmt, + code, clean); + } +} + +/* + * State maintenance + */ + +static void init_res_struct(struct object *o) +{ + memset(PIKE_ODBC_RES, 0, sizeof(struct precompiled_odbc_result)); + PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT; +} + +static void exit_res_struct(struct object *o) +{ + if (PIKE_ODBC_RES->hstmt != SQL_NULL_HSTMT) { + HSTMT hstmt = PIKE_ODBC_RES->hstmt; + PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT; + odbc_check_error("exit_res_struct", "Freeing of HSTMT failed", + SQLFreeStmt(hstmt, SQL_DROP), clean_sql_res); + } + clean_sql_res(); +} + +/* + * More help functions + */ + +static void odbc_fix_fields(void) +{ + int i; + struct odbc_field_info { + int type; /* Pike-type */ + int size; + } *odbc_fields = alloca(sizeof(struct odbc_field_info)*PIKE_ODBC_RES->num_fields); + size_t buf_size = 1024; + char *buf = alloca(buf_size); + int membuf_size = 0; + char *membuf = NULL; + + if ((!buf)||(!odbc_fields)) { + error("odbc_fix_fields(): Out of memory\n"); + } + + /* + * First build the fields-array; + */ + for (i=0; i < PIKE_ODBC_RES->num_fields; i++) { + int nbits; + SWORD name_len; + SWORD sql_type; + UDWORD precision; + SWORD scale; + SWORD nullable; + + while (1) { + odbc_check_error("odbc_fix_fields", "Failed to fetch field info", + SQLDescribeCol(PIKE_ODBC_RES->hstmt, i+1, + buf, buf_size, &name_len, + &sql_type, &precision, &scale, &nullable), + 0); + if (name_len < (int)buf_size) { + break; + } + do { + buf_size *= 2; + } while (name_len >= (int)buf_size); + if (!(buf = alloca(buf_size))) { + error("odbc_fix_fields(): Out of memory\n"); + } + } + odbc_fields[i].type = T_STRING; + odbc_fields[i].size = precision+1; + /* Create the mapping */ + push_text("name"); push_string(make_shared_binary_string(buf, name_len)); + push_text("type"); + switch(sql_type) { + case SQL_CHAR: + push_text("char"); + break; + case SQL_DATE: + push_text("date"); + odbc_fields[i].size = 7; + break; + case SQL_DECIMAL: + push_text("decimal"); + odbc_fields[i].size += 2; + break; + case SQL_DOUBLE: + push_text("double"); + odbc_fields[i].size = 8; + odbc_fields[i].type = T_FLOAT; + break; + case SQL_INTEGER: + push_text("integer"); + odbc_fields[i].size = 4; + odbc_fields[i].type = T_INT; + break; + case SQL_LONGVARBINARY: + push_text("long blob"); + break; + case SQL_LONGVARCHAR: + push_text("var string"); + break; + case SQL_REAL: + push_text("float"); + odbc_fields[i].size = 4; + odbc_fields[i].type = T_FLOAT; + break; + case SQL_SMALLINT: + push_text("short"); + odbc_fields[i].size = 4; + odbc_fields[i].type = T_INT; + break; + case SQL_TIMESTAMP: + push_text("time"); + odbc_fields[i].size = 22 + scale; + break; + case SQL_VARCHAR: + push_text("var string"); + break; + default: + push_text("unknown"); + break; + } + push_text("length"); push_int(precision); + push_text("decimals"); push_int(scale); + push_text("flags"); + nbits = 0; + if (nullable == SQL_NULLABLE) { + nbits++; + push_text("nullable"); + } + if ((sql_type == SQL_LONGVARCHAR) || + (sql_type == SQL_LONGVARBINARY)) { + nbits++; + push_text("blob"); + } + f_aggregate_multiset(nbits); + + f_aggregate_mapping(5*2); + + /* Align to long-word size */ + odbc_fields[i].size += 3; + odbc_fields[i].size &= ~3; + + membuf_size += odbc_fields[i].size; + } + f_aggregate_array(PIKE_ODBC_RES->num_fields); + + sp[-1].u.array->refs++; + PIKE_ODBC_RES->fields = sp[-1].u.array; + pop_stack(); + + PIKE_ODBC_RES->field_info = (struct field_info *) + xalloc(sizeof(struct field_info) * PIKE_ODBC_RES->num_fields + + membuf_size); + membuf = ((char *) PIKE_ODBC_RES->field_info) + + sizeof(struct field_info) * PIKE_ODBC_RES->num_fields; + + /* + * Now it's time to bind the columns + */ + for (i=0; i < PIKE_ODBC_RES->num_fields; i++) { + PIKE_ODBC_RES->field_info[i].type = odbc_fields[i].type; + PIKE_ODBC_RES->field_info[i].buf = membuf; + PIKE_ODBC_RES->field_info[i].size = odbc_fields[i].size; + + switch(odbc_fields[i].type) { + case T_STRING: + odbc_check_error("odbc_fix_fields", "Couldn't bind string field", + SQLBindCol(PIKE_ODBC_RES->hstmt, i+1, + SQL_C_CHAR, membuf, odbc_fields[i].size, + &PIKE_ODBC_RES->field_info[i].len), NULL); + break; + case T_INT: + odbc_check_error("odbc_fix_fields", "Couldn't bind integer field", + SQLBindCol(PIKE_ODBC_RES->hstmt, i+1, + SQL_C_SLONG, membuf, odbc_fields[i].size, + &PIKE_ODBC_RES->field_info[i].len), NULL); + break; + case T_FLOAT: + odbc_check_error("odbc_fix_fields", "Couldn't bind float field", + SQLBindCol(PIKE_ODBC_RES->hstmt, i+1, + SQL_C_DOUBLE, membuf, odbc_fields[i].size, + &PIKE_ODBC_RES->field_info[i].len), NULL); + break; + default: + error("odbc_fix_fields(): Internal error: Unhandled type:%d\n", + odbc_fields[i].type); + break; + } + membuf += odbc_fields[i].size; + } +} + +/* + * Methods + */ + +/* void create(object(odbc)) */ +static void f_create(INT32 args) +{ + if (!args) { + error("Too few arguments to odbc_result()\n"); + } + if ((sp[-args].type != T_OBJECT) || + (sp[-args].u.object->prog != odbc_program)) { + error("Bad argument 1 to odbc_result()\n"); + } + + PIKE_ODBC_RES->obj = sp[-args].u.object; + PIKE_ODBC_RES->obj->refs++; + PIKE_ODBC_RES->odbc = ((struct precompiled_odbc *)sp[-args].u.object->storage); + PIKE_ODBC_RES->hstmt = PIKE_ODBC_RES->odbc->hstmt; + PIKE_ODBC_RES->odbc->hstmt = SQL_NULL_HSTMT; + + pop_n_elems(args); + + if (PIKE_ODBC_RES->hstmt == SQL_NULL_HSTMT) { + free_object(PIKE_ODBC_RES->obj); + PIKE_ODBC_RES->odbc = NULL; + PIKE_ODBC_RES->obj = NULL; + error("odbc_result(): No result to clone\n"); + } + PIKE_ODBC_RES->num_fields = PIKE_ODBC_RES->odbc->num_fields; + PIKE_ODBC_RES->num_rows = PIKE_ODBC_RES->odbc->affected_rows; + + odbc_fix_fields(); +} + +/* int num_rows() */ +static void f_num_rows(INT32 args) +{ + pop_n_elems(args); + push_int(PIKE_ODBC_RES->num_rows); +} + +/* int num_fields() */ +static void f_num_fields(INT32 args) +{ + pop_n_elems(args); + push_int(PIKE_ODBC_RES->num_fields); +} + +/* array(int|mapping(string:mixed)) fetch_fields() */ +static void f_fetch_fields(INT32 args) +{ + pop_n_elems(args); + + PIKE_ODBC_RES->fields->refs++; + push_array(PIKE_ODBC_RES->fields); +} + +/* int|array(string|float|int) fetch_row() */ +static void f_fetch_row(INT32 args) +{ + int i; + RETCODE code; + + pop_n_elems(args); + + code = SQLFetch(PIKE_ODBC_RES->hstmt); + + if (code == SQL_NO_DATA_FOUND) { + /* No rows left in result */ + push_int(0); + } else { + odbc_check_error("odbc->fetch_row", "Couldn't fetch row", + code, NULL); + + for (i=0; i < PIKE_ODBC_RES->num_fields; i++) { + if (PIKE_ODBC_RES->field_info[i].len != SQL_NULL_DATA) { + switch (PIKE_ODBC_RES->field_info[i].type) { + case T_INT: + push_int(*((int *)PIKE_ODBC_RES->field_info[i].buf)); + break; + case T_FLOAT: + push_float(*((double *)PIKE_ODBC_RES->field_info[i].buf)); + break; + case T_STRING: + push_string(make_shared_binary_string(PIKE_ODBC_RES->field_info[i].buf, + PIKE_ODBC_RES->field_info[i].len)); + break; + default: + error("odbc->fetch_row(): Internal error: Unknown type (%d)\n", + PIKE_ODBC_RES->field_info[i].type); + break; + } + } else { + /* NULL */ + push_int(0); + } + } + f_aggregate(PIKE_ODBC_RES->num_fields); + } +} + + + +/* + * Module linkage + */ + +void init_odbc_res_programs(void) +{ + /* + * start_new_program(); + * + * add_storage(); + * + * add_function(); + * add_function(); + * ... + * + * set_init_callback(); + * set_exit_callback(); + * + * program = end_c_program(); + * program->refs++; + * + */ + + start_new_program(); + add_storage(sizeof(struct precompiled_odbc_result)); + + add_function("create", f_create, "function(object:void)", ID_PUBLIC); + add_function("num_rows", f_num_rows, "function(void:int)", ID_PUBLIC); + add_function("num_fields", f_num_fields, "function(void:int)", ID_PUBLIC); +#ifdef SUPPORT_FIELD_SEEK + add_function("field_seek", f_field_seek, "function(int:void)", ID_PUBLIC); +#endif /* SUPPORT_FIELD_SEEK */ + add_function("eof", f_eof, "function(void:int)", ID_PUBLIC); +#ifdef SUPPORT_FIELD_SEEK + add_function("fetch_field", f_fetch_field, "function(void:int|mapping(string:mixed))", ID_PUBLIC); +#endif /* SUPPORT_FIELD_SEEK */ + add_function("fetch_fields", f_fetch_fields, "function(void:array(int|mapping(string:mixed)))", ID_PUBLIC); + add_function("seek", f_seek, "function(int:void)", ID_PUBLIC); + add_function("fetch_row", f_fetch_row, "function(void:int|array(string|int|float))", ID_PUBLIC); + + set_init_callback(init_res_struct); + set_exit_callback(exit_res_struct); + + odbc_result_program = end_program(); + add_program_constant("odbc_result",odbc_result_program, 0); +} + +void exit_odbc_res(void) +{ + if (odbc_result_program) { + free_program(odbc_result_program); + odbc_result_program = NULL; + } +} + +#endif /* HAVE_ODBC */ + diff --git a/src/modules/Odbc/precompiled_odbc.h b/src/modules/Odbc/precompiled_odbc.h new file mode 100644 index 0000000000000000000000000000000000000000..4dda0ed30dd9b4390531830f266b1acdda70d4ec --- /dev/null +++ b/src/modules/Odbc/precompiled_odbc.h @@ -0,0 +1,79 @@ +/* + * $Id: precompiled_odbc.h,v 1.1 1997/03/10 19:01:54 grubba Exp $ + * + * Pike interface to ODBC compliant databases. + * + * Henrik Grubbstr�m + */ + +#ifndef PIKE_PRECOMPILED_ODBC_H +#define PIKE_PRECOMPILED_ODBC_H + +/* + * Includes + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <qeodbc.h> +#ifdef HAVE_SQL_H +#include <sql.h> +#endif /* HAVE_SQL_H */ +#ifdef HAVE_SQLEXT_H +#include <sqlext.h> +#endif /* HAVE_SQLEXT_H */ + +/* + * Globals + */ + +extern struct program *odbc_program; +extern struct program *odbc_result_program; + +/* + * Structures + */ + +struct field_info { + int type; /* Pike-type */ + SDWORD len; + int size; /* Size of buffer */ + void *buf; +}; + +struct precompiled_odbc { + HENV henv; + HDBC hdbc; + HSTMT hstmt; + SDWORD num_fields; + SWORD affected_rows; + struct pike_string *last_error; +}; + +struct precompiled_odbc_result { + struct object *obj; + struct precompiled_odbc *odbc; + HSTMT hstmt; + int num_fields; + int num_rows; + struct array *fields; + struct field_info *field_info; +}; + +/* + * Defines + */ + +#define PIKE_ODBC ((struct precompiled_odbc *)(fp->current_storage)) +#define PIKE_ODBC_RES ((struct precompiled_odbc_result *)(fp->current_storage)) + +/* + * Prototypes + */ +volatile void odbc_error(const char *fun, const char *msg, + struct precompiled_odbc *odbc, HSTMT hstmt, + RETCODE code, void (*clean)(void)); + +#endif /* PIKE_PRECOMPILED_ODBC_H */