cvs: php3 /functions php3_sesam.h sesam.c
| From: | Martin Kraemer | Date: | Thu, 14 Dec 2000 21:35:22 +0000 |
| Subject: | cvs: php3 /functions php3_sesam.h sesam.c | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-3163@lists.php.net to get a copy of this message | ||
martin Thu Dec 14 13:35:22 2000 EDT
Added files:
/php3/functions php3_sesam.h sesam.c
Log:
Add a new PHP driver module for the SESAM SQL database.
It runs on the BS2000 (EBCDEIC-based) mainframes sold by Fujitsu-Siemens.
See http://its.siemens.de/lobs/its/techinf/oltp/sesam/manuals/index_en.htm
for more info about the database.
Index: php3/functions/php3_sesam.h +++ php3/functions/php3_sesam.h /* +----------------------------------------------------------------------+ | PHP HTML Embedded Scripting Language Version 3.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997-1999 PHP Development Team (See Credits file) | +----------------------------------------------------------------------+ | This program is free software; you can redistribute it and/or modify | | it under the terms of one of the following licenses: | | | | A) the GNU General Public License as published by the Free Software | | Foundation; either version 2 of the License, or (at your option) | | any later version. | | | | B) the PHP License as published by the PHP Development Team and | | included in the distribution in the file: LICENSE | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of both licenses referred to here. | | If you did not, or have any questions about PHP licensing, please | | contact core@php.net. | +----------------------------------------------------------------------+ | Authors: Martin Kraemer <Martin.Kraemer@MchP.Siemens.De> | +----------------------------------------------------------------------+ */ /* $Id: php3_sesam.h,v 1.1 2000/12/14 21:35:21 martin Exp $ */ #ifndef _SESAM_H #define _SESAM_H #if HAVE_SESAM extern php3_module_entry sesam_module_entry; #define sesam_module_ptr &sesam_module_entry extern void php3_sesam_connect(INTERNAL_FUNCTION_PARAMETERS); extern void php3_info_sesam(void); #else #define sesam_module_ptr NULL #endif #endif /* _SESAM_H */ Index: php3/functions/sesam.c +++ php3/functions/sesam.c /* +----------------------------------------------------------------------+ | PHP HTML Embedded Scripting Language Version 3.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2000 PHP Development Team (See Credits file) | +----------------------------------------------------------------------+ | This program is free software; you can redistribute it and/or modify | | it under the terms of one of the following licenses: | | | | A) the GNU General Public License as published by the Free Software | | Foundation; either version 2 of the License, or (at your option) | | any later version. | | | | B) the PHP License as published by the PHP Development Team and | | included in the distribution in the file: LICENSE | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of both licenses referred to here. | | If you did not, or have any questions about PHP licensing, please | | contact core@php.net. | +----------------------------------------------------------------------+ | Authors: Martin Kraemer <Martin.Kraemer@MchP.Siemens.De> | | | +----------------------------------------------------------------------+ */ #define PHP_SESAM_VERSION "V1.0A00" /* $Id: sesam.c,v 1.1 2000/12/14 21:35:21 martin Exp $ */ #include <stdlib.h> #include <ctype.h> #include <stdio.h> #ifdef THREAD_SAFE #include "tls.h" #endif #include "php.h" #ifndef MSVC5 #include "build-defs.h" #endif #include "internal_functions.h" #if PHP_31 #include "ext/standard/php3_string.h" #else #include "functions/php3_string.h" #endif #include "php3_sesam.h" #if APACHE # ifndef DEBUG # undef palloc # endif #endif #if HAVE_SESAM #include "sqlsci.h" #undef DEBUG_SESAM #define DEBUG_SESAM #undef AVOID_DANGEROUS #define AVOID_DANGEROUS /* sesam.c */ static SQLSTATE SESAM_PrepareCursor(const char *stmt); static SQLSTATE SESAM_OpenCursor(void); static SQLSTATE SESAM_DescribeOutput(const char *descr_name); static SQLSTATE SESAM_FetchCursor(const char *descr_name); static SQLSTATE SESAM_CloseCursor(void); static SQLSTATE SESAM_PrepareScrollCursor(const char *stmt); static SQLSTATE SESAM_OpenScrollCursor(void); static SQLSTATE SESAM_DescribeScrollOutput(const char *descr_name); static SQLSTATE FetchCursorAutoScroll(const char *result_id); static SQLSTATE SESAM_CloseScrollCursor(void); #ifdef DEBUG_SESAM static const char *SESAM_SeekType(SQL_Fetch_Orientation fetch_orient); #endif static const char *SESAM_TypeName(SQL_Type val); static const char none[] = "<none>"; static const char str_none[] = "\"*NONE\""; static const char noresult[] = "NoResult"; #define RESULT_ID_FMT "ResultDescr_pid%u" #define SCROLLABLE_ID_FMT "ScrollDescr_pid%u" /* This structure defines the handler functions which are differnt for scrolled * and non-scrolled cursors. Depending on the type of query, one of the * two versions is used until end-of-result. */ typedef struct { SQLSTATE (*Prepare)(const char *stmt); SQLSTATE (*Open)(void); SQLSTATE (*DescribeOutput)(const char *result_id); SQLSTATE (*Fetch)(const char *result_id); SQLSTATE (*Close)(void); } SESAM_CURSOR_FUNCTIONS; static const SESAM_CURSOR_FUNCTIONS scrollable_functions = { /* The functions for scrollable cursors: */ SESAM_PrepareScrollCursor, SESAM_OpenScrollCursor, SESAM_DescribeScrollOutput, FetchCursorAutoScroll, SESAM_CloseScrollCursor }, nonscrollable_functions = { /* The functions for non-scrollable cursors: */ SESAM_PrepareCursor, SESAM_OpenCursor, SESAM_DescribeOutput, SESAM_FetchCursor, SESAM_CloseCursor }; static struct { int bs2000_initialized; /* nonzero if the bs2000 /ADD-MESSAGE etc has been done */ int sesam_configured; /* nonzero if the user used one php3_sesam_ config directive */ const SESAM_CURSOR_FUNCTIONS *Cursor; /* points to (non)?scrollable_functions */ char *Descriptor; long ScrollPosition; SQL_Fetch_Orientation ScrollOrigin; /* FETCH_{NEXT,PRIOR,FIRST,LAST,ABSOLUTE,RELATIVE} */ int ResultCount; struct { int valid; union { char bytes[5+1]; SQLSTATE sqlerr; } errorcode; int sqlcode; char message[256+1]; int errlin; int errcol; int row_count; } last_error; struct { SQL_Type type; int repetitions; int length; int precision; int scale; char name[128+1]; /* Spaltenname */ pval val; } *MetaInfo; sigset_t sesam_blocked_signals; sigset_t sesam_current_signals; #ifdef DEBUG_SESAM int DebugActive; #endif } RequestLocal; #ifdef DEBUG_SESAM #define CHECK(name,args,addstr) check(#name, name args, addstr) static int check(const char *func, SQLSTATE err, const char *parm); #else #define CHECK(name,args,addstr) name args #endif /*********************************************** * Set of "signal-proof" wrapped functions for * the sqlsci interface. Because the sqlsci * functions would not correctly deal with * SIGALRM or SIGUSR1 (or any other) signals, * we have to protect them with a SIG_BLOCK * until they return to our context. * NOTE: when sqlsci adds new functionality, * it must be added here! **********************************************/ #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_Connect (const char *connection_id) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Connect, (connection_id), connection_id); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Disconnect (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Disconnect, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_InitTask (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_InitTask, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_SetAuthorization (const char *user_id) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetAuthorization, (user_id), user_id); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetCatalog (const char *catalog) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetCatalog, (catalog), catalog); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetSchema (const char *schema) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetSchema, (schema), schema); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetTransaction (int isolation_level, int read_only) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "isol_lvl=%d, rdonly=%d", isolation_level, read_only); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetTransaction, (isolation_level, read_only), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Commit (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Commit, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Rollback (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Rollback, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_ExecImm (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_ExecImm, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_Prepare (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Prepare, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Exec (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Exec, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_PrepareCursor (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_PrepareCursor, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_OpenCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_OpenCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_FetchCursor (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_FetchCursor, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_CloseCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_CloseCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_StoreCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_StoreCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_RestoreCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_RestoreCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_PrepareScrollCursor (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_PrepareScrollCursor, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_OpenScrollCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_OpenScrollCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DescribeScrollOutput (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DescribeScrollOutput, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_FetchScrollCursor (const char *descr_name, SQL_Fetch_Orientation fetch_orient, int cu_position) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "%s, %s, %d", descr_name, SESAM_SeekType(fetch_orient), cu_position); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_FetchScrollCursor, (descr_name, fetch_orient, cu_position), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_CloseScrollCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_CloseScrollCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_AllocDescriptor (const char *descr_name, int count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_AllocDescriptor, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DeallocDescriptor (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DeallocDescriptor, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DescribeOutput (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DescribeOutput, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetCount (const char *descr_name, int *count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetCount, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_SetCount (const char *descr_name, int count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetCount, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_SetType (const char *descr_name, int index, int repetitions, SQL_Type type, int length, int precision, int scale) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "%s, idx=%d, rep=%d, type=%s, " "len=%d, prec=%d, scl=%d", descr_name, index, repetitions, SESAM_TypeName(type), length, precision, scale); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetType, (descr_name,index,repetitions,type,length,precision,scale), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetType (const char *descr_name, int index, int *repetitions, SQL_Type *type, int *length, int *precision, int *scale) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetType, (descr_name,index,repetitions,type,length,precision,scale), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetName (const char *descr_name, int index, char *name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetName, (descr_name,index,name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetData (const char *descr_name, int index, SQL_Type type, SQL_Data *data, int *indicator, int *lengthOfValue /* darf NULL sein, wenn keine Laenge gewuenscht */ ) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetData , (descr_name,index,type,data,indicator,lengthOfValue), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static void SESAM_Diagnostic (SQLSTATE *sqlstate, int *sqlcode, char *message, int *errlin, int *errcol, int *row_count) { sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); SQL_Diagnostic(sqlstate,sqlcode,message,errlin,errcol,row_count); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); } #ifdef INCLUDE_UNUSED_FUNCTIONS static void SESAM_Property (int property_type, SQL_Data *property_info) { sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); SQL_Property(property_type, property_info); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); } #endif #ifdef DEBUG_SESAM static const char * SESAM_SeekType(SQL_Fetch_Orientation fetch_orient) { switch(fetch_orient) { case FETCH_NEXT: return "FETCH_NEXT"; case FETCH_PRIOR: return "FETCH_PRIOR"; case FETCH_FIRST: return "FETCH_FIRST"; case FETCH_LAST: return "FETCH_LAST"; case FETCH_ABSOLUTE: return "FETCH_ABSOLUTE"; case FETCH_RELATIVE: return "FETCH_RELATIVE"; default: return "*Invalid*"; } } #endif static const char * SESAM_TypeName(SQL_Type val) { static char errstr[128]; switch (val) { case TYPE_CHARACTER: return "CHARACTER"; case TYPE_NUMERIC: return "NUMERIC"; case TYPE_DECIMAL: return "DECIMAL"; case TYPE_INTEGER: return "INTEGER"; case TYPE_SMALLINT: return "SMALLINT"; case TYPE_FLOAT: return "FLOAT"; case TYPE_REAL: return "REAL"; case TYPE_DOUBLE: return "DOUBLE"; case TYPE_DATE: return "DATE"; case TYPE_TIME: return "TIME"; case TYPE_TIMESTAMP: return "TIMESTAMP"; case TYPE_VARCHAR: return "VARCHAR"; default: sprintf(errstr, "Invalid Type %d", val); return errstr; } } #define RESULT_NONE 1 #define RESULT_SEQUENTIAL 2 #define RESULT_SCROLLABLE 4 static int ValidResultId(const char *result_id) { int scrollable = 0, sequential = 0, none = 0; if (result_id != NULL) { if ((none = RESULT_NONE *(strcmp (result_id, noresult) == 0)) || (sequential = RESULT_SEQUENTIAL*(strncmp(result_id, RESULT_ID_FMT, sizeof(RESULT_ID_FMT)-3) == 0)) || (scrollable = RESULT_SCROLLABLE*(strncmp(result_id, SCROLLABLE_ID_FMT, sizeof(SCROLLABLE_ID_FMT)-3) == 0))) { return none | sequential | scrollable; } } return 0; } static SQL_Type TypeConvert(SQL_Type in) { switch (in) { case TYPE_SMALLINT: case TYPE_INTEGER: return TYPE_INTEGER; case TYPE_NUMERIC: case TYPE_DECIMAL: case TYPE_FLOAT: case TYPE_REAL: case TYPE_DOUBLE: return TYPE_DOUBLE; default: /* Error?!?!!?*/ /* Handle dates/times */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: return in; case TYPE_VARCHAR: case TYPE_CHARACTER: return in; } } static int SensibleColumnName(const char *name) { return (name != NULL && strlen(name) > 0 && strcmp(name, "count") != 0 && strcmp(name, "type") != 0 && strcmp(name, "sqltype") != 0 && strcmp(name, "length") != 0 && strcmp(name, "repetitions") != 0 && strcmp(name, "precision") != 0 && strcmp(name, "scale") != 0); } static void SetLastErrorTo(const char *sqlerr, int sqlcode, const char *message, int errlin, int errcol, int row_count) { strncpy(RequestLocal.last_error.errorcode.bytes, sqlerr, sizeof RequestLocal.last_error.errorcode.bytes); RequestLocal.last_error.sqlcode = sqlcode; strncpy(RequestLocal.last_error.message, message, sizeof RequestLocal.last_error.message); RequestLocal.last_error.errlin = errlin; RequestLocal.last_error.errcol = errcol; RequestLocal.last_error.row_count = row_count; RequestLocal.last_error.errorcode.bytes[5] = '\0'; /* 5 character string */ RequestLocal.last_error.valid = 1; } static void SetLastError(void) { SESAM_Diagnostic (&RequestLocal.last_error.errorcode.sqlerr, &RequestLocal.last_error.sqlcode, &RequestLocal.last_error.message[0], &RequestLocal.last_error.errlin, &RequestLocal.last_error.errcol, &RequestLocal.last_error.row_count); RequestLocal.last_error.errorcode.bytes[5] = '\0'; /* 5 character string */ RequestLocal.last_error.valid = 1; } static SQLSTATE FetchCursorAutoScroll(const char *result_id) { SQLSTATE err; err = SESAM_FetchScrollCursor(result_id, RequestLocal.ScrollOrigin, RequestLocal.ScrollPosition); if (SQL_SUCCESS(err)) { /* Auto-increment absolute position */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; else if (RequestLocal.ScrollOrigin == FETCH_LAST) RequestLocal.ScrollOrigin = FETCH_PRIOR; else if (RequestLocal.ScrollOrigin == FETCH_FIRST) RequestLocal.ScrollOrigin = FETCH_NEXT; } return err; } static void Free_Query_Vars() { /* Call either CloseScrollCursor() or CloseCursor() */ if (RequestLocal.Cursor != NULL) { RequestLocal.Cursor->Close(); RequestLocal.Cursor = NULL; } /* If a descriptor is still open, free it. */ if (RequestLocal.Descriptor != NULL) { if (strcmp(RequestLocal.Descriptor, noresult) != 0) (void)SESAM_DeallocDescriptor(RequestLocal.Descriptor); efree(RequestLocal.Descriptor); RequestLocal.Descriptor = NULL; } /* If MetaInfo is still allocated, free it */ if (RequestLocal.MetaInfo != NULL) { efree(RequestLocal.MetaInfo); RequestLocal.MetaInfo = NULL; } } #ifdef DEBUG_SESAM static int check(const char *func, SQLSTATE err, const char *parm) { if (parm == NULL) parm = ""; if (RequestLocal.DebugActive) if (!SQL_SUCCESS(err)) { SetLastError(); php3_printf("%-20s(%s): Error SEW%-5.5s: %s<BR>\n", func, parm, (char *)&err, RequestLocal.last_error.message); return 0; } else php3_printf("%-20s(%s): Okay<BR>\n", func, parm); return (SQL_SUCCESS(err) != 0); } #endif #ifdef DEBUG_SESAM /* {{{ proto boolean sesam_debug(int on_off) Set global debugging flag to on/off */ static PHP_FUNCTION(sesam_debug) { pval *on_off; TLS_VARS; if (ARG_COUNT(ht) != 1) { WRONG_PARAM_COUNT; } if (getParameters(ht, 1, &on_off) == FAILURE) { php3_error(E_ERROR, "Invalid argument in sesam_debug(on_off)"); RETURN_FALSE; } convert_to_long(on_off); RequestLocal.DebugActive = on_off->value.lval; RETURN_TRUE; } /* }}} */ #endif /* {{{ proto boolean sesam_connect(string database, string schema, string user) Build connection to SESAM database, and identify the Catalog, Schema and User to use. */ static PHP_FUNCTION(sesam_connect) { pval *catalog; pval *schema; pval *user; SQLSTATE err; TLS_VARS; if (!RequestLocal.bs2000_initialized) { php3_error(E_ERROR, "SESAM has not been configured."); RETURN_FALSE; } RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 3) { WRONG_PARAM_COUNT; } if (getParameters(ht, 3, &catalog, &schema, &user) == FAILURE) { php3_error(E_ERROR, "Invalid arguments in sesam_connect()"); RETURN_FALSE; } /* Catalog: */ convert_to_string(catalog); if (catalog->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>catalog</b> argument in sesam_connect()"); RETURN_FALSE; } /* Schema: */ convert_to_string(schema); if (schema->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>schema</b> argument in sesam_connect()"); RETURN_FALSE; } /* User: */ convert_to_string(user); if (user->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>user</b> argument in sesam_connect()"); RETURN_FALSE; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); SESAM_Rollback(); err = SESAM_SetCatalog(catalog->value.str.val); if (SQL_SUCCESS(err)) err = SESAM_SetSchema(schema->value.str.val); if (SQL_SUCCESS(err)) err = SESAM_SetAuthorization(user->value.str.val); SetLastError(); /* Force transaction start */ SESAM_Commit(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto boolean sesam_disconnect() Detach from SESAM connection */ static PHP_FUNCTION(sesam_disconnect) { TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); /* Discard any uncommitted transactions. */ SESAM_Rollback(); /* Forget any static settings in the holder task. */ SESAM_SetCatalog(str_none); SESAM_SetSchema(str_none); SESAM_SetAuthorization(str_none); SESAM_Commit(); RETURN_TRUE; } /* }}} */ /* {{{ proto boolean sesam_settransaction(int isolation_level, int read_only) Set SESAM transaction parameters where isolation_level is: 1 == READ UNCOMMITTED 2 == READ COMMITTED 3 == REPEATABLE READ 4 == SERIALIZABLE and read_only is: 0 == READ WRITE 1 == READ ONLY */ static PHP_FUNCTION(sesam_settransaction) { pval *isolation_level, *read_only; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &isolation_level, &read_only) == FAILURE) { WRONG_PARAM_COUNT; } /* Schema_Str: */ convert_to_long(isolation_level); convert_to_long(read_only); if (isolation_level->value.lval < 1 || isolation_level->value.lval > 4) { php3_error(E_ERROR, "Sesam_SetTransaction(): value of <b>isolation_level</b> argument invalid"); RETURN_FALSE; } if (read_only->value.lval < 0 || read_only->value.lval > 1) { php3_error(E_ERROR, "Sesam_SetTransaction(): value of <b>read_only</b> argument invalid"); RETURN_FALSE; } err = SESAM_SetTransaction(isolation_level->value.lval, read_only->value.lval); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto int sesam_commit() sesam_commit */ static PHP_FUNCTION(sesam_commit) { SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } err = SESAM_Commit(); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto int sesam_rollback() sesam_rollback */ static PHP_FUNCTION(sesam_rollback) { SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); err = SESAM_Rollback(); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_execimm(string stmt) Execute an "immediate" statement (i.e., a statement like UPDATE or INSERT which delivers no result, and has no INPUT or OUTPUT variables). */ static PHP_FUNCTION(sesam_execimm) { pval *stmt; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; Free_Query_Vars(); if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &stmt) == FAILURE) { WRONG_PARAM_COUNT; } /* ExecImm_str: */ convert_to_string(stmt); if (stmt->value.str.val == NULL) { php3_error(E_WARNING, "No <b>stmt</b> argument in sesam_ExecImm command"); RETURN_FALSE; } err = SESAM_ExecImm(stmt->value.str.val); SetLastError(); if (SQL_SUCCESS(err)) { RequestLocal.ResultCount = 0; /* Return Success! For symmetry with sesam_query(), return a */ /* result descriptor which is used in the subsequent calls to Fetch_Array() */ RequestLocal.Descriptor = estrdup(noresult); RETURN_STRING(noresult,1); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_query(string sql_stmt, int scrollable) Perform a SESAM ExecImmediate (or, if that fails, Perform a SESAM SQL Query) Allocate a cursor for the result set. Prepare the cursor Describe the Output Enable type conversion for individual result columns */ static PHP_FUNCTION(sesam_query) { pval *stmt; pval *scrollable_arg = NULL; char result_id[128]; int col, scrollable = 0; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; Free_Query_Vars(); if (ARG_COUNT(ht) == 1 && getParameters(ht, 1, &stmt) != FAILURE) { } else if (ARG_COUNT(ht) == 2 && getParameters(ht, 2, &stmt, &scrollable_arg) != FAILURE) { } else { WRONG_PARAM_COUNT; } /* Did the user want a scrollable cursor? */ if (scrollable_arg != NULL) { convert_to_long(scrollable_arg); scrollable = scrollable_arg->value.lval; } convert_to_string(stmt); if (stmt->value.str.val == NULL) { php3_error(E_WARNING, "Erroneous SQL statement in SESAM_Query()"); RETURN_FALSE; } err = SESAM_ExecImm(stmt->value.str.val); if (SQL_SUCCESS(err)) { SetLastError(); RequestLocal.ResultCount = 0; /* Everything done already. Return Success! */ /* The result descriptor is used in the subsequent calls to Fetch_Array() */ RETURN_STRING(noresult,1); } /* If we tried to execute a "non-immediate" statement * (e.g., a SELECT-type query), we will get the error: * E SEW42BW SQL STATEMENT TYPE CANNOT BE PREPARED * OR IS NOT PERMITTED IN APPLICATION PROGRAMS * All other errors are "real" errors and are caught here. */ else if (memcmp((void*)&err, "42SBW", 5) != 0) { SetLastError(); /* No recovery possible. Return an error. */ RETURN_FALSE; } else { /* ExecImmediate did not work. Try a real query. */ if (scrollable) { RequestLocal.Cursor = &scrollable_functions; RequestLocal.ScrollOrigin = FETCH_NEXT; RequestLocal.ScrollPosition = 0L; } else { RequestLocal.Cursor = &nonscrollable_functions; } /* Create a result_id string that will be valid * for the current result set. (depends on scrollable/nonscroll) */ snprintf(result_id, sizeof result_id, (scrollable) ? SCROLLABLE_ID_FMT : RESULT_ID_FMT, getpid()); err = RequestLocal.Cursor->Prepare(stmt->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } err = SESAM_AllocDescriptor(result_id, 600); /* 600 = recommended size */ /* @@@ This is a kludge: it points to an error in our * (or SQLSCI's) descriptor management; * If the descriptor is already allocated, free it. */ if (!SQL_SUCCESS(err) && memcmp((void*)&err, "33SA1", 5) == 0) { err = SESAM_DeallocDescriptor(result_id); err = SESAM_AllocDescriptor(result_id, 600); /* 600 = recommended size */ } if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } RequestLocal.Descriptor = estrdup(result_id); err = RequestLocal.Cursor->DescribeOutput(result_id); if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } err = SESAM_GetCount(result_id, &RequestLocal.ResultCount); if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } RequestLocal.MetaInfo = ecalloc(RequestLocal.ResultCount, sizeof(*RequestLocal.MetaInfo)); if (RequestLocal.MetaInfo == NULL) { php3_error(E_ERROR, "Out of Memory in sesam_query()"); Free_Query_Vars(); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } for (col=0; SQL_SUCCESS(err) && col < RequestLocal.ResultCount; ++col) { int newlen; SQL_Type newtype; err = SESAM_GetName(result_id, col+1, &RequestLocal.MetaInfo[col].name[0]); _php3_strtolower(RequestLocal.MetaInfo[col].name); err = SESAM_GetType(result_id, col+1, &RequestLocal.MetaInfo[col].repetitions, &RequestLocal.MetaInfo[col].type, &RequestLocal.MetaInfo[col].length, &RequestLocal.MetaInfo[col].precision, &RequestLocal.MetaInfo[col].scale); newlen = RequestLocal.MetaInfo[col].length; if ((newtype = TypeConvert(RequestLocal.MetaInfo[col].type)) == TYPE_CHARACTER) newlen = sizeof(((SQL_Data *)0)->char_value) - 1; else if (newtype == TYPE_VARCHAR) newlen = sizeof(((SQL_Data *)0)->varchar_value) - 1; if (newtype != RequestLocal.MetaInfo[col].type || newlen != RequestLocal.MetaInfo[col].length) { /* Enable SQL-Type to 'C'-Type conversion */ err = SESAM_SetType(result_id, col+1, RequestLocal.MetaInfo[col].repetitions, newtype, newlen, RequestLocal.MetaInfo[col].precision, RequestLocal.MetaInfo[col].scale); } } /* for all columns */ err = RequestLocal.Cursor->Open(); SetLastError(); if (SQL_SUCCESS(err)) { /* The result descriptor is used in the subsequent calls to Fetch_Array() */ RETURN_STRING(result_id,1); } else { /* Clean up after errors */ Free_Query_Vars(); RETURN_FALSE; } } } /* }}} */ /* sesam_query */ /* {{{ proto int sesam_num_fields(string result_id) Returns the number of fields/columns in a result set. (@@@ TODO: Multiple fields count like a single column) */ static PHP_FUNCTION(sesam_num_fields) { pval *result_id; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_num_fields() call"); RETURN_FALSE; } if (result_type == RESULT_NONE) /* For "immediate" queries, nothing is returned. */ RETURN_LONG(0); RETURN_LONG(RequestLocal.ResultCount); } /* }}} */ /* {{{ proto string sesam_field_name(string result_id, int index) Return the name of a field (i.e., the column name) in the result set. The index is zero-based. (@@@ TODO: Multiple fields count like a single column) */ static PHP_FUNCTION(sesam_field_name) { pval *result_id, *idx; SQLSTATE err; char name[128+1]; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &result_id, &idx) == FAILURE) { WRONG_PARAM_COUNT; } /* ResultId_str: */ convert_to_string(result_id); convert_to_long(idx); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_name() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* For "immediate" queries, nothing is returned. */ if (result_type == RESULT_NONE) { RETURN_STRING("",0); } err = SESAM_GetName(result_id->value.str.val, idx->value.lval+1, &name[0]); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_STRING(name, 1); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_field_data(string result_id, int index, int& notempty) Returns the contents of one cell from a result set. index is 0 ... (nCol - 1)! */ static PHP_FUNCTION(sesam_field_data) { pval *result_id, *idx, *notempty = NULL; int hasvalue = 0; /* Possibly sizeof(int) != sizeof(long) */ int lengthOfValue; int argc; SQL_Data data; SQL_Type type; SQLSTATE err; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if ((argc = ARG_COUNT(ht)) == 2 && getParameters(ht, argc, &result_id, &idx) != FAILURE) { } else if (argc == 3 && getParameters(ht, argc, &result_id, &idx, ¬empty) != FAILURE && ParameterPassedByReference(ht,3)) { } else WRONG_PARAM_COUNT; convert_to_string(result_id); convert_to_long(idx); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_data() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { /* The caller should not have called us in the first place: * we told him that the array count was zero! */ SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); RETURN_FALSE; } if (idx->value.lval >= RequestLocal.ResultCount) { php3_error(E_ERROR, "Invalid <b>Index</b> arg in sesam_field_data() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } type = TypeConvert(RequestLocal.MetaInfo[idx->value.lval].type); err = SESAM_GetData(result_id->value.str.val, idx->value.lval+1, type, &data, &hasvalue, &lengthOfValue /* ignored for now. Intended for binary VARCHARs */ ); SetLastError(); if (SQL_SUCCESS(err)) { /* Return "notempty" if passed by reference */ if (notempty != NULL) { notempty->type = IS_LONG; notempty->value.lval = hasvalue; } switch (type) { case TYPE_INTEGER: RETURN_LONG(data.exact_value); case TYPE_DOUBLE: RETURN_DOUBLE(data.approx_value); /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_CHARACTER: case TYPE_VARCHAR: RETURN_STRING(data.char_value, 1); } } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto array sesam_diagnostic() Return an associative array of status and return codes for the last sql query/stmt/command. Elements of the array are: $array["sqlstate"] := 5 digit SQL return code $array["errmsg"] := "human readable" error message string $array["errlin"] := error line number of previous error (1... or 0) $array["errcol"] := error column number of previous error (1... or 0) $array["rowcount"] := number of affected rows in last update */ static PHP_FUNCTION(sesam_diagnostic) { TLS_VARS; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } if (!RequestLocal.last_error.valid) SetLastError(); array_init(return_value); /* ret["sqlstate"] = error code string */ add_assoc_string(return_value, "sqlstate", RequestLocal.last_error.errorcode.bytes, 1); /* ret["errmsg"] = error message */ add_assoc_string(return_value, "errmsg", RequestLocal.last_error.message, 1); /* ret["errlin"] = Error Line Number in last SQL stmt */ add_assoc_long(return_value, "errlin", RequestLocal.last_error.errlin-1); /* ret["errcol"] = Error Column Number in last SQL stmt */ add_assoc_long(return_value, "errcol", RequestLocal.last_error.errcol-1); /* ret["rowcount"] = Number of affected rows in last update */ add_assoc_long(return_value, "rowcount", RequestLocal.last_error.row_count); } /* }}} */ /* {{{ proto array sesam_fetch_result(string result_id [, int max_rows]) Get all result entries: sesam_fetch_result() returns a mixed associative/indexed array of the result: $arr["rows"] = number of rows in result set $arr["cols"] = number of columns $arr["truncated"] = 1 if max_rows count exceeded $arr[col][....] = column information ( col := 0 ... $arr["cols"]-1 ) ** individual fields in table: ** $arr[col][row] = field at row(row) column(col): ( row := 0 ... $arr["rows"]-1 ) */ static PHP_FUNCTION(sesam_fetch_result) { pval *result_id, *max_rows = NULL; int result_type; int num_columns = 0, truncated = 1; int col, row, argc; SQLSTATE err; SQL_Type type; struct { SQL_Data data; pval val; char name[128+1]; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ } *pCol = NULL; TLS_VARS; RequestLocal.last_error.valid = 0; if ((argc = ARG_COUNT(ht)) == 1 && getParameters(ht, argc, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } else if (argc < 1 || argc > 2 || getParameters(ht, argc, &result_id, &max_rows) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || RequestLocal.Cursor == NULL) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_fetch_result() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* For "immediate" queries, nothing is returned. */ if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Immediate Query - no Result data", 0, 0, 0); } num_columns = RequestLocal.ResultCount; array_init(return_value); /* ret["count"] = num_columns */ add_assoc_long(return_value, "count", num_columns); /* ret["cols"] = num_columns */ add_assoc_long(return_value, "cols", num_columns); if (num_columns == 0) return; pCol = (void*) emalloc(num_columns * sizeof(*pCol)); if (pCol == NULL) { php3_error(E_ERROR, "Out of Memory in sesam_fetch_result()"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* Add associative information about the columns: * ret[col]["name"] = "fieldname of col'th field" */ for (col=0; col < num_columns; ++col) { array_init(&pCol[col].val); SESAM_GetName(result_id->value.str.val, col+1, &pCol[col].name[0]); } /* for all columns */ for (row=0; max_rows == NULL || row < max_rows->value.lval; ++row) /* for all rows */ { err = RequestLocal.Cursor->Fetch(result_id->value.str.val); /* If the fetch failed, abort immediately */ if (!SQL_NODATA(err) && !SQL_SUCCESS(err)) { SetLastError(); efree (pCol); RETURN_FALSE; } /* End-of-Result reached? */ if (SQL_NODATA(err)) { truncated = 0; break; } for (col=0; col < num_columns; ++col) { type = TypeConvert(RequestLocal.MetaInfo[col].type); /*@@*/ strcpy (pCol[col].data.char_value, none); err = SESAM_GetData(result_id->value.str.val, col+1, type, &pCol[col].data, &pCol[col].belegt, &pCol[col].lengthOfValue /* ignored for now. Intended for binary VARCHARs */ ); switch (pCol[col].belegt) { case -1: /* field has no value */ continue; case 0: /* field valid */ default: /* truncated to pCol[col].belegt columns */ break; } switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: case TYPE_VARCHAR: case TYPE_CHARACTER: add_index_string(&pCol[col].val, row, pCol[col].data.char_value, 1); break; case TYPE_DOUBLE: add_index_double(&pCol[col].val, row, pCol[col].data.approx_value); break; case TYPE_INTEGER: add_index_long(&pCol[col].val, row, pCol[col].data.exact_value); break; default: /* Error?!?!!?*/ php3_error(E_WARNING, "@@@ Not implemented: " "Invalid Result Type in sesam_fetch_result()"); break; } } /* for all columns */ } /* for all rows */ /* Finally, we have collected all the rows into temporary space. */ /* Return them as associative array */ for (col=0; col < num_columns; ++col) { /* ret["column_name"]["count"] = num_rows */ /* ret[col]["count"] = num_rows */ add_assoc_long(&pCol[col].val, "count", row); #ifndef AVOID_DANGEROUS /* ret["column_name"] = "complete row" */ if (SensibleColumnName(pCol[col].name)) _php3_hash_update(return_value->value.ht, _php3_strtolower(pCol[col].name), strlen(pCol[col].name)+1, (void *) &pCol[col].val, sizeof(pCol[col].val), NULL); #endif /* ret[col] = "complete row" */ _php3_hash_index_update(return_value->value.ht, col, (void *) &pCol[col].val, sizeof(pCol[col].val), NULL); } efree (pCol); /* ret["rows"] = number of rows in table */ add_assoc_long(return_value, "rows", row); /* ret["truncated"] = 1 if max_rows count exceeded */ add_assoc_long(return_value, "truncated", truncated); return; } /* }}} */ /* sesam_fetch_result */ /* {{{ proto array sesam_field_array(string result_id) Return Meta Information (type, precision, ...) about individual columns: sesam_field_array() returns a mixed associative/indexed array of the result: $arr["count"] = number of columns ** associative fields in column information: ** $arr[col]["name"] = column name for column(col) $arr[col]["type"] = type of column(col) => ("int" | "string" | "real") $arr[col]["sqltype"] = SQL type => ("CHARACTER" | "NUMERIC" | "DECIMAL" | "INTEGER" | "SMALLINT" | "FLOAT" | "REAL" | "DOUBLE" | "DATE" | "TIME" | "TIMESTAMP" | "VARCHAR") $arr[col]["length"] = length as configured for column $arr[col]["precision"] = precision as configured for column $arr[col]["scale"] = scale as configured for column $arr[col]["count"] = number of "multiple fields" (only valid in 1st column of multi-field) */ static PHP_FUNCTION(sesam_field_array) { pval *result_id; int result_type; int col; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_array() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ } array_init(return_value); /* ret["count"] = num_columns */ add_assoc_long(return_value, "count", RequestLocal.ResultCount); if (RequestLocal.ResultCount == 0) return; /* Add associative information about the columns: * ret[col]["name"] = "fieldname of col'th field" */ for (col=0; col < RequestLocal.ResultCount; ++col) { pval val; char name[128+1]; array_init(&val); err = SESAM_GetName(result_id->value.str.val, col+1, &name[0]); if (!SQL_SUCCESS(err)) break; /* * ret[0]["name"] = "firstcol_name" * ret[1]["name"] = "secondcol_name" etc. */ add_assoc_string(&val, "name", name, 1); add_assoc_string(&val, "sqltype", SESAM_TypeName(RequestLocal.MetaInfo[col].type), 1); add_assoc_long (&val, "length", RequestLocal.MetaInfo[col].length); add_assoc_long (&val, "count", RequestLocal.MetaInfo[col].repetitions); add_assoc_long (&val, "repetitions", RequestLocal.MetaInfo[col].repetitions); /*@@*/ add_assoc_long (&val, "precision", RequestLocal.MetaInfo[col].precision); add_assoc_long (&val, "scale", RequestLocal.MetaInfo[col].scale); switch (TypeConvert(RequestLocal.MetaInfo[col].type)) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_assoc_string(&val, "type", "string", 1 /*@@ 0? */); break; case TYPE_DOUBLE: add_assoc_string(&val, "type", "real", 1 /*@@ 0? */); break; case TYPE_INTEGER: add_assoc_string(&val, "type", "int", 1 /*@@ 0? */); break; } /* ret[col][] = "complete column info" */ _php3_hash_index_update(return_value->value.ht, col, (void *) &val, sizeof(val), NULL); #ifndef AVOID_DANGEROUS /* Add associative info only if column name makes sense */ /* ret["column_name"][] = "complete column info" */ if (SensibleColumnName(name)) _php3_hash_update(return_value->value.ht, name, strlen(name)+1, (void *) &val, sizeof(val), NULL); #endif } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* php3_sesam_field_array */ /* {{{ proto array sesam_fetch_row(string result_id [, int whence [, int offset]]) Get all result entries of one row, optionally using a scrolled cursor with given seek parameters. Whence can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... Offset specifies a relative or absolute offset (only used if whence==SESAM_SEEK_ABSOLUTE or whence==SESAM_SEEK_RELATIVE) sesam_fetch_row returns a mixed associative/indexed array of one row: ** individual fields: ** $arr[0] = first value in column $arr[cols-1] = last value in column (where cols:=$arr["count"]-1) */ static PHP_FUNCTION(sesam_fetch_row) { pval *result_id, *whence = NULL, *row_index = NULL; int result_type; int col; int valid = 0; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; switch(ARG_COUNT(ht)) { case 1: if (getParameters(ht, 1, &result_id) != FAILURE) valid = 1; break; case 2: if (getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval != (FETCH_ABSOLUTE-1) && whence->value.lval != (FETCH_RELATIVE-1)) valid = 1; /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); } break; case 3: if (getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); convert_to_long(row_index); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; valid = 1; } break; } if (whence != NULL && (whence->value.lval < 0 || whence->value.lval > FETCH_RELATIVE-1)) valid = 0; if (!valid) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || (result_type != RESULT_NONE && RequestLocal.Cursor == NULL)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> argument in sesam_fetch_row() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ RETURN_FALSE; } array_init(return_value); /* ret["count"] = num_columns */ /* Because columns may be empty (no result), the use of * count($array) would return invalid results. * Therefore, we provide the count as $array["count"], and * disapprove of the use of count($array) in the documentation. */ add_assoc_long(return_value, "count", RequestLocal.ResultCount); if (RequestLocal.ResultCount == 0) return; err = RequestLocal.Cursor->Fetch(result_id->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } /* Loop over all columns and collect the data */ for (col=0; col < RequestLocal.ResultCount; ++col) { SQL_Type type = TypeConvert(RequestLocal.MetaInfo[col].type); SQL_Data data; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ err = SESAM_GetData(result_id->value.str.val, col+1, type, &data, &belegt, &lengthOfValue); if (!SQL_SUCCESS(err)) break; switch (belegt) { case -1: /* field has no value */ continue; case 0: /* field valid */ default: /* truncated to belegt columns */ break; } switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_index_string(return_value, col, data.char_value, 1); break; case TYPE_DOUBLE: add_index_double(return_value, col, data.approx_value); break; case TYPE_INTEGER: add_index_long(return_value, col, data.exact_value); break; } } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* sesam_fetch_row */ /* {{{ proto array sesam_fetch_array(string result_id [, int whence [, int offset]]) Get all result entries of one row, optionally using a scrolled cursor with given seek parameters. Whence can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... Offset specifies a relative or absolute offset (only used if whence==SESAM_SEEK_ABSOLUTE or whence==SESAM_SEEK_RELATIVE) sesam_fetch_array returns an associative array of one row, where the column names (as returned by sesam_field_array()) are the inex names. Example: SELECT (ID,NAME) FROM TBL will return a column "ID" and a column "NAME". $arr["ID"] = value in column ID $arr["NAME"] = value in column NAME etc. */ static PHP_FUNCTION(sesam_fetch_array) { pval *result_id, *whence = NULL, *row_index = NULL; int result_type; int col; int valid = 0; int mf_count = 0; /* Multiple Field Counter */ SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; switch(ARG_COUNT(ht)) { case 1: if (getParameters(ht, 1, &result_id) != FAILURE) valid = 1; break; case 2: if (getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval != (FETCH_ABSOLUTE-1) && whence->value.lval != (FETCH_RELATIVE-1)) valid = 1; /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); } break; case 3: if (getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); convert_to_long(row_index); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; valid = 1; } break; } if (whence != NULL && (whence->value.lval < 0 || whence->value.lval > FETCH_RELATIVE-1)) valid = 0; if (!valid) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || (result_type != RESULT_NONE && RequestLocal.Cursor == NULL)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> argument in sesam_fetch_array() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ RETURN_FALSE; } array_init(return_value); if (RequestLocal.ResultCount == 0) return; err = RequestLocal.Cursor->Fetch(result_id->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } /* Loop over all columns and collect the data */ for (col=0; col < RequestLocal.ResultCount; ++col) { SQL_Type type = TypeConvert(RequestLocal.MetaInfo[col].type); SQL_Data data; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ char suffix[16]; int mf; char *name; /* If there are "multiple fields", the repetition factor * is available in the 1st occurrence */ if (mf_count <= 0) { /* repetitions is "1" for a normal column */ mf = mf_count = RequestLocal.MetaInfo[col].repetitions; } /* Count down the column before using the "continue;" short cut */ --mf_count; /* Columns without a name (e.g., arithmetic results like COUNT(NAME)) * are skipped; They cannot be accessed as an associative index */ if (strlen(RequestLocal.MetaInfo[col].name) == 0) continue; /* Bail out on errors */ err = SESAM_GetData(result_id->value.str.val, col+1, type, &data, &belegt, &lengthOfValue); if (!SQL_SUCCESS(err)) break; switch (belegt) { case -1: /* columns without a value are left empty */ continue; case 0: /* field valid */ default: /* truncated to 'belegt' columns */ break; } /* for "multiple fields", tack on the index of this column, * like: "name(1)" ... "name(3)" */ if (mf_count > 0 || (mf > 1)) { name = emalloc(strlen(RequestLocal.MetaInfo[col].name)+sizeof("(4000000000)")); sprintf(name, "%s(%d)", RequestLocal.MetaInfo[col].name, mf - mf_count); } else name = RequestLocal.MetaInfo[col].name; switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_assoc_string(return_value, name, data.char_value, 1); break; case TYPE_DOUBLE: add_assoc_double(return_value, name, data.approx_value); break; case TYPE_INTEGER: add_assoc_long(return_value, name, data.exact_value); break; } if (name != RequestLocal.MetaInfo[col].name) efree(name); } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* sesam_fetch_array */ /* {{{ proto boolean sesam_seek_row(string result_id, int whence [, int row_index]) -only useable if query specified "scrollable=1" Position the scrollable cursor according to "whence" argument. "whence" specifies the desired position in the result set; it can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... */ static PHP_FUNCTION(sesam_seek_row) { pval *result_id, *whence, *row_index = NULL; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) == 2 && getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval == (FETCH_ABSOLUTE-1) || whence->value.lval == (FETCH_RELATIVE-1)) WRONG_PARAM_COUNT; } else if (ARG_COUNT(ht) == 3 && getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); } else { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { /*SetLastErrorTo("???@@@???");*/ php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_seek_row() call"); RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - not seekable", 0, 0, 0); /* For "immediate" queries, nothing is returned. */ RETURN_FALSE; } if (result_type != RESULT_SCROLLABLE) { php3_error(E_WARNING, "Invalid <b>ResultId</b> in sesam_seek_row() -- is the result_id scrollable?"); SetLastErrorTo("02000", 0, "No Scrollable Cursor - not seekable", 0, 0, 0); RETURN_FALSE; } convert_to_long(whence); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); if (row_index != NULL) { convert_to_long(row_index); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; } else RequestLocal.ScrollPosition = 0L; SetLastErrorTo("00000", 0, "Okay", 0, 0, 0); RETURN_TRUE; } /* }}} */ /* {{{ proto boolean sesam_free_result(string result_id) Clean up after looping through the result set Deallocate the cursor for the result set. */ static PHP_FUNCTION(sesam_free_result) { pval *result_id; int result_type; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { /*SetLastErrorTo("???@@@???");*/ php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_free_result() call"); RETURN_FALSE; } /* Transparently ignore "immediate" queries. */ if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Okay", 0, 0, 0); err = RequestLocal.last_error.errorcode.sqlerr; } else { /* If an invalid cursor is passed, let SESAM complain */ err = SESAM_DeallocDescriptor(result_id->value.str.val); SetLastError(); Free_Query_Vars(); } if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* sesam_free_result */ /* {{{ proto int sesam_affected_rows(string result_id) Return the number of affected rows in a previous updating operation (INSERT/UPDATE/DELETE). */ static PHP_FUNCTION(sesam_affected_rows) { pval *result_id; int row_count; TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!ValidResultId(result_id->value.str.val)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_affected_rows() call"); RETURN_FALSE; } if (RequestLocal.last_error.valid) row_count = RequestLocal.last_error.row_count; else SESAM_Diagnostic (NULL, NULL, NULL, NULL, NULL, &row_count); RETURN_LONG(row_count); } /* }}} */ /* sesam_affected_rows */ /* {{{ proto string sesam_errormsg() Return the last error message in clear text */ static PHP_FUNCTION(sesam_errormsg) { char message[256+1]; TLS_VARS; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } if (RequestLocal.last_error.valid) { RETURN_STRING(RequestLocal.last_error.message, 1); } else { SESAM_Diagnostic (NULL, NULL, &message[0], NULL, NULL, NULL); RETURN_STRING(message, 1); } } /* }}} */ /* sesam_errormsg */ /* = = = = = = = = = = = = = Module specifics = = = = = = = = = = = = = */ static void php3_info_sesam(void) { TLS_VARS; php3_printf("SESAM <EM>SQLSCI</EM> Driver " PHP_SESAM_VERSION " for BS2000/OSD included"); /*@@*/ php3_printf("<BR>(compiled on " __DATE__ " at " __TIME__ ")"); } int php3_modinit_sesam(INIT_FUNC_ARGS) { /* Force re-init of BS2000 stuff (config might have changed) */ RequestLocal.bs2000_initialized = 0; /* Constants for sesam_seek_row() */ REGISTER_LONG_CONSTANT("SESAM_SEEK_NEXT", FETCH_NEXT-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_PRIOR", FETCH_PRIOR-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_FIRST", FETCH_FIRST-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_LAST", FETCH_LAST-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_ABSOLUTE", FETCH_ABSOLUTE-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_RELATIVE", FETCH_RELATIVE-1, CONST_CS | CONST_PERSISTENT); /* Constants for sesam_settransaction(isolation_level) */ REGISTER_LONG_CONSTANT("SESAM_TXISOL_READ_UNCOMMITTED", 1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_READ_COMMITTED", 2, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_REPEATABLE_READ", 3, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_SERIALIZABLE", 4, CONST_CS | CONST_PERSISTENT); /* Constants for sesam_settransaction(read_only) */ REGISTER_LONG_CONSTANT("SESAM_TXREAD_READWRITE", 0, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXREAD_READONLY", 1, CONST_CS | CONST_PERSISTENT); sigemptyset(&RequestLocal.sesam_blocked_signals); sigaddset(&RequestLocal.sesam_blocked_signals, SIGALRM); sigaddset(&RequestLocal.sesam_blocked_signals, SIGHUP); sigaddset(&RequestLocal.sesam_blocked_signals, SIGUSR1); sigaddset(&RequestLocal.sesam_blocked_signals, SIGTERM); /* It is just too tempting to move the initialization of the BS2000 * environment (/MODIFY-MSG-FILE-ASSIGNMENT, /SET-FILE-LINK,...) * from php3_reqinit_sesam() to this place. It only happens once, * and loading the SESAM interface at module startup seems to * be the most sensible option. * Unfortunately, there are two major problems which make * this impossible: * a) loading of the SESAM connection module takes several * seconds. During this time, the Apache monitor process * panics because it tried to create new children, but * they never appear to get ready, so it creates even * more of them. Within a few minutes, your machine will * come to a grinding halt (unless you set MaxClients * to a low value). :-( * b) php3_modinit_sesam() is executed before the fork() of * the apache child. Unfortunately, neither the * MSG-ASSIGNMENTs nor the FILE-LINKs are inherited by * the child process. So, when the child tries to load * the SESAM connection module, it does not find it because * the FILE-LINK is gone. * c) Loading the SESAM connection module before the fork() * would make it unuseable, because the SESAM database * handler would not accept anything from it because its * pid (TSN) changed by the fork(). * So, alas, we have to load the SESAM connection module in * each child. :-(( */ return SUCCESS; } int php3_modterm_sesam(void) { return SUCCESS; } int php3_reqinit_sesam(INIT_FUNC_ARGS) { /* Configured means: at least one of the sesam directives appears */ RequestLocal.sesam_configured = ( php3_ini.sesam_messagecatalog || php3_ini.sesam_oml_filelink || php3_ini.sesam_configuration_filelink); /* Just to be sure that no descriptor is allocated any more */ if (RequestLocal.sesam_configured) Free_Query_Vars(); RequestLocal.Cursor = NULL; RequestLocal.Descriptor = NULL; RequestLocal.ScrollPosition = 0L; RequestLocal.ScrollOrigin = FETCH_NEXT; RequestLocal.last_error.valid = 0; RequestLocal.MetaInfo = NULL; #ifdef DEBUG_SESAM RequestLocal.DebugActive = 0; #endif if (RequestLocal.sesam_configured && !RequestLocal.bs2000_initialized) { /* See the note in php3_modinit_sesam() for the reason * that the initialization is done here, not there. */ char temp[512+1]; if (php3_ini.sesam_messagecatalog) { /* The MODIFY-MSG-FILE-ASSIGNMENT spits out an * annoying message on stderr which gets logged * in the error_log file. Unfortunately, redirecting * the SYSOUT to *DUMMY does not work: you'd get * % SSM0004 SYSFILE SERVICE REQUEST NOT ALLOWED FOR THIS TASK * (why the #$%@?) logged **in addition to** the message * % NMH1180 FILE ':DC1R:$SESAM.SYSMSA.SESAMSQL.022' HAS BEEN ADDED * % NMH1102 MESSAGE OUTPUT FILE ':DC1R:$SESAM.SYSMSA.SESAMSQL.022', ACCESS=ISAM, ACTION=ADD */ /* see note above bs2system("/ASSIGN-SYSOUT TO-FILE=*DUMMY");*/ snprintf(temp, sizeof temp, "/MODIFY-MSG-FILE-ASSIGNMENT ADD-FILE=%s", php3_ini.sesam_messagecatalog); bs2system(temp); /* see note above bs2system("/ASSIGN-SYSOUT TO-FILE=*PRIMARY");*/ } if (php3_ini.sesam_oml_filelink) { snprintf(temp, sizeof temp, "/SET-FILE-LINK SESAMOML,FILE-NAME=%s", php3_ini.sesam_oml_filelink); bs2system(temp); } if (php3_ini.sesam_configuration_filelink) { snprintf(temp, sizeof temp, "/SET-FILE-LINK SESCONF,FILE-NAME=%s", php3_ini.sesam_configuration_filelink); bs2system(temp); } RequestLocal.bs2000_initialized = 1; } if (RequestLocal.sesam_configured) { /* Send a dummy command to force loading of the DB link interface */ /* SESAM_InitTask();*/ /* If I call this, then it exits immediately */ /* Send a dummy command to force loading of the DB link interface */ SESAM_SetCatalog(str_none); /*@@@ SESAM_SetTransaction (?, ?);*/ } return SUCCESS; } int php3_reqterm_sesam(void) { if (!RequestLocal.sesam_configured) return SUCCESS; Free_Query_Vars(); /* Discard any uncommitted transactions. */ SESAM_Rollback(); /* Forget any static settings in the holder task. */ SESAM_SetCatalog(str_none); SESAM_SetSchema(str_none); SESAM_SetAuthorization(str_none); SESAM_Commit(); return SUCCESS; } static function_entry sesam_functions[] = { #ifdef DEBUG_SESAM PHP_FE(sesam_debug, NULL) #endif PHP_FE(sesam_connect, NULL) PHP_FE(sesam_disconnect, NULL) PHP_FE(sesam_settransaction, NULL) PHP_FE(sesam_commit, NULL) PHP_FE(sesam_rollback, NULL) PHP_FE(sesam_execimm, NULL) PHP_FE(sesam_query, NULL) PHP_FE(sesam_num_fields, NULL) PHP_FE(sesam_field_name, NULL) PHP_FE(sesam_field_data, NULL) PHP_FE(sesam_diagnostic, NULL) PHP_FE(sesam_fetch_result, NULL) PHP_FE(sesam_field_array, NULL) PHP_FE(sesam_fetch_row, NULL) PHP_FE(sesam_fetch_array, NULL) PHP_FE(sesam_seek_row, NULL) PHP_FE(sesam_free_result, NULL) PHP_FE(sesam_affected_rows, NULL) PHP_FE(sesam_errormsg, NULL) {NULL, NULL, NULL} }; php3_module_entry sesam_module_entry = { "SESAM", sesam_functions, php3_modinit_sesam, php3_modterm_sesam, php3_reqinit_sesam, php3_reqterm_sesam, php3_info_sesam, STANDARD_MODULE_PROPERTIES }; #if COMPILE_DL DLEXPORT php3_module_entry *get_module(void) { return &sesam_module_entry; } #endif #else PHP_FUNCTION(sesam_connect) {} void php3_info_sesam() {} #endif /* * Local variables: * tab-width: 4 * End: */
Index: php3/functions/php3_sesam.h +++ php3/functions/php3_sesam.h /* +----------------------------------------------------------------------+ | PHP HTML Embedded Scripting Language Version 3.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997-1999 PHP Development Team (See Credits file) | +----------------------------------------------------------------------+ | This program is free software; you can redistribute it and/or modify | | it under the terms of one of the following licenses: | | | | A) the GNU General Public License as published by the Free Software | | Foundation; either version 2 of the License, or (at your option) | | any later version. | | | | B) the PHP License as published by the PHP Development Team and | | included in the distribution in the file: LICENSE | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of both licenses referred to here. | | If you did not, or have any questions about PHP licensing, please | | contact core@php.net. | +----------------------------------------------------------------------+ | Authors: Martin Kraemer <Martin.Kraemer@MchP.Siemens.De> | +----------------------------------------------------------------------+ */ /* $Id: php3_sesam.h,v 1.1 2000/12/14 21:35:21 martin Exp $ */ #ifndef _SESAM_H #define _SESAM_H #if HAVE_SESAM extern php3_module_entry sesam_module_entry; #define sesam_module_ptr &sesam_module_entry extern void php3_sesam_connect(INTERNAL_FUNCTION_PARAMETERS); extern void php3_info_sesam(void); #else #define sesam_module_ptr NULL #endif #endif /* _SESAM_H */ Index: php3/functions/sesam.c +++ php3/functions/sesam.c /* +----------------------------------------------------------------------+ | PHP HTML Embedded Scripting Language Version 3.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2000 PHP Development Team (See Credits file) | +----------------------------------------------------------------------+ | This program is free software; you can redistribute it and/or modify | | it under the terms of one of the following licenses: | | | | A) the GNU General Public License as published by the Free Software | | Foundation; either version 2 of the License, or (at your option) | | any later version. | | | | B) the PHP License as published by the PHP Development Team and | | included in the distribution in the file: LICENSE | | | | This program is distributed in the hope that it will be useful, | | but WITHOUT ANY WARRANTY; without even the implied warranty of | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | GNU General Public License for more details. | | | | You should have received a copy of both licenses referred to here. | | If you did not, or have any questions about PHP licensing, please | | contact core@php.net. | +----------------------------------------------------------------------+ | Authors: Martin Kraemer <Martin.Kraemer@MchP.Siemens.De> | | | +----------------------------------------------------------------------+ */ #define PHP_SESAM_VERSION "V1.0A00" /* $Id: sesam.c,v 1.1 2000/12/14 21:35:21 martin Exp $ */ #include <stdlib.h> #include <ctype.h> #include <stdio.h> #ifdef THREAD_SAFE #include "tls.h" #endif #include "php.h" #ifndef MSVC5 #include "build-defs.h" #endif #include "internal_functions.h" #if PHP_31 #include "ext/standard/php3_string.h" #else #include "functions/php3_string.h" #endif #include "php3_sesam.h" #if APACHE # ifndef DEBUG # undef palloc # endif #endif #if HAVE_SESAM #include "sqlsci.h" #undef DEBUG_SESAM #define DEBUG_SESAM #undef AVOID_DANGEROUS #define AVOID_DANGEROUS /* sesam.c */ static SQLSTATE SESAM_PrepareCursor(const char *stmt); static SQLSTATE SESAM_OpenCursor(void); static SQLSTATE SESAM_DescribeOutput(const char *descr_name); static SQLSTATE SESAM_FetchCursor(const char *descr_name); static SQLSTATE SESAM_CloseCursor(void); static SQLSTATE SESAM_PrepareScrollCursor(const char *stmt); static SQLSTATE SESAM_OpenScrollCursor(void); static SQLSTATE SESAM_DescribeScrollOutput(const char *descr_name); static SQLSTATE FetchCursorAutoScroll(const char *result_id); static SQLSTATE SESAM_CloseScrollCursor(void); #ifdef DEBUG_SESAM static const char *SESAM_SeekType(SQL_Fetch_Orientation fetch_orient); #endif static const char *SESAM_TypeName(SQL_Type val); static const char none[] = "<none>"; static const char str_none[] = "\"*NONE\""; static const char noresult[] = "NoResult"; #define RESULT_ID_FMT "ResultDescr_pid%u" #define SCROLLABLE_ID_FMT "ScrollDescr_pid%u" /* This structure defines the handler functions which are differnt for scrolled * and non-scrolled cursors. Depending on the type of query, one of the * two versions is used until end-of-result. */ typedef struct { SQLSTATE (*Prepare)(const char *stmt); SQLSTATE (*Open)(void); SQLSTATE (*DescribeOutput)(const char *result_id); SQLSTATE (*Fetch)(const char *result_id); SQLSTATE (*Close)(void); } SESAM_CURSOR_FUNCTIONS; static const SESAM_CURSOR_FUNCTIONS scrollable_functions = { /* The functions for scrollable cursors: */ SESAM_PrepareScrollCursor, SESAM_OpenScrollCursor, SESAM_DescribeScrollOutput, FetchCursorAutoScroll, SESAM_CloseScrollCursor }, nonscrollable_functions = { /* The functions for non-scrollable cursors: */ SESAM_PrepareCursor, SESAM_OpenCursor, SESAM_DescribeOutput, SESAM_FetchCursor, SESAM_CloseCursor }; static struct { int bs2000_initialized; /* nonzero if the bs2000 /ADD-MESSAGE etc has been done */ int sesam_configured; /* nonzero if the user used one php3_sesam_ config directive */ const SESAM_CURSOR_FUNCTIONS *Cursor; /* points to (non)?scrollable_functions */ char *Descriptor; long ScrollPosition; SQL_Fetch_Orientation ScrollOrigin; /* FETCH_{NEXT,PRIOR,FIRST,LAST,ABSOLUTE,RELATIVE} */ int ResultCount; struct { int valid; union { char bytes[5+1]; SQLSTATE sqlerr; } errorcode; int sqlcode; char message[256+1]; int errlin; int errcol; int row_count; } last_error; struct { SQL_Type type; int repetitions; int length; int precision; int scale; char name[128+1]; /* Spaltenname */ pval val; } *MetaInfo; sigset_t sesam_blocked_signals; sigset_t sesam_current_signals; #ifdef DEBUG_SESAM int DebugActive; #endif } RequestLocal; #ifdef DEBUG_SESAM #define CHECK(name,args,addstr) check(#name, name args, addstr) static int check(const char *func, SQLSTATE err, const char *parm); #else #define CHECK(name,args,addstr) name args #endif /*********************************************** * Set of "signal-proof" wrapped functions for * the sqlsci interface. Because the sqlsci * functions would not correctly deal with * SIGALRM or SIGUSR1 (or any other) signals, * we have to protect them with a SIG_BLOCK * until they return to our context. * NOTE: when sqlsci adds new functionality, * it must be added here! **********************************************/ #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_Connect (const char *connection_id) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Connect, (connection_id), connection_id); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Disconnect (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Disconnect, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_InitTask (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_InitTask, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_SetAuthorization (const char *user_id) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetAuthorization, (user_id), user_id); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetCatalog (const char *catalog) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetCatalog, (catalog), catalog); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetSchema (const char *schema) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetSchema, (schema), schema); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_SetTransaction (int isolation_level, int read_only) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "isol_lvl=%d, rdonly=%d", isolation_level, read_only); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetTransaction, (isolation_level, read_only), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Commit (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Commit, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Rollback (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Rollback, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_ExecImm (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_ExecImm, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_Prepare (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Prepare, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_Exec (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_Exec, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_PrepareCursor (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_PrepareCursor, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_OpenCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_OpenCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_FetchCursor (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_FetchCursor, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_CloseCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_CloseCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_StoreCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_StoreCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_RestoreCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_RestoreCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_PrepareScrollCursor (const char *stmt) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_PrepareScrollCursor, (stmt), stmt); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_OpenScrollCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_OpenScrollCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DescribeScrollOutput (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DescribeScrollOutput, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_FetchScrollCursor (const char *descr_name, SQL_Fetch_Orientation fetch_orient, int cu_position) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "%s, %s, %d", descr_name, SESAM_SeekType(fetch_orient), cu_position); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_FetchScrollCursor, (descr_name, fetch_orient, cu_position), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_CloseScrollCursor (void) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_CloseScrollCursor, (), ""); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_AllocDescriptor (const char *descr_name, int count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_AllocDescriptor, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DeallocDescriptor (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DeallocDescriptor, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_DescribeOutput (const char *descr_name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_DescribeOutput, (descr_name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetCount (const char *descr_name, int *count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetCount, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #ifdef INCLUDE_UNUSED_FUNCTIONS static SQLSTATE SESAM_SetCount (const char *descr_name, int count) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetCount, (descr_name, count), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } #endif static SQLSTATE SESAM_SetType (const char *descr_name, int index, int repetitions, SQL_Type type, int length, int precision, int scale) { SQLSTATE ret; #ifdef DEBUG_SESAM char argdebug[512]; snprintf(argdebug, sizeof argdebug, "%s, idx=%d, rep=%d, type=%s, " "len=%d, prec=%d, scl=%d", descr_name, index, repetitions, SESAM_TypeName(type), length, precision, scale); #endif sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_SetType, (descr_name,index,repetitions,type,length,precision,scale), argdebug); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetType (const char *descr_name, int index, int *repetitions, SQL_Type *type, int *length, int *precision, int *scale) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetType, (descr_name,index,repetitions,type,length,precision,scale), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetName (const char *descr_name, int index, char *name) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetName, (descr_name,index,name), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static SQLSTATE SESAM_GetData (const char *descr_name, int index, SQL_Type type, SQL_Data *data, int *indicator, int *lengthOfValue /* darf NULL sein, wenn keine Laenge gewuenscht */ ) { SQLSTATE ret; sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); CHECK(ret = SQL_GetData , (descr_name,index,type,data,indicator,lengthOfValue), descr_name); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); return ret; } static void SESAM_Diagnostic (SQLSTATE *sqlstate, int *sqlcode, char *message, int *errlin, int *errcol, int *row_count) { sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); SQL_Diagnostic(sqlstate,sqlcode,message,errlin,errcol,row_count); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); } #ifdef INCLUDE_UNUSED_FUNCTIONS static void SESAM_Property (int property_type, SQL_Data *property_info) { sigprocmask (SIG_BLOCK, &RequestLocal.sesam_blocked_signals, &RequestLocal.sesam_current_signals); SQL_Property(property_type, property_info); sigprocmask (SIG_SETMASK, &RequestLocal.sesam_current_signals, NULL); } #endif #ifdef DEBUG_SESAM static const char * SESAM_SeekType(SQL_Fetch_Orientation fetch_orient) { switch(fetch_orient) { case FETCH_NEXT: return "FETCH_NEXT"; case FETCH_PRIOR: return "FETCH_PRIOR"; case FETCH_FIRST: return "FETCH_FIRST"; case FETCH_LAST: return "FETCH_LAST"; case FETCH_ABSOLUTE: return "FETCH_ABSOLUTE"; case FETCH_RELATIVE: return "FETCH_RELATIVE"; default: return "*Invalid*"; } } #endif static const char * SESAM_TypeName(SQL_Type val) { static char errstr[128]; switch (val) { case TYPE_CHARACTER: return "CHARACTER"; case TYPE_NUMERIC: return "NUMERIC"; case TYPE_DECIMAL: return "DECIMAL"; case TYPE_INTEGER: return "INTEGER"; case TYPE_SMALLINT: return "SMALLINT"; case TYPE_FLOAT: return "FLOAT"; case TYPE_REAL: return "REAL"; case TYPE_DOUBLE: return "DOUBLE"; case TYPE_DATE: return "DATE"; case TYPE_TIME: return "TIME"; case TYPE_TIMESTAMP: return "TIMESTAMP"; case TYPE_VARCHAR: return "VARCHAR"; default: sprintf(errstr, "Invalid Type %d", val); return errstr; } } #define RESULT_NONE 1 #define RESULT_SEQUENTIAL 2 #define RESULT_SCROLLABLE 4 static int ValidResultId(const char *result_id) { int scrollable = 0, sequential = 0, none = 0; if (result_id != NULL) { if ((none = RESULT_NONE *(strcmp (result_id, noresult) == 0)) || (sequential = RESULT_SEQUENTIAL*(strncmp(result_id, RESULT_ID_FMT, sizeof(RESULT_ID_FMT)-3) == 0)) || (scrollable = RESULT_SCROLLABLE*(strncmp(result_id, SCROLLABLE_ID_FMT, sizeof(SCROLLABLE_ID_FMT)-3) == 0))) { return none | sequential | scrollable; } } return 0; } static SQL_Type TypeConvert(SQL_Type in) { switch (in) { case TYPE_SMALLINT: case TYPE_INTEGER: return TYPE_INTEGER; case TYPE_NUMERIC: case TYPE_DECIMAL: case TYPE_FLOAT: case TYPE_REAL: case TYPE_DOUBLE: return TYPE_DOUBLE; default: /* Error?!?!!?*/ /* Handle dates/times */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: return in; case TYPE_VARCHAR: case TYPE_CHARACTER: return in; } } static int SensibleColumnName(const char *name) { return (name != NULL && strlen(name) > 0 && strcmp(name, "count") != 0 && strcmp(name, "type") != 0 && strcmp(name, "sqltype") != 0 && strcmp(name, "length") != 0 && strcmp(name, "repetitions") != 0 && strcmp(name, "precision") != 0 && strcmp(name, "scale") != 0); } static void SetLastErrorTo(const char *sqlerr, int sqlcode, const char *message, int errlin, int errcol, int row_count) { strncpy(RequestLocal.last_error.errorcode.bytes, sqlerr, sizeof RequestLocal.last_error.errorcode.bytes); RequestLocal.last_error.sqlcode = sqlcode; strncpy(RequestLocal.last_error.message, message, sizeof RequestLocal.last_error.message); RequestLocal.last_error.errlin = errlin; RequestLocal.last_error.errcol = errcol; RequestLocal.last_error.row_count = row_count; RequestLocal.last_error.errorcode.bytes[5] = '\0'; /* 5 character string */ RequestLocal.last_error.valid = 1; } static void SetLastError(void) { SESAM_Diagnostic (&RequestLocal.last_error.errorcode.sqlerr, &RequestLocal.last_error.sqlcode, &RequestLocal.last_error.message[0], &RequestLocal.last_error.errlin, &RequestLocal.last_error.errcol, &RequestLocal.last_error.row_count); RequestLocal.last_error.errorcode.bytes[5] = '\0'; /* 5 character string */ RequestLocal.last_error.valid = 1; } static SQLSTATE FetchCursorAutoScroll(const char *result_id) { SQLSTATE err; err = SESAM_FetchScrollCursor(result_id, RequestLocal.ScrollOrigin, RequestLocal.ScrollPosition); if (SQL_SUCCESS(err)) { /* Auto-increment absolute position */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; else if (RequestLocal.ScrollOrigin == FETCH_LAST) RequestLocal.ScrollOrigin = FETCH_PRIOR; else if (RequestLocal.ScrollOrigin == FETCH_FIRST) RequestLocal.ScrollOrigin = FETCH_NEXT; } return err; } static void Free_Query_Vars() { /* Call either CloseScrollCursor() or CloseCursor() */ if (RequestLocal.Cursor != NULL) { RequestLocal.Cursor->Close(); RequestLocal.Cursor = NULL; } /* If a descriptor is still open, free it. */ if (RequestLocal.Descriptor != NULL) { if (strcmp(RequestLocal.Descriptor, noresult) != 0) (void)SESAM_DeallocDescriptor(RequestLocal.Descriptor); efree(RequestLocal.Descriptor); RequestLocal.Descriptor = NULL; } /* If MetaInfo is still allocated, free it */ if (RequestLocal.MetaInfo != NULL) { efree(RequestLocal.MetaInfo); RequestLocal.MetaInfo = NULL; } } #ifdef DEBUG_SESAM static int check(const char *func, SQLSTATE err, const char *parm) { if (parm == NULL) parm = ""; if (RequestLocal.DebugActive) if (!SQL_SUCCESS(err)) { SetLastError(); php3_printf("%-20s(%s): Error SEW%-5.5s: %s<BR>\n", func, parm, (char *)&err, RequestLocal.last_error.message); return 0; } else php3_printf("%-20s(%s): Okay<BR>\n", func, parm); return (SQL_SUCCESS(err) != 0); } #endif #ifdef DEBUG_SESAM /* {{{ proto boolean sesam_debug(int on_off) Set global debugging flag to on/off */ static PHP_FUNCTION(sesam_debug) { pval *on_off; TLS_VARS; if (ARG_COUNT(ht) != 1) { WRONG_PARAM_COUNT; } if (getParameters(ht, 1, &on_off) == FAILURE) { php3_error(E_ERROR, "Invalid argument in sesam_debug(on_off)"); RETURN_FALSE; } convert_to_long(on_off); RequestLocal.DebugActive = on_off->value.lval; RETURN_TRUE; } /* }}} */ #endif /* {{{ proto boolean sesam_connect(string database, string schema, string user) Build connection to SESAM database, and identify the Catalog, Schema and User to use. */ static PHP_FUNCTION(sesam_connect) { pval *catalog; pval *schema; pval *user; SQLSTATE err; TLS_VARS; if (!RequestLocal.bs2000_initialized) { php3_error(E_ERROR, "SESAM has not been configured."); RETURN_FALSE; } RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 3) { WRONG_PARAM_COUNT; } if (getParameters(ht, 3, &catalog, &schema, &user) == FAILURE) { php3_error(E_ERROR, "Invalid arguments in sesam_connect()"); RETURN_FALSE; } /* Catalog: */ convert_to_string(catalog); if (catalog->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>catalog</b> argument in sesam_connect()"); RETURN_FALSE; } /* Schema: */ convert_to_string(schema); if (schema->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>schema</b> argument in sesam_connect()"); RETURN_FALSE; } /* User: */ convert_to_string(user); if (user->value.str.val == NULL) { php3_error(E_ERROR, "Invalid <b>user</b> argument in sesam_connect()"); RETURN_FALSE; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); SESAM_Rollback(); err = SESAM_SetCatalog(catalog->value.str.val); if (SQL_SUCCESS(err)) err = SESAM_SetSchema(schema->value.str.val); if (SQL_SUCCESS(err)) err = SESAM_SetAuthorization(user->value.str.val); SetLastError(); /* Force transaction start */ SESAM_Commit(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto boolean sesam_disconnect() Detach from SESAM connection */ static PHP_FUNCTION(sesam_disconnect) { TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); /* Discard any uncommitted transactions. */ SESAM_Rollback(); /* Forget any static settings in the holder task. */ SESAM_SetCatalog(str_none); SESAM_SetSchema(str_none); SESAM_SetAuthorization(str_none); SESAM_Commit(); RETURN_TRUE; } /* }}} */ /* {{{ proto boolean sesam_settransaction(int isolation_level, int read_only) Set SESAM transaction parameters where isolation_level is: 1 == READ UNCOMMITTED 2 == READ COMMITTED 3 == REPEATABLE READ 4 == SERIALIZABLE and read_only is: 0 == READ WRITE 1 == READ ONLY */ static PHP_FUNCTION(sesam_settransaction) { pval *isolation_level, *read_only; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &isolation_level, &read_only) == FAILURE) { WRONG_PARAM_COUNT; } /* Schema_Str: */ convert_to_long(isolation_level); convert_to_long(read_only); if (isolation_level->value.lval < 1 || isolation_level->value.lval > 4) { php3_error(E_ERROR, "Sesam_SetTransaction(): value of <b>isolation_level</b> argument invalid"); RETURN_FALSE; } if (read_only->value.lval < 0 || read_only->value.lval > 1) { php3_error(E_ERROR, "Sesam_SetTransaction(): value of <b>read_only</b> argument invalid"); RETURN_FALSE; } err = SESAM_SetTransaction(isolation_level->value.lval, read_only->value.lval); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto int sesam_commit() sesam_commit */ static PHP_FUNCTION(sesam_commit) { SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } err = SESAM_Commit(); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto int sesam_rollback() sesam_rollback */ static PHP_FUNCTION(sesam_rollback) { SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } /* Free dangling variables (descriptor, cursor) */ Free_Query_Vars(); err = SESAM_Rollback(); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_execimm(string stmt) Execute an "immediate" statement (i.e., a statement like UPDATE or INSERT which delivers no result, and has no INPUT or OUTPUT variables). */ static PHP_FUNCTION(sesam_execimm) { pval *stmt; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; Free_Query_Vars(); if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &stmt) == FAILURE) { WRONG_PARAM_COUNT; } /* ExecImm_str: */ convert_to_string(stmt); if (stmt->value.str.val == NULL) { php3_error(E_WARNING, "No <b>stmt</b> argument in sesam_ExecImm command"); RETURN_FALSE; } err = SESAM_ExecImm(stmt->value.str.val); SetLastError(); if (SQL_SUCCESS(err)) { RequestLocal.ResultCount = 0; /* Return Success! For symmetry with sesam_query(), return a */ /* result descriptor which is used in the subsequent calls to Fetch_Array() */ RequestLocal.Descriptor = estrdup(noresult); RETURN_STRING(noresult,1); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_query(string sql_stmt, int scrollable) Perform a SESAM ExecImmediate (or, if that fails, Perform a SESAM SQL Query) Allocate a cursor for the result set. Prepare the cursor Describe the Output Enable type conversion for individual result columns */ static PHP_FUNCTION(sesam_query) { pval *stmt; pval *scrollable_arg = NULL; char result_id[128]; int col, scrollable = 0; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; Free_Query_Vars(); if (ARG_COUNT(ht) == 1 && getParameters(ht, 1, &stmt) != FAILURE) { } else if (ARG_COUNT(ht) == 2 && getParameters(ht, 2, &stmt, &scrollable_arg) != FAILURE) { } else { WRONG_PARAM_COUNT; } /* Did the user want a scrollable cursor? */ if (scrollable_arg != NULL) { convert_to_long(scrollable_arg); scrollable = scrollable_arg->value.lval; } convert_to_string(stmt); if (stmt->value.str.val == NULL) { php3_error(E_WARNING, "Erroneous SQL statement in SESAM_Query()"); RETURN_FALSE; } err = SESAM_ExecImm(stmt->value.str.val); if (SQL_SUCCESS(err)) { SetLastError(); RequestLocal.ResultCount = 0; /* Everything done already. Return Success! */ /* The result descriptor is used in the subsequent calls to Fetch_Array() */ RETURN_STRING(noresult,1); } /* If we tried to execute a "non-immediate" statement * (e.g., a SELECT-type query), we will get the error: * E SEW42BW SQL STATEMENT TYPE CANNOT BE PREPARED * OR IS NOT PERMITTED IN APPLICATION PROGRAMS * All other errors are "real" errors and are caught here. */ else if (memcmp((void*)&err, "42SBW", 5) != 0) { SetLastError(); /* No recovery possible. Return an error. */ RETURN_FALSE; } else { /* ExecImmediate did not work. Try a real query. */ if (scrollable) { RequestLocal.Cursor = &scrollable_functions; RequestLocal.ScrollOrigin = FETCH_NEXT; RequestLocal.ScrollPosition = 0L; } else { RequestLocal.Cursor = &nonscrollable_functions; } /* Create a result_id string that will be valid * for the current result set. (depends on scrollable/nonscroll) */ snprintf(result_id, sizeof result_id, (scrollable) ? SCROLLABLE_ID_FMT : RESULT_ID_FMT, getpid()); err = RequestLocal.Cursor->Prepare(stmt->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } err = SESAM_AllocDescriptor(result_id, 600); /* 600 = recommended size */ /* @@@ This is a kludge: it points to an error in our * (or SQLSCI's) descriptor management; * If the descriptor is already allocated, free it. */ if (!SQL_SUCCESS(err) && memcmp((void*)&err, "33SA1", 5) == 0) { err = SESAM_DeallocDescriptor(result_id); err = SESAM_AllocDescriptor(result_id, 600); /* 600 = recommended size */ } if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } RequestLocal.Descriptor = estrdup(result_id); err = RequestLocal.Cursor->DescribeOutput(result_id); if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } err = SESAM_GetCount(result_id, &RequestLocal.ResultCount); if (!SQL_SUCCESS(err)) { SetLastError(); Free_Query_Vars(); RETURN_FALSE; } RequestLocal.MetaInfo = ecalloc(RequestLocal.ResultCount, sizeof(*RequestLocal.MetaInfo)); if (RequestLocal.MetaInfo == NULL) { php3_error(E_ERROR, "Out of Memory in sesam_query()"); Free_Query_Vars(); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } for (col=0; SQL_SUCCESS(err) && col < RequestLocal.ResultCount; ++col) { int newlen; SQL_Type newtype; err = SESAM_GetName(result_id, col+1, &RequestLocal.MetaInfo[col].name[0]); _php3_strtolower(RequestLocal.MetaInfo[col].name); err = SESAM_GetType(result_id, col+1, &RequestLocal.MetaInfo[col].repetitions, &RequestLocal.MetaInfo[col].type, &RequestLocal.MetaInfo[col].length, &RequestLocal.MetaInfo[col].precision, &RequestLocal.MetaInfo[col].scale); newlen = RequestLocal.MetaInfo[col].length; if ((newtype = TypeConvert(RequestLocal.MetaInfo[col].type)) == TYPE_CHARACTER) newlen = sizeof(((SQL_Data *)0)->char_value) - 1; else if (newtype == TYPE_VARCHAR) newlen = sizeof(((SQL_Data *)0)->varchar_value) - 1; if (newtype != RequestLocal.MetaInfo[col].type || newlen != RequestLocal.MetaInfo[col].length) { /* Enable SQL-Type to 'C'-Type conversion */ err = SESAM_SetType(result_id, col+1, RequestLocal.MetaInfo[col].repetitions, newtype, newlen, RequestLocal.MetaInfo[col].precision, RequestLocal.MetaInfo[col].scale); } } /* for all columns */ err = RequestLocal.Cursor->Open(); SetLastError(); if (SQL_SUCCESS(err)) { /* The result descriptor is used in the subsequent calls to Fetch_Array() */ RETURN_STRING(result_id,1); } else { /* Clean up after errors */ Free_Query_Vars(); RETURN_FALSE; } } } /* }}} */ /* sesam_query */ /* {{{ proto int sesam_num_fields(string result_id) Returns the number of fields/columns in a result set. (@@@ TODO: Multiple fields count like a single column) */ static PHP_FUNCTION(sesam_num_fields) { pval *result_id; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_num_fields() call"); RETURN_FALSE; } if (result_type == RESULT_NONE) /* For "immediate" queries, nothing is returned. */ RETURN_LONG(0); RETURN_LONG(RequestLocal.ResultCount); } /* }}} */ /* {{{ proto string sesam_field_name(string result_id, int index) Return the name of a field (i.e., the column name) in the result set. The index is zero-based. (@@@ TODO: Multiple fields count like a single column) */ static PHP_FUNCTION(sesam_field_name) { pval *result_id, *idx; SQLSTATE err; char name[128+1]; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 2 || getParameters(ht, 2, &result_id, &idx) == FAILURE) { WRONG_PARAM_COUNT; } /* ResultId_str: */ convert_to_string(result_id); convert_to_long(idx); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_name() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* For "immediate" queries, nothing is returned. */ if (result_type == RESULT_NONE) { RETURN_STRING("",0); } err = SESAM_GetName(result_id->value.str.val, idx->value.lval+1, &name[0]); SetLastError(); if (SQL_SUCCESS(err)) { RETURN_STRING(name, 1); } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto string sesam_field_data(string result_id, int index, int& notempty) Returns the contents of one cell from a result set. index is 0 ... (nCol - 1)! */ static PHP_FUNCTION(sesam_field_data) { pval *result_id, *idx, *notempty = NULL; int hasvalue = 0; /* Possibly sizeof(int) != sizeof(long) */ int lengthOfValue; int argc; SQL_Data data; SQL_Type type; SQLSTATE err; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if ((argc = ARG_COUNT(ht)) == 2 && getParameters(ht, argc, &result_id, &idx) != FAILURE) { } else if (argc == 3 && getParameters(ht, argc, &result_id, &idx, ¬empty) != FAILURE && ParameterPassedByReference(ht,3)) { } else WRONG_PARAM_COUNT; convert_to_string(result_id); convert_to_long(idx); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_data() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { /* The caller should not have called us in the first place: * we told him that the array count was zero! */ SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); RETURN_FALSE; } if (idx->value.lval >= RequestLocal.ResultCount) { php3_error(E_ERROR, "Invalid <b>Index</b> arg in sesam_field_data() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } type = TypeConvert(RequestLocal.MetaInfo[idx->value.lval].type); err = SESAM_GetData(result_id->value.str.val, idx->value.lval+1, type, &data, &hasvalue, &lengthOfValue /* ignored for now. Intended for binary VARCHARs */ ); SetLastError(); if (SQL_SUCCESS(err)) { /* Return "notempty" if passed by reference */ if (notempty != NULL) { notempty->type = IS_LONG; notempty->value.lval = hasvalue; } switch (type) { case TYPE_INTEGER: RETURN_LONG(data.exact_value); case TYPE_DOUBLE: RETURN_DOUBLE(data.approx_value); /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_CHARACTER: case TYPE_VARCHAR: RETURN_STRING(data.char_value, 1); } } else { RETURN_FALSE; } } /* }}} */ /* {{{ proto array sesam_diagnostic() Return an associative array of status and return codes for the last sql query/stmt/command. Elements of the array are: $array["sqlstate"] := 5 digit SQL return code $array["errmsg"] := "human readable" error message string $array["errlin"] := error line number of previous error (1... or 0) $array["errcol"] := error column number of previous error (1... or 0) $array["rowcount"] := number of affected rows in last update */ static PHP_FUNCTION(sesam_diagnostic) { TLS_VARS; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } if (!RequestLocal.last_error.valid) SetLastError(); array_init(return_value); /* ret["sqlstate"] = error code string */ add_assoc_string(return_value, "sqlstate", RequestLocal.last_error.errorcode.bytes, 1); /* ret["errmsg"] = error message */ add_assoc_string(return_value, "errmsg", RequestLocal.last_error.message, 1); /* ret["errlin"] = Error Line Number in last SQL stmt */ add_assoc_long(return_value, "errlin", RequestLocal.last_error.errlin-1); /* ret["errcol"] = Error Column Number in last SQL stmt */ add_assoc_long(return_value, "errcol", RequestLocal.last_error.errcol-1); /* ret["rowcount"] = Number of affected rows in last update */ add_assoc_long(return_value, "rowcount", RequestLocal.last_error.row_count); } /* }}} */ /* {{{ proto array sesam_fetch_result(string result_id [, int max_rows]) Get all result entries: sesam_fetch_result() returns a mixed associative/indexed array of the result: $arr["rows"] = number of rows in result set $arr["cols"] = number of columns $arr["truncated"] = 1 if max_rows count exceeded $arr[col][....] = column information ( col := 0 ... $arr["cols"]-1 ) ** individual fields in table: ** $arr[col][row] = field at row(row) column(col): ( row := 0 ... $arr["rows"]-1 ) */ static PHP_FUNCTION(sesam_fetch_result) { pval *result_id, *max_rows = NULL; int result_type; int num_columns = 0, truncated = 1; int col, row, argc; SQLSTATE err; SQL_Type type; struct { SQL_Data data; pval val; char name[128+1]; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ } *pCol = NULL; TLS_VARS; RequestLocal.last_error.valid = 0; if ((argc = ARG_COUNT(ht)) == 1 && getParameters(ht, argc, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } else if (argc < 1 || argc > 2 || getParameters(ht, argc, &result_id, &max_rows) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || RequestLocal.Cursor == NULL) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_fetch_result() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* For "immediate" queries, nothing is returned. */ if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Immediate Query - no Result data", 0, 0, 0); } num_columns = RequestLocal.ResultCount; array_init(return_value); /* ret["count"] = num_columns */ add_assoc_long(return_value, "count", num_columns); /* ret["cols"] = num_columns */ add_assoc_long(return_value, "cols", num_columns); if (num_columns == 0) return; pCol = (void*) emalloc(num_columns * sizeof(*pCol)); if (pCol == NULL) { php3_error(E_ERROR, "Out of Memory in sesam_fetch_result()"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } /* Add associative information about the columns: * ret[col]["name"] = "fieldname of col'th field" */ for (col=0; col < num_columns; ++col) { array_init(&pCol[col].val); SESAM_GetName(result_id->value.str.val, col+1, &pCol[col].name[0]); } /* for all columns */ for (row=0; max_rows == NULL || row < max_rows->value.lval; ++row) /* for all rows */ { err = RequestLocal.Cursor->Fetch(result_id->value.str.val); /* If the fetch failed, abort immediately */ if (!SQL_NODATA(err) && !SQL_SUCCESS(err)) { SetLastError(); efree (pCol); RETURN_FALSE; } /* End-of-Result reached? */ if (SQL_NODATA(err)) { truncated = 0; break; } for (col=0; col < num_columns; ++col) { type = TypeConvert(RequestLocal.MetaInfo[col].type); /*@@*/ strcpy (pCol[col].data.char_value, none); err = SESAM_GetData(result_id->value.str.val, col+1, type, &pCol[col].data, &pCol[col].belegt, &pCol[col].lengthOfValue /* ignored for now. Intended for binary VARCHARs */ ); switch (pCol[col].belegt) { case -1: /* field has no value */ continue; case 0: /* field valid */ default: /* truncated to pCol[col].belegt columns */ break; } switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: case TYPE_VARCHAR: case TYPE_CHARACTER: add_index_string(&pCol[col].val, row, pCol[col].data.char_value, 1); break; case TYPE_DOUBLE: add_index_double(&pCol[col].val, row, pCol[col].data.approx_value); break; case TYPE_INTEGER: add_index_long(&pCol[col].val, row, pCol[col].data.exact_value); break; default: /* Error?!?!!?*/ php3_error(E_WARNING, "@@@ Not implemented: " "Invalid Result Type in sesam_fetch_result()"); break; } } /* for all columns */ } /* for all rows */ /* Finally, we have collected all the rows into temporary space. */ /* Return them as associative array */ for (col=0; col < num_columns; ++col) { /* ret["column_name"]["count"] = num_rows */ /* ret[col]["count"] = num_rows */ add_assoc_long(&pCol[col].val, "count", row); #ifndef AVOID_DANGEROUS /* ret["column_name"] = "complete row" */ if (SensibleColumnName(pCol[col].name)) _php3_hash_update(return_value->value.ht, _php3_strtolower(pCol[col].name), strlen(pCol[col].name)+1, (void *) &pCol[col].val, sizeof(pCol[col].val), NULL); #endif /* ret[col] = "complete row" */ _php3_hash_index_update(return_value->value.ht, col, (void *) &pCol[col].val, sizeof(pCol[col].val), NULL); } efree (pCol); /* ret["rows"] = number of rows in table */ add_assoc_long(return_value, "rows", row); /* ret["truncated"] = 1 if max_rows count exceeded */ add_assoc_long(return_value, "truncated", truncated); return; } /* }}} */ /* sesam_fetch_result */ /* {{{ proto array sesam_field_array(string result_id) Return Meta Information (type, precision, ...) about individual columns: sesam_field_array() returns a mixed associative/indexed array of the result: $arr["count"] = number of columns ** associative fields in column information: ** $arr[col]["name"] = column name for column(col) $arr[col]["type"] = type of column(col) => ("int" | "string" | "real") $arr[col]["sqltype"] = SQL type => ("CHARACTER" | "NUMERIC" | "DECIMAL" | "INTEGER" | "SMALLINT" | "FLOAT" | "REAL" | "DOUBLE" | "DATE" | "TIME" | "TIMESTAMP" | "VARCHAR") $arr[col]["length"] = length as configured for column $arr[col]["precision"] = precision as configured for column $arr[col]["scale"] = scale as configured for column $arr[col]["count"] = number of "multiple fields" (only valid in 1st column of multi-field) */ static PHP_FUNCTION(sesam_field_array) { pval *result_id; int result_type; int col; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_field_array() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ } array_init(return_value); /* ret["count"] = num_columns */ add_assoc_long(return_value, "count", RequestLocal.ResultCount); if (RequestLocal.ResultCount == 0) return; /* Add associative information about the columns: * ret[col]["name"] = "fieldname of col'th field" */ for (col=0; col < RequestLocal.ResultCount; ++col) { pval val; char name[128+1]; array_init(&val); err = SESAM_GetName(result_id->value.str.val, col+1, &name[0]); if (!SQL_SUCCESS(err)) break; /* * ret[0]["name"] = "firstcol_name" * ret[1]["name"] = "secondcol_name" etc. */ add_assoc_string(&val, "name", name, 1); add_assoc_string(&val, "sqltype", SESAM_TypeName(RequestLocal.MetaInfo[col].type), 1); add_assoc_long (&val, "length", RequestLocal.MetaInfo[col].length); add_assoc_long (&val, "count", RequestLocal.MetaInfo[col].repetitions); add_assoc_long (&val, "repetitions", RequestLocal.MetaInfo[col].repetitions); /*@@*/ add_assoc_long (&val, "precision", RequestLocal.MetaInfo[col].precision); add_assoc_long (&val, "scale", RequestLocal.MetaInfo[col].scale); switch (TypeConvert(RequestLocal.MetaInfo[col].type)) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_assoc_string(&val, "type", "string", 1 /*@@ 0? */); break; case TYPE_DOUBLE: add_assoc_string(&val, "type", "real", 1 /*@@ 0? */); break; case TYPE_INTEGER: add_assoc_string(&val, "type", "int", 1 /*@@ 0? */); break; } /* ret[col][] = "complete column info" */ _php3_hash_index_update(return_value->value.ht, col, (void *) &val, sizeof(val), NULL); #ifndef AVOID_DANGEROUS /* Add associative info only if column name makes sense */ /* ret["column_name"][] = "complete column info" */ if (SensibleColumnName(name)) _php3_hash_update(return_value->value.ht, name, strlen(name)+1, (void *) &val, sizeof(val), NULL); #endif } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* php3_sesam_field_array */ /* {{{ proto array sesam_fetch_row(string result_id [, int whence [, int offset]]) Get all result entries of one row, optionally using a scrolled cursor with given seek parameters. Whence can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... Offset specifies a relative or absolute offset (only used if whence==SESAM_SEEK_ABSOLUTE or whence==SESAM_SEEK_RELATIVE) sesam_fetch_row returns a mixed associative/indexed array of one row: ** individual fields: ** $arr[0] = first value in column $arr[cols-1] = last value in column (where cols:=$arr["count"]-1) */ static PHP_FUNCTION(sesam_fetch_row) { pval *result_id, *whence = NULL, *row_index = NULL; int result_type; int col; int valid = 0; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; switch(ARG_COUNT(ht)) { case 1: if (getParameters(ht, 1, &result_id) != FAILURE) valid = 1; break; case 2: if (getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval != (FETCH_ABSOLUTE-1) && whence->value.lval != (FETCH_RELATIVE-1)) valid = 1; /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); } break; case 3: if (getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); convert_to_long(row_index); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; valid = 1; } break; } if (whence != NULL && (whence->value.lval < 0 || whence->value.lval > FETCH_RELATIVE-1)) valid = 0; if (!valid) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || (result_type != RESULT_NONE && RequestLocal.Cursor == NULL)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> argument in sesam_fetch_row() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ RETURN_FALSE; } array_init(return_value); /* ret["count"] = num_columns */ /* Because columns may be empty (no result), the use of * count($array) would return invalid results. * Therefore, we provide the count as $array["count"], and * disapprove of the use of count($array) in the documentation. */ add_assoc_long(return_value, "count", RequestLocal.ResultCount); if (RequestLocal.ResultCount == 0) return; err = RequestLocal.Cursor->Fetch(result_id->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } /* Loop over all columns and collect the data */ for (col=0; col < RequestLocal.ResultCount; ++col) { SQL_Type type = TypeConvert(RequestLocal.MetaInfo[col].type); SQL_Data data; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ err = SESAM_GetData(result_id->value.str.val, col+1, type, &data, &belegt, &lengthOfValue); if (!SQL_SUCCESS(err)) break; switch (belegt) { case -1: /* field has no value */ continue; case 0: /* field valid */ default: /* truncated to belegt columns */ break; } switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_index_string(return_value, col, data.char_value, 1); break; case TYPE_DOUBLE: add_index_double(return_value, col, data.approx_value); break; case TYPE_INTEGER: add_index_long(return_value, col, data.exact_value); break; } } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* sesam_fetch_row */ /* {{{ proto array sesam_fetch_array(string result_id [, int whence [, int offset]]) Get all result entries of one row, optionally using a scrolled cursor with given seek parameters. Whence can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... Offset specifies a relative or absolute offset (only used if whence==SESAM_SEEK_ABSOLUTE or whence==SESAM_SEEK_RELATIVE) sesam_fetch_array returns an associative array of one row, where the column names (as returned by sesam_field_array()) are the inex names. Example: SELECT (ID,NAME) FROM TBL will return a column "ID" and a column "NAME". $arr["ID"] = value in column ID $arr["NAME"] = value in column NAME etc. */ static PHP_FUNCTION(sesam_fetch_array) { pval *result_id, *whence = NULL, *row_index = NULL; int result_type; int col; int valid = 0; int mf_count = 0; /* Multiple Field Counter */ SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; switch(ARG_COUNT(ht)) { case 1: if (getParameters(ht, 1, &result_id) != FAILURE) valid = 1; break; case 2: if (getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval != (FETCH_ABSOLUTE-1) && whence->value.lval != (FETCH_RELATIVE-1)) valid = 1; /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); } break; case 3: if (getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); convert_to_long(row_index); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; valid = 1; } break; } if (whence != NULL && (whence->value.lval < 0 || whence->value.lval > FETCH_RELATIVE-1)) valid = 0; if (!valid) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val)) || (result_type != RESULT_NONE && RequestLocal.Cursor == NULL)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> argument in sesam_fetch_array() call"); /*SetLastErrorTo("???@@@???");*/ RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - no Result data", 0, 0, 0); /* For "immediate" queries, RequestLocal.ResultCount is zero */ RETURN_FALSE; } array_init(return_value); if (RequestLocal.ResultCount == 0) return; err = RequestLocal.Cursor->Fetch(result_id->value.str.val); if (!SQL_SUCCESS(err)) { SetLastError(); RETURN_FALSE; } /* Loop over all columns and collect the data */ for (col=0; col < RequestLocal.ResultCount; ++col) { SQL_Type type = TypeConvert(RequestLocal.MetaInfo[col].type); SQL_Data data; int belegt; /* ist Feld belegt? (oder Null-Value?) */ int lengthOfValue; /* ignored for now. Intended for binary VARCHARs */ char suffix[16]; int mf; char *name; /* If there are "multiple fields", the repetition factor * is available in the 1st occurrence */ if (mf_count <= 0) { /* repetitions is "1" for a normal column */ mf = mf_count = RequestLocal.MetaInfo[col].repetitions; } /* Count down the column before using the "continue;" short cut */ --mf_count; /* Columns without a name (e.g., arithmetic results like COUNT(NAME)) * are skipped; They cannot be accessed as an associative index */ if (strlen(RequestLocal.MetaInfo[col].name) == 0) continue; /* Bail out on errors */ err = SESAM_GetData(result_id->value.str.val, col+1, type, &data, &belegt, &lengthOfValue); if (!SQL_SUCCESS(err)) break; switch (belegt) { case -1: /* columns without a value are left empty */ continue; case 0: /* field valid */ default: /* truncated to 'belegt' columns */ break; } /* for "multiple fields", tack on the index of this column, * like: "name(1)" ... "name(3)" */ if (mf_count > 0 || (mf > 1)) { name = emalloc(strlen(RequestLocal.MetaInfo[col].name)+sizeof("(4000000000)")); sprintf(name, "%s(%d)", RequestLocal.MetaInfo[col].name, mf - mf_count); } else name = RequestLocal.MetaInfo[col].name; switch (type) { /* The TIME/DATE Types are internally converted to * CHARACTER by the SQLSCI interface. * When talking to SESAM, however, we must NOT tell it * to convert them for us -- it would deny it with * an error: * E SEW42R1 VALUES CANNOT BE COMPARED * Therefore, the TypeConvert() function does not change them. */ case TYPE_DATE: case TYPE_TIME: case TYPE_TIMESTAMP: default: /* Error?!?!!?*/ case TYPE_VARCHAR: case TYPE_CHARACTER: add_assoc_string(return_value, name, data.char_value, 1); break; case TYPE_DOUBLE: add_assoc_double(return_value, name, data.approx_value); break; case TYPE_INTEGER: add_assoc_long(return_value, name, data.exact_value); break; } if (name != RequestLocal.MetaInfo[col].name) efree(name); } /* for all columns */ SetLastError(); /* No RETURN_XXX statement is needed, as we already * filled in the return_value-> fields. */ } /* }}} */ /* sesam_fetch_array */ /* {{{ proto boolean sesam_seek_row(string result_id, int whence [, int row_index]) -only useable if query specified "scrollable=1" Position the scrollable cursor according to "whence" argument. "whence" specifies the desired position in the result set; it can be one of: SESAM_SEEK_NEXT = 0, -- read sequentially SESAM_SEEK_PRIOR = 1, -- read sequentially backwards SESAM_SEEK_FIRST = 2, -- rewind to first row SESAM_SEEK_LAST = 3, -- seek to (behind) last row SESAM_SEEK_ABSOLUTE = 4, -- seek to row number given as "row_index" (0...) SESAM_SEEK_RELATIVE = 5 -- seek relative +/- row_index +/-1 ... */ static PHP_FUNCTION(sesam_seek_row) { pval *result_id, *whence, *row_index = NULL; int result_type; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) == 2 && getParameters(ht, 2, &result_id, &whence) != FAILURE) { convert_to_long(whence); if (whence->value.lval == (FETCH_ABSOLUTE-1) || whence->value.lval == (FETCH_RELATIVE-1)) WRONG_PARAM_COUNT; } else if (ARG_COUNT(ht) == 3 && getParameters(ht, 3, &result_id, &whence, &row_index) != FAILURE) { convert_to_long(whence); } else { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { /*SetLastErrorTo("???@@@???");*/ php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_seek_row() call"); RETURN_FALSE; } if (result_type == RESULT_NONE) { SetLastErrorTo("02000", 0, "Immediate Query - not seekable", 0, 0, 0); /* For "immediate" queries, nothing is returned. */ RETURN_FALSE; } if (result_type != RESULT_SCROLLABLE) { php3_error(E_WARNING, "Invalid <b>ResultId</b> in sesam_seek_row() -- is the result_id scrollable?"); SetLastErrorTo("02000", 0, "No Scrollable Cursor - not seekable", 0, 0, 0); RETURN_FALSE; } convert_to_long(whence); /* Convert 0-based "whence" value to 1-based */ RequestLocal.ScrollOrigin = (SQL_Fetch_Orientation)(1+whence->value.lval); if (row_index != NULL) { convert_to_long(row_index); RequestLocal.ScrollPosition = row_index->value.lval; /* Convert 0-based ABSOLUTE position to 1-based */ if (RequestLocal.ScrollOrigin == FETCH_ABSOLUTE) ++RequestLocal.ScrollPosition; } else RequestLocal.ScrollPosition = 0L; SetLastErrorTo("00000", 0, "Okay", 0, 0, 0); RETURN_TRUE; } /* }}} */ /* {{{ proto boolean sesam_free_result(string result_id) Clean up after looping through the result set Deallocate the cursor for the result set. */ static PHP_FUNCTION(sesam_free_result) { pval *result_id; int result_type; SQLSTATE err; TLS_VARS; RequestLocal.last_error.valid = 0; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!(result_type = ValidResultId(result_id->value.str.val))) { /*SetLastErrorTo("???@@@???");*/ php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_free_result() call"); RETURN_FALSE; } /* Transparently ignore "immediate" queries. */ if (result_type == RESULT_NONE) { SetLastErrorTo("00000", 0, "Okay", 0, 0, 0); err = RequestLocal.last_error.errorcode.sqlerr; } else { /* If an invalid cursor is passed, let SESAM complain */ err = SESAM_DeallocDescriptor(result_id->value.str.val); SetLastError(); Free_Query_Vars(); } if (SQL_SUCCESS(err)) { RETURN_TRUE; } else { RETURN_FALSE; } } /* }}} */ /* sesam_free_result */ /* {{{ proto int sesam_affected_rows(string result_id) Return the number of affected rows in a previous updating operation (INSERT/UPDATE/DELETE). */ static PHP_FUNCTION(sesam_affected_rows) { pval *result_id; int row_count; TLS_VARS; if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &result_id) == FAILURE) { WRONG_PARAM_COUNT; } convert_to_string(result_id); if (!ValidResultId(result_id->value.str.val)) { php3_error(E_WARNING, "Invalid <b>ResultId</b> arg in sesam_affected_rows() call"); RETURN_FALSE; } if (RequestLocal.last_error.valid) row_count = RequestLocal.last_error.row_count; else SESAM_Diagnostic (NULL, NULL, NULL, NULL, NULL, &row_count); RETURN_LONG(row_count); } /* }}} */ /* sesam_affected_rows */ /* {{{ proto string sesam_errormsg() Return the last error message in clear text */ static PHP_FUNCTION(sesam_errormsg) { char message[256+1]; TLS_VARS; if (ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; } if (RequestLocal.last_error.valid) { RETURN_STRING(RequestLocal.last_error.message, 1); } else { SESAM_Diagnostic (NULL, NULL, &message[0], NULL, NULL, NULL); RETURN_STRING(message, 1); } } /* }}} */ /* sesam_errormsg */ /* = = = = = = = = = = = = = Module specifics = = = = = = = = = = = = = */ static void php3_info_sesam(void) { TLS_VARS; php3_printf("SESAM <EM>SQLSCI</EM> Driver " PHP_SESAM_VERSION " for BS2000/OSD included"); /*@@*/ php3_printf("<BR>(compiled on " __DATE__ " at " __TIME__ ")"); } int php3_modinit_sesam(INIT_FUNC_ARGS) { /* Force re-init of BS2000 stuff (config might have changed) */ RequestLocal.bs2000_initialized = 0; /* Constants for sesam_seek_row() */ REGISTER_LONG_CONSTANT("SESAM_SEEK_NEXT", FETCH_NEXT-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_PRIOR", FETCH_PRIOR-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_FIRST", FETCH_FIRST-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_LAST", FETCH_LAST-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_ABSOLUTE", FETCH_ABSOLUTE-1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_SEEK_RELATIVE", FETCH_RELATIVE-1, CONST_CS | CONST_PERSISTENT); /* Constants for sesam_settransaction(isolation_level) */ REGISTER_LONG_CONSTANT("SESAM_TXISOL_READ_UNCOMMITTED", 1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_READ_COMMITTED", 2, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_REPEATABLE_READ", 3, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXISOL_SERIALIZABLE", 4, CONST_CS | CONST_PERSISTENT); /* Constants for sesam_settransaction(read_only) */ REGISTER_LONG_CONSTANT("SESAM_TXREAD_READWRITE", 0, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SESAM_TXREAD_READONLY", 1, CONST_CS | CONST_PERSISTENT); sigemptyset(&RequestLocal.sesam_blocked_signals); sigaddset(&RequestLocal.sesam_blocked_signals, SIGALRM); sigaddset(&RequestLocal.sesam_blocked_signals, SIGHUP); sigaddset(&RequestLocal.sesam_blocked_signals, SIGUSR1); sigaddset(&RequestLocal.sesam_blocked_signals, SIGTERM); /* It is just too tempting to move the initialization of the BS2000 * environment (/MODIFY-MSG-FILE-ASSIGNMENT, /SET-FILE-LINK,...) * from php3_reqinit_sesam() to this place. It only happens once, * and loading the SESAM interface at module startup seems to * be the most sensible option. * Unfortunately, there are two major problems which make * this impossible: * a) loading of the SESAM connection module takes several * seconds. During this time, the Apache monitor process * panics because it tried to create new children, but * they never appear to get ready, so it creates even * more of them. Within a few minutes, your machine will * come to a grinding halt (unless you set MaxClients * to a low value). :-( * b) php3_modinit_sesam() is executed before the fork() of * the apache child. Unfortunately, neither the * MSG-ASSIGNMENTs nor the FILE-LINKs are inherited by * the child process. So, when the child tries to load * the SESAM connection module, it does not find it because * the FILE-LINK is gone. * c) Loading the SESAM connection module before the fork() * would make it unuseable, because the SESAM database * handler would not accept anything from it because its * pid (TSN) changed by the fork(). * So, alas, we have to load the SESAM connection module in * each child. :-(( */ return SUCCESS; } int php3_modterm_sesam(void) { return SUCCESS; } int php3_reqinit_sesam(INIT_FUNC_ARGS) { /* Configured means: at least one of the sesam directives appears */ RequestLocal.sesam_configured = ( php3_ini.sesam_messagecatalog || php3_ini.sesam_oml_filelink || php3_ini.sesam_configuration_filelink); /* Just to be sure that no descriptor is allocated any more */ if (RequestLocal.sesam_configured) Free_Query_Vars(); RequestLocal.Cursor = NULL; RequestLocal.Descriptor = NULL; RequestLocal.ScrollPosition = 0L; RequestLocal.ScrollOrigin = FETCH_NEXT; RequestLocal.last_error.valid = 0; RequestLocal.MetaInfo = NULL; #ifdef DEBUG_SESAM RequestLocal.DebugActive = 0; #endif if (RequestLocal.sesam_configured && !RequestLocal.bs2000_initialized) { /* See the note in php3_modinit_sesam() for the reason * that the initialization is done here, not there. */ char temp[512+1]; if (php3_ini.sesam_messagecatalog) { /* The MODIFY-MSG-FILE-ASSIGNMENT spits out an * annoying message on stderr which gets logged * in the error_log file. Unfortunately, redirecting * the SYSOUT to *DUMMY does not work: you'd get * % SSM0004 SYSFILE SERVICE REQUEST NOT ALLOWED FOR THIS TASK * (why the #$%@?) logged **in addition to** the message * % NMH1180 FILE ':DC1R:$SESAM.SYSMSA.SESAMSQL.022' HAS BEEN ADDED * % NMH1102 MESSAGE OUTPUT FILE ':DC1R:$SESAM.SYSMSA.SESAMSQL.022', ACCESS=ISAM, ACTION=ADD */ /* see note above bs2system("/ASSIGN-SYSOUT TO-FILE=*DUMMY");*/ snprintf(temp, sizeof temp, "/MODIFY-MSG-FILE-ASSIGNMENT ADD-FILE=%s", php3_ini.sesam_messagecatalog); bs2system(temp); /* see note above bs2system("/ASSIGN-SYSOUT TO-FILE=*PRIMARY");*/ } if (php3_ini.sesam_oml_filelink) { snprintf(temp, sizeof temp, "/SET-FILE-LINK SESAMOML,FILE-NAME=%s", php3_ini.sesam_oml_filelink); bs2system(temp); } if (php3_ini.sesam_configuration_filelink) { snprintf(temp, sizeof temp, "/SET-FILE-LINK SESCONF,FILE-NAME=%s", php3_ini.sesam_configuration_filelink); bs2system(temp); } RequestLocal.bs2000_initialized = 1; } if (RequestLocal.sesam_configured) { /* Send a dummy command to force loading of the DB link interface */ /* SESAM_InitTask();*/ /* If I call this, then it exits immediately */ /* Send a dummy command to force loading of the DB link interface */ SESAM_SetCatalog(str_none); /*@@@ SESAM_SetTransaction (?, ?);*/ } return SUCCESS; } int php3_reqterm_sesam(void) { if (!RequestLocal.sesam_configured) return SUCCESS; Free_Query_Vars(); /* Discard any uncommitted transactions. */ SESAM_Rollback(); /* Forget any static settings in the holder task. */ SESAM_SetCatalog(str_none); SESAM_SetSchema(str_none); SESAM_SetAuthorization(str_none); SESAM_Commit(); return SUCCESS; } static function_entry sesam_functions[] = { #ifdef DEBUG_SESAM PHP_FE(sesam_debug, NULL) #endif PHP_FE(sesam_connect, NULL) PHP_FE(sesam_disconnect, NULL) PHP_FE(sesam_settransaction, NULL) PHP_FE(sesam_commit, NULL) PHP_FE(sesam_rollback, NULL) PHP_FE(sesam_execimm, NULL) PHP_FE(sesam_query, NULL) PHP_FE(sesam_num_fields, NULL) PHP_FE(sesam_field_name, NULL) PHP_FE(sesam_field_data, NULL) PHP_FE(sesam_diagnostic, NULL) PHP_FE(sesam_fetch_result, NULL) PHP_FE(sesam_field_array, NULL) PHP_FE(sesam_fetch_row, NULL) PHP_FE(sesam_fetch_array, NULL) PHP_FE(sesam_seek_row, NULL) PHP_FE(sesam_free_result, NULL) PHP_FE(sesam_affected_rows, NULL) PHP_FE(sesam_errormsg, NULL) {NULL, NULL, NULL} }; php3_module_entry sesam_module_entry = { "SESAM", sesam_functions, php3_modinit_sesam, php3_modterm_sesam, php3_reqinit_sesam, php3_reqterm_sesam, php3_info_sesam, STANDARD_MODULE_PROPERTIES }; #if COMPILE_DL DLEXPORT php3_module_entry *get_module(void) { return &sesam_module_entry; } #endif #else PHP_FUNCTION(sesam_connect) {} void php3_info_sesam() {} #endif /* * Local variables: * tab-width: 4 * End: */