diff options
Diffstat (limited to 'ext/interbase')
36 files changed, 7959 insertions, 0 deletions
diff --git a/ext/interbase/CREDITS b/ext/interbase/CREDITS new file mode 100644 index 0000000..d66f13b --- /dev/null +++ b/ext/interbase/CREDITS @@ -0,0 +1,2 @@ +InterBase +Jouni Ahto, Andrew Avdeev, Ard Biesheuvel diff --git a/ext/interbase/config.m4 b/ext/interbase/config.m4 new file mode 100644 index 0000000..603145a --- /dev/null +++ b/ext/interbase/config.m4 @@ -0,0 +1,46 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_WITH(interbase,for InterBase support, +[ --with-interbase[=DIR] Include InterBase support. DIR is the InterBase base + install directory [/usr/interbase]]) + +if test "$PHP_INTERBASE" != "no"; then + if test "$PHP_INTERBASE" = "yes"; then + IBASE_INCDIR=/usr/interbase/include + IBASE_LIBDIR=/usr/interbase/lib + else + IBASE_INCDIR=$PHP_INTERBASE/include + IBASE_LIBDIR=$PHP_INTERBASE/$PHP_LIBDIR + fi + + PHP_CHECK_LIBRARY(fbclient, isc_detach_database, + [ + IBASE_LIBNAME=fbclient + ], [ + PHP_CHECK_LIBRARY(gds, isc_detach_database, + [ + IBASE_LIBNAME=gds + ], [ + PHP_CHECK_LIBRARY(ib_util, isc_detach_database, + [ + IBASE_LIBNAME=ib_util + ], [ + AC_MSG_ERROR([libgds, libib_util or libfbclient not found! Check config.log for more information.]) + ], [ + -L$IBASE_LIBDIR + ]) + ], [ + -L$IBASE_LIBDIR + ]) + ], [ + -L$IBASE_LIBDIR + ]) + + PHP_ADD_LIBRARY_WITH_PATH($IBASE_LIBNAME, $IBASE_LIBDIR, INTERBASE_SHARED_LIBADD) + PHP_ADD_INCLUDE($IBASE_INCDIR) + AC_DEFINE(HAVE_IBASE,1,[ ]) + PHP_NEW_EXTENSION(interbase, interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c, $ext_shared) + PHP_SUBST(INTERBASE_SHARED_LIBADD) +fi diff --git a/ext/interbase/config.w32 b/ext/interbase/config.w32 new file mode 100644 index 0000000..5512125 --- /dev/null +++ b/ext/interbase/config.w32 @@ -0,0 +1,18 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("interbase", "InterBase support", "no"); + +if (PHP_INTERBASE != "no") { + + if (CHECK_HEADER_ADD_INCLUDE("ibase.h", "CFLAGS_INTERBASE", + PHP_PHP_BUILD + "\\include\\interbase;" + PHP_PHP_BUILD + "\\interbase\\include;" + PHP_INTERBASE) && + (CHECK_LIB("fbclient_ms.lib", "interbase", PHP_PHP_BUILD + "\\interbase\\lib_ms;" + PHP_INTERBASE) || + CHECK_LIB("gds32_ms.lib", "interbase", PHP_PHP_BUILD + "\\interbase\\lib_ms;" + PHP_INTERBASE))) { + + EXTENSION("interbase", "interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c"); + AC_DEFINE('HAVE_IBASE', 1, 'Have interbase library'); + } else { + WARNING("interbase not enabled; libraries and headers not found"); + } +} diff --git a/ext/interbase/ibase_blobs.c b/ext/interbase/ibase_blobs.c new file mode 100644 index 0000000..9d9d2f8 --- /dev/null +++ b/ext/interbase/ibase_blobs.c @@ -0,0 +1,593 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_IBASE + +#include "php_interbase.h" +#include "php_ibase_includes.h" + +#define BLOB_CLOSE 1 +#define BLOB_CANCEL 2 + +static int le_blob; + +static void _php_ibase_free_blob(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_blob *ib_blob = (ibase_blob *)rsrc->ptr; + + if (ib_blob->bl_handle != NULL) { /* blob open*/ + if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) { + _php_ibase_module_error("You can lose data. Close any blob after reading from or " + "writing to it. Use ibase_blob_close() before calling ibase_close()" TSRMLS_CC); + } + } + efree(ib_blob); +} +/* }}} */ + +void php_ibase_blobs_minit(INIT_FUNC_ARGS) /* {{{ */ +{ + le_blob = zend_register_list_destructors_ex(_php_ibase_free_blob, NULL, + "interbase blob", module_number); +} +/* }}} */ + +int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */ +{ + /* shortcut for most common case */ + if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) { + return sscanf(id, BLOB_ID_MASK, (ISC_UINT64 *) qd); + } else { + ISC_UINT64 res; + if (sscanf(id, BLOB_ID_MASK, &res)) { + qd->gds_quad_high = (ISC_LONG) (res >> 0x20); + qd->gds_quad_low = (ISC_LONG) (res & 0xFFFFFFFF); + return 1; + } + return 0; + } +} +/* }}} */ + +char *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */ +{ + char *result; + + /* shortcut for most common case */ + if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) { + spprintf(&result, BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, *(ISC_UINT64*)(void *) &qd); + } else { + ISC_UINT64 res = ((ISC_UINT64) qd.gds_quad_high << 0x20) | qd.gds_quad_low; + spprintf(&result, BLOB_ID_LEN+1, "0x%0*" LL_MASK "x", 16, res); + } + return result; +} +/* }}} */ + +typedef struct { /* {{{ */ + ISC_LONG max_segment; /* Length of longest segment */ + ISC_LONG num_segments; /* Total number of segments */ + ISC_LONG total_length; /* Total length of blob */ + int bl_stream; /* blob is stream ? */ +/* }}} */ +} IBASE_BLOBINFO; + +int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC) /* {{{ */ +{ + if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/ + + ISC_STATUS stat; + char *bl_data; + unsigned long cur_len; + unsigned short seg_len; + + bl_data = safe_emalloc(1, max_len, 1); + + for (cur_len = stat = 0; (stat == 0 || stat == isc_segment) && cur_len < max_len; cur_len += seg_len) { + + unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX + : (unsigned short)(max_len-cur_len); + + stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &bl_data[cur_len]); + } + + bl_data[cur_len] = '\0'; + if (IB_STATUS[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) { + efree(bl_data); + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + RETVAL_STRINGL(bl_data, cur_len, 0); + } else { /* null blob */ + RETVAL_STRING("", 1); /* empty string */ + } + return SUCCESS; +} +/* }}} */ + +int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{ */ +{ + unsigned long put_cnt = 0, rem_cnt; + unsigned short chunk_size; + + convert_to_string_ex(string_arg); + + for (rem_cnt = Z_STRLEN_PP(string_arg); rem_cnt > 0; rem_cnt -= chunk_size) { + + chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt; + + if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, chunk_size, &Z_STRVAL_PP(string_arg)[put_cnt] )) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + put_cnt += chunk_size; + } + return SUCCESS; +} +/* }}} */ + +static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_info TSRMLS_DC) /* {{{ */ +{ + static char bl_items[] = { + isc_info_blob_num_segments, + isc_info_blob_max_segment, + isc_info_blob_total_length, + isc_info_blob_type + }; + + char bl_inf[sizeof(long)*8], *p; + + bl_info->max_segment = 0; + bl_info->num_segments = 0; + bl_info->total_length = 0; + bl_info->bl_stream = 0; + + if (isc_blob_info(IB_STATUS, &bl_handle, sizeof(bl_items), bl_items, sizeof(bl_inf), bl_inf)) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + + for (p = bl_inf; *p != isc_info_end && p < bl_inf + sizeof(bl_inf);) { + unsigned short item_len; + int item = *p++; + + item_len = (short) isc_vax_integer(p, 2); + p += 2; + switch (item) { + case isc_info_blob_num_segments: + bl_info->num_segments = isc_vax_integer(p, item_len); + break; + case isc_info_blob_max_segment: + bl_info->max_segment = isc_vax_integer(p, item_len); + break; + case isc_info_blob_total_length: + bl_info->total_length = isc_vax_integer(p, item_len); + break; + case isc_info_blob_type: + bl_info->bl_stream = isc_vax_integer(p, item_len); + break; + case isc_info_end: + break; + case isc_info_truncated: + case isc_info_error: /* hmm. don't think so...*/ + _php_ibase_module_error("PHP module internal error" TSRMLS_CC); + return FAILURE; + } /* switch */ + p += item_len; + } /* for */ + return SUCCESS; +} +/* }}} */ + +/* {{{ proto resource ibase_blob_create([resource link_identifier]) + Create blob for adding data */ +PHP_FUNCTION(ibase_blob_create) +{ + zval *link = NULL; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + ibase_blob *ib_blob; + + RESET_ERRMSG; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link)) { + RETURN_FALSE; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob)); + ib_blob->bl_handle = NULL; + ib_blob->type = BLOB_INPUT; + + if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, &ib_blob->bl_qd)) { + _php_ibase_error(TSRMLS_C); + efree(ib_blob); + RETURN_FALSE; + } + + ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob); +} +/* }}} */ + +/* {{{ proto resource ibase_blob_open([ resource link_identifier, ] string blob_id) + Open blob for retrieving data parts */ +PHP_FUNCTION(ibase_blob_open) +{ + char *blob_id; + int blob_id_len; + zval *link = NULL; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + ibase_blob *ib_blob; + + RESET_ERRMSG; + + switch (ZEND_NUM_ARGS()) { + default: + WRONG_PARAM_COUNT; + case 1: + if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + case 2: + if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob)); + ib_blob->bl_handle = NULL; + ib_blob->type = BLOB_OUTPUT; + + do { + if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) { + _php_ibase_module_error("String is not a BLOB ID" TSRMLS_CC); + break; + } + + if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, + &ib_blob->bl_qd)) { + _php_ibase_error(TSRMLS_C); + break; + } + + ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob); + return; + + } while (0); + + efree(ib_blob); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool ibase_blob_add(resource blob_handle, string data) + Add data into created blob */ +PHP_FUNCTION(ibase_blob_add) +{ + zval **blob_arg, **string_arg; + ibase_blob *ib_blob; + + RESET_ERRMSG; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &string_arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob); + + if (ib_blob->type != BLOB_INPUT) { + _php_ibase_module_error("BLOB is not open for input" TSRMLS_CC); + RETURN_FALSE; + } + + if (_php_ibase_blob_add(string_arg, ib_blob TSRMLS_CC) != SUCCESS) { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto string ibase_blob_get(resource blob_handle, int len) + Get len bytes data from open blob */ +PHP_FUNCTION(ibase_blob_get) +{ + zval **blob_arg, **len_arg; + ibase_blob *ib_blob; + + RESET_ERRMSG; + + if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &blob_arg, &len_arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob); + + if (ib_blob->type != BLOB_OUTPUT) { + _php_ibase_module_error("BLOB is not open for output" TSRMLS_CC); + RETURN_FALSE; + } + + convert_to_long_ex(len_arg); + + if (_php_ibase_blob_get(return_value, ib_blob, Z_LVAL_PP(len_arg) TSRMLS_CC) != SUCCESS) { + RETURN_FALSE; + } +} +/* }}} */ + +static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{ */ +{ + zval **blob_arg; + ibase_blob *ib_blob; + + RESET_ERRMSG; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &blob_arg) == FAILURE) { + WRONG_PARAM_COUNT; + } + + ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob); + + if (bl_end == BLOB_CLOSE) { /* return id here */ + + if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/ + if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + } + ib_blob->bl_handle = NULL; + + RETVAL_STRINGL(_php_ibase_quad_to_string(ib_blob->bl_qd), BLOB_ID_LEN, 0); + } else { /* discard created blob */ + if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + ib_blob->bl_handle = NULL; + RETVAL_TRUE; + } + zend_list_delete(Z_LVAL_PP(blob_arg)); +} +/* }}} */ + +/* {{{ proto string ibase_blob_close(resource blob_handle) + Close blob */ +PHP_FUNCTION(ibase_blob_close) +{ + _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CLOSE); +} +/* }}} */ + +/* {{{ proto bool ibase_blob_cancel(resource blob_handle) + Cancel creating blob */ +PHP_FUNCTION(ibase_blob_cancel) +{ + _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, BLOB_CANCEL); +} +/* }}} */ + +/* {{{ proto array ibase_blob_info([ resource link_identifier, ] string blob_id) + Return blob length and other useful info */ +PHP_FUNCTION(ibase_blob_info) +{ + char *blob_id; + int blob_id_len; + zval *link = NULL; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + ibase_blob ib_blob = { NULL, BLOB_INPUT }; + IBASE_BLOBINFO bl_info; + + RESET_ERRMSG; + + switch (ZEND_NUM_ARGS()) { + default: + WRONG_PARAM_COUNT; + case 1: + if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + case 2: + if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + if (! _php_ibase_string_to_quad(blob_id, &ib_blob.bl_qd)) { + _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC); + RETURN_FALSE; + } + + if (ib_blob.bl_qd.gds_quad_high || ib_blob.bl_qd.gds_quad_low) { /* not null ? */ + if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle, + &ib_blob.bl_qd)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + if (_php_ibase_blob_info(ib_blob.bl_handle, &bl_info TSRMLS_CC)) { + RETURN_FALSE; + } + if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + } else { /* null blob, all values to zero */ + bl_info.max_segment = 0; + bl_info.num_segments = 0; + bl_info.total_length = 0; + bl_info.bl_stream = 0; + } + + array_init(return_value); + + add_index_long(return_value, 0, bl_info.total_length); + add_assoc_long(return_value, "length", bl_info.total_length); + + add_index_long(return_value, 1, bl_info.num_segments); + add_assoc_long(return_value, "numseg", bl_info.num_segments); + + add_index_long(return_value, 2, bl_info.max_segment); + add_assoc_long(return_value, "maxseg", bl_info.max_segment); + + add_index_bool(return_value, 3, bl_info.bl_stream); + add_assoc_bool(return_value, "stream", bl_info.bl_stream); + + add_index_bool(return_value, 4, (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low)); + add_assoc_bool(return_value, "isnull", (!ib_blob.bl_qd.gds_quad_high && !ib_blob.bl_qd.gds_quad_low)); +} +/* }}} */ + +/* {{{ proto bool ibase_blob_echo([ resource link_identifier, ] string blob_id) + Output blob contents to browser */ +PHP_FUNCTION(ibase_blob_echo) +{ + char *blob_id; + int blob_id_len; + zval *link = NULL; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + ibase_blob ib_blob_id = { NULL, BLOB_OUTPUT }; + char bl_data[IBASE_BLOB_SEG]; + unsigned short seg_len; + + RESET_ERRMSG; + + switch (ZEND_NUM_ARGS()) { + default: + WRONG_PARAM_COUNT; + case 1: + if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + case 2: + if (FAILURE == zend_parse_parameters(2 TSRMLS_CC, "rs", &link, &blob_id, &blob_id_len)) { + RETURN_FALSE; + } + break; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + if (! _php_ibase_string_to_quad(blob_id, &ib_blob_id.bl_qd)) { + _php_ibase_module_error("Unrecognized BLOB ID" TSRMLS_CC); + RETURN_FALSE; + } + + do { + if (isc_open_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob_id.bl_handle, + &ib_blob_id.bl_qd)) { + break; + } + + while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data) + || IB_STATUS[1] == isc_segment) { + PHPWRITE(bl_data, seg_len); + } + + if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) { + break; + } + + if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) { + break; + } + RETURN_TRUE; + } while (0); + + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto string ibase_blob_import([ resource link_identifier, ] resource file) + Create blob, copy file in it, and close it */ +PHP_FUNCTION(ibase_blob_import) +{ + zval *link = NULL, *file; + int size; + unsigned short b; + ibase_blob ib_blob = { NULL, 0 }; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + char bl_data[IBASE_BLOB_SEG]; + php_stream *stream; + + RESET_ERRMSG; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|r", + (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) { + RETURN_FALSE; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + php_stream_from_zval(stream, &file); + + do { + if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle, + &ib_blob.bl_qd)) { + break; + } + + for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) { + if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) { + break; + } + } + + if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) { + break; + } + RETURN_STRINGL( _php_ibase_quad_to_string(ib_blob.bl_qd), BLOB_ID_LEN, 0); + } while (0); + + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; +} +/* }}} */ + +#endif /* HAVE_IBASE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/interbase/ibase_events.c b/ext/interbase/ibase_events.c new file mode 100644 index 0000000..3380def --- /dev/null +++ b/ext/interbase/ibase_events.c @@ -0,0 +1,410 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_IBASE + +#include "php_interbase.h" +#include "php_ibase_includes.h" + +static int le_event; + +static void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */ +{ + isc_free(event_buf); + isc_free(result_buf); +} +/* }}} */ + +void _php_ibase_free_event(ibase_event *event TSRMLS_DC) /* {{{ */ +{ + unsigned short i; + + event->state = DEAD; + + if (event->link != NULL) { + ibase_event **node; + + if (event->link->handle != NULL && + isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) { + _php_ibase_error(TSRMLS_C); + } + + /* delete this event from the link struct */ + for (node = &event->link->event_head; *node != event; node = &(*node)->event_next); + *node = event->event_next; + } + + if (event->callback) { + zval_dtor(event->callback); + FREE_ZVAL(event->callback); + event->callback = NULL; + + _php_ibase_event_free(event->event_buffer,event->result_buffer); + + for (i = 0; i < event->event_count; ++i) { + efree(event->events[i]); + } + efree(event->events); + } +} +/* }}} */ + +static void _php_ibase_free_event_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_event *e = (ibase_event *) rsrc->ptr; + + _php_ibase_free_event(e TSRMLS_CC); + + efree(e); +} +/* }}} */ + +void php_ibase_events_minit(INIT_FUNC_ARGS) /* {{{ */ +{ + le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL, + "interbase event", module_number); +} +/* }}} */ + +static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, /* {{{ */ + char **events, unsigned short *l, char **event_buf, char **result_buf) +{ + ISC_STATUS dummy_result[20]; + unsigned long dummy_count[15]; + + /** + * Unfortunately, there's no clean and portable way in C to pass arguments to + * a variadic function if you don't know the number of arguments at compile time. + * (And even if there were a way, the Interbase API doesn't provide a version of + * this function that takes a va_list as an argument) + * + * In this case, the number of arguments is limited to 18 by the underlying API, + * so we can work around it. + */ + + *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0], + events[1], events[2], events[3], events[4], events[5], events[6], events[7], + events[8], events[9], events[10], events[11], events[12], events[13], events[14]); + + /** + * Currently, this is the only way to correctly initialize an event buffer. + * This is clearly something that should be fixed, cause the semantics of + * isc_wait_for_event() indicate that it blocks until an event occurs. + * If the Firebird people ever fix this, these lines should be removed, + * otherwise, events will have to fire twice before ibase_wait_event() returns. + */ + + isc_wait_for_event(dummy_result, &ib_link->handle, *l, *event_buf, *result_buf); + isc_event_counts(dummy_count, *l, *event_buf, *result_buf); +} +/* }}} */ + +/* {{{ proto string ibase_wait_event([resource link_identifier,] string event [, string event [, ...]]) + Waits for any one of the passed Interbase events to be posted by the database, and returns its name */ +PHP_FUNCTION(ibase_wait_event) +{ + zval ***args; + ibase_db_link *ib_link; + int num_args; + char *event_buffer, *result_buffer, *events[15]; + unsigned short i = 0, event_count = 0, buffer_size; + unsigned long occurred_event[15]; + + RESET_ERRMSG; + + /* no more than 15 events */ + if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 16) { + WRONG_PARAM_COUNT; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) { + return; + } + + if (Z_TYPE_PP(args[0]) == IS_RESOURCE) { + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) { + efree(args); + RETURN_FALSE; + } + i = 1; + } else { + if (ZEND_NUM_ARGS() > 15) { + efree(args); + WRONG_PARAM_COUNT; + } + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) { + efree(args); + RETURN_FALSE; + } + } + + for (; i < ZEND_NUM_ARGS(); ++i) { + convert_to_string_ex(args[i]); + events[event_count++] = Z_STRVAL_PP(args[i]); + } + + /* fills the required data structure with information about the events */ + _php_ibase_event_block(ib_link, event_count, events, &buffer_size, &event_buffer, &result_buffer); + + /* now block until an event occurs */ + if (isc_wait_for_event(IB_STATUS, &ib_link->handle, buffer_size, event_buffer, result_buffer)) { + _php_ibase_error(TSRMLS_C); + _php_ibase_event_free(event_buffer,result_buffer); + efree(args); + RETURN_FALSE; + } + + /* find out which event occurred */ + isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer); + for (i = 0; i < event_count; ++i) { + if (occurred_event[i]) { + char *result = estrdup(events[i]); + _php_ibase_event_free(event_buffer,result_buffer); + efree(args); + RETURN_STRING(result,0); + } + } + + /* If we reach this line, isc_wait_for_event() did return, but we don't know + which event fired. */ + _php_ibase_event_free(event_buffer,result_buffer); + efree(args); + RETURN_FALSE; +} +/* }}} */ + +static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */ + unsigned short buffer_size, char *result_buf) +{ + /* this function is called asynchronously by the Interbase client library. */ + TSRMLS_FETCH_FROM_CTX(event->thread_ctx); + + /** + * The callback function is called when the event is first registered and when the event + * is cancelled. I consider this is a bug. By clearing event->callback first and setting + * it to -1 later, we make sure nothing happens if no event was actually posted. + */ + switch (event->state) { + unsigned short i; + unsigned long occurred_event[15]; + zval event_name, link_id, return_value, *args[2]; + + default: /* == DEAD */ + break; + case ACTIVE: + args[0] = &event_name; + args[1] = &link_id; + + /* copy the updated results into the result buffer */ + memcpy(event->result_buffer, result_buf, buffer_size); + + INIT_ZVAL(event_name); + INIT_ZVAL(link_id); + ZVAL_RESOURCE(&link_id, event->link_res_id); + + /* find out which event occurred */ + isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer); + for (i = 0; i < event->event_count; ++i) { + if (occurred_event[i]) { + ZVAL_STRING(&event_name,event->events[i],0); + break; + } + } + + /* call the callback provided by the user */ + if (SUCCESS != call_user_function(EG(function_table), NULL, + event->callback, &return_value, 2, args TSRMLS_CC)) { + _php_ibase_module_error("Error calling callback %s" TSRMLS_CC, Z_STRVAL_P(event->callback)); + break; + } + + if (Z_TYPE(return_value) == IS_BOOL && !Z_BVAL(return_value)) { + event->state = DEAD; + break; + } + case NEW: + /* re-register the event */ + if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size, + event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) { + + _php_ibase_error(TSRMLS_C); + } + event->state = ACTIVE; + } + return 0; +} +/* }}} */ + +/* {{{ proto resource ibase_set_event_handler([resource link_identifier,] callback handler, string event [, string event [, ...]]) + Register the callback for handling each of the named events */ +PHP_FUNCTION(ibase_set_event_handler) +{ + /** + * The callback passed to this function should take an event name (string) and a + * link resource id (int) as arguments. The value returned from the function is + * used to determine if the event handler should remain set. + */ + char *cb_name; + zval ***args, **cb_arg; + ibase_db_link *ib_link; + ibase_event *event; + unsigned short i = 1, buffer_size; + int link_res_id, num_args; + + RESET_ERRMSG; + + /* Minimum and maximum number of arguments allowed */ + if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 17) { + WRONG_PARAM_COUNT; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) { + return; + } + + /* get a working link */ + if (Z_TYPE_PP(args[0]) != IS_STRING) { + /* resource, callback, event_1 [, ... event_15] + * No more than 15 events + */ + if (ZEND_NUM_ARGS() < 3 || ZEND_NUM_ARGS() > 17) { + efree(args); + WRONG_PARAM_COUNT; + } + + cb_arg = args[1]; + i = 2; + + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, args[0], -1, "InterBase link", le_link, le_plink)) { + efree(args); + RETURN_FALSE; + } + + convert_to_long_ex(args[0]); + link_res_id = Z_LVAL_PP(args[0]); + + } else { + /* callback, event_1 [, ... event_15] + * No more than 15 events + */ + if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 16) { + efree(args); + WRONG_PARAM_COUNT; + } + + cb_arg = args[0]; + + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink)) { + efree(args); + RETURN_FALSE; + } + link_res_id = IBG(default_link); + } + + /* get the callback */ + if (!zend_is_callable(*cb_arg, 0, &cb_name TSRMLS_CC)) { + _php_ibase_module_error("Callback argument %s is not a callable function" TSRMLS_CC, cb_name); + efree(cb_name); + efree(args); + RETURN_FALSE; + } + efree(cb_name); + + /* allocate the event resource */ + event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0); + TSRMLS_SET_CTX(event->thread_ctx); + event->link_res_id = link_res_id; + event->link = ib_link; + event->event_count = 0; + event->state = NEW; + event->events = (char **) safe_emalloc(sizeof(char *),ZEND_NUM_ARGS()-i,0); + + ALLOC_ZVAL(event->callback); + *event->callback = **cb_arg; + INIT_PZVAL(event->callback); + zval_copy_ctor(event->callback); + + for (; i < ZEND_NUM_ARGS(); ++i) { + convert_to_string_ex(args[i]); + event->events[event->event_count++] = estrdup(Z_STRVAL_PP(args[i])); + } + + /* fills the required data structure with information about the events */ + _php_ibase_event_block(ib_link, event->event_count, event->events, + &buffer_size, &event->event_buffer, &event->result_buffer); + + /* now register the events with the Interbase API */ + if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size, + event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) { + + _php_ibase_error(TSRMLS_C); + efree(event); + efree(args); + RETURN_FALSE; + } + + event->event_next = ib_link->event_head; + ib_link->event_head = event; + + ZEND_REGISTER_RESOURCE(return_value, event, le_event); + zend_list_addref(Z_LVAL_P(return_value)); + efree(args); +} +/* }}} */ + +/* {{{ proto bool ibase_free_event_handler(resource event) + Frees the event handler set by ibase_set_event_handler() */ +PHP_FUNCTION(ibase_free_event_handler) +{ + zval *event_arg; + + RESET_ERRMSG; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &event_arg)) { + ibase_event *event; + + ZEND_FETCH_RESOURCE(event, ibase_event *, &event_arg, -1, "Interbase event", le_event); + + event->state = DEAD; + + zend_list_delete(Z_LVAL_P(event_arg)); + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +#endif /* HAVE_IBASE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/interbase/ibase_query.c b/ext/interbase/ibase_query.c new file mode 100644 index 0000000..b30d741 --- /dev/null +++ b/ext/interbase/ibase_query.c @@ -0,0 +1,2102 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" + +#if HAVE_IBASE + +#include "ext/standard/php_standard.h" +#include "php_interbase.h" +#include "php_ibase_includes.h" + +#define ISC_LONG_MIN INT_MIN +#define ISC_LONG_MAX INT_MAX + +#define QUERY_RESULT 1 +#define EXECUTE_RESULT 2 + +#define FETCH_ROW 1 +#define FETCH_ARRAY 2 + +typedef struct { + ISC_ARRAY_DESC ar_desc; + ISC_LONG ar_size; /* size of entire array in bytes */ + unsigned short el_type, el_size; +} ibase_array; + +typedef struct { + ibase_db_link *link; + ibase_trans *trans; + struct _ib_query *query; + isc_stmt_handle stmt; + unsigned short type; + unsigned char has_more_rows, statement_type; + XSQLDA *out_sqlda; + ibase_array out_array[1]; /* last member */ +} ibase_result; + +typedef struct _ib_query { + ibase_db_link *link; + ibase_trans *trans; + ibase_result *result; + int result_res_id; + isc_stmt_handle stmt; + XSQLDA *in_sqlda, *out_sqlda; + ibase_array *in_array, *out_array; + unsigned short in_array_cnt, out_array_cnt; + unsigned short dialect; + char statement_type; + char *query; + long trans_res_id; +} ibase_query; + +typedef struct { + unsigned short vary_length; + char vary_string[1]; +} IBVARY; + +/* sql variables union + * used for convert and binding input variables + */ +typedef struct { + union { + short sval; + float fval; + ISC_LONG lval; + ISC_QUAD qval; + ISC_TIMESTAMP tsval; + ISC_DATE dtval; + ISC_TIME tmval; + } val; + short sqlind; +} BIND_BUF; + +static int le_result, le_query; + +#define LE_RESULT "Firebird/InterBase result" +#define LE_QUERY "Firebird/InterBase query" + +static void _php_ibase_free_xsqlda(XSQLDA *sqlda) /* {{{ */ +{ + int i; + XSQLVAR *var; + + IBDEBUG("Free XSQLDA?"); + if (sqlda) { + IBDEBUG("Freeing XSQLDA..."); + var = sqlda->sqlvar; + for (i = 0; i < sqlda->sqld; i++, var++) { + efree(var->sqldata); + if (var->sqlind) { + efree(var->sqlind); + } + } + efree(sqlda); + } +} +/* }}} */ + +static void _php_ibase_free_stmt_handle(ibase_db_link *link, isc_stmt_handle stmt TSRMLS_DC) /* {{{ */ +{ + static char info[] = { isc_info_base_level, isc_info_end }; + + if (stmt) { + char res_buf[8]; + IBDEBUG("Dropping statement handle (free_stmt_handle)..."); + /* Only free statement if db-connection is still open */ + if (SUCCESS == isc_database_info(IB_STATUS, &link->handle, + sizeof(info), info, sizeof(res_buf), res_buf)) { + if (isc_dsql_free_statement(IB_STATUS, &stmt, DSQL_drop)) { + _php_ibase_error(TSRMLS_C); + } + } + } +} +/* }}} */ + +static void _php_ibase_free_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_result *ib_result = (ibase_result *) rsrc->ptr; + + IBDEBUG("Freeing result by dtor..."); + if (ib_result) { + _php_ibase_free_xsqlda(ib_result->out_sqlda); + if (ib_result->query != NULL) { + IBDEBUG("query still valid; don't drop statement handle"); + ib_result->query->result = NULL; /* Indicate to query, that result is released */ + } else { + _php_ibase_free_stmt_handle(ib_result->link, ib_result->stmt TSRMLS_CC); + } + efree(ib_result); + } +} +/* }}} */ + +static void _php_ibase_free_query(ibase_query *ib_query TSRMLS_DC) /* {{{ */ +{ + IBDEBUG("Freeing query..."); + + if (ib_query->in_sqlda) { + efree(ib_query->in_sqlda); + } + if (ib_query->out_sqlda) { + efree(ib_query->out_sqlda); + } + if (ib_query->result != NULL) { + IBDEBUG("result still valid; don't drop statement handle"); + ib_query->result->query = NULL; /* Indicate to result, that query is released */ + } else { + _php_ibase_free_stmt_handle(ib_query->link, ib_query->stmt TSRMLS_CC); + } + if (ib_query->in_array) { + efree(ib_query->in_array); + } + if (ib_query->out_array) { + efree(ib_query->out_array); + } + if (ib_query->query) { + efree(ib_query->query); + } +} +/* }}} */ + +static void php_ibase_free_query_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_query *ib_query = (ibase_query *)rsrc->ptr; + + if (ib_query != NULL) { + IBDEBUG("Preparing to free query by dtor..."); + _php_ibase_free_query(ib_query TSRMLS_CC); + efree(ib_query); + } +} +/* }}} */ + +void php_ibase_query_minit(INIT_FUNC_ARGS) /* {{{ */ +{ + le_result = zend_register_list_destructors_ex(_php_ibase_free_result, NULL, + "interbase result", module_number); + le_query = zend_register_list_destructors_ex(php_ibase_free_query_rsrc, NULL, + "interbase query", module_number); +} +/* }}} */ + +static int _php_ibase_alloc_array(ibase_array **ib_arrayp, XSQLDA *sqlda, /* {{{ */ + isc_db_handle link, isc_tr_handle trans, unsigned short *array_cnt TSRMLS_DC) +{ + unsigned short i, n; + ibase_array *ar; + + /* first check if we have any arrays at all */ + for (i = *array_cnt = 0; i < sqlda->sqld; ++i) { + if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_ARRAY) { + ++*array_cnt; + } + } + if (! *array_cnt) return SUCCESS; + + ar = safe_emalloc(sizeof(ibase_array), *array_cnt, 0); + + for (i = n = 0; i < sqlda->sqld; ++i) { + unsigned short dim; + unsigned long ar_size = 1; + XSQLVAR *var = &sqlda->sqlvar[i]; + + if ((var->sqltype & ~1) == SQL_ARRAY) { + ibase_array *a = &ar[n++]; + ISC_ARRAY_DESC *ar_desc = &a->ar_desc; + + if (isc_array_lookup_bounds(IB_STATUS, &link, &trans, var->relname, + var->sqlname, ar_desc)) { + _php_ibase_error(TSRMLS_C); + efree(ar); + return FAILURE; + } + + switch (ar_desc->array_desc_dtype) { + case blr_text: + case blr_text2: + a->el_type = SQL_TEXT; + a->el_size = ar_desc->array_desc_length; + break; + case blr_short: + a->el_type = SQL_SHORT; + a->el_size = sizeof(short); + break; + case blr_long: + a->el_type = SQL_LONG; + a->el_size = sizeof(ISC_LONG); + break; + case blr_float: + a->el_type = SQL_FLOAT; + a->el_size = sizeof(float); + break; + case blr_double: + a->el_type = SQL_DOUBLE; + a->el_size = sizeof(double); + break; + case blr_int64: + a->el_type = SQL_INT64; + a->el_size = sizeof(ISC_INT64); + break; + case blr_timestamp: + a->el_type = SQL_TIMESTAMP; + a->el_size = sizeof(ISC_TIMESTAMP); + break; + case blr_sql_date: + a->el_type = SQL_TYPE_DATE; + a->el_size = sizeof(ISC_DATE); + break; + case blr_sql_time: + a->el_type = SQL_TYPE_TIME; + a->el_size = sizeof(ISC_TIME); + break; + case blr_varying: + case blr_varying2: + /** + * IB has a strange way of handling VARCHAR arrays. It doesn't store + * the length in the first short, as with VARCHAR fields. It does, + * however, expect the extra short to be allocated for each element. + */ + a->el_type = SQL_TEXT; + a->el_size = ar_desc->array_desc_length + sizeof(short); + break; + case blr_quad: + case blr_blob_id: + case blr_cstring: + case blr_cstring2: + /** + * These types are mentioned as array types in the manual, but I + * wouldn't know how to create an array field with any of these + * types. I assume these types are not applicable to arrays, and + * were mentioned erroneously. + */ + default: + _php_ibase_module_error("Unsupported array type %d in relation '%s' column '%s'" + TSRMLS_CC, ar_desc->array_desc_dtype, var->relname, var->sqlname); + efree(ar); + return FAILURE; + } /* switch array_desc_type */ + + /* calculate elements count */ + for (dim = 0; dim < ar_desc->array_desc_dimensions; dim++) { + ar_size *= 1 + ar_desc->array_desc_bounds[dim].array_bound_upper + -ar_desc->array_desc_bounds[dim].array_bound_lower; + } + a->ar_size = a->el_size * ar_size; + } /* if SQL_ARRAY */ + } /* for column */ + *ib_arrayp = ar; + return SUCCESS; +} +/* }}} */ + +/* allocate and prepare query */ +static int _php_ibase_alloc_query(ibase_query *ib_query, ibase_db_link *link, /* {{{ */ + ibase_trans *trans, char *query, unsigned short dialect, int trans_res_id TSRMLS_DC) +{ + static char info_type[] = {isc_info_sql_stmt_type}; + char result[8]; + + /* Return FAILURE, if querystring is empty */ + if (*query == '\0') { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Querystring empty."); + return FAILURE; + } + + ib_query->link = link; + ib_query->trans = trans; + ib_query->result_res_id = 0; + ib_query->result = NULL; + ib_query->stmt = NULL; + ib_query->in_array = NULL; + ib_query->out_array = NULL; + ib_query->dialect = dialect; + ib_query->query = estrdup(query); + ib_query->trans_res_id = trans_res_id; + ib_query->out_sqlda = NULL; + ib_query->in_sqlda = NULL; + + if (isc_dsql_allocate_statement(IB_STATUS, &link->handle, &ib_query->stmt)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + + ib_query->out_sqlda = (XSQLDA *) emalloc(XSQLDA_LENGTH(1)); + ib_query->out_sqlda->sqln = 1; + ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION; + + if (isc_dsql_prepare(IB_STATUS, &ib_query->trans->handle, &ib_query->stmt, + 0, query, dialect, ib_query->out_sqlda)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + + /* find out what kind of statement was prepared */ + if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_type), + info_type, sizeof(result), result)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + ib_query->statement_type = result[3]; + + /* not enough output variables ? */ + if (ib_query->out_sqlda->sqld > ib_query->out_sqlda->sqln) { + ib_query->out_sqlda = erealloc(ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld)); + ib_query->out_sqlda->sqln = ib_query->out_sqlda->sqld; + ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION; + if (isc_dsql_describe(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->out_sqlda)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + } + + /* maybe have input placeholders? */ + ib_query->in_sqlda = emalloc(XSQLDA_LENGTH(1)); + ib_query->in_sqlda->sqln = 1; + ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION; + if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + + /* not enough input variables ? */ + if (ib_query->in_sqlda->sqln < ib_query->in_sqlda->sqld) { + ib_query->in_sqlda = erealloc(ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld)); + ib_query->in_sqlda->sqln = ib_query->in_sqlda->sqld; + ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION; + + if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt, + SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_alloc_query_error; + } + } + + /* no, haven't placeholders at all */ + if (ib_query->in_sqlda->sqld == 0) { + efree(ib_query->in_sqlda); + ib_query->in_sqlda = NULL; + } else if (FAILURE == _php_ibase_alloc_array(&ib_query->in_array, ib_query->in_sqlda, + link->handle, trans->handle, &ib_query->in_array_cnt TSRMLS_CC)) { + goto _php_ibase_alloc_query_error; + } + + if (ib_query->out_sqlda->sqld == 0) { + efree(ib_query->out_sqlda); + ib_query->out_sqlda = NULL; + } else if (FAILURE == _php_ibase_alloc_array(&ib_query->out_array, ib_query->out_sqlda, + link->handle, trans->handle, &ib_query->out_array_cnt TSRMLS_CC)) { + goto _php_ibase_alloc_query_error; + } + + return SUCCESS; + +_php_ibase_alloc_query_error: + + if (ib_query->out_sqlda) { + efree(ib_query->out_sqlda); + } + if (ib_query->in_sqlda) { + efree(ib_query->in_sqlda); + } + if (ib_query->out_array) { + efree(ib_query->out_array); + } + if (ib_query->query) { + efree(ib_query->query); + } + return FAILURE; +} +/* }}} */ + +static int _php_ibase_bind_array(zval *val, char *buf, unsigned long buf_size, /* {{{ */ + ibase_array *array, int dim TSRMLS_DC) +{ + zval null_val, *pnull_val = &null_val; + int u_bound = array->ar_desc.array_desc_bounds[dim].array_bound_upper, + l_bound = array->ar_desc.array_desc_bounds[dim].array_bound_lower, + dim_len = 1 + u_bound - l_bound; + + ZVAL_NULL(pnull_val); + + if (dim < array->ar_desc.array_desc_dimensions) { + unsigned long slice_size = buf_size / dim_len; + unsigned short i; + zval **subval = &val; + + if (Z_TYPE_P(val) == IS_ARRAY) { + zend_hash_internal_pointer_reset(Z_ARRVAL_P(val)); + } + + for (i = 0; i < dim_len; ++i) { + + if (Z_TYPE_P(val) == IS_ARRAY && + zend_hash_get_current_data(Z_ARRVAL_P(val), (void *) &subval) == FAILURE) + { + subval = &pnull_val; + } + + if (_php_ibase_bind_array(*subval, buf, slice_size, array, dim+1 TSRMLS_CC) == FAILURE) + { + return FAILURE; + } + buf += slice_size; + + if (Z_TYPE_P(val) == IS_ARRAY) { + zend_hash_move_forward(Z_ARRVAL_P(val)); + } + } + + if (Z_TYPE_P(val) == IS_ARRAY) { + zend_hash_internal_pointer_reset(Z_ARRVAL_P(val)); + } + + } else { + /* expect a single value */ + if (Z_TYPE_P(val) == IS_NULL) { + memset(buf, 0, buf_size); + } else if (array->ar_desc.array_desc_scale < 0) { + + /* no coercion for array types */ + double l; + + convert_to_double(val); + + if (Z_DVAL_P(val) > 0) { + l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) + .5; + } else { + l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) - .5; + } + + switch (array->el_type) { + case SQL_SHORT: + if (l > SHRT_MAX || l < SHRT_MIN) { + _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC); + return FAILURE; + } + *(short*) buf = (short) l; + break; + case SQL_LONG: + if (l > ISC_LONG_MAX || l < ISC_LONG_MIN) { + _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC); + return FAILURE; + } + *(ISC_LONG*) buf = (ISC_LONG) l; + break; + case SQL_INT64: + { + long double l; + + convert_to_string(val); + + if (!sscanf(Z_STRVAL_P(val), "%Lf", &l)) { + _php_ibase_module_error("Cannot convert '%s' to long double" + TSRMLS_CC, Z_STRVAL_P(val)); + return FAILURE; + } + + if (l > 0) { + *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, + -array->ar_desc.array_desc_scale) + .5); + } else { + *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, + -array->ar_desc.array_desc_scale) - .5); + } + } + break; + } + } else { + struct tm t = { 0, 0, 0, 0, 0, 0 }; + + switch (array->el_type) { + unsigned short n; + ISC_INT64 l; + + case SQL_SHORT: + convert_to_long(val); + if (Z_LVAL_P(val) > SHRT_MAX || Z_LVAL_P(val) < SHRT_MIN) { + _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC); + return FAILURE; + } + *(short *) buf = (short) Z_LVAL_P(val); + break; + case SQL_LONG: + convert_to_long(val); +#if (SIZEOF_LONG > 4) + if (Z_LVAL_P(val) > ISC_LONG_MAX || Z_LVAL_P(val) < ISC_LONG_MIN) { + _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC); + return FAILURE; + } +#endif + *(ISC_LONG *) buf = (ISC_LONG) Z_LVAL_P(val); + break; + case SQL_INT64: +#if (SIZEOF_LONG >= 8) + convert_to_long(val); + *(long *) buf = Z_LVAL_P(val); +#else + convert_to_string(val); + if (!sscanf(Z_STRVAL_P(val), "%" LL_MASK "d", &l)) { + _php_ibase_module_error("Cannot convert '%s' to long integer" + TSRMLS_CC, Z_STRVAL_P(val)); + return FAILURE; + } else { + *(ISC_INT64 *) buf = l; + } +#endif + break; + case SQL_FLOAT: + convert_to_double(val); + *(float*) buf = (float) Z_DVAL_P(val); + break; + case SQL_DOUBLE: + convert_to_double(val); + *(double*) buf = Z_DVAL_P(val); + break; + case SQL_TIMESTAMP: + convert_to_string(val); +#ifdef HAVE_STRPTIME + strptime(Z_STRVAL_P(val), INI_STR("ibase.timestampformat"), &t); +#else + n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d %d%*[:]%d%*[:]%d", + &t.tm_mon, &t.tm_mday, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec); + + if (n != 3 && n != 6) { + _php_ibase_module_error("Invalid date/time format (expected 3 or 6 fields, got %d." + " Use format 'm/d/Y H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val)); + return FAILURE; + } + t.tm_year -= 1900; + t.tm_mon--; +#endif + isc_encode_timestamp(&t, (ISC_TIMESTAMP * ) buf); + break; + case SQL_TYPE_DATE: + convert_to_string(val); +#ifdef HAVE_STRPTIME + strptime(Z_STRVAL_P(val), INI_STR("ibase.dateformat"), &t); +#else + n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d", &t.tm_mon, &t.tm_mday, &t.tm_year); + + if (n != 3) { + _php_ibase_module_error("Invalid date format (expected 3 fields, got %d. " + "Use format 'm/d/Y' You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val)); + return FAILURE; + } + t.tm_year -= 1900; + t.tm_mon--; +#endif + isc_encode_sql_date(&t, (ISC_DATE *) buf); + break; + case SQL_TYPE_TIME: + convert_to_string(val); +#ifdef HAVE_STRPTIME + strptime(Z_STRVAL_P(val), INI_STR("ibase.timeformat"), &t); +#else + n = sscanf(Z_STRVAL_P(val), "%d%*[:]%d%*[:]%d", &t.tm_hour, &t.tm_min, &t.tm_sec); + + if (n != 3) { + _php_ibase_module_error("Invalid time format (expected 3 fields, got %d. " + "Use format 'H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val)); + return FAILURE; + } +#endif + isc_encode_sql_time(&t, (ISC_TIME *) buf); + break; + default: + convert_to_string(val); + strlcpy(buf, Z_STRVAL_P(val), buf_size); + } + } + } + return SUCCESS; +} +/* }}} */ + +static int _php_ibase_bind(XSQLDA *sqlda, zval ***b_vars, BIND_BUF *buf, /* {{{ */ + ibase_query *ib_query TSRMLS_DC) +{ + int i, array_cnt = 0, rv = SUCCESS; + + for (i = 0; i < sqlda->sqld; ++i) { /* bound vars */ + + zval *b_var = *b_vars[i]; + XSQLVAR *var = &sqlda->sqlvar[i]; + + var->sqlind = &buf[i].sqlind; + + /* check if a NULL should be inserted */ + switch (Z_TYPE_P(b_var)) { + int force_null; + + case IS_STRING: + + force_null = 0; + + /* for these types, an empty string can be handled like a NULL value */ + switch (var->sqltype & ~1) { + case SQL_SHORT: + case SQL_LONG: + case SQL_INT64: + case SQL_FLOAT: + case SQL_DOUBLE: + case SQL_TIMESTAMP: + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + force_null = (Z_STRLEN_P(b_var) == 0); + } + + if (! force_null) break; + + case IS_NULL: + buf[i].sqlind = -1; + + if (var->sqltype & SQL_ARRAY) ++array_cnt; + + continue; + } + + /* if we make it to this point, we must provide a value for the parameter */ + + buf[i].sqlind = 0; + + var->sqldata = (void*)&buf[i].val; + + switch (var->sqltype & ~1) { + struct tm t; + + case SQL_TIMESTAMP: + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + if (Z_TYPE_P(b_var) == IS_LONG) { + struct tm *res; + res = php_gmtime_r(&Z_LVAL_P(b_var), &t); + if (!res) { + return FAILURE; + } + } else { +#ifdef HAVE_STRPTIME + char *format = INI_STR("ibase.timestampformat"); + + convert_to_string(b_var); + + switch (var->sqltype & ~1) { + case SQL_TYPE_DATE: + format = INI_STR("ibase.dateformat"); + break; + case SQL_TYPE_TIME: + format = INI_STR("ibase.timeformat"); + } + if (!strptime(Z_STRVAL_P(b_var), format, &t)) { + /* strptime() cannot handle it, so let IB have a try */ + break; + } +#else /* ifndef HAVE_STRPTIME */ + break; /* let IB parse it as a string */ +#endif + } + + switch (var->sqltype & ~1) { + default: /* == case SQL_TIMESTAMP */ + isc_encode_timestamp(&t, &buf[i].val.tsval); + break; + case SQL_TYPE_DATE: + isc_encode_sql_date(&t, &buf[i].val.dtval); + break; + case SQL_TYPE_TIME: + isc_encode_sql_time(&t, &buf[i].val.tmval); + break; + } + continue; + + case SQL_BLOB: + + convert_to_string(b_var); + + if (Z_STRLEN_P(b_var) != BLOB_ID_LEN || + !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) { + + ibase_blob ib_blob = { NULL, BLOB_INPUT }; + + if (isc_create_blob(IB_STATUS, &ib_query->link->handle, + &ib_query->trans->handle, &ib_blob.bl_handle, &ib_blob.bl_qd)) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + + if (_php_ibase_blob_add(&b_var, &ib_blob TSRMLS_CC) != SUCCESS) { + return FAILURE; + } + + if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + buf[i].val.qval = ib_blob.bl_qd; + } + continue; + + case SQL_ARRAY: + + if (Z_TYPE_P(b_var) != IS_ARRAY) { + convert_to_string(b_var); + + if (Z_STRLEN_P(b_var) != BLOB_ID_LEN || + !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) { + + _php_ibase_module_error("Parameter %d: invalid array ID" TSRMLS_CC,i+1); + rv = FAILURE; + } + } else { + /* convert the array data into something IB can understand */ + ibase_array *ar = &ib_query->in_array[array_cnt]; + void *array_data = emalloc(ar->ar_size); + ISC_QUAD array_id = { 0, 0 }; + + if (FAILURE == _php_ibase_bind_array(b_var, array_data, ar->ar_size, + ar, 0 TSRMLS_CC)) { + _php_ibase_module_error("Parameter %d: failed to bind array argument" + TSRMLS_CC,i+1); + efree(array_data); + rv = FAILURE; + continue; + } + + if (isc_array_put_slice(IB_STATUS, &ib_query->link->handle, &ib_query->trans->handle, + &array_id, &ar->ar_desc, array_data, &ar->ar_size)) { + _php_ibase_error(TSRMLS_C); + efree(array_data); + return FAILURE; + } + buf[i].val.qval = array_id; + efree(array_data); + } + ++array_cnt; + continue; + } /* switch */ + + /* we end up here if none of the switch cases handled the field */ + convert_to_string(b_var); + var->sqldata = Z_STRVAL_P(b_var); + var->sqllen = Z_STRLEN_P(b_var); + var->sqltype = SQL_TEXT; + } /* for */ + return rv; +} +/* }}} */ + +static void _php_ibase_alloc_xsqlda(XSQLDA *sqlda) /* {{{ */ +{ + int i; + + for (i = 0; i < sqlda->sqld; i++) { + XSQLVAR *var = &sqlda->sqlvar[i]; + + switch (var->sqltype & ~1) { + case SQL_TEXT: + var->sqldata = safe_emalloc(sizeof(char), var->sqllen, 0); + break; + case SQL_VARYING: + var->sqldata = safe_emalloc(sizeof(char), var->sqllen + sizeof(short), 0); + break; + case SQL_SHORT: + var->sqldata = emalloc(sizeof(short)); + break; + case SQL_LONG: + var->sqldata = emalloc(sizeof(ISC_LONG)); + break; + case SQL_FLOAT: + var->sqldata = emalloc(sizeof(float)); + break; + case SQL_DOUBLE: + var->sqldata = emalloc(sizeof(double)); + break; + case SQL_INT64: + var->sqldata = emalloc(sizeof(ISC_INT64)); + break; + case SQL_TIMESTAMP: + var->sqldata = emalloc(sizeof(ISC_TIMESTAMP)); + break; + case SQL_TYPE_DATE: + var->sqldata = emalloc(sizeof(ISC_DATE)); + break; + case SQL_TYPE_TIME: + var->sqldata = emalloc(sizeof(ISC_TIME)); + break; + case SQL_BLOB: + case SQL_ARRAY: + var->sqldata = emalloc(sizeof(ISC_QUAD)); + break; + } /* switch */ + + if (var->sqltype & 1) { /* sql NULL flag */ + var->sqlind = emalloc(sizeof(short)); + } else { + var->sqlind = NULL; + } + } /* for */ +} +/* }}} */ + +static int _php_ibase_exec(INTERNAL_FUNCTION_PARAMETERS, ibase_result **ib_resultp, /* {{{ */ + ibase_query *ib_query, zval ***args) +{ + XSQLDA *in_sqlda = NULL, *out_sqlda = NULL; + BIND_BUF *bind_buf = NULL; + int i, rv = FAILURE; + static char info_count[] = { isc_info_sql_records }; + char result[64]; + ISC_STATUS isc_result; + int argc = ib_query->in_sqlda ? ib_query->in_sqlda->sqld : 0; + + RESET_ERRMSG; + + for (i = 0; i < argc; ++i) { + SEPARATE_ZVAL(args[i]); + } + + switch (ib_query->statement_type) { + isc_tr_handle tr; + ibase_tr_list **l; + ibase_trans *trans; + + case isc_info_sql_stmt_start_trans: + + /* a SET TRANSACTION statement should be executed with a NULL trans handle */ + tr = NULL; + + if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, &tr, 0, + ib_query->query, ib_query->dialect, NULL)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_exec_error; + } + + trans = (ibase_trans *) emalloc(sizeof(ibase_trans)); + trans->handle = tr; + trans->link_cnt = 1; + trans->affected_rows = 0; + trans->db_link[0] = ib_query->link; + + if (ib_query->link->tr_list == NULL) { + ib_query->link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list)); + ib_query->link->tr_list->trans = NULL; + ib_query->link->tr_list->next = NULL; + } + + /* link the transaction into the connection-transaction list */ + for (l = &ib_query->link->tr_list; *l != NULL; l = &(*l)->next); + *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list)); + (*l)->trans = trans; + (*l)->next = NULL; + + ZEND_REGISTER_RESOURCE(return_value, trans, le_trans); + + return SUCCESS; + + case isc_info_sql_stmt_commit: + case isc_info_sql_stmt_rollback: + + if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, + &ib_query->trans->handle, 0, ib_query->query, ib_query->dialect, NULL)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_exec_error; + } + + if (ib_query->trans->handle == NULL && ib_query->trans_res_id != 0) { + /* transaction was released by the query and was a registered resource, + so we have to release it */ + zend_list_delete(ib_query->trans_res_id); + } + + RETVAL_TRUE; + + return SUCCESS; + + default: + RETVAL_FALSE; + } + + /* allocate sqlda and output buffers */ + if (ib_query->out_sqlda) { /* output variables in select, select for update */ + ibase_result *res; + + IBDEBUG("Query wants XSQLDA for output"); + res = emalloc(sizeof(ibase_result)+sizeof(ibase_array)*max(0,ib_query->out_array_cnt-1)); + res->link = ib_query->link; + res->trans = ib_query->trans; + res->stmt = ib_query->stmt; + /* ib_result and ib_query point at each other to handle release of statement handle properly */ + res->query = ib_query; + ib_query->result = res; + res->statement_type = ib_query->statement_type; + res->has_more_rows = 1; + + out_sqlda = res->out_sqlda = emalloc(XSQLDA_LENGTH(ib_query->out_sqlda->sqld)); + memcpy(out_sqlda, ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld)); + _php_ibase_alloc_xsqlda(out_sqlda); + + if (ib_query->out_array) { + memcpy(&res->out_array, ib_query->out_array, sizeof(ibase_array)*ib_query->out_array_cnt); + } + *ib_resultp = res; + } + + if (ib_query->in_sqlda) { /* has placeholders */ + IBDEBUG("Query wants XSQLDA for input"); + in_sqlda = emalloc(XSQLDA_LENGTH(ib_query->in_sqlda->sqld)); + memcpy(in_sqlda, ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld)); + bind_buf = safe_emalloc(sizeof(BIND_BUF), ib_query->in_sqlda->sqld, 0); + if (_php_ibase_bind(in_sqlda, args, bind_buf, ib_query TSRMLS_CC) == FAILURE) { + IBDEBUG("Could not bind input XSQLDA"); + goto _php_ibase_exec_error; + } + } + + if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) { + isc_result = isc_dsql_execute2(IB_STATUS, &ib_query->trans->handle, + &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda, out_sqlda); + } else { + isc_result = isc_dsql_execute(IB_STATUS, &ib_query->trans->handle, + &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda); + } + if (isc_result) { + IBDEBUG("Could not execute query"); + _php_ibase_error(TSRMLS_C); + goto _php_ibase_exec_error; + } + ib_query->trans->affected_rows = 0; + + switch (ib_query->statement_type) { + + unsigned long affected_rows; + + case isc_info_sql_stmt_insert: + case isc_info_sql_stmt_update: + case isc_info_sql_stmt_delete: + case isc_info_sql_stmt_exec_procedure: + + if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_count), + info_count, sizeof(result), result)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_exec_error; + } + + affected_rows = 0; + + if (result[0] == isc_info_sql_records) { + unsigned i = 3, result_size = isc_vax_integer(&result[1],2); + + while (result[i] != isc_info_end && i < result_size) { + short len = (short)isc_vax_integer(&result[i+1],2); + if (result[i] != isc_info_req_select_count) { + affected_rows += isc_vax_integer(&result[i+3],len); + } + i += len+3; + } + } + + ib_query->trans->affected_rows = affected_rows; + + if (!ib_query->out_sqlda) { /* no result set is being returned */ + if (affected_rows) { + RETVAL_LONG(affected_rows); + } else { + RETVAL_TRUE; + } + break; + } + default: + RETVAL_TRUE; + } + + rv = SUCCESS; + +_php_ibase_exec_error: + + if (in_sqlda) { + efree(in_sqlda); + } + if (bind_buf) + efree(bind_buf); + + if (rv == FAILURE) { + if (*ib_resultp) { + efree(*ib_resultp); + *ib_resultp = NULL; + } + if (out_sqlda) { + _php_ibase_free_xsqlda(out_sqlda); + } + } + + return rv; +} +/* }}} */ + +/* {{{ proto mixed ibase_query([resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]]) + Execute a query */ +PHP_FUNCTION(ibase_query) +{ + zval *zlink, *ztrans, ***bind_args = NULL; + char *query; + int bind_i, query_len, bind_num; + long trans_res_id = 0; + ibase_db_link *ib_link = NULL; + ibase_trans *trans = NULL; + ibase_query ib_query = { NULL, NULL, 0, 0 }; + ibase_result *result = NULL; + + RESET_ERRMSG; + + RETVAL_FALSE; + + switch (ZEND_NUM_ARGS()) { + long l; + + default: + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, 3 TSRMLS_CC, "rrs", + &zlink, &ztrans, &query, &query_len)) { + + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link*, &zlink, -1, LE_LINK, le_link, le_plink); + ZEND_FETCH_RESOURCE(trans, ibase_trans*, &ztrans, -1, LE_TRANS, le_trans); + + trans_res_id = Z_LVAL_P(ztrans); + bind_i = 3; + break; + } + case 2: + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, 2 TSRMLS_CC, "rs", + &zlink, &query, &query_len)) { + _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, &zlink, &ib_link, &trans); + + if (trans != NULL) { + trans_res_id = Z_LVAL_P(zlink); + } + bind_i = 2; + break; + } + + /* the statement is 'CREATE DATABASE ...' if the link argument is IBASE_CREATE */ + if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() + TSRMLS_CC, "ls", &l, &query, &query_len) && l == PHP_IBASE_CREATE) { + isc_db_handle db = NULL; + isc_tr_handle trans = NULL; + + if (PG(sql_safe_mode)) { + _php_ibase_module_error("CREATE DATABASE is not allowed in SQL safe mode" + TSRMLS_CC); + + } else if (((l = INI_INT("ibase.max_links")) != -1) && (IBG(num_links) >= l)) { + _php_ibase_module_error("CREATE DATABASE is not allowed: maximum link count " + "(%ld) reached" TSRMLS_CC, l); + + } else if (isc_dsql_execute_immediate(IB_STATUS, &db, &trans, (short)query_len, + query, SQL_DIALECT_CURRENT, NULL)) { + _php_ibase_error(TSRMLS_C); + + } else if (!db) { + _php_ibase_module_error("Connection to created database could not be " + "established" TSRMLS_CC); + + } else { + + /* register the link as a resource; unfortunately, we cannot register + it in the hash table, because we don't know the connection params */ + ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link)); + ib_link->handle = db; + ib_link->dialect = SQL_DIALECT_CURRENT; + ib_link->tr_list = NULL; + ib_link->event_head = NULL; + + ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link); + zend_list_addref(Z_LVAL_P(return_value)); + IBG(default_link) = Z_LVAL_P(return_value); + ++IBG(num_links); + } + return; + } + case 1: + case 0: + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() ? 1 : 0 TSRMLS_CC, "s", &query, + &query_len)) { + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), LE_LINK, + le_link, le_plink); + + bind_i = 1; + break; + } + return; + } + + /* open default transaction */ + if (ib_link == NULL || FAILURE == _php_ibase_def_trans(ib_link, &trans TSRMLS_CC) + || FAILURE == _php_ibase_alloc_query(&ib_query, ib_link, trans, query, ib_link->dialect, + trans_res_id TSRMLS_CC)) { + return; + } + + do { + int bind_n = ZEND_NUM_ARGS() - bind_i, + expected_n = ib_query.in_sqlda ? ib_query.in_sqlda->sqld : 0; + + if (bind_n != expected_n) { + php_error_docref(NULL TSRMLS_CC, (bind_n < expected_n) ? E_WARNING : E_NOTICE, + "Statement expects %d arguments, %d given", expected_n, bind_n); + if (bind_n < expected_n) { + break; + } + } else if (bind_n > 0) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &bind_args, &bind_num) == FAILURE) { + return; + } + } + + if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, &ib_query, + &bind_args[bind_i])) { + break; + } + + if (result != NULL) { /* statement returns a result */ + result->type = QUERY_RESULT; + + /* EXECUTE PROCEDURE returns only one row => statement can be released immediately */ + if (ib_query.statement_type != isc_info_sql_stmt_exec_procedure) { + ib_query.stmt = NULL; /* keep stmt when free query */ + } + ZEND_REGISTER_RESOURCE(return_value, result, le_result); + } + } while (0); + + _php_ibase_free_query(&ib_query TSRMLS_CC); + + if (bind_args) { + efree(bind_args); + } +} +/* }}} */ + +/* {{{ proto int ibase_affected_rows( [ resource link_identifier ] ) + Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement */ +PHP_FUNCTION(ibase_affected_rows) +{ + ibase_trans *trans = NULL; + ibase_db_link *ib_link; + zval *arg = NULL; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &arg) == FAILURE) { + return; + } + + if (!arg) { + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink); + if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) { + RETURN_FALSE; + } + trans = ib_link->tr_list->trans; + } else { + /* one id was passed, could be db or trans id */ + _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, &arg, &ib_link, &trans); + if (trans == NULL) { + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &arg, -1, LE_LINK, le_link, le_plink); + + if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) { + RETURN_FALSE; + } + trans = ib_link->tr_list->trans; + } + } + RETURN_LONG(trans->affected_rows); +} +/* }}} */ + +/* {{{ proto int ibase_num_rows( resource result_identifier ) + Return the number of rows that are available in a result */ +#if abies_0 +PHP_FUNCTION(ibase_num_rows) +{ + /** + * As this function relies on the InterBase API function isc_dsql_sql_info() + * which has a couple of limitations (which I hope will be fixed in future + * releases of Firebird), this function is fairly useless. I'm leaving it + * in place for people who can live with the limitations, which I only + * found out about after I had implemented it anyway. + * + * Currently, there's no way to determine how many rows can be fetched from + * a cursor. The only number that _can_ be determined is the number of rows + * that have already been pre-fetched by the client library. + * This implies the following: + * - num_rows() always returns zero before the first fetch; + * - num_rows() for SELECT ... FOR UPDATE is broken -> never returns a + * higher number than the number of records fetched so far (no pre-fetch); + * - the result of num_rows() for other statements is merely a lower bound + * on the number of records => calling ibase_num_rows() again after a couple + * of fetches will most likely return a new (higher) figure for large result + * sets. + */ + + zval *result_arg; + ibase_result *ib_result; + static char info_count[] = {isc_info_sql_records}; + char result[64]; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result_arg) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result_arg, -1, LE_RESULT, le_result); + + if (isc_dsql_sql_info(IB_STATUS, &ib_result->stmt, sizeof(info_count), info_count, sizeof(result), result)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + if (result[0] == isc_info_sql_records) { + unsigned i = 3, result_size = isc_vax_integer(&result[1],2); + + while (result[i] != isc_info_end && i < result_size) { + short len = (short)isc_vax_integer(&result[i+1],2); + if (result[i] == isc_info_req_select_count) { + RETURN_LONG(isc_vax_integer(&result[i+3],len)); + } + i += len+3; + } + } +} +#endif +/* }}} */ + +static int _php_ibase_var_zval(zval *val, void *data, int type, int len, /* {{{ */ + int scale, int flag TSRMLS_DC) +{ + static ISC_INT64 const scales[] = { 1, 10, 100, 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + LL_LIT(10000000000), + LL_LIT(100000000000), + LL_LIT(1000000000000), + LL_LIT(10000000000000), + LL_LIT(100000000000000), + LL_LIT(1000000000000000), + LL_LIT(10000000000000000), + LL_LIT(100000000000000000), + LL_LIT(1000000000000000000) + }; + + switch (type & ~1) { + unsigned short l; + long n; + char string_data[255]; + struct tm t; + char *format; + + case SQL_VARYING: + len = ((IBVARY *) data)->vary_length; + data = ((IBVARY *) data)->vary_string; + /* no break */ + case SQL_TEXT: + ZVAL_STRINGL(val,(char *) data,len,1); + break; + case SQL_SHORT: + n = *(short *) data; + goto _sql_long; + case SQL_INT64: +#if (SIZEOF_LONG >= 8) + n = *(long *) data; + goto _sql_long; +#else + if (scale == 0) { + l = slprintf(string_data, sizeof(string_data), "%" LL_MASK "d", *(ISC_INT64 *) data); + ZVAL_STRINGL(val,string_data,l,1); + } else { + ISC_INT64 n = *(ISC_INT64 *) data, f = scales[-scale]; + + if (n >= 0) { + l = slprintf(string_data, sizeof(string_data), "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, n % f); + } else if (n <= -f) { + l = slprintf(string_data, sizeof(string_data), "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, -n % f); + } else { + l = slprintf(string_data, sizeof(string_data), "-0.%0*" LL_MASK "d", -scale, -n % f); + } + ZVAL_STRINGL(val,string_data,l,1); + } + break; +#endif + case SQL_LONG: + n = *(ISC_LONG *) data; + _sql_long: + if (scale == 0) { + ZVAL_LONG(val,n); + } else { + long f = (long) scales[-scale]; + + if (n >= 0) { + l = slprintf(string_data, sizeof(string_data), "%ld.%0*ld", n / f, -scale, n % f); + } else if (n <= -f) { + l = slprintf(string_data, sizeof(string_data), "%ld.%0*ld", n / f, -scale, -n % f); + } else { + l = slprintf(string_data, sizeof(string_data), "-0.%0*ld", -scale, -n % f); + } + ZVAL_STRINGL(val,string_data,l,1); + } + break; + case SQL_FLOAT: + ZVAL_DOUBLE(val, *(float *) data); + break; + case SQL_DOUBLE: + ZVAL_DOUBLE(val, *(double *) data); + break; + case SQL_DATE: /* == case SQL_TIMESTAMP: */ + format = INI_STR("ibase.timestampformat"); + isc_decode_timestamp((ISC_TIMESTAMP *) data, &t); + goto format_date_time; + case SQL_TYPE_DATE: + format = INI_STR("ibase.dateformat"); + isc_decode_sql_date((ISC_DATE *) data, &t); + goto format_date_time; + case SQL_TYPE_TIME: + format = INI_STR("ibase.timeformat"); + isc_decode_sql_time((ISC_TIME *) data, &t); + +format_date_time: + /* + XXX - Might have to remove this later - seems that isc_decode_date() + always sets tm_isdst to 0, sometimes incorrectly (InterBase 6 bug?) + */ + t.tm_isdst = -1; +#if HAVE_TM_ZONE + t.tm_zone = tzname[0]; +#endif + if (flag & PHP_IBASE_UNIXTIME) { + ZVAL_LONG(val, mktime(&t)); + } else { +#if HAVE_STRFTIME + l = strftime(string_data, sizeof(string_data), format, &t); +#else + switch (type & ~1) { + default: + l = slprintf(string_data, sizeof(string_data), "%02d/%02d/%4d %02d:%02d:%02d", t.tm_mon+1, t.tm_mday, + t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec); + break; + case SQL_TYPE_DATE: + l = slprintf(string_data, sizeof(string_data), "%02d/%02d/%4d", t.tm_mon + 1, t.tm_mday, t.tm_year+1900); + break; + case SQL_TYPE_TIME: + l = slprintf(string_data, sizeof(string_data), "%02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec); + break; + } +#endif + ZVAL_STRINGL(val,string_data,l,1); + break; + } + } /* switch (type) */ + return SUCCESS; +} +/* }}} */ + +static int _php_ibase_arr_zval(zval *ar_zval, char *data, unsigned long data_size, /* {{{ */ + ibase_array *ib_array, int dim, int flag TSRMLS_DC) +{ + /** + * Create multidimension array - recursion function + */ + int + u_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_upper, + l_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_lower, + dim_len = 1 + u_bound - l_bound; + unsigned short i; + + if (dim < ib_array->ar_desc.array_desc_dimensions) { /* array again */ + unsigned long slice_size = data_size / dim_len; + + array_init(ar_zval); + + for (i = 0; i < dim_len; ++i) { + zval *slice_zval; + ALLOC_INIT_ZVAL(slice_zval); + + /* recursion here */ + if (FAILURE == _php_ibase_arr_zval(slice_zval, data, slice_size, ib_array, dim + 1, + flag TSRMLS_CC)) { + return FAILURE; + } + data += slice_size; + + add_index_zval(ar_zval,l_bound+i,slice_zval); + } + } else { /* data at last */ + + if (FAILURE == _php_ibase_var_zval(ar_zval, data, ib_array->el_type, + ib_array->ar_desc.array_desc_length, ib_array->ar_desc.array_desc_scale, flag TSRMLS_CC)) { + return FAILURE; + } + + /* fix for peculiar handling of VARCHAR arrays; + truncate the field to the cstring length */ + if (ib_array->ar_desc.array_desc_dtype == blr_varying || + ib_array->ar_desc.array_desc_dtype == blr_varying2) { + + Z_STRLEN_P(ar_zval) = strlen(Z_STRVAL_P(ar_zval)); + } + } + return SUCCESS; +} +/* }}} */ + +static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type) /* {{{ */ +{ + zval *result_arg; + long i, array_cnt = 0, flag = 0; + ibase_result *ib_result; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &result_arg, &flag)) { + return; + } + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result_arg, -1, LE_RESULT, le_result); + + if (ib_result->out_sqlda == NULL || !ib_result->has_more_rows) { + RETURN_FALSE; + } + + if (ib_result->statement_type != isc_info_sql_stmt_exec_procedure) { + if (isc_dsql_fetch(IB_STATUS, &ib_result->stmt, 1, ib_result->out_sqlda)) { + ib_result->has_more_rows = 0; + if (IB_STATUS[0] && IB_STATUS[1]) { /* error in fetch */ + _php_ibase_error(TSRMLS_C); + } + RETURN_FALSE; + } + } else { + ib_result->has_more_rows = 0; + } + + array_init(return_value); + + for (i = 0; i < ib_result->out_sqlda->sqld; ++i) { + XSQLVAR *var = &ib_result->out_sqlda->sqlvar[i]; + char buf[METADATALENGTH+4], *alias = var->aliasname; + + if (! (fetch_type & FETCH_ROW)) { + int i = 0; + char const *base = "FIELD"; /* use 'FIELD' if name is empty */ + + /** + * Ensure no two columns have identical names: + * keep generating new names until we find one that is unique. + */ + switch (*alias) { + void *p; + + default: + i = 1; + base = alias; + + while (SUCCESS == zend_symtable_find( + Z_ARRVAL_P(return_value),alias,strlen(alias)+1,&p)) { + + case '\0': + snprintf(alias = buf, sizeof(buf), "%s_%02d", base, i++); + } + } + } + + if (((var->sqltype & 1) == 0) || *var->sqlind != -1) { + zval *result; + ALLOC_INIT_ZVAL(result); + + switch (var->sqltype & ~1) { + + default: + _php_ibase_var_zval(result, var->sqldata, var->sqltype, var->sqllen, + var->sqlscale, flag TSRMLS_CC); + break; + case SQL_BLOB: + if (flag & PHP_IBASE_FETCH_BLOBS) { /* fetch blob contents into hash */ + + ibase_blob blob_handle; + unsigned long max_len = 0; + static char bl_items[] = {isc_info_blob_total_length}; + char bl_info[20]; + unsigned short i; + + blob_handle.bl_handle = NULL; + blob_handle.bl_qd = *(ISC_QUAD *) var->sqldata; + + if (isc_open_blob(IB_STATUS, &ib_result->link->handle, &ib_result->trans->handle, + &blob_handle.bl_handle, &blob_handle.bl_qd)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_fetch_error; + } + + if (isc_blob_info(IB_STATUS, &blob_handle.bl_handle, sizeof(bl_items), + bl_items, sizeof(bl_info), bl_info)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_fetch_error; + } + + /* find total length of blob's data */ + for (i = 0; i < sizeof(bl_info); ) { + unsigned short item_len; + char item = bl_info[i++]; + + if (item == isc_info_end || item == isc_info_truncated || + item == isc_info_error || i >= sizeof(bl_info)) { + + _php_ibase_module_error("Could not determine BLOB size (internal error)" + TSRMLS_CC); + goto _php_ibase_fetch_error; + } + + item_len = (unsigned short) isc_vax_integer(&bl_info[i], 2); + + if (item == isc_info_blob_total_length) { + max_len = isc_vax_integer(&bl_info[i+2], item_len); + break; + } + i += item_len+2; + } + + if (max_len == 0) { + ZVAL_STRING(result, "", 1); + } else if (SUCCESS != _php_ibase_blob_get(result, &blob_handle, + max_len TSRMLS_CC)) { + goto _php_ibase_fetch_error; + } + + if (isc_close_blob(IB_STATUS, &blob_handle.bl_handle)) { + _php_ibase_error(TSRMLS_C); + goto _php_ibase_fetch_error; + } + + } else { /* blob id only */ + ISC_QUAD bl_qd = *(ISC_QUAD *) var->sqldata; + ZVAL_STRINGL(result,_php_ibase_quad_to_string(bl_qd), BLOB_ID_LEN, 0); + } + break; + case SQL_ARRAY: + if (flag & PHP_IBASE_FETCH_ARRAYS) { /* array can be *huge* so only fetch if asked */ + ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata; + ibase_array *ib_array = &ib_result->out_array[array_cnt++]; + void *ar_data = emalloc(ib_array->ar_size); + + if (isc_array_get_slice(IB_STATUS, &ib_result->link->handle, + &ib_result->trans->handle, &ar_qd, &ib_array->ar_desc, + ar_data, &ib_array->ar_size)) { + _php_ibase_error(TSRMLS_C); + efree(ar_data); + goto _php_ibase_fetch_error; + } + + if (FAILURE == _php_ibase_arr_zval(result, ar_data, ib_array->ar_size, ib_array, + 0, flag TSRMLS_CC)) { + efree(ar_data); + goto _php_ibase_fetch_error; + } + efree(ar_data); + + } else { /* blob id only */ + ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata; + ZVAL_STRINGL(result,_php_ibase_quad_to_string(ar_qd), BLOB_ID_LEN, 0); + } + break; + _php_ibase_fetch_error: + zval_dtor(result); + FREE_ZVAL(result); + RETURN_FALSE; + } /* switch */ + + if (fetch_type & FETCH_ROW) { + add_index_zval(return_value, i, result); + } else { + add_assoc_zval(return_value, alias, result); + } + } else { + if (fetch_type & FETCH_ROW) { + add_index_null(return_value, i); + } else { + add_assoc_null(return_value, alias); + } + } + } /* for field */ +} +/* }}} */ + +/* {{{ proto array ibase_fetch_row(resource result [, int fetch_flags]) + Fetch a row from the results of a query */ +PHP_FUNCTION(ibase_fetch_row) +{ + _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ROW); +} +/* }}} */ + +/* {{{ proto array ibase_fetch_assoc(resource result [, int fetch_flags]) + Fetch a row from the results of a query */ +PHP_FUNCTION(ibase_fetch_assoc) +{ + _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY); +} +/* }}} */ + +/* {{{ proto object ibase_fetch_object(resource result [, int fetch_flags]) + Fetch a object from the results of a query */ +PHP_FUNCTION(ibase_fetch_object) +{ + _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY); + + if (Z_TYPE_P(return_value) == IS_ARRAY) { + object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value)); + } +} +/* }}} */ + + +/* {{{ proto bool ibase_name_result(resource result, string name) + Assign a name to a result for use with ... WHERE CURRENT OF <name> statements */ +PHP_FUNCTION(ibase_name_result) +{ + zval *result_arg; + char *name_arg; + int name_arg_len; + ibase_result *ib_result; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &result_arg, &name_arg, &name_arg_len) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result_arg, -1, LE_RESULT, le_result); + + if (isc_dsql_set_cursor_name(IB_STATUS, &ib_result->stmt, name_arg, 0)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + + +/* {{{ proto bool ibase_free_result(resource result) + Free the memory used by a result */ +PHP_FUNCTION(ibase_free_result) +{ + zval *result_arg; + ibase_result *ib_result; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result_arg) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result_arg, -1, LE_RESULT, le_result); + zend_list_delete(Z_RESVAL_P(result_arg)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource ibase_prepare(resource link_identifier[, string query [, resource trans_identifier ]]) + Prepare a query for later execution */ +PHP_FUNCTION(ibase_prepare) +{ + zval *link_arg, *trans_arg; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + int query_len, trans_res_id = 0; + ibase_query *ib_query; + char *query; + + RESET_ERRMSG; + + if (ZEND_NUM_ARGS() == 1) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query, &query_len) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink); + } else if (ZEND_NUM_ARGS() == 2) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &link_arg, &query, &query_len) == FAILURE) { + return; + } + _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, &link_arg, &ib_link, &trans); + + if (trans != NULL) { + trans_res_id = Z_RESVAL_P(link_arg); + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &link_arg, &trans_arg, &query, &query_len) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, -1, LE_LINK, le_link, le_plink); + ZEND_FETCH_RESOURCE(trans, ibase_trans *, &trans_arg, -1, LE_TRANS, le_trans); + trans_res_id = Z_RESVAL_P(trans_arg); + } + + if (FAILURE == _php_ibase_def_trans(ib_link, &trans TSRMLS_CC)) { + RETURN_FALSE; + } + + ib_query = (ibase_query *) emalloc(sizeof(ibase_query)); + + if (FAILURE == _php_ibase_alloc_query(ib_query, ib_link, trans, query, ib_link->dialect, trans_res_id TSRMLS_CC)) { + efree(ib_query); + RETURN_FALSE; + } + ZEND_REGISTER_RESOURCE(return_value, ib_query, le_query); +} +/* }}} */ + +/* {{{ proto mixed ibase_execute(resource query [, mixed bind_arg [, mixed bind_arg [, ...]]]) + Execute a previously prepared query */ +PHP_FUNCTION(ibase_execute) +{ + zval *query, ***args = NULL; + ibase_query *ib_query; + ibase_result *result = NULL; + ALLOCA_FLAG(use_heap) + + RESET_ERRMSG; + + RETVAL_FALSE; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() ? 1 : 0 TSRMLS_CC, "r", &query)) { + return; + } + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &query, -1, LE_QUERY, le_query); + + do { + int bind_n = ZEND_NUM_ARGS() - 1, + expected_n = ib_query->in_sqlda ? ib_query->in_sqlda->sqld : 0; + + if (bind_n != expected_n) { + php_error_docref(NULL TSRMLS_CC, (bind_n < expected_n) ? E_WARNING : E_NOTICE, + "Statement expects %d arguments, %d given", expected_n, bind_n); + + if (bind_n < expected_n) { + break; + } + } + + /* have variables to bind */ + args = (zval ***) do_alloca((expected_n + 1) * sizeof(zval **), use_heap); + + if (FAILURE == zend_get_parameters_array_ex((expected_n + 1), args)) { + break; + } + + /* Have we used this cursor before and it's still open (exec proc has no cursor) ? */ + if (ib_query->result_res_id != 0 + && ib_query->statement_type != isc_info_sql_stmt_exec_procedure) { + IBDEBUG("Implicitly closing a cursor"); + + if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_close)) { + _php_ibase_error(TSRMLS_C); + break; + } + /* invalidate previous results returned by this query (not necessary for exec proc) */ + zend_list_delete(ib_query->result_res_id); + } + + if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, ib_query, + &args[1])) { + break; + } + + /* free the query if trans handle was released */ + if (ib_query->trans->handle == NULL) { + zend_list_delete(Z_LVAL_P(query)); + } + + if (result != NULL) { + result->type = EXECUTE_RESULT; + if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) { + result->stmt = NULL; + } + ib_query->result_res_id = zend_list_insert(result, le_result TSRMLS_CC); + RETVAL_RESOURCE(ib_query->result_res_id); + } + } while (0); + + if (args) { + free_alloca(args, use_heap); + } +} +/* }}} */ + +/* {{{ proto bool ibase_free_query(resource query) + Free memory used by a query */ +PHP_FUNCTION(ibase_free_query) +{ + zval *query_arg; + ibase_query *ib_query; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &query_arg) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &query_arg, -1, LE_QUERY, le_query); + zend_list_delete(Z_RESVAL_P(query_arg)); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int ibase_num_fields(resource query_result) + Get the number of fields in result */ +PHP_FUNCTION(ibase_num_fields) +{ + zval *result; + int type; + XSQLDA *sqlda; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) { + return; + } + + zend_list_find(Z_RESVAL_P(result), &type); + + if (type == le_query) { + ibase_query *ib_query; + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &result, -1, LE_QUERY, le_query); + sqlda = ib_query->out_sqlda; + } else { + ibase_result *ib_result; + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result, -1, LE_RESULT, le_result); + sqlda = ib_result->out_sqlda; + } + + if (sqlda == NULL) { + RETURN_LONG(0); + } else { + RETURN_LONG(sqlda->sqld); + } +} +/* }}} */ + +static void _php_ibase_field_info(zval *return_value, XSQLVAR *var) /* {{{ */ +{ + unsigned short len; + char buf[16], *s = buf; + + array_init(return_value); + + add_index_stringl(return_value, 0, var->sqlname, var->sqlname_length, 1); + add_assoc_stringl(return_value, "name", var->sqlname, var->sqlname_length, 1); + + add_index_stringl(return_value, 1, var->aliasname, var->aliasname_length, 1); + add_assoc_stringl(return_value, "alias", var->aliasname, var->aliasname_length, 1); + + add_index_stringl(return_value, 2, var->relname, var->relname_length, 1); + add_assoc_stringl(return_value, "relation", var->relname, var->relname_length, 1); + + len = slprintf(buf, 16, "%d", var->sqllen); + add_index_stringl(return_value, 3, buf, len, 1); + add_assoc_stringl(return_value, "length", buf, len, 1); + + if (var->sqlscale < 0) { + unsigned short precision = 0; + + switch (var->sqltype & ~1) { + + case SQL_SHORT: + precision = 4; + break; + case SQL_LONG: + precision = 9; + break; + case SQL_INT64: + precision = 18; + break; + } + len = slprintf(buf, 16, "NUMERIC(%d,%d)", precision, -var->sqlscale); + add_index_stringl(return_value, 4, s, len, 1); + add_assoc_stringl(return_value, "type", s, len, 1); + } else { + switch (var->sqltype & ~1) { + case SQL_TEXT: + s = "CHAR"; + break; + case SQL_VARYING: + s = "VARCHAR"; + break; + case SQL_SHORT: + s = "SMALLINT"; + break; + case SQL_LONG: + s = "INTEGER"; + break; + case SQL_FLOAT: + s = "FLOAT"; break; + case SQL_DOUBLE: + case SQL_D_FLOAT: + s = "DOUBLE PRECISION"; break; + case SQL_INT64: + s = "BIGINT"; + break; + case SQL_TIMESTAMP: + s = "TIMESTAMP"; + break; + case SQL_TYPE_DATE: + s = "DATE"; + break; + case SQL_TYPE_TIME: + s = "TIME"; + break; + case SQL_BLOB: + s = "BLOB"; + break; + case SQL_ARRAY: + s = "ARRAY"; + break; + /* FIXME: provide more detailed information about the field type, field size + * and array dimensions */ + case SQL_QUAD: + s = "QUAD"; + break; + } + add_index_string(return_value, 4, s, 1); + add_assoc_string(return_value, "type", s, 1); + } +} +/* }}} */ + +/* {{{ proto array ibase_field_info(resource query_result, int field_number) + Get information about a field */ +PHP_FUNCTION(ibase_field_info) +{ + zval *result_arg; + long field_arg; + int type; + XSQLDA *sqlda; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &result_arg, &field_arg) == FAILURE) { + return; + } + + zend_list_find(Z_RESVAL_P(result_arg), &type); + + if (type == le_query) { + ibase_query *ib_query; + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &result_arg, -1, LE_QUERY, le_query); + sqlda = ib_query->out_sqlda; + } else { + ibase_result *ib_result; + + ZEND_FETCH_RESOURCE(ib_result, ibase_result *, &result_arg, -1, LE_RESULT, le_result); + sqlda = ib_result->out_sqlda; + } + + if (sqlda == NULL) { + _php_ibase_module_error("Trying to get field info from a non-select query" TSRMLS_CC); + RETURN_FALSE; + } + + if (field_arg < 0 || field_arg >= sqlda->sqld) { + RETURN_FALSE; + } + _php_ibase_field_info(return_value, sqlda->sqlvar + field_arg); +} +/* }}} */ + +/* {{{ proto int ibase_num_params(resource query) + Get the number of params in a prepared query */ +PHP_FUNCTION(ibase_num_params) +{ + zval *result; + ibase_query *ib_query; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &result) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &result, -1, LE_QUERY, le_query); + + if (ib_query->in_sqlda == NULL) { + RETURN_LONG(0); + } else { + RETURN_LONG(ib_query->in_sqlda->sqld); + } +} +/* }}} */ + +/* {{{ proto array ibase_param_info(resource query, int field_number) + Get information about a parameter */ +PHP_FUNCTION(ibase_param_info) +{ + zval *result_arg; + long field_arg; + ibase_query *ib_query; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &result_arg, &field_arg) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(ib_query, ibase_query *, &result_arg, -1, LE_QUERY, le_query); + + if (ib_query->in_sqlda == NULL) { + RETURN_FALSE; + } + + if (field_arg < 0 || field_arg >= ib_query->in_sqlda->sqld) { + RETURN_FALSE; + } + + _php_ibase_field_info(return_value,ib_query->in_sqlda->sqlvar + field_arg); +} +/* }}} */ + +#endif /* HAVE_IBASE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/interbase/ibase_service.c b/ext/interbase/ibase_service.c new file mode 100644 index 0000000..956ef04 --- /dev/null +++ b/ext/interbase/ibase_service.c @@ -0,0 +1,629 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" + +#if HAVE_IBASE + +#include "php_interbase.h" +#include "php_ibase_includes.h" + +typedef struct { + isc_svc_handle handle; + char *hostname; + char *username; + long res_id; +} ibase_service; + +static int le_service; + +static void _php_ibase_free_service(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_service *sv = (ibase_service *) rsrc->ptr; + + if (isc_service_detach(IB_STATUS, &sv->handle)) { + _php_ibase_error(TSRMLS_C); + } + + if (sv->hostname) { + efree(sv->hostname); + } + if (sv->username) { + efree(sv->username); + } + + efree(sv); +} +/* }}} */ + +/* the svc api seems to get confused after an error has occurred, + so invalidate the handle on errors */ +#define IBASE_SVC_ERROR(svm) \ + do { zend_list_delete(svm->res_id); _php_ibase_error(TSRMLS_C); } while (0) + + +void php_ibase_service_minit(INIT_FUNC_ARGS) /* {{{ */ +{ + le_service = zend_register_list_destructors_ex(_php_ibase_free_service, NULL, + "interbase service manager handle", module_number); + + /* backup options */ + REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_CHECKSUMS", isc_spb_bkp_ignore_checksums, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_LIMBO", isc_spb_bkp_ignore_limbo, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_METADATA_ONLY", isc_spb_bkp_metadata_only, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_NO_GARBAGE_COLLECT", isc_spb_bkp_no_garbage_collect, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_OLD_DESCRIPTIONS", isc_spb_bkp_old_descriptions, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_NON_TRANSPORTABLE", isc_spb_bkp_non_transportable, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_BKP_CONVERT", isc_spb_bkp_convert, CONST_PERSISTENT); + + /* restore options */ + REGISTER_LONG_CONSTANT("IBASE_RES_DEACTIVATE_IDX", isc_spb_res_deactivate_idx, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_NO_SHADOW", isc_spb_res_no_shadow, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_NO_VALIDITY", isc_spb_res_no_validity, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_ONE_AT_A_TIME", isc_spb_res_one_at_a_time, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_REPLACE", isc_spb_res_replace, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_CREATE", isc_spb_res_create, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RES_USE_ALL_SPACE", isc_spb_res_use_all_space, CONST_PERSISTENT); + + /* manage options */ + REGISTER_LONG_CONSTANT("IBASE_PRP_PAGE_BUFFERS", isc_spb_prp_page_buffers, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_SWEEP_INTERVAL", isc_spb_prp_sweep_interval, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_SHUTDOWN_DB", isc_spb_prp_shutdown_db, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_TRANSACTIONS", isc_spb_prp_deny_new_transactions, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_ATTACHMENTS", isc_spb_prp_deny_new_attachments, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_RESERVE_SPACE", isc_spb_prp_reserve_space, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_RES_USE_FULL", isc_spb_prp_res_use_full, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_RES", isc_spb_prp_res, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_WRITE_MODE", isc_spb_prp_write_mode, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_WM_ASYNC", isc_spb_prp_wm_async, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_WM_SYNC", isc_spb_prp_wm_sync, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_ACCESS_MODE", isc_spb_prp_access_mode, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READONLY", isc_spb_prp_am_readonly, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READWRITE", isc_spb_prp_am_readwrite, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_SET_SQL_DIALECT", isc_spb_prp_set_sql_dialect, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_ACTIVATE", isc_spb_prp_activate, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_PRP_DB_ONLINE", isc_spb_prp_db_online, CONST_PERSISTENT); + + /* repair options */ + REGISTER_LONG_CONSTANT("IBASE_RPR_CHECK_DB", isc_spb_rpr_check_db, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_IGNORE_CHECKSUM", isc_spb_rpr_ignore_checksum, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_KILL_SHADOWS", isc_spb_rpr_kill_shadows, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_MEND_DB", isc_spb_rpr_mend_db, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_VALIDATE_DB", isc_spb_rpr_validate_db, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_FULL", isc_spb_rpr_full, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_RPR_SWEEP_DB", isc_spb_rpr_sweep_db, CONST_PERSISTENT); + + /* db info arguments */ + REGISTER_LONG_CONSTANT("IBASE_STS_DATA_PAGES", isc_spb_sts_data_pages, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_STS_DB_LOG", isc_spb_sts_db_log, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_STS_HDR_PAGES", isc_spb_sts_hdr_pages, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_STS_IDX_PAGES", isc_spb_sts_idx_pages, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_STS_SYS_RELATIONS", isc_spb_sts_sys_relations, CONST_PERSISTENT); + + /* server info arguments */ + REGISTER_LONG_CONSTANT("IBASE_SVC_SERVER_VERSION", isc_info_svc_server_version, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_IMPLEMENTATION", isc_info_svc_implementation, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV", isc_info_svc_get_env, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_LOCK", isc_info_svc_get_env_lock, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_MSG", isc_info_svc_get_env_msg, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_USER_DBPATH", isc_info_svc_user_dbpath, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_SVR_DB_INFO", isc_info_svc_svr_db_info, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_SVC_GET_USERS", isc_info_svc_get_users, CONST_PERSISTENT); +} +/* }}} */ + +static void _php_ibase_user(INTERNAL_FUNCTION_PARAMETERS, char operation) /* {{{ */ +{ + /* user = 0, password = 1, first_name = 2, middle_name = 3, last_name = 4 */ + static char const user_flags[] = { isc_spb_sec_username, isc_spb_sec_password, + isc_spb_sec_firstname, isc_spb_sec_middlename, isc_spb_sec_lastname }; + char buf[128], *args[] = { NULL, NULL, NULL, NULL, NULL }; + int i, args_len[] = { 0, 0, 0, 0, 0 }; + unsigned short spb_len = 1; + zval *res; + ibase_service *svm; + + RESET_ERRMSG; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + (operation == isc_action_svc_delete_user) ? "rs" : "rss|sss", + &res, &args[0], &args_len[0], &args[1], &args_len[1], &args[2], &args_len[2], + &args[3], &args_len[3], &args[4], &args_len[4])) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, "Interbase service manager handle", + le_service); + + buf[0] = operation; + + for (i = 0; i < sizeof(user_flags); ++i) { + if (args[i] != NULL) { + int chunk = slprintf(&buf[spb_len], sizeof(buf) - spb_len, "%c%c%c%s", + user_flags[i], (char)args_len[i], (char)(args_len[i] >> 8), args[i]); + + if ((spb_len + chunk) > sizeof(buf) || chunk <= 0) { + _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)" + TSRMLS_CC, spb_len); + RETURN_FALSE; + } + spb_len += chunk; + } + } + + /* now start the job */ + if (isc_service_start(IB_STATUS, &svm->handle, NULL, spb_len, buf)) { + IBASE_SVC_ERROR(svm); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ibase_add_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]) + Add a user to security database */ +PHP_FUNCTION(ibase_add_user) +{ + _php_ibase_user(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_add_user); +} +/* }}} */ + +/* {{{ proto bool ibase_modify_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]) + Modify a user in security database */ +PHP_FUNCTION(ibase_modify_user) +{ + _php_ibase_user(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_modify_user); +} +/* }}} */ + +/* {{{ proto bool ibase_delete_user(resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]) + Delete a user from security database */ +PHP_FUNCTION(ibase_delete_user) +{ + _php_ibase_user(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_delete_user); +} +/* }}} */ + +/* {{{ proto resource ibase_service_attach(string host, string dba_username, string dba_password) + Connect to the service manager */ +PHP_FUNCTION(ibase_service_attach) +{ + int hlen, ulen, plen, spb_len; + ibase_service *svm; + char buf[128], *host, *user, *pass, *loc; + isc_svc_handle handle = NULL; + + RESET_ERRMSG; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", + &host, &hlen, &user, &ulen, &pass, &plen)) { + + RETURN_FALSE; + } + + /* construct the spb, hack the service name into it as well */ + spb_len = slprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%s" "%s:service_mgr", + isc_spb_version, isc_spb_current_version, isc_spb_user_name, (char)ulen, + user, isc_spb_password, (char)plen, pass, host); + + if (spb_len > sizeof(buf) || spb_len == -1) { + _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)" TSRMLS_CC, spb_len); + RETURN_FALSE; + } + + spb_len -= hlen + 12; + loc = buf + spb_len; /* points to %s:service_mgr part */ + + /* attach to the service manager */ + if (isc_service_attach(IB_STATUS, 0, loc, &handle, (unsigned short)spb_len, buf)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + svm = (ibase_service*)emalloc(sizeof(ibase_service)); + svm->handle = handle; + svm->hostname = estrdup(host); + svm->username = estrdup(user); + + ZEND_REGISTER_RESOURCE(return_value, svm, le_service); + svm->res_id = Z_LVAL_P(return_value); +} +/* }}} */ + +/* {{{ proto bool ibase_service_detach(resource service_handle) + Disconnect from the service manager */ +PHP_FUNCTION(ibase_service_detach) +{ + zval *res; + + RESET_ERRMSG; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &res)) { + RETURN_FALSE; + } + + zend_list_delete(Z_LVAL_P(res)); + + RETURN_TRUE; +} +/* }}} */ + +static void _php_ibase_service_query(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */ + ibase_service *svm, char info_action) +{ + static char spb[] = { isc_info_svc_timeout, 10, 0, 0, 0 }; + + char res_buf[400], *result, *heap_buf = NULL, *heap_p; + long heap_buf_size = 200, line_len; + + /* info about users requires an action first */ + if (info_action == isc_info_svc_get_users) { + static char action[] = { isc_action_svc_display_user }; + + if (isc_service_start(IB_STATUS, &svm->handle, NULL, sizeof(action), action)) { + IBASE_SVC_ERROR(svm); + RETURN_FALSE; + } + } + +query_loop: + result = res_buf; + + if (isc_service_query(IB_STATUS, &svm->handle, NULL, sizeof(spb), spb, + 1, &info_action, sizeof(res_buf), res_buf)) { + + IBASE_SVC_ERROR(svm); + RETURN_FALSE; + } + while (*result != isc_info_end) { + switch (*result++) { + default: + RETURN_FALSE; + + case isc_info_svc_line: + if (! (line_len = isc_vax_integer(result, 2))) { + /* done */ + if (heap_buf) { + RETURN_STRING(heap_buf,0); + } else { + RETURN_TRUE; + } + } + if (!heap_buf || (heap_p - heap_buf + line_len +2) > heap_buf_size) { + long res_size = heap_buf ? heap_p - heap_buf : 0; + + while (heap_buf_size < (res_size + line_len +2)) { + heap_buf_size *= 2; + } + heap_buf = (char*) erealloc(heap_buf, heap_buf_size); + heap_p = heap_buf + res_size; + } + result += 2; + *(result+line_len) = 0; + snprintf(heap_p, heap_buf_size - (heap_p - heap_buf), "%s\n", result); + heap_p += line_len +1; + goto query_loop; /* repeat until result is exhausted */ + + case isc_info_svc_server_version: + case isc_info_svc_implementation: + case isc_info_svc_get_env: + case isc_info_svc_get_env_lock: + case isc_info_svc_get_env_msg: + case isc_info_svc_user_dbpath: + RETURN_STRINGL(result + 2, isc_vax_integer(result, 2), 1); + + case isc_info_svc_svr_db_info: + array_init(return_value); + + do { + switch (*result++) { + int len; + + case isc_spb_num_att: + add_assoc_long(return_value, "attachments", isc_vax_integer(result,4)); + result += 4; + break; + + case isc_spb_num_db: + add_assoc_long(return_value, "databases", isc_vax_integer(result,4)); + result += 4; + break; + + case isc_spb_dbname: + len = isc_vax_integer(result,2); + add_next_index_stringl(return_value, result +2, len, 1); + result += len+2; + } + } while (*result != isc_info_flag_end); + return; + + case isc_info_svc_get_users: { + zval *user; + array_init(return_value); + + while (*result != isc_info_end) { + + switch (*result++) { + int len; + + case isc_spb_sec_username: + /* it appears that the username is always first */ + ALLOC_INIT_ZVAL(user); + array_init(user); + add_next_index_zval(return_value, user); + + len = isc_vax_integer(result,2); + add_assoc_stringl(user, "user_name", result +2, len, 1); + result += len+2; + break; + + case isc_spb_sec_firstname: + len = isc_vax_integer(result,2); + add_assoc_stringl(user, "first_name", result +2, len, 1); + result += len+2; + break; + + case isc_spb_sec_middlename: + len = isc_vax_integer(result,2); + add_assoc_stringl(user, "middle_name", result +2, len, 1); + result += len+2; + break; + + case isc_spb_sec_lastname: + len = isc_vax_integer(result,2); + add_assoc_stringl(user, "last_name", result +2, len, 1); + result += len+2; + break; + + case isc_spb_sec_userid: + add_assoc_long(user, "user_id", isc_vax_integer(result, 4)); + result += 4; + break; + + case isc_spb_sec_groupid: + add_assoc_long(user, "group_id", isc_vax_integer(result, 4)); + result += 4; + break; + } + } + return; + } + } + } +} +/* }}} */ + +static void _php_ibase_backup_restore(INTERNAL_FUNCTION_PARAMETERS, char operation) /* {{{ */ +{ + /** + * It appears that the service API is a little bit confused about which flag + * to use for the source and destination in the case of a restore operation. + * When passing the backup file as isc_spb_dbname and the destination db as + * bpk_file, things work well. + */ + zval *res; + char *db, *bk, buf[200]; + int dblen, bklen, spb_len; + long opts = 0; + zend_bool verbose = 0; + ibase_service *svm; + + RESET_ERRMSG; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss|lb", + &res, &db, &dblen, &bk, &bklen, &opts, &verbose)) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, + "Interbase service manager handle", le_service); + + /* fill the param buffer */ + spb_len = slprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%c%s%c%c%c%c%c", + operation, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db, + isc_spb_bkp_file, (char)bklen, (char)(bklen >> 8), bk, isc_spb_options, + (char)opts,(char)(opts >> 8), (char)(opts >> 16), (char)(opts >> 24)); + + if (verbose) { + buf[spb_len++] = isc_spb_verbose; + } + + if (spb_len > sizeof(buf) || spb_len <= 0) { + _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)" TSRMLS_CC, spb_len); + RETURN_FALSE; + } + + /* now start the backup/restore job */ + if (isc_service_start(IB_STATUS, &svm->handle, NULL, (unsigned short)spb_len, buf)) { + IBASE_SVC_ERROR(svm); + RETURN_FALSE; + } + + if (!verbose) { + RETURN_TRUE; + } else { + _php_ibase_service_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, svm, isc_info_svc_line); + } +} +/* }}} */ + +/* {{{ proto mixed ibase_backup(resource service_handle, string source_db, string dest_file [, int options [, bool verbose]]) + Initiates a backup task in the service manager and returns immediately */ +PHP_FUNCTION(ibase_backup) +{ + _php_ibase_backup_restore(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_backup); +} +/* }}} */ + +/* {{{ proto mixed ibase_restore(resource service_handle, string source_file, string dest_db [, int options [, bool verbose]]) + Initiates a restore task in the service manager and returns immediately */ +PHP_FUNCTION(ibase_restore) +{ + _php_ibase_backup_restore(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_restore); +} +/* }}} */ + +static void _php_ibase_service_action(INTERNAL_FUNCTION_PARAMETERS, char svc_action) /* {{{ */ +{ + zval *res; + char buf[128], *db; + int dblen, spb_len; + long action, argument = 0; + ibase_service *svm; + + RESET_ERRMSG; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl|l", + &res, &db, &dblen, &action, &argument)) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, + "Interbase service manager handle", le_service); + + if (svc_action == isc_action_svc_db_stats) { + switch (action) { + default: + goto unknown_option; + + case isc_spb_sts_data_pages: + case isc_spb_sts_db_log: + case isc_spb_sts_hdr_pages: + case isc_spb_sts_idx_pages: + case isc_spb_sts_sys_relations: + goto options_argument; + } + } else { + /* these actions all expect different types of arguments */ + switch (action) { + default: +unknown_option: + _php_ibase_module_error("Unrecognised option (%ld)" TSRMLS_CC, action); + RETURN_FALSE; + + case isc_spb_rpr_check_db: + case isc_spb_rpr_ignore_checksum: + case isc_spb_rpr_kill_shadows: + case isc_spb_rpr_mend_db: + case isc_spb_rpr_validate_db: + case isc_spb_rpr_sweep_db: + svc_action = isc_action_svc_repair; + + case isc_spb_prp_activate: + case isc_spb_prp_db_online: +options_argument: + argument |= action; + action = isc_spb_options; + + case isc_spb_prp_page_buffers: + case isc_spb_prp_sweep_interval: + case isc_spb_prp_shutdown_db: + case isc_spb_prp_deny_new_transactions: + case isc_spb_prp_deny_new_attachments: + case isc_spb_prp_set_sql_dialect: + spb_len = slprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%c%c%c", + svc_action, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db, + (char)action, (char)argument, (char)(argument >> 8), (char)(argument >> 16), + (char)(argument >> 24)); + break; + + case isc_spb_prp_reserve_space: + case isc_spb_prp_write_mode: + case isc_spb_prp_access_mode: + spb_len = slprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c", + isc_action_svc_properties, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), + db, (char)action, (char)argument); + } + } + + if (spb_len > sizeof(buf) || spb_len == -1) { + _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%d)" TSRMLS_CC, spb_len); + RETURN_FALSE; + } + + if (isc_service_start(IB_STATUS, &svm->handle, NULL, (unsigned short)spb_len, buf)) { + IBASE_SVC_ERROR(svm); + RETURN_FALSE; + } + + if (svc_action == isc_action_svc_db_stats) { + _php_ibase_service_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, svm, isc_info_svc_line); + } else { + RETURN_TRUE; + } +} +/* }}} */ + +/* {{{ proto bool ibase_maintain_db(resource service_handle, string db, int action [, int argument]) + Execute a maintenance command on the database server */ +PHP_FUNCTION(ibase_maintain_db) +{ + _php_ibase_service_action(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_properties); +} +/* }}} */ + +/* {{{ proto string ibase_db_info(resource service_handle, string db, int action [, int argument]) + Request statistics about a database */ +PHP_FUNCTION(ibase_db_info) +{ + _php_ibase_service_action(INTERNAL_FUNCTION_PARAM_PASSTHRU, isc_action_svc_db_stats); +} +/* }}} */ + +/* {{{ proto string ibase_server_info(resource service_handle, int action) + Request information about a database server */ +PHP_FUNCTION(ibase_server_info) +{ + zval *res; + long action; + ibase_service *svm; + + RESET_ERRMSG; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &res, &action)) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, + "Interbase service manager handle", le_service); + + _php_ibase_service_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, svm, (char)action); +} +/* }}} */ + +#else + +void php_ibase_register_service_constants(INIT_FUNC_ARGS) { /* nop */ } + +#endif /* HAVE_IBASE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/interbase/interbase.c b/ext/interbase/interbase.c new file mode 100644 index 0000000..132ad35 --- /dev/null +++ b/ext/interbase/interbase.c @@ -0,0 +1,1482 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jouni Ahto <jouni.ahto@exdec.fi> | + | Andrew Avdeev <andy@rsc.mv.ru> | + | Ard Biesheuvel <a.k.biesheuvel@ewi.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define _GNU_SOURCE + +#include "php.h" + +#if HAVE_IBASE + +#include "php_ini.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/md5.h" +#include "php_interbase.h" +#include "php_ibase_includes.h" +#include "SAPI.h" + +#include <time.h> + +#define ROLLBACK 0 +#define COMMIT 1 +#define RETAIN 2 + +#define CHECK_LINK(link) { if (link==-1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "A link to the server could not be established"); RETURN_FALSE; } } + +ZEND_DECLARE_MODULE_GLOBALS(ibase) +static PHP_GINIT_FUNCTION(ibase); + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO(arginfo_ibase_errmsg, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_ibase_errcode, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_connect, 0, 0, 1) + ZEND_ARG_INFO(0, database) + ZEND_ARG_INFO(0, username) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, charset) + ZEND_ARG_INFO(0, buffers) + ZEND_ARG_INFO(0, dialect) + ZEND_ARG_INFO(0, role) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_pconnect, 0, 0, 1) + ZEND_ARG_INFO(0, database) + ZEND_ARG_INFO(0, username) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, charset) + ZEND_ARG_INFO(0, buffers) + ZEND_ARG_INFO(0, dialect) + ZEND_ARG_INFO(0, role) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_close, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_drop_db, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_trans, 0, 0, 0) + ZEND_ARG_INFO(0, trans_args) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, trans_args) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_commit, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_rollback, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_commit_ret, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_rollback_ret, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_gen_id, 0, 0, 1) + ZEND_ARG_INFO(0, generator) + ZEND_ARG_INFO(0, increment) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_create, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_open, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, blob_id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_add, 0, 0, 2) + ZEND_ARG_INFO(0, blob_handle) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_get, 0, 0, 2) + ZEND_ARG_INFO(0, blob_handle) + ZEND_ARG_INFO(0, len) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_close, 0, 0, 1) + ZEND_ARG_INFO(0, blob_handle) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_cancel, 0, 0, 1) + ZEND_ARG_INFO(0, blob_handle) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_info, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, blob_id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_echo, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, blob_id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_blob_import, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, file) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_query, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, query) + ZEND_ARG_INFO(0, bind_arg) + ZEND_ARG_INFO(0, bind_arg) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_affected_rows, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) +ZEND_END_ARG_INFO() + +#if abies_0 +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_rows, 0, 0, 1) + ZEND_ARG_INFO(0, result_identifier) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_row, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, fetch_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_assoc, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, fetch_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_fetch_object, 0, 0, 1) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, fetch_flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_name_result, 0, 0, 2) + ZEND_ARG_INFO(0, result) + ZEND_ARG_INFO(0, name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_result, 0, 0, 1) + ZEND_ARG_INFO(0, result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_prepare, 0, 0, 0) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_execute, 0, 0, 1) + ZEND_ARG_INFO(0, query) + ZEND_ARG_INFO(0, bind_arg) + ZEND_ARG_INFO(0, bind_arg) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_query, 0, 0, 1) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_fields, 0, 0, 1) + ZEND_ARG_INFO(0, query_result) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_field_info, 0, 0, 2) + ZEND_ARG_INFO(0, query_result) + ZEND_ARG_INFO(0, field_number) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_num_params, 0, 0, 1) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_param_info, 0, 0, 2) + ZEND_ARG_INFO(0, query) + ZEND_ARG_INFO(0, field_number) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_add_user, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, user_name) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, first_name) + ZEND_ARG_INFO(0, middle_name) + ZEND_ARG_INFO(0, last_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_modify_user, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, user_name) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, first_name) + ZEND_ARG_INFO(0, middle_name) + ZEND_ARG_INFO(0, last_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_delete_user, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, user_name) + ZEND_ARG_INFO(0, password) + ZEND_ARG_INFO(0, first_name) + ZEND_ARG_INFO(0, middle_name) + ZEND_ARG_INFO(0, last_name) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_service_attach, 0, 0, 3) + ZEND_ARG_INFO(0, host) + ZEND_ARG_INFO(0, dba_username) + ZEND_ARG_INFO(0, dba_password) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_service_detach, 0, 0, 1) + ZEND_ARG_INFO(0, service_handle) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_backup, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, source_db) + ZEND_ARG_INFO(0, dest_file) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, verbose) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_restore, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, source_file) + ZEND_ARG_INFO(0, dest_db) + ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, verbose) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_maintain_db, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, db) + ZEND_ARG_INFO(0, action) + ZEND_ARG_INFO(0, argument) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_db_info, 0, 0, 3) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, db) + ZEND_ARG_INFO(0, action) + ZEND_ARG_INFO(0, argument) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_server_info, 0, 0, 2) + ZEND_ARG_INFO(0, service_handle) + ZEND_ARG_INFO(0, action) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_wait_event, 0, 0, 1) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, event) + ZEND_ARG_INFO(0, event2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_set_event_handler, 0, 0, 2) + ZEND_ARG_INFO(0, link_identifier) + ZEND_ARG_INFO(0, handler) + ZEND_ARG_INFO(0, event) + ZEND_ARG_INFO(0, event2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_ibase_free_event_handler, 0, 0, 1) + ZEND_ARG_INFO(0, event) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ extension definition structures */ +const zend_function_entry ibase_functions[] = { + PHP_FE(ibase_connect, arginfo_ibase_connect) + PHP_FE(ibase_pconnect, arginfo_ibase_pconnect) + PHP_FE(ibase_close, arginfo_ibase_close) + PHP_FE(ibase_drop_db, arginfo_ibase_drop_db) + PHP_FE(ibase_query, arginfo_ibase_query) + PHP_FE(ibase_fetch_row, arginfo_ibase_fetch_row) + PHP_FE(ibase_fetch_assoc, arginfo_ibase_fetch_assoc) + PHP_FE(ibase_fetch_object, arginfo_ibase_fetch_object) + PHP_FE(ibase_free_result, arginfo_ibase_free_result) + PHP_FE(ibase_name_result, arginfo_ibase_name_result) + PHP_FE(ibase_prepare, arginfo_ibase_prepare) + PHP_FE(ibase_execute, arginfo_ibase_execute) + PHP_FE(ibase_free_query, arginfo_ibase_free_query) + PHP_FE(ibase_gen_id, arginfo_ibase_gen_id) + PHP_FE(ibase_num_fields, arginfo_ibase_num_fields) + PHP_FE(ibase_num_params, arginfo_ibase_num_params) +#if abies_0 + PHP_FE(ibase_num_rows, arginfo_ibase_num_rows) +#endif + PHP_FE(ibase_affected_rows, arginfo_ibase_affected_rows) + PHP_FE(ibase_field_info, arginfo_ibase_field_info) + PHP_FE(ibase_param_info, arginfo_ibase_param_info) + + PHP_FE(ibase_trans, arginfo_ibase_trans) + PHP_FE(ibase_commit, arginfo_ibase_commit) + PHP_FE(ibase_rollback, arginfo_ibase_rollback) + PHP_FE(ibase_commit_ret, arginfo_ibase_commit_ret) + PHP_FE(ibase_rollback_ret, arginfo_ibase_rollback_ret) + + PHP_FE(ibase_blob_info, arginfo_ibase_blob_info) + PHP_FE(ibase_blob_create, arginfo_ibase_blob_create) + PHP_FE(ibase_blob_add, arginfo_ibase_blob_add) + PHP_FE(ibase_blob_cancel, arginfo_ibase_blob_cancel) + PHP_FE(ibase_blob_close, arginfo_ibase_blob_close) + PHP_FE(ibase_blob_open, arginfo_ibase_blob_open) + PHP_FE(ibase_blob_get, arginfo_ibase_blob_get) + PHP_FE(ibase_blob_echo, arginfo_ibase_blob_echo) + PHP_FE(ibase_blob_import, arginfo_ibase_blob_import) + PHP_FE(ibase_errmsg, arginfo_ibase_errmsg) + PHP_FE(ibase_errcode, arginfo_ibase_errcode) + + PHP_FE(ibase_add_user, arginfo_ibase_add_user) + PHP_FE(ibase_modify_user, arginfo_ibase_modify_user) + PHP_FE(ibase_delete_user, arginfo_ibase_delete_user) + + PHP_FE(ibase_service_attach, arginfo_ibase_service_attach) + PHP_FE(ibase_service_detach, arginfo_ibase_service_detach) + PHP_FE(ibase_backup, arginfo_ibase_backup) + PHP_FE(ibase_restore, arginfo_ibase_restore) + PHP_FE(ibase_maintain_db, arginfo_ibase_maintain_db) + PHP_FE(ibase_db_info, arginfo_ibase_db_info) + PHP_FE(ibase_server_info, arginfo_ibase_server_info) + + PHP_FE(ibase_wait_event, arginfo_ibase_wait_event) + PHP_FE(ibase_set_event_handler, arginfo_ibase_set_event_handler) + PHP_FE(ibase_free_event_handler, arginfo_ibase_free_event_handler) + + /** + * These aliases are provided in order to maintain forward compatibility. As Firebird + * and InterBase are developed independently, functionality might be different between + * the two branches in future versions. + * Firebird users should use the aliases, so future InterBase-specific changes will + * not affect their code + */ + PHP_FALIAS(fbird_connect, ibase_connect, arginfo_ibase_connect) + PHP_FALIAS(fbird_pconnect, ibase_pconnect, arginfo_ibase_pconnect) + PHP_FALIAS(fbird_close, ibase_close, arginfo_ibase_close) + PHP_FALIAS(fbird_drop_db, ibase_drop_db, arginfo_ibase_drop_db) + PHP_FALIAS(fbird_query, ibase_query, arginfo_ibase_query) + PHP_FALIAS(fbird_fetch_row, ibase_fetch_row, arginfo_ibase_fetch_row) + PHP_FALIAS(fbird_fetch_assoc, ibase_fetch_assoc, arginfo_ibase_fetch_assoc) + PHP_FALIAS(fbird_fetch_object, ibase_fetch_object, arginfo_ibase_fetch_object) + PHP_FALIAS(fbird_free_result, ibase_free_result, arginfo_ibase_free_result) + PHP_FALIAS(fbird_name_result, ibase_name_result, arginfo_ibase_name_result) + PHP_FALIAS(fbird_prepare, ibase_prepare, arginfo_ibase_prepare) + PHP_FALIAS(fbird_execute, ibase_execute, arginfo_ibase_execute) + PHP_FALIAS(fbird_free_query, ibase_free_query, arginfo_ibase_free_query) + PHP_FALIAS(fbird_gen_id, ibase_gen_id, arginfo_ibase_gen_id) + PHP_FALIAS(fbird_num_fields, ibase_num_fields, arginfo_ibase_num_fields) + PHP_FALIAS(fbird_num_params, ibase_num_params, arginfo_ibase_num_params) +#if abies_0 + PHP_FALIAS(fbird_num_rows, ibase_num_rows, arginfo_ibase_num_rows) +#endif + PHP_FALIAS(fbird_affected_rows, ibase_affected_rows, arginfo_ibase_affected_rows) + PHP_FALIAS(fbird_field_info, ibase_field_info, arginfo_ibase_field_info) + PHP_FALIAS(fbird_param_info, ibase_param_info, arginfo_ibase_param_info) + + PHP_FALIAS(fbird_trans, ibase_trans, arginfo_ibase_trans) + PHP_FALIAS(fbird_commit, ibase_commit, arginfo_ibase_commit) + PHP_FALIAS(fbird_rollback, ibase_rollback, arginfo_ibase_rollback) + PHP_FALIAS(fbird_commit_ret, ibase_commit_ret, arginfo_ibase_commit_ret) + PHP_FALIAS(fbird_rollback_ret, ibase_rollback_ret, arginfo_ibase_rollback_ret) + + PHP_FALIAS(fbird_blob_info, ibase_blob_info, arginfo_ibase_blob_info) + PHP_FALIAS(fbird_blob_create, ibase_blob_create, arginfo_ibase_blob_create) + PHP_FALIAS(fbird_blob_add, ibase_blob_add, arginfo_ibase_blob_add) + PHP_FALIAS(fbird_blob_cancel, ibase_blob_cancel, arginfo_ibase_blob_cancel) + PHP_FALIAS(fbird_blob_close, ibase_blob_close, arginfo_ibase_blob_close) + PHP_FALIAS(fbird_blob_open, ibase_blob_open, arginfo_ibase_blob_open) + PHP_FALIAS(fbird_blob_get, ibase_blob_get, arginfo_ibase_blob_get) + PHP_FALIAS(fbird_blob_echo, ibase_blob_echo, arginfo_ibase_blob_echo) + PHP_FALIAS(fbird_blob_import, ibase_blob_import, arginfo_ibase_blob_import) + PHP_FALIAS(fbird_errmsg, ibase_errmsg, arginfo_ibase_errmsg) + PHP_FALIAS(fbird_errcode, ibase_errcode, arginfo_ibase_errcode) + + PHP_FALIAS(fbird_add_user, ibase_add_user, arginfo_ibase_add_user) + PHP_FALIAS(fbird_modify_user, ibase_modify_user, arginfo_ibase_modify_user) + PHP_FALIAS(fbird_delete_user, ibase_delete_user, arginfo_ibase_delete_user) + + PHP_FALIAS(fbird_service_attach, ibase_service_attach, arginfo_ibase_service_attach) + PHP_FALIAS(fbird_service_detach, ibase_service_detach, arginfo_ibase_service_detach) + PHP_FALIAS(fbird_backup, ibase_backup, arginfo_ibase_backup) + PHP_FALIAS(fbird_restore, ibase_restore, arginfo_ibase_restore) + PHP_FALIAS(fbird_maintain_db, ibase_maintain_db, arginfo_ibase_maintain_db) + PHP_FALIAS(fbird_db_info, ibase_db_info, arginfo_ibase_db_info) + PHP_FALIAS(fbird_server_info, ibase_server_info, arginfo_ibase_server_info) + + PHP_FALIAS(fbird_wait_event, ibase_wait_event, arginfo_ibase_wait_event) + PHP_FALIAS(fbird_set_event_handler, ibase_set_event_handler, arginfo_ibase_set_event_handler) + PHP_FALIAS(fbird_free_event_handler, ibase_free_event_handler, arginfo_ibase_free_event_handler) + PHP_FE_END +}; + +zend_module_entry ibase_module_entry = { + STANDARD_MODULE_HEADER, + "interbase", + ibase_functions, + PHP_MINIT(ibase), + PHP_MSHUTDOWN(ibase), + NULL, + PHP_RSHUTDOWN(ibase), + PHP_MINFO(ibase), + NO_VERSION_YET, + PHP_MODULE_GLOBALS(ibase), + PHP_GINIT(ibase), + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; + +#ifdef COMPILE_DL_INTERBASE +ZEND_GET_MODULE(ibase) +#endif + +/* True globals, no need for thread safety */ +int le_link, le_plink, le_trans; + +/* }}} */ + +/* error handling ---------------------------- */ + +/* {{{ proto string ibase_errmsg(void) + Return error message */ +PHP_FUNCTION(ibase_errmsg) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (IBG(sql_code) != 0) { + RETURN_STRING(IBG(errmsg), 1); + } + + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto int ibase_errcode(void) + Return error code */ +PHP_FUNCTION(ibase_errcode) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (IBG(sql_code) != 0) { + RETURN_LONG(IBG(sql_code)); + } + RETURN_FALSE; +} +/* }}} */ + +/* print interbase error and save it for ibase_errmsg() */ +void _php_ibase_error(TSRMLS_D) /* {{{ */ +{ + char *s = IBG(errmsg); + ISC_STATUS *statusp = IB_STATUS; + + IBG(sql_code) = isc_sqlcode(IB_STATUS); + + while ((s - IBG(errmsg)) < MAX_ERRMSG - (IBASE_MSGSIZE + 2) && isc_interprete(s, &statusp)) { + strcat(IBG(errmsg), " "); + s = IBG(errmsg) + strlen(IBG(errmsg)); + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", IBG(errmsg)); +} +/* }}} */ + +/* print php interbase module error and save it for ibase_errmsg() */ +void _php_ibase_module_error(char *msg TSRMLS_DC, ...) /* {{{ */ +{ + va_list ap; + +#ifdef ZTS + va_start(ap, TSRMLS_C); +#else + va_start(ap, msg); +#endif + + /* vsnprintf NUL terminates the buf and writes at most n-1 chars+NUL */ + vsnprintf(IBG(errmsg), MAX_ERRMSG, msg, ap); + va_end(ap); + + IBG(sql_code) = -999; /* no SQL error */ + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", IBG(errmsg)); +} +/* }}} */ + +/* {{{ internal macros, functions and structures */ +typedef struct { + isc_db_handle *db_ptr; + long tpb_len; + char *tpb_ptr; +} ISC_TEB; + +/* }}} */ + +/* Fill ib_link and trans with the correct database link and transaction. */ +void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */ + zval **link_id, ibase_db_link **ib_link, ibase_trans **trans) +{ + int type; + + IBDEBUG("Transaction or database link?"); + if (zend_list_find(Z_LVAL_PP(link_id), &type)) { + if (type == le_trans) { + /* Transaction resource: make sure it refers to one link only, then + fetch it; database link is stored in ib_trans->db_link[]. */ + IBDEBUG("Type is le_trans"); + ZEND_FETCH_RESOURCE(*trans, ibase_trans *, link_id, -1, LE_TRANS, le_trans); + if ((*trans)->link_cnt > 1) { + _php_ibase_module_error("Link id is ambiguous: transaction spans multiple connections." + TSRMLS_CC); + return; + } + *ib_link = (*trans)->db_link[0]; + return; + } + } + IBDEBUG("Type is le_[p]link or id not found"); + /* Database link resource, use default transaction. */ + *trans = NULL; + ZEND_FETCH_RESOURCE2(*ib_link, ibase_db_link *, link_id, -1, LE_LINK, le_link, le_plink); +} +/* }}} */ + +/* destructors ---------------------- */ + +static void _php_ibase_commit_link(ibase_db_link *link TSRMLS_DC) /* {{{ */ +{ + unsigned short i = 0, j; + ibase_tr_list *l; + ibase_event *e; + IBDEBUG("Checking transactions to close..."); + + for (l = link->tr_list; l != NULL; ++i) { + ibase_tr_list *p = l; + if (p->trans != NULL) { + if (i == 0) { + if (p->trans->handle != NULL) { + IBDEBUG("Committing default transaction..."); + if (isc_commit_transaction(IB_STATUS, &p->trans->handle)) { + _php_ibase_error(TSRMLS_C); + } + } + efree(p->trans); /* default transaction is not a registered resource: clean up */ + } else { + if (p->trans->handle != NULL) { + /* non-default trans might have been rolled back by other call of this dtor */ + IBDEBUG("Rolling back other transactions..."); + if (isc_rollback_transaction(IB_STATUS, &p->trans->handle)) { + _php_ibase_error(TSRMLS_C); + } + } + /* set this link pointer to NULL in the transaction */ + for (j = 0; j < p->trans->link_cnt; ++j) { + if (p->trans->db_link[j] == link) { + p->trans->db_link[j] = NULL; + break; + } + } + } + } + l = l->next; + efree(p); + } + link->tr_list = NULL; + + for (e = link->event_head; e; e = e->event_next) { + _php_ibase_free_event(e TSRMLS_CC); + e->link = NULL; + } +} + +/* }}} */ + +static void php_ibase_commit_link_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_db_link *link = (ibase_db_link *) rsrc->ptr; + + _php_ibase_commit_link(link TSRMLS_CC); +} +/* }}} */ + +static void _php_ibase_close_link(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_db_link *link = (ibase_db_link *) rsrc->ptr; + + _php_ibase_commit_link(link TSRMLS_CC); + if (link->handle != NULL) { + IBDEBUG("Closing normal link..."); + isc_detach_database(IB_STATUS, &link->handle); + } + IBG(num_links)--; + efree(link); +} +/* }}} */ + +static void _php_ibase_close_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_db_link *link = (ibase_db_link *) rsrc->ptr; + + _php_ibase_commit_link(link TSRMLS_CC); + IBDEBUG("Closing permanent link..."); + if (link->handle != NULL) { + isc_detach_database(IB_STATUS, &link->handle); + } + IBG(num_persistent)--; + IBG(num_links)--; + free(link); +} +/* }}} */ + +static void _php_ibase_free_trans(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */ +{ + ibase_trans *trans = (ibase_trans *)rsrc->ptr; + unsigned short i; + + IBDEBUG("Cleaning up transaction resource..."); + if (trans->handle != NULL) { + IBDEBUG("Rolling back unhandled transaction..."); + if (isc_rollback_transaction(IB_STATUS, &trans->handle)) { + _php_ibase_error(TSRMLS_C); + } + } + + /* now remove this transaction from all the connection-transaction lists */ + for (i = 0; i < trans->link_cnt; ++i) { + if (trans->db_link[i] != NULL) { + ibase_tr_list **l; + for (l = &trans->db_link[i]->tr_list; *l != NULL; l = &(*l)->next) { + if ( (*l)->trans == trans) { + ibase_tr_list *p = *l; + *l = p->next; + efree(p); + break; + } + } + } + } + efree(trans); +} +/* }}} */ + +/* TODO this function should be part of either Zend or PHP API */ +static PHP_INI_DISP(php_ibase_password_displayer_cb) +{ + TSRMLS_FETCH(); + + if ((type == PHP_INI_DISPLAY_ORIG && ini_entry->orig_value) + || (type == PHP_INI_DISPLAY_ACTIVE && ini_entry->value)) { + PUTS("********"); + } else if (!sapi_module.phpinfo_as_text) { + PUTS("<i>no value</i>"); + } else { + PUTS("no value"); + } +} + +/* {{{ startup, shutdown and info functions */ +PHP_INI_BEGIN() + PHP_INI_ENTRY_EX("ibase.allow_persistent", "1", PHP_INI_SYSTEM, NULL, zend_ini_boolean_displayer_cb) + PHP_INI_ENTRY_EX("ibase.max_persistent", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers) + PHP_INI_ENTRY_EX("ibase.max_links", "-1", PHP_INI_SYSTEM, NULL, display_link_numbers) + PHP_INI_ENTRY("ibase.default_db", NULL, PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("ibase.default_user", NULL, PHP_INI_ALL, NULL) + PHP_INI_ENTRY_EX("ibase.default_password", NULL, PHP_INI_ALL, NULL, php_ibase_password_displayer_cb) + PHP_INI_ENTRY("ibase.default_charset", NULL, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("ibase.timestampformat", IB_DEF_DATE_FMT " " IB_DEF_TIME_FMT, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("ibase.dateformat", IB_DEF_DATE_FMT, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("ibase.timeformat", IB_DEF_TIME_FMT, PHP_INI_ALL, NULL) +PHP_INI_END() + +static PHP_GINIT_FUNCTION(ibase) +{ + ibase_globals->num_persistent = ibase_globals->num_links = 0; + ibase_globals->sql_code = *ibase_globals->errmsg = 0; + ibase_globals->default_link = -1; +} + +PHP_MINIT_FUNCTION(ibase) +{ + REGISTER_INI_ENTRIES(); + + le_link = zend_register_list_destructors_ex(_php_ibase_close_link, NULL, LE_LINK, module_number); + le_plink = zend_register_list_destructors_ex(php_ibase_commit_link_rsrc, _php_ibase_close_plink, LE_PLINK, module_number); + le_trans = zend_register_list_destructors_ex(_php_ibase_free_trans, NULL, LE_TRANS, module_number); + + REGISTER_LONG_CONSTANT("IBASE_DEFAULT", PHP_IBASE_DEFAULT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_CREATE", PHP_IBASE_CREATE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_TEXT", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT); /* deprecated, for BC only */ + REGISTER_LONG_CONSTANT("IBASE_FETCH_BLOBS", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_FETCH_ARRAYS", PHP_IBASE_FETCH_ARRAYS, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_UNIXTIME", PHP_IBASE_UNIXTIME, CONST_PERSISTENT); + + /* transactions */ + REGISTER_LONG_CONSTANT("IBASE_WRITE", PHP_IBASE_WRITE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_READ", PHP_IBASE_READ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_COMMITTED", PHP_IBASE_COMMITTED, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_CONSISTENCY", PHP_IBASE_CONSISTENCY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_CONCURRENCY", PHP_IBASE_CONCURRENCY, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_REC_VERSION", PHP_IBASE_REC_VERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_REC_NO_VERSION", PHP_IBASE_REC_NO_VERSION, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_NOWAIT", PHP_IBASE_NOWAIT, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IBASE_WAIT", PHP_IBASE_WAIT, CONST_PERSISTENT); + + php_ibase_query_minit(INIT_FUNC_ARGS_PASSTHRU); + php_ibase_blobs_minit(INIT_FUNC_ARGS_PASSTHRU); + php_ibase_events_minit(INIT_FUNC_ARGS_PASSTHRU); + php_ibase_service_minit(INIT_FUNC_ARGS_PASSTHRU); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(ibase) +{ +#ifndef PHP_WIN32 + /** + * When the Interbase client API library libgds.so is first loaded, it registers a call to + * gds__cleanup() with atexit(), in order to clean up after itself when the process exits. + * This means that the library is called at process shutdown, and cannot be unloaded beforehand. + * PHP tries to unload modules after every request [dl()'ed modules], and right before the + * process shuts down [modules loaded from php.ini]. This results in a segfault for this module. + * By NULLing the dlopen() handle in the module entry, Zend omits the call to dlclose(), + * ensuring that the module will remain present until the process exits. However, the functions + * and classes exported by the module will not be available until the module is 'reloaded'. + * When reloaded, dlopen() will return the handle of the already loaded module. The module will + * be unloaded automatically when the process exits. + */ + zend_module_entry *ibase_entry; + if (SUCCESS == zend_hash_find(&module_registry, ibase_module_entry.name, + strlen(ibase_module_entry.name) +1, (void*) &ibase_entry)) { + ibase_entry->handle = NULL; + } +#endif + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(ibase) +{ + IBG(num_links) = IBG(num_persistent); + IBG(default_link)= -1; + + RESET_ERRMSG; + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(ibase) +{ + char tmp[64], *s; + + php_info_print_table_start(); + php_info_print_table_row(2, "Firebird/InterBase Support", +#ifdef COMPILE_DL_INTERBASE + "dynamic"); +#else + "static"); +#endif + +#ifdef FB_API_VER + snprintf( (s = tmp), sizeof(tmp), "Firebird API version %d", FB_API_VER); +#elif (SQLDA_CURRENT_VERSION > 1) + s = "Interbase 7.0 and up"; +#elif !defined(DSC_null) + s = "Interbase 6"; +#else + s = "Firebird 1.0"; +#endif + php_info_print_table_row(2, "Compile-time Client Library Version", s); + +#if defined(__GNUC__) || defined(PHP_WIN32) + do { + info_func_t info_func = NULL; +#ifdef __GNUC__ + info_func = (info_func_t)dlsym(RTLD_DEFAULT, "isc_get_client_version"); +#else + HMODULE l = GetModuleHandle("fbclient"); + + if (!l && !(l = GetModuleHandle("gds32"))) { + break; + } + info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version"); +#endif + if (info_func) { + info_func(s = tmp); + } else { + s = "Firebird 1.0/Interbase 6"; + } + php_info_print_table_row(2, "Run-time Client Library Version", s); + } while (0); +#endif + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); + +} +/* }}} */ + +enum connect_args { DB = 0, USER = 1, PASS = 2, CSET = 3, ROLE = 4, BUF = 0, DLECT = 1, SYNC = 2 }; + +static char const dpb_args[] = { + 0, isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name, 0 +}; + +int _php_ibase_attach_db(char **args, int *len, long *largs, isc_db_handle *db TSRMLS_DC) +{ + short i, dpb_len, buf_len = 257-2; /* version byte at the front, and a null at the end */ + char dpb_buffer[257] = { isc_dpb_version1, 0 }, *dpb; + + dpb = dpb_buffer + 1; + + for (i = 0; i < sizeof(dpb_args); ++i) { + if (dpb_args[i] && args[i] && len[i] && buf_len > 0) { + dpb_len = slprintf(dpb, buf_len, "%c%c%s", dpb_args[i],(unsigned char)len[i],args[i]); + dpb += dpb_len; + buf_len -= dpb_len; + } + } + if (largs[BUF] && buf_len > 0) { + dpb_len = slprintf(dpb, buf_len, "%c\2%c%c", isc_dpb_num_buffers, + (char)(largs[BUF] >> 8), (char)(largs[BUF] & 0xff)); + dpb += dpb_len; + buf_len -= dpb_len; + } + if (largs[SYNC] && buf_len > 0) { + dpb_len = slprintf(dpb, buf_len, "%c\1%c", isc_dpb_force_write, largs[SYNC] == isc_spb_prp_wm_sync ? 1 : 0); + dpb += dpb_len; + buf_len -= dpb_len; + } + if (isc_attach_database(IB_STATUS, (short)len[DB], args[DB], db, (short)(dpb-dpb_buffer), dpb_buffer)) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +static void _php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* {{{ */ +{ + char *c, hash[16], *args[] = { NULL, NULL, NULL, NULL, NULL }; + int i, len[] = { 0, 0, 0, 0, 0 }; + long largs[] = { 0, 0, 0 }; + PHP_MD5_CTX hash_context; + zend_rsrc_list_entry new_index_ptr, *le; + isc_db_handle db_handle = NULL; + ibase_db_link *ib_link; + + RESET_ERRMSG; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ssssllsl", + &args[DB], &len[DB], &args[USER], &len[USER], &args[PASS], &len[PASS], + &args[CSET], &len[CSET], &largs[BUF], &largs[DLECT], &args[ROLE], &len[ROLE], + &largs[SYNC])) { + RETURN_FALSE; + } + + /* restrict to the server/db in the .ini if in safe mode */ + if ((!len[DB] || PG(sql_safe_mode)) && (c = INI_STR("ibase.default_db"))) { + args[DB] = c; + len[DB] = strlen(c); + } + if (!len[USER] && (c = INI_STR("ibase.default_user"))) { + args[USER] = c; + len[USER] = strlen(c); + } + if (!len[PASS] && (c = INI_STR("ibase.default_password"))) { + args[PASS] = c; + len[PASS] = strlen(c); + } + if (!len[CSET] && (c = INI_STR("ibase.default_charset"))) { + args[CSET] = c; + len[CSET] = strlen(c); + } + + /* don't want usernames and passwords floating around */ + PHP_MD5Init(&hash_context); + for (i = 0; i < sizeof(args)/sizeof(char*); ++i) { + PHP_MD5Update(&hash_context,args[i],len[i]); + } + for (i = 0; i < sizeof(largs)/sizeof(long); ++i) { + PHP_MD5Update(&hash_context,(char*)&largs[i],sizeof(long)); + } + PHP_MD5Final(hash, &hash_context); + + /* try to reuse a connection */ + if (SUCCESS == zend_hash_find(&EG(regular_list), hash, sizeof(hash), (void *) &le)) { + long xlink; + int type; + + if (Z_TYPE_P(le) != le_index_ptr) { + RETURN_FALSE; + } + + xlink = (long) le->ptr; + if (zend_list_find(xlink, &type) && ((!persistent && type == le_link) || type == le_plink)) { + zend_list_addref(xlink); + RETURN_RESOURCE(IBG(default_link) = xlink); + } else { + zend_hash_del(&EG(regular_list), hash, sizeof(hash)); + } + } + + /* ... or a persistent one */ + switch (zend_hash_find(&EG(persistent_list), hash, sizeof(hash), (void *) &le)) { + long l; + + static char info[] = { isc_info_base_level, isc_info_end }; + char result[8]; + ISC_STATUS status[20]; + + case SUCCESS: + + if (Z_TYPE_P(le) != le_plink) { + RETURN_FALSE; + } + /* check if connection has timed out */ + ib_link = (ibase_db_link *) le->ptr; + if (!isc_database_info(status, &ib_link->handle, sizeof(info), info, sizeof(result), result)) { + ZEND_REGISTER_RESOURCE(return_value, ib_link, le_plink); + break; + } + zend_hash_del(&EG(persistent_list), hash, sizeof(hash)); + + default: + + /* no link found, so we have to open one */ + + if ((l = INI_INT("ibase.max_links")) != -1 && IBG(num_links) >= l) { + _php_ibase_module_error("Too many open links (%ld)" TSRMLS_CC, IBG(num_links)); + RETURN_FALSE; + } + + /* create the ib_link */ + if (FAILURE == _php_ibase_attach_db(args, len, largs, &db_handle TSRMLS_CC)) { + RETURN_FALSE; + } + + /* use non-persistent if allowed number of persistent links is exceeded */ + if (!persistent || ((l = INI_INT("ibase.max_persistent") != -1) && IBG(num_persistent) >= l)) { + ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link)); + ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link); + } else { + zend_rsrc_list_entry new_le; + + ib_link = (ibase_db_link *) malloc(sizeof(ibase_db_link)); + if (!ib_link) { + RETURN_FALSE; + } + + /* hash it up */ + Z_TYPE(new_le) = le_plink; + new_le.ptr = ib_link; + if (FAILURE == zend_hash_update(&EG(persistent_list), hash, sizeof(hash), + (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL)) { + free(ib_link); + RETURN_FALSE; + } + ZEND_REGISTER_RESOURCE(return_value, ib_link, le_plink); + ++IBG(num_persistent); + } + ib_link->handle = db_handle; + ib_link->dialect = largs[DLECT] ? (unsigned short)largs[DLECT] : SQL_DIALECT_CURRENT; + ib_link->tr_list = NULL; + ib_link->event_head = NULL; + + ++IBG(num_links); + } + + /* add it to the hash */ + new_index_ptr.ptr = (void *) Z_LVAL_P(return_value); + Z_TYPE(new_index_ptr) = le_index_ptr; + if (FAILURE == zend_hash_update(&EG(regular_list), hash, sizeof(hash), + (void *) &new_index_ptr, sizeof(zend_rsrc_list_entry), NULL)) { + RETURN_FALSE; + } + zend_list_addref(IBG(default_link) = Z_LVAL_P(return_value)); +} +/* }}} */ + +/* {{{ proto resource ibase_connect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]]) + Open a connection to an InterBase database */ +PHP_FUNCTION(ibase_connect) +{ + _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto resource ibase_pconnect(string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]]) + Open a persistent connection to an InterBase database */ +PHP_FUNCTION(ibase_pconnect) +{ + _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INI_INT("ibase.allow_persistent")); +} +/* }}} */ + +/* {{{ proto bool ibase_close([resource link_identifier]) + Close an InterBase connection */ +PHP_FUNCTION(ibase_close) +{ + zval *link_arg = NULL; + ibase_db_link *ib_link; + int link_id; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link_arg) == FAILURE) { + return; + } + + if (ZEND_NUM_ARGS() == 0) { + link_id = IBG(default_link); + CHECK_LINK(link_id); + IBG(default_link) = -1; + } else { + link_id = Z_RESVAL_P(link_arg); + } + + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, link_id, LE_LINK, le_link, le_plink); + zend_list_delete(link_id); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ibase_drop_db([resource link_identifier]) + Drop an InterBase database */ +PHP_FUNCTION(ibase_drop_db) +{ + zval *link_arg = NULL; + ibase_db_link *ib_link; + ibase_tr_list *l; + int link_id; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link_arg) == FAILURE) { + return; + } + + if (ZEND_NUM_ARGS() == 0) { + link_id = IBG(default_link); + CHECK_LINK(link_id); + IBG(default_link) = -1; + } else { + link_id = Z_RESVAL_P(link_arg); + } + + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, link_id, LE_LINK, le_link, le_plink); + + if (isc_drop_database(IB_STATUS, &ib_link->handle)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + /* isc_drop_database() doesn't invalidate the transaction handles */ + for (l = ib_link->tr_list; l != NULL; l = l->next) { + if (l->trans != NULL) l->trans->handle = NULL; + } + + zend_list_delete(link_id); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]]) + Start a transaction over one or several databases */ + +#define TPB_MAX_SIZE (8*sizeof(char)) + +PHP_FUNCTION(ibase_trans) +{ + unsigned short i, link_cnt = 0, tpb_len = 0; + int argn; + char last_tpb[TPB_MAX_SIZE]; + ibase_db_link **ib_link = NULL; + ibase_trans *ib_trans; + isc_tr_handle tr_handle = NULL; + ISC_STATUS result; + + RESET_ERRMSG; + + argn = ZEND_NUM_ARGS(); + + /* (1+argn) is an upper bound for the number of links this trans connects to */ + ib_link = (ibase_db_link **) safe_emalloc(sizeof(ibase_db_link *),1+argn,0); + + if (argn > 0) { + long trans_argl = 0; + char *tpb; + ISC_TEB *teb; + zval ***args = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argn) == FAILURE) { + efree(args); + efree(ib_link); + RETURN_FALSE; + } + + teb = (ISC_TEB *) safe_emalloc(sizeof(ISC_TEB),argn,0); + tpb = (char *) safe_emalloc(TPB_MAX_SIZE,argn,0); + + /* enumerate all the arguments: assume every non-resource argument + specifies modifiers for the link ids that follow it */ + for (i = 0; i < argn; ++i) { + + if (Z_TYPE_PP(args[i]) == IS_RESOURCE) { + + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link[link_cnt], ibase_db_link *, args[i], -1, LE_LINK, le_link, le_plink)) { + efree(teb); + efree(tpb); + efree(ib_link); + efree(args); + RETURN_FALSE; + } + + /* copy the most recent modifier string into tbp[] */ + memcpy(&tpb[TPB_MAX_SIZE * link_cnt], last_tpb, TPB_MAX_SIZE); + + /* add a database handle to the TEB with the most recently specified set of modifiers */ + teb[link_cnt].db_ptr = &ib_link[link_cnt]->handle; + teb[link_cnt].tpb_len = tpb_len; + teb[link_cnt].tpb_ptr = &tpb[TPB_MAX_SIZE * link_cnt]; + + ++link_cnt; + + } else { + + tpb_len = 0; + + convert_to_long_ex(args[i]); + trans_argl = Z_LVAL_PP(args[i]); + + if (trans_argl != PHP_IBASE_DEFAULT) { + last_tpb[tpb_len++] = isc_tpb_version3; + + /* access mode */ + if (PHP_IBASE_READ == (trans_argl & PHP_IBASE_READ)) { + last_tpb[tpb_len++] = isc_tpb_read; + } else if (PHP_IBASE_WRITE == (trans_argl & PHP_IBASE_WRITE)) { + last_tpb[tpb_len++] = isc_tpb_write; + } + + /* isolation level */ + if (PHP_IBASE_COMMITTED == (trans_argl & PHP_IBASE_COMMITTED)) { + last_tpb[tpb_len++] = isc_tpb_read_committed; + if (PHP_IBASE_REC_VERSION == (trans_argl & PHP_IBASE_REC_VERSION)) { + last_tpb[tpb_len++] = isc_tpb_rec_version; + } else if (PHP_IBASE_REC_NO_VERSION == (trans_argl & PHP_IBASE_REC_NO_VERSION)) { + last_tpb[tpb_len++] = isc_tpb_no_rec_version; + } + } else if (PHP_IBASE_CONSISTENCY == (trans_argl & PHP_IBASE_CONSISTENCY)) { + last_tpb[tpb_len++] = isc_tpb_consistency; + } else if (PHP_IBASE_CONCURRENCY == (trans_argl & PHP_IBASE_CONCURRENCY)) { + last_tpb[tpb_len++] = isc_tpb_concurrency; + } + + /* lock resolution */ + if (PHP_IBASE_NOWAIT == (trans_argl & PHP_IBASE_NOWAIT)) { + last_tpb[tpb_len++] = isc_tpb_nowait; + } else if (PHP_IBASE_WAIT == (trans_argl & PHP_IBASE_WAIT)) { + last_tpb[tpb_len++] = isc_tpb_wait; + } + } + } + } + + if (link_cnt > 0) { + result = isc_start_multiple(IB_STATUS, &tr_handle, link_cnt, teb); + } + + efree(args); + efree(tpb); + efree(teb); + } + + if (link_cnt == 0) { + link_cnt = 1; + if (!ZEND_FETCH_RESOURCE2_NO_RETURN(ib_link[0], ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink)) { + efree(ib_link); + RETURN_FALSE; + } + result = isc_start_transaction(IB_STATUS, &tr_handle, 1, &ib_link[0]->handle, tpb_len, last_tpb); + } + + /* start the transaction */ + if (result) { + _php_ibase_error(TSRMLS_C); + efree(ib_link); + RETURN_FALSE; + } + + /* register the transaction in our own data structures */ + ib_trans = (ibase_trans *) safe_emalloc(link_cnt-1, sizeof(ibase_db_link *), sizeof(ibase_trans)); + ib_trans->handle = tr_handle; + ib_trans->link_cnt = link_cnt; + ib_trans->affected_rows = 0; + for (i = 0; i < link_cnt; ++i) { + ibase_tr_list **l; + ib_trans->db_link[i] = ib_link[i]; + + /* the first item in the connection-transaction list is reserved for the default transaction */ + if (ib_link[i]->tr_list == NULL) { + ib_link[i]->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list)); + ib_link[i]->tr_list->trans = NULL; + ib_link[i]->tr_list->next = NULL; + } + + /* link the transaction into the connection-transaction list */ + for (l = &ib_link[i]->tr_list; *l != NULL; l = &(*l)->next); + *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list)); + (*l)->trans = ib_trans; + (*l)->next = NULL; + } + efree(ib_link); + ZEND_REGISTER_RESOURCE(return_value, ib_trans, le_trans); +} +/* }}} */ + +int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC) /* {{{ */ +{ + if (ib_link == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid database link"); + return FAILURE; + } + + /* the first item in the connection-transaction list is reserved for the default transaction */ + if (ib_link->tr_list == NULL) { + ib_link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list)); + ib_link->tr_list->trans = NULL; + ib_link->tr_list->next = NULL; + } + + if (*trans == NULL) { + ibase_trans *tr = ib_link->tr_list->trans; + + if (tr == NULL) { + tr = (ibase_trans *) emalloc(sizeof(ibase_trans)); + tr->handle = NULL; + tr->link_cnt = 1; + tr->affected_rows = 0; + tr->db_link[0] = ib_link; + ib_link->tr_list->trans = tr; + } + if (tr->handle == NULL) { + if (isc_start_transaction(IB_STATUS, &tr->handle, 1, &ib_link->handle, 0, NULL)) { + _php_ibase_error(TSRMLS_C); + return FAILURE; + } + } + *trans = tr; + } + return SUCCESS; +} +/* }}} */ + +static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit) /* {{{ */ +{ + ibase_trans *trans = NULL; + int res_id = 0; + ISC_STATUS result; + ibase_db_link *ib_link; + zval *arg = NULL; + int type; + + RESET_ERRMSG; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &arg) == FAILURE) { + return; + } + + if (ZEND_NUM_ARGS() == 0) { + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), LE_LINK, le_link, le_plink); + if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) { + /* this link doesn't have a default transaction */ + _php_ibase_module_error("Default link has no default transaction" TSRMLS_CC); + RETURN_FALSE; + } + trans = ib_link->tr_list->trans; + } else { + /* one id was passed, could be db or trans id */ + if (zend_list_find(Z_RESVAL_P(arg), &type) && type == le_trans) { + ZEND_FETCH_RESOURCE(trans, ibase_trans *, &arg, -1, LE_TRANS, le_trans); + res_id = Z_RESVAL_P(arg); + } else { + ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &arg, -1, LE_LINK, le_link, le_plink); + + if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) { + /* this link doesn't have a default transaction */ + _php_ibase_module_error("Link has no default transaction" TSRMLS_CC); + RETURN_FALSE; + } + trans = ib_link->tr_list->trans; + } + } + + switch (commit) { + default: /* == case ROLLBACK: */ + result = isc_rollback_transaction(IB_STATUS, &trans->handle); + break; + case COMMIT: + result = isc_commit_transaction(IB_STATUS, &trans->handle); + break; + case (ROLLBACK | RETAIN): + result = isc_rollback_retaining(IB_STATUS, &trans->handle); + break; + case (COMMIT | RETAIN): + result = isc_commit_retaining(IB_STATUS, &trans->handle); + break; + } + + if (result) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + /* Don't try to destroy implicitly opened transaction from list... */ + if ((commit & RETAIN) == 0 && res_id != 0) { + zend_list_delete(res_id); + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ibase_commit( resource link_identifier ) + Commit transaction */ +PHP_FUNCTION(ibase_commit) +{ + _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT); +} +/* }}} */ + +/* {{{ proto bool ibase_rollback( resource link_identifier ) + Rollback transaction */ +PHP_FUNCTION(ibase_rollback) +{ + _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK); +} +/* }}} */ + +/* {{{ proto bool ibase_commit_ret( resource link_identifier ) + Commit transaction and retain the transaction context */ +PHP_FUNCTION(ibase_commit_ret) +{ + _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT | RETAIN); +} +/* }}} */ + +/* {{{ proto bool ibase_rollback_ret( resource link_identifier ) + Rollback transaction and retain the transaction context */ +PHP_FUNCTION(ibase_rollback_ret) +{ + _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK | RETAIN); +} +/* }}} */ + +/* {{{ proto int ibase_gen_id(string generator [, int increment [, resource link_identifier ]]) + Increments the named generator and returns its new value */ +PHP_FUNCTION(ibase_gen_id) +{ + zval *link = NULL; + char query[128], *generator; + int gen_len; + long inc = 1; + ibase_db_link *ib_link; + ibase_trans *trans = NULL; + XSQLDA out_sqlda; + ISC_INT64 result; + + RESET_ERRMSG; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &generator, &gen_len, + &inc, &link)) { + RETURN_FALSE; + } + + if (gen_len > 31) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid generator name"); + RETURN_FALSE; + } + + PHP_IBASE_LINK_TRANS(link, ib_link, trans); + + snprintf(query, sizeof(query), "SELECT GEN_ID(%s,%ld) FROM rdb$database", generator, inc); + + /* allocate a minimal descriptor area */ + out_sqlda.sqln = out_sqlda.sqld = 1; + out_sqlda.version = SQLDA_CURRENT_VERSION; + + /* allocate the field for the result */ + out_sqlda.sqlvar[0].sqltype = SQL_INT64; + out_sqlda.sqlvar[0].sqlscale = 0; + out_sqlda.sqlvar[0].sqllen = sizeof(result); + out_sqlda.sqlvar[0].sqldata = (void*) &result; + + /* execute the query */ + if (isc_dsql_exec_immed2(IB_STATUS, &ib_link->handle, &trans->handle, 0, query, + SQL_DIALECT_CURRENT, NULL, &out_sqlda)) { + _php_ibase_error(TSRMLS_C); + RETURN_FALSE; + } + + /* don't return the generator value as a string unless it doesn't fit in a long */ +#if SIZEOF_LONG < 8 + if (result < LONG_MIN || result > LONG_MAX) { + char *res; + int l; + + l = spprintf(&res, 0, "%" LL_MASK "d", result); + RETURN_STRINGL(res, l, 0); + } +#endif + RETURN_LONG((long)result); +} + +/* }}} */ + +#endif /* HAVE_IBASE */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/ext/interbase/interbase.dsp b/ext/interbase/interbase.dsp new file mode 100644 index 0000000..c151deb --- /dev/null +++ b/ext/interbase/interbase.dsp @@ -0,0 +1,135 @@ +# Microsoft Developer Studio Project File - Name="interbase" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=interbase - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "interbase.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "interbase.mak" CFG="interbase - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "interbase - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "interbase - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName "interbase"
+# PROP Scc_LocalPath "."
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "interbase - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\main" /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\..\php_build\Interbase SDK\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INTERBASE_EXPORTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D ZEND_DEBUG=1 /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\main" /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\..\php_build\Interbase SDK\include" /I "..\..\..\bindlib_w32" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INTERBASE_EXPORTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_IBASE=1 /D "COMPILE_DL_INTERBASE" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "_DEBUG"
+# ADD RSC /l 0x40d /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ib_util_ms.lib gds32_ms.lib php5ts_debug.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts_debug.lib gds32_ms.lib /nologo /dll /debug /machine:I386 /out:"..\..\Debug_TS/php_interbase.dll" /pdbtype:sept /libpath:"..\..\..\php_build\Interbase SDK\lib_ms" /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "interbase - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\..\main" /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\..\php_build\Interbase SDK\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INTERBASE_EXPORTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_IBASE=1 /D ZEND_DEBUG=0 /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\main" /I "..\.." /I "..\..\Zend" /I "..\..\TSRM" /I "..\..\..\php_build\Interbase SDK\include" /I "..\..\..\bindlib_w32" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INTERBASE_EXPORTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D HAVE_IBASE=1 /D "COMPILE_DL_INTERBASE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ib_util_ms.lib gds32_ms.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib gds32_ms.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS/php_interbase.dll" /libpath:"..\..\..\php_build\Interbase SDK\lib_ms" /libpath:"..\..\Release_TS" /libpath:"..\..\Release_TS_Inline"
+
+!ENDIF
+
+# Begin Target
+
+# Name "interbase - Win32 Debug_TS"
+# Name "interbase - Win32 Release_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ibase_blobs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ibase_events.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ibase_query.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ibase_service.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\interbase.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_interbase.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\interbase.rc
+# ADD BASE RSC /l 0x413
+# ADD RSC /l 0x413 /i "..\..\main" /i "..\..\win32" /d "PHP_H"
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/ext/interbase/php_ibase_includes.h b/ext/interbase/php_ibase_includes.h new file mode 100644 index 0000000..559be77 --- /dev/null +++ b/ext/interbase/php_ibase_includes.h @@ -0,0 +1,200 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jouni Ahto <jouni.ahto@exdec.fi> | + | Andrew Avdeev <andy@simgts.mv.ru> | + | Ard Biesheuvel <a.k.biesheuvel@ewi.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_IBASE_INCLUDES_H +#define PHP_IBASE_INCLUDES_H + +#include <ibase.h> + +#ifndef SQLDA_CURRENT_VERSION +#define SQLDA_CURRENT_VERSION SQLDA_VERSION1 +#endif + +#ifndef METADATALENGTH +#define METADATALENGTH 68 +#endif + +#define RESET_ERRMSG do { IBG(errmsg)[0] = '\0'; IBG(sql_code) = 0; } while (0) + +#define IB_STATUS (IBG(status)) + +#ifdef ZEND_DEBUG_ +#define IBDEBUG(a) php_printf("::: %s (%d)\n", a, __LINE__); +#endif + +#ifndef IBDEBUG +#define IBDEBUG(a) +#endif + +extern int le_link, le_plink, le_trans; + +#define LE_LINK "Firebird/InterBase link" +#define LE_PLINK "Firebird/InterBase persistent link" +#define LE_TRANS "Firebird/InterBase transaction" + +#define IBASE_MSGSIZE 512 +#define MAX_ERRMSG (IBASE_MSGSIZE*2) + +#define IB_DEF_DATE_FMT "%Y-%m-%d" +#define IB_DEF_TIME_FMT "%H:%M:%S" + +/* this value should never be > USHRT_MAX */ +#define IBASE_BLOB_SEG 4096 + +ZEND_BEGIN_MODULE_GLOBALS(ibase) + ISC_STATUS status[20]; + long default_link; + long num_links, num_persistent; + char errmsg[MAX_ERRMSG]; + long sql_code; +ZEND_END_MODULE_GLOBALS(ibase) + +ZEND_EXTERN_MODULE_GLOBALS(ibase) + +typedef struct { + isc_db_handle handle; + struct tr_list *tr_list; + unsigned short dialect; + struct event *event_head; +} ibase_db_link; + +typedef struct { + isc_tr_handle handle; + unsigned short link_cnt; + unsigned long affected_rows; + ibase_db_link *db_link[1]; /* last member */ +} ibase_trans; + +typedef struct tr_list { + ibase_trans *trans; + struct tr_list *next; +} ibase_tr_list; + +typedef struct { + isc_blob_handle bl_handle; + unsigned short type; + ISC_QUAD bl_qd; +} ibase_blob; + +typedef struct event { + ibase_db_link *link; + long link_res_id; + ISC_LONG event_id; + unsigned short event_count; + char **events; + char *event_buffer, *result_buffer; + zval *callback; + void **thread_ctx; + struct event *event_next; + enum event_state { NEW, ACTIVE, DEAD } state; +} ibase_event; + +enum php_interbase_option { + PHP_IBASE_DEFAULT = 0, + PHP_IBASE_CREATE = 0, + /* fetch flags */ + PHP_IBASE_FETCH_BLOBS = 1, + PHP_IBASE_FETCH_ARRAYS = 2, + PHP_IBASE_UNIXTIME = 4, + /* transaction access mode */ + PHP_IBASE_WRITE = 1, + PHP_IBASE_READ = 2, + /* transaction isolation level */ + PHP_IBASE_CONCURRENCY = 4, + PHP_IBASE_COMMITTED = 8, + PHP_IBASE_REC_NO_VERSION = 32, + PHP_IBASE_REC_VERSION = 64, + PHP_IBASE_CONSISTENCY = 16, + /* transaction lock resolution */ + PHP_IBASE_WAIT = 128, + PHP_IBASE_NOWAIT = 256 +}; + +#ifdef ZTS +#define IBG(v) TSRMG(ibase_globals_id, zend_ibase_globals *, v) +#else +#define IBG(v) (ibase_globals.v) +#endif + +#define BLOB_ID_LEN 18 +#define BLOB_ID_MASK "0x%" LL_MASK "x" + +#define BLOB_INPUT 1 +#define BLOB_OUTPUT 2 + +#ifdef PHP_WIN32 +#define LL_MASK "I64" +#define LL_LIT(lit) lit ## I64 +typedef void (__stdcall *info_func_t)(char*); +#else +#define LL_MASK "ll" +#define LL_LIT(lit) lit ## ll +typedef void (*info_func_t)(char*); +#endif + +void _php_ibase_error(TSRMLS_D); +void _php_ibase_module_error(char * TSRMLS_DC, ...) + PHP_ATTRIBUTE_FORMAT(printf,1,PHP_ATTR_FMT_OFFSET +2); + +/* determine if a resource is a link or transaction handle */ +#define PHP_IBASE_LINK_TRANS(pzval, lh, th) \ + do { if (!pzval) { \ + ZEND_FETCH_RESOURCE2(lh, ibase_db_link *, NULL, IBG(default_link), \ + "InterBase link", le_link, le_plink) } \ + else \ + _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, &pzval, &lh, &th); \ + if (SUCCESS != _php_ibase_def_trans(lh, &th TSRMLS_CC)) { RETURN_FALSE; } \ + } while (0) + +int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC); +void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, zval **link_id, + ibase_db_link **ib_link, ibase_trans **trans); + +/* provided by ibase_query.c */ +void php_ibase_query_minit(INIT_FUNC_ARGS); + +/* provided by ibase_blobs.c */ +void php_ibase_blobs_minit(INIT_FUNC_ARGS); +int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd); +char *_php_ibase_quad_to_string(ISC_QUAD const qd); +int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC); +int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC); + +/* provided by ibase_events.c */ +void php_ibase_events_minit(INIT_FUNC_ARGS); +void _php_ibase_free_event(ibase_event *event TSRMLS_DC); + +/* provided by ibase_service.c */ +void php_ibase_service_minit(INIT_FUNC_ARGS); + +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#endif /* PHP_IBASE_INCLUDES_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/interbase/php_ibase_udf.c b/ext/interbase/php_ibase_udf.c new file mode 100644 index 0000000..7faf314 --- /dev/null +++ b/ext/interbase/php_ibase_udf.c @@ -0,0 +1,409 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Ard Biesheuvel <a.k.biesheuvel@ewi.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/** +* This UDF library adds the ability to call PHP functions from SQL +* statements. Because of SQL's strong typing, you will have to declare +* an external function for every combination { output type, #args } that +* your application requires. +* +* Declare the functions like this: +* +* DECLARE EXTERNAL FUNCTION CALL_PHP1 +* CSTRING(xx), +* <return type> BY DESCRIPTOR, +* INTEGER BY DESCRIPTOR +* RETURNS PARAMETER 2 +* ENTRY_POINT 'udf_call_php1' MODULE_NAME 'php_ibase_udf' +* +* DECLARE EXTERNAL FUNCTION CALL_PHP2 +* CSTRING(xx), +* <return type> BY DESCRIPTOR, +* INTEGER BY DESCRIPTOR, +* INTEGER BY DESCRIPTOR +* RETURNS PARAMETER 2 +* ENTRY_POINT 'udf_call_php2' MODULE_NAME 'php_ibase_udf' +* +* ... and so on. [for up to 8 input arguments] +* +* The first input parameter contains the name of the PHP function you want +* to call. The second argument is the result. (omit this argument when calling +* the function) The return type of the function is the declared type of the +* result. The value returned from the PHP function being called will +* automatically be converted if necessary. +* The arguments should have their types declared as well, but you're free +* to pass arguments of other types instead. They will be converted to the +* best matching PHP type before being passed to the PHP function. +* +* The declared functions can be called from SQL like: +* +* SELECT * FROM <table> WHERE CALL_PHP1('soundex',<field>) NOT LIKE ? +* or +* UPDATE <table> SET <field> = CALL_PHP1('ucwords',<field>) +* +* Additionally, there's a function 'exec_php' which allows the contents +* of text BLOB fields to be parsed and executed by PHP. This is most useful +* for declaring functions that can then be called with CALL_PHPx. +* +* DECLARE EXTERNAL FUNCTION EXEC_PHP +* BLOB, +* INTEGER BY DESCRIPTOR, +* SMALLINT +* RETURNS PARAMETER 2 +* ENTRY_POINT 'exec_php' MODULE_NAME 'php_ibase_udf' +* +* The function will return 1 if execution succeeded and 0 if an error +* occurred. The result that is returned from the executed PHP code is +* ignored. You can pass a non-zero value as second argument to force +* the embedded PHP engine to re-initialise. +* +* There are several ways to build this library, depending on which way the +* database is accessed. If you're using the classic server on a local +* connection, you should compile the library like this: +* +* gcc -shared `php-config --includes` `php-config --ldflags` \ +* `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c +* +* If you connect to the classic server by TCP/IP, you should build the +* PHP embedded static library and link against that. +* +* gcc -shared `php-config --includes` `php-config --ldflags` \ +* `php-config --libs` -o php_ibase_udf.so php_ibase_udf.c \ +* /usr/lib/libphp5.a +* +* If you use the super server, you should also link against the embedded +* library, but be sure to enable thread safety, as the super server is +* multi-threaded. After building, copy the resulting file to the folder +* where your database expects to find its UDFs. +*/ + +#include "zend.h" +#include "zend_API.h" + +#include "php.h" +#include "php_ini.h" + +#include "ibase.h" + +#define min(a,b) ((a)<(b)?(a):(b)) + +#ifdef PHP_WIN32 +#define LL_LIT(lit) lit ## I64 +#else +#define LL_LIT(lit) lit ## ll +#endif + +#ifdef ZTS +# include <pthread.h> + +static void ***tsrm_ls; +pthread_mutex_t mtx_res = PTHREAD_MUTEX_INITIALIZER; + +#define LOCK() do { pthread_mutex_lock(&mtx_res); } while (0) +#define UNLOCK() do { pthread_mutex_unlock(&mtx_res); } while (0) +#else +#define LOCK() +#define UNLOCK() +#endif + +#ifdef PHP_EMBED +# include "php_main.h" +# include "sapi/embed/php_embed.h" + +static void __attribute__((constructor)) init() +{ + php_embed_init(0, NULL PTSRMLS_CC); +} + +static void __attribute__((destructor)) fini() +{ + php_embed_shutdown(TSRMLS_C); +} + +#endif + +/** +* Gets the contents of the BLOB b and offers it to Zend for parsing/execution +*/ +void exec_php(BLOBCALLBACK b, PARAMDSC *res, ISC_SHORT *init) +{ + int result, remaining = b->blob_total_length, i = 0; + char *code = pemalloc(remaining+1, 1); + ISC_USHORT read; + + for (code[remaining] = '\0'; remaining > 0; remaining -= read) + b->blob_get_segment(b->blob_handle, &code[i++<<16],min(0x10000,remaining), &read); + + LOCK(); + + switch (init && *init) { + + default: +#ifdef PHP_EMBED + php_request_shutdown(NULL); + if (FAILURE == (result = php_request_startup(TSRMLS_C))) { + break; + } + case 0: +#endif + /* feed it to the parser */ + zend_first_try { + result = zend_eval_stringl(code, b->blob_total_length, NULL, "Firebird Embedded PHP engine" TSRMLS_CC); + } zend_end_try(); + } + + UNLOCK(); + + free(code); + + res->dsc_dtype = dtype_long; + *(ISC_LONG*)res->dsc_address = (result == SUCCESS); +} + +static ISC_INT64 const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000, + 1000000000, LL_LIT(10000000000),LL_LIT(100000000000),LL_LIT(10000000000000),LL_LIT(100000000000000), + LL_LIT(1000000000000000),LL_LIT(1000000000000000),LL_LIT(1000000000000000000) }; + + +static void call_php(char *name, PARAMDSC *r, int argc, PARAMDSC **argv) +{ + do { + zval callback, args[4], *argp[4], return_value; + PARAMVARY *res = (PARAMVARY*)r->dsc_address; + int i; + + INIT_ZVAL(callback); + ZVAL_STRING(&callback,name,0); + + LOCK(); + + /* check if the requested function exists */ + if (!zend_is_callable(&callback, 0, NULL TSRMLS_CC)) { + break; + } + + UNLOCK(); + + /* create the argument array */ + for (i = 0; i < argc; ++i) { + + INIT_ZVAL(args[i]); + argp[i] = &args[i]; + + /* test arg for null */ + if (argv[i]->dsc_flags & DSC_null) { + ZVAL_NULL(argp[i]); + continue; + } + + switch (argv[i]->dsc_dtype) { + ISC_INT64 l; + struct tm t; + char const *fmt; + char d[64]; + + case dtype_cstring: + ZVAL_STRING(argp[i], (char*)argv[i]->dsc_address,0); + break; + + case dtype_text: + ZVAL_STRINGL(argp[i], (char*)argv[i]->dsc_address, argv[i]->dsc_length,0); + break; + + case dtype_varying: + ZVAL_STRINGL(argp[i], ((PARAMVARY*)argv[i]->dsc_address)->vary_string, + ((PARAMVARY*)argv[i]->dsc_address)->vary_length,0); + break; + + case dtype_short: + if (argv[i]->dsc_scale == 0) { + ZVAL_LONG(argp[i], *(short*)argv[i]->dsc_address); + } else { + ZVAL_DOUBLE(argp[i], + ((double)*(short*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]); + } + break; + + case dtype_long: + if (argv[i]->dsc_scale == 0) { + ZVAL_LONG(argp[i], *(ISC_LONG*)argv[i]->dsc_address); + } else { + ZVAL_DOUBLE(argp[i], + ((double)*(ISC_LONG*)argv[i]->dsc_address)/scales[-argv[i]->dsc_scale]); + } + break; + + case dtype_int64: + l = *(ISC_INT64*)argv[i]->dsc_address; + + if (argv[i]->dsc_scale == 0 && l <= LONG_MAX && l >= LONG_MIN) { + ZVAL_LONG(argp[i], (long)l); + } else { + ZVAL_DOUBLE(argp[i], ((double)l)/scales[-argv[i]->dsc_scale]); + } + break; + + case dtype_real: + ZVAL_DOUBLE(argp[i], *(float*)argv[i]->dsc_address); + break; + + case dtype_double: + ZVAL_DOUBLE(argp[i], *(double*)argv[i]->dsc_address); + break; + + case dtype_sql_date: + isc_decode_sql_date((ISC_DATE*)argv[i]->dsc_address, &t); + ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.dateformat"), &t),1); + break; + + case dtype_sql_time: + isc_decode_sql_time((ISC_TIME*)argv[i]->dsc_address, &t); + ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.timeformat"), &t),1); + break; + + case dtype_timestamp: + isc_decode_timestamp((ISC_TIMESTAMP*)argv[i]->dsc_address, &t); + ZVAL_STRINGL(argp[i], d, strftime(d, sizeof(d), INI_STR("ibase.timestampformat"), &t),1); + break; + } + } + + LOCK(); + + /* now call the function */ + if (FAILURE == call_user_function(EG(function_table), NULL, + &callback, &return_value, argc, argp TSRMLS_CC)) { + UNLOCK(); + break; + } + + UNLOCK(); + + for (i = 0; i < argc; ++i) { + switch (argv[i]->dsc_dtype) { + case dtype_sql_date: + case dtype_sql_time: + case dtype_timestamp: + zval_dtor(argp[i]); + + } + } + + /* return whatever type we got back from the callback: let DB handle conversion */ + switch (Z_TYPE(return_value)) { + + case IS_LONG: + r->dsc_dtype = dtype_long; + *(long*)r->dsc_address = Z_LVAL(return_value); + r->dsc_length = sizeof(long); + break; + + case IS_DOUBLE: + r->dsc_dtype = dtype_double; + *(double*)r->dsc_address = Z_DVAL(return_value); + r->dsc_length = sizeof(double); + break; + + case IS_NULL: + r->dsc_flags |= DSC_null; + break; + + default: + convert_to_string(&return_value); + + case IS_STRING: + r->dsc_dtype = dtype_varying; + memcpy(res->vary_string, Z_STRVAL(return_value), + (res->vary_length = min(r->dsc_length-2,Z_STRLEN(return_value)))); + r->dsc_length = res->vary_length+2; + break; + } + + zval_dtor(&return_value); + + return; + + } while (0); + + /** + * If we end up here, we should report an error back to the DB engine, but + * that's not possible. We can however report it back to PHP. + */ + LOCK(); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling function '%s' from database", name); + UNLOCK(); +} + + +/* Entry points for the DB engine */ + +void udf_call_php1(char *name, PARAMDSC *r, PARAMDSC *arg1) +{ + PARAMDSC *args[1] = { arg1 }; + call_php(name, r, 1, args); +} + +void udf_call_php2(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2) +{ + PARAMDSC *args[2] = { arg1, arg2 }; + call_php(name, r, 2, args); +} + +void udf_call_php3(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3) +{ + PARAMDSC *args[3] = { arg1, arg2, arg3 }; + call_php(name, r, 3, args); +} + +void udf_call_php4(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, + PARAMDSC *arg4) +{ + PARAMDSC *args[4] = { arg1, arg2, arg3, arg4 }; + call_php(name, r, 4, args); +} + +void udf_call_php5(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, + PARAMDSC *arg4, PARAMDSC *arg5) +{ + PARAMDSC *args[5] = { arg1, arg2, arg3, arg4, arg5 }; + call_php(name, r, 5, args); +} + +void udf_call_php6(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, + PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6) +{ + PARAMDSC *args[6] = { arg1, arg2, arg3, arg4, arg5, arg6 }; + call_php(name, r, 6, args); +} + +void udf_call_php7(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, + PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7) +{ + PARAMDSC *args[7] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; + call_php(name, r, 7, args); +} + +void udf_call_php8(char *name, PARAMDSC *r, PARAMDSC *arg1, PARAMDSC *arg2, PARAMDSC *arg3, + PARAMDSC *arg4, PARAMDSC *arg5, PARAMDSC *arg6, PARAMDSC *arg7, PARAMDSC *arg8) +{ + PARAMDSC *args[8] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; + call_php(name, r, 8, args); +} + diff --git a/ext/interbase/php_interbase.h b/ext/interbase/php_interbase.h new file mode 100644 index 0000000..149319e --- /dev/null +++ b/ext/interbase/php_interbase.h @@ -0,0 +1,107 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jouni Ahto <jouni.ahto@exdec.fi> | + | Andrew Avdeev <andy@simgts.mv.ru> | + | Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_INTERBASE_H +#define PHP_INTERBASE_H + +extern zend_module_entry ibase_module_entry; +#define phpext_interbase_ptr &ibase_module_entry + +PHP_MINIT_FUNCTION(ibase); +PHP_RINIT_FUNCTION(ibase); +PHP_MSHUTDOWN_FUNCTION(ibase); +PHP_RSHUTDOWN_FUNCTION(ibase); +PHP_MINFO_FUNCTION(ibase); + +PHP_FUNCTION(ibase_connect); +PHP_FUNCTION(ibase_pconnect); +PHP_FUNCTION(ibase_close); +PHP_FUNCTION(ibase_drop_db); +PHP_FUNCTION(ibase_query); +PHP_FUNCTION(ibase_fetch_row); +PHP_FUNCTION(ibase_fetch_assoc); +PHP_FUNCTION(ibase_fetch_object); +PHP_FUNCTION(ibase_free_result); +PHP_FUNCTION(ibase_name_result); +PHP_FUNCTION(ibase_prepare); +PHP_FUNCTION(ibase_execute); +PHP_FUNCTION(ibase_free_query); + +PHP_FUNCTION(ibase_timefmt); + +PHP_FUNCTION(ibase_gen_id); +PHP_FUNCTION(ibase_num_fields); +PHP_FUNCTION(ibase_num_params); +#if abies_0 +PHP_FUNCTION(ibase_num_rows); +#endif +PHP_FUNCTION(ibase_affected_rows); +PHP_FUNCTION(ibase_field_info); +PHP_FUNCTION(ibase_param_info); + +PHP_FUNCTION(ibase_trans); +PHP_FUNCTION(ibase_commit); +PHP_FUNCTION(ibase_rollback); +PHP_FUNCTION(ibase_commit_ret); +PHP_FUNCTION(ibase_rollback_ret); + +PHP_FUNCTION(ibase_blob_create); +PHP_FUNCTION(ibase_blob_add); +PHP_FUNCTION(ibase_blob_cancel); +PHP_FUNCTION(ibase_blob_open); +PHP_FUNCTION(ibase_blob_get); +PHP_FUNCTION(ibase_blob_close); +PHP_FUNCTION(ibase_blob_echo); +PHP_FUNCTION(ibase_blob_info); +PHP_FUNCTION(ibase_blob_import); + +PHP_FUNCTION(ibase_add_user); +PHP_FUNCTION(ibase_modify_user); +PHP_FUNCTION(ibase_delete_user); + +PHP_FUNCTION(ibase_service_attach); +PHP_FUNCTION(ibase_service_detach); +PHP_FUNCTION(ibase_backup); +PHP_FUNCTION(ibase_restore); +PHP_FUNCTION(ibase_maintain_db); +PHP_FUNCTION(ibase_db_info); +PHP_FUNCTION(ibase_server_info); + +PHP_FUNCTION(ibase_errmsg); +PHP_FUNCTION(ibase_errcode); + +PHP_FUNCTION(ibase_wait_event); +PHP_FUNCTION(ibase_set_event_handler); +PHP_FUNCTION(ibase_free_event_handler); + +#else + +#define phpext_interbase_ptr NULL + +#endif /* PHP_INTERBASE_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/interbase/tests/002.phpt b/ext/interbase/tests/002.phpt new file mode 100644 index 0000000..070a6f0 --- /dev/null +++ b/ext/interbase/tests/002.phpt @@ -0,0 +1,33 @@ +--TEST-- +InterBase: connect, close and pconnect +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + + ibase_connect($test_base); + out_table("test1"); + ibase_close(); + + $con = ibase_connect($test_base); + $pcon1 = ibase_pconnect($test_base); + $pcon2 = ibase_pconnect($test_base); + ibase_close($con); + unset($con); + ibase_close($pcon1); + unset($pcon1); + + out_table("test1"); + + ibase_close($pcon2); + unset($pcon2); +?> +--EXPECT-- +--- test1 --- +1 test table not created with isql +--- +--- test1 --- +1 test table not created with isql +--- diff --git a/ext/interbase/tests/003.phpt b/ext/interbase/tests/003.phpt new file mode 100644 index 0000000..652e3bd --- /dev/null +++ b/ext/interbase/tests/003.phpt @@ -0,0 +1,183 @@ +--TEST-- +InterBase: misc sql types (may take a while) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + ibase_connect($test_base); + + ibase_query( + "create table test3 ( + iter integer not null, + v_char char(1000), + v_date timestamp, + v_decimal4_2 decimal(4,2), + v_decimal4_0 decimal(4,0), + v_decimal7_2 decimal(7,2), + v_decimal7_0 decimal(7,0), + v_numeric15_15 numeric(15,15), + v_numeric15_0 numeric(15,0), + v_double double precision, + v_float float, + v_integer integer, + v_smallint smallint, + v_varchar varchar(10000) + )"); + ibase_commit(); + + /* should fail, but gracefully */ + @ibase_query("insert into test3 (iter) values (?)", null); + + /* if timefmt is not supported, suppress error here */ + ini_set('ibase.timestampformat',"%m/%d/%Y %H:%M:%S"); + + for($iter = 0; $iter < 10; $iter++){ + /* prepare data */ + $v_char = rand_str(1000); + $v_date = rand_datetime(); + $v_decimal4_2 = rand_number(4,2); + $v_decimal4_0 = rand_number(4,0); + $v_decimal7_2 = rand_number(7,2); + $v_decimal7_0 = rand_number(7,0); + $v_numeric15_15 = rand_number(15,15); + $v_numeric15_0 = $iter ? rand_number(15,0) : 0; + $v_double = rand_number(18); + $v_float = rand_number(7); + $v_integer = rand_number(9,0); + $v_smallint = rand_number(5) % 32767; + $v_varchar = rand_str(10000); + + ibase_query( + "insert into test3 (iter, v_char,v_date,v_decimal4_2, v_decimal4_0, v_decimal7_2, v_decimal7_0,v_numeric15_15, v_numeric15_0,v_double,v_float,v_integer,v_smallint,v_varchar) + values ($iter, '$v_char','$v_date',$v_decimal4_2, $v_decimal4_0, $v_decimal7_2, $v_decimal7_0,$v_numeric15_15, $v_numeric15_0,$v_double,$v_float,$v_integer,$v_smallint,'$v_varchar')"); + $sel = ibase_query("select * from test3 where iter = $iter"); + $row = ibase_fetch_object($sel); + if(substr($row->V_CHAR,0,strlen($v_char)) != $v_char){ + echo " CHAR fail:\n"; + echo " in: $v_char\n"; + echo " out: $row->V_CHAR\n"; + } + if($row->V_DATE != $v_date){ + echo " DATE fail\n"; + echo " in: $v_date\n"; + echo " out: $row->V_DATE\n"; + } + if($row->V_DECIMAL4_2 != $v_decimal4_2){ + echo " DECIMAL4_2 fail\n"; + echo " in: $v_decimal4_2\n"; + echo " out: $row->V_DECIMAL4_2\n"; + } + if($row->V_DECIMAL4_0 != $v_decimal4_0){ + echo " DECIMAL4_0 fail\n"; + echo " in: $v_decimal4_0\n"; + echo " out: $row->V_DECIMAL4_0\n"; + } + if($row->V_DECIMAL7_2 != $v_decimal7_2){ + echo " DECIMAL7_2 fail\n"; + echo " in: $v_decimal7_2\n"; + echo " out: $row->V_DECIMAL7_2\n"; + } + if($row->V_DECIMAL7_0 != $v_decimal7_0){ + echo " DECIMAL7_0 fail\n"; + echo " in: $v_decimal7_0\n"; + echo " out: $row->V_DECIMAL7_0\n"; + } + if($row->V_NUMERIC15_15 != $v_numeric15_15){ + echo " NUMERIC15_15 fail\n"; + echo " in: $v_numeric15_15\n"; + echo " out: $row->V_NUMERIC15_15\n"; + } + if($row->V_NUMERIC15_0 != (string)$v_numeric15_0){ + echo " NUMERIC15_0 fail\n"; + echo " in: $v_numeric15_0\n"; + echo " out: $row->V_NUMERIC15_0\n"; + } + + if(abs($row->V_DOUBLE - $v_double) > abs($v_double / 1E15)){ + echo " DOUBLE fail\n"; + echo " in: $v_double\n"; + echo " out: $row->V_DOUBLE\n"; + } + if(abs($row->V_FLOAT - $v_float) > abs($v_float / 1E7)){ + echo " FLOAT fail\n"; + echo " in: $v_float\n"; + echo " out: $row->V_FLOAT\n"; + } + if($row->V_INTEGER != $v_integer){ + echo " INTEGER fail\n"; + echo " in: $v_integer\n"; + echo " out: $row->V_INTEGER\n"; + } + if($row->V_SMALLINT != $v_smallint){ + echo " SMALLINT fail\n"; + echo " in: $v_smallint\n"; + echo " out: $row->V_SMALLINT\n"; + } + + if(substr($row->V_VARCHAR,0,strlen($v_varchar)) != $v_varchar){ + echo " VARCHAR fail:\n"; + echo " in: $v_varchar\n"; + echo " out: $row->V_VARCHAR\n"; + } + + ibase_free_result($sel); + } /* for($iter) */ + + /* check for correct handling of duplicate field names */ + $q = ibase_query('SELECT 1 AS id, 2 AS id, 3 AS id, 4 AS id, 5 AS id, 6 AS id, 7 AS id, 8 AS id, 9 AS id, + 10 AS id, 11 AS id, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 FROM rdb$database'); + var_dump(ibase_fetch_assoc($q)); + + ibase_close(); + echo "end of test\n"; +?> +--EXPECT-- +array(22) { + ["ID"]=> + int(1) + ["ID_01"]=> + int(2) + ["ID_02"]=> + int(3) + ["ID_03"]=> + int(4) + ["ID_04"]=> + int(5) + ["ID_05"]=> + int(6) + ["ID_06"]=> + int(7) + ["ID_07"]=> + int(8) + ["ID_08"]=> + int(9) + ["ID_09"]=> + int(10) + ["ID_10"]=> + int(11) + ["CONSTANT"]=> + int(12) + ["CONSTANT_01"]=> + int(13) + ["CONSTANT_02"]=> + int(14) + ["CONSTANT_03"]=> + int(15) + ["CONSTANT_04"]=> + int(16) + ["CONSTANT_05"]=> + int(17) + ["CONSTANT_06"]=> + int(18) + ["CONSTANT_07"]=> + int(19) + ["CONSTANT_08"]=> + int(20) + ["CONSTANT_09"]=> + int(21) + ["CONSTANT_10"]=> + int(22) +} +end of test diff --git a/ext/interbase/tests/004.phpt b/ext/interbase/tests/004.phpt new file mode 100644 index 0000000..579445d --- /dev/null +++ b/ext/interbase/tests/004.phpt @@ -0,0 +1,182 @@ +--TEST-- +InterBase: BLOB test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + + $link = ibase_connect($test_base); + + ibase_query( + "CREATE TABLE test4 ( + v_integer integer, + v_blob blob)"); + ibase_commit(); + + /* create 100k blob file */ + $blob_str = rand_binstr(100*1024); + + $name = tempnam(dirname(__FILE__),"blob.tmp"); + $ftmp = fopen($name,"w"); + fwrite($ftmp,$blob_str); + fclose($ftmp); + + echo "import blob 1\n"; + $ftmp = fopen($name,"r"); + $bl_s = ibase_blob_import($ftmp); + ibase_query("INSERT INTO test4 (v_integer, v_blob) VALUES (1, ?)", $bl_s); + + $bl_s = ibase_blob_import($link,$ftmp); + ibase_query($link, "INSERT INTO test4 (v_integer, v_blob) VALUES (1, ?)", $bl_s); + fclose($ftmp); + + echo "test blob 1\n"; + $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 1"); + + $row = ibase_fetch_object($q); + $bl_h = ibase_blob_open($row->V_BLOB); + + $blob = ''; + while($piece = ibase_blob_get($bl_h, 1 + rand() % 1024)) + $blob .= $piece; + if($blob != $blob_str) + echo " BLOB 1 fail (1)\n"; + ibase_blob_close($bl_h); + + $bl_h = ibase_blob_open($link,$row->V_BLOB); + + $blob = ''; + while($piece = ibase_blob_get($bl_h, 100 * 1024)) + $blob .= $piece; + if($blob != $blob_str) + echo " BLOB 1 fail (2)\n"; + ibase_blob_close($bl_h); + ibase_free_result($q); + unset($blob); + + echo "create blob 2\n"; + + ibase_query("INSERT INTO test4 (v_integer, v_blob) VALUES (2, ?)", $blob_str); + + echo "test blob 2\n"; + + $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 2"); + $row = ibase_fetch_object($q,IBASE_TEXT); + + if($row->V_BLOB != $blob_str) + echo " BLOB 2 fail\n"; + ibase_free_result($q); + unset($blob); + + + echo "create blob 3\n"; + + $bl_h = ibase_blob_create($link); + + ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); + ibase_blob_add($bl_h, "| PHP HTML Embedded Scripting Language Version 3.0 |\n"); + ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); + ibase_blob_add($bl_h, "| Copyright (c) 1997-2000 PHP Development Team (See Credits file) |\n"); + ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); + ibase_blob_add($bl_h, "| This program is free software; you can redistribute it and/or modify |\n"); + ibase_blob_add($bl_h, "| it under the terms of one of the following licenses: |\n"); + ibase_blob_add($bl_h, "| |\n"); + ibase_blob_add($bl_h, "| A) the GNU General Public License as published by the Free Software |\n"); + ibase_blob_add($bl_h, "| Foundation; either version 2 of the License, or (at your option) |\n"); + ibase_blob_add($bl_h, "| any later version. |\n"); + ibase_blob_add($bl_h, "| |\n"); + ibase_blob_add($bl_h, "| B) the PHP License as published by the PHP Development Team and |\n"); + ibase_blob_add($bl_h, "| included in the distribution in the file: LICENSE |\n"); + ibase_blob_add($bl_h, "| |\n"); + ibase_blob_add($bl_h, "| This program is distributed in the hope that it will be useful, |\n"); + ibase_blob_add($bl_h, "| but WITHOUT ANY WARRANTY; without even the implied warranty of |\n"); + ibase_blob_add($bl_h, "| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |\n"); + ibase_blob_add($bl_h, "| GNU General Public License for more details. |\n"); + ibase_blob_add($bl_h, "| |\n"); + ibase_blob_add($bl_h, "| You should have received a copy of both licenses referred to here. |\n"); + ibase_blob_add($bl_h, "| If you did not, or have any questions about PHP licensing, please |\n"); + ibase_blob_add($bl_h, "| contact core@php.net. |\n"); + ibase_blob_add($bl_h, "+----------------------------------------------------------------------+\n"); + $bl_s = ibase_blob_close($bl_h); + ibase_query("INSERT INTO test4 (v_integer, v_blob) VALUES (3, ?)", $bl_s); + ibase_commit(); + echo "echo blob 3\n"; + + $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 3"); + $row = ibase_fetch_object($q); + ibase_commit(); + ibase_close(); + + ibase_connect($test_base); + ibase_blob_echo($link, $row->V_BLOB); + ibase_free_result($q); + + echo "fetch blob 3\n"; + $q = ibase_query("SELECT v_blob FROM test4 WHERE v_integer = 3"); + $row = ibase_fetch_object($q,IBASE_TEXT); + echo $row->V_BLOB; + ibase_free_result($q); + + ibase_close(); + unlink($name); + echo "end of test\n"; +?> +--EXPECT-- +import blob 1 +test blob 1 +create blob 2 +test blob 2 +create blob 3 +echo blob 3 ++----------------------------------------------------------------------+ +| 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. | ++----------------------------------------------------------------------+ +fetch blob 3 ++----------------------------------------------------------------------+ +| 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. | ++----------------------------------------------------------------------+ +end of test diff --git a/ext/interbase/tests/005.phpt b/ext/interbase/tests/005.phpt new file mode 100644 index 0000000..5b16ac2 --- /dev/null +++ b/ext/interbase/tests/005.phpt @@ -0,0 +1,290 @@ +--TEST-- +InterBase: transactions +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + + ibase_connect($test_base); + + @ibase_query("create table test5 (i integer)"); + @ibase_query("delete from test5"); + ibase_close(); + + + echo "default transaction:\n"; + +/* +Difference between default and other transactions: +default commited when you call ibase_close(). +Other transaction doing rollback. + +If you not open default transaction with +ibase_trans, default transaction open +when you call ibase_query(), ibase_prepare(), +ibase_blob_create(), ibase_blob_import() first time. +*/ + +/* +simple default transaction test without ibase_trans() +*/ + + ibase_connect($test_base); + + echo "empty table\n"; + + /* out_table call ibase_query() + and ibase_query() start default transaction */ + out_table("test5"); + + /* in default transaction context */ + ibase_query("insert into test5 (i) values (1)"); + + echo "one row\n"; + out_table("test5"); + + ibase_rollback(); /* default rolled */ + + echo "after rollback table empty again\n"; + out_table("test5"); /* started new default transaction */ + + ibase_query("insert into test5 (i) values (2)"); + + ibase_close(); /* commit here! */ + + ibase_connect($test_base); + + echo "one row\n"; + out_table("test5"); + ibase_close(); + +/* +default transaction on default link +First open transaction on link will be default. +$tr_def_l1 may be ommited. All queryes without link and trans +parameters run in this context +*/ + + $link_def = ibase_connect($test_base); + + $tr_def_l1 = ibase_trans(IBASE_READ); /* here transaction start */ + + /* all default */ + $res = ibase_query("select * from test5"); + + echo "one row\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + /* specify transaction context... */ + $res = ibase_query($tr_def_l1, "select * from test5"); + + echo "one row... again.\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + /* specify default transaction on link */ + $res = ibase_query($link_def, "select * from test5"); + + echo "one row.\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + ibase_rollback($link_def); /* just for example */ + + ibase_close(); + +/* +three transaction on default link +*/ + ibase_connect($test_base); + + $res = ibase_query("select * from test5"); + + echo "one row\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + $tr_1 = ibase_query("SET TRANSACTION"); + $tr_2 = ibase_query("SET TRANSACTION READ ONLY"); + $tr_3 = ibase_trans(IBASE_READ+IBASE_COMMITTED+IBASE_REC_VERSION+IBASE_WAIT); + $tr_4 = ibase_trans(IBASE_READ+IBASE_COMMITTED+IBASE_REC_NO_VERSION+IBASE_NOWAIT); + + /* insert in first transaction context... */ + /* as default */ + ibase_query("insert into test5 (i) values (3)"); + /* specify context */ + ibase_query($tr_1, "insert into test5 (i) values (4)"); + + $res = ibase_query("select * from test5"); + + echo "two rows\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + $res = ibase_query($tr_1, "select * from test5"); + + echo "two rows again\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + ibase_commit(); + ibase_commit($tr_1); + + $tr_1 = ibase_trans(); + ibase_query($tr_1, "insert into test5 (i) values (5)"); + + /* tr_2 is IBASE_READ + IBASE_CONCURRENCY + IBASE_WAIT */ + $res = ibase_query($tr_2, "select * from test5"); + + echo "one row in second transaction\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + /* tr_3 is IBASE_COMMITTED + IBASE_REC_VERSION + IBASE_WAIT */ + $res = ibase_query($tr_3, "select * from test5"); + + echo "three rows in third transaction\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + /* tr_4 IBASE_COMMITED + IBASE_REC_NO_VERSION + IBASE_NOWAIT */ + $res = ibase_query($tr_4, "select * from test5"); + + echo "three rows in fourth transaction with deadlock\n"; + out_result_trap_error($res,"test5"); + + ibase_free_result($res); + + ibase_rollback($tr_1); + ibase_close(); +/* +transactions on second link +*/ + $link_1 = ibase_pconnect($test_base); + $link_2 = ibase_pconnect($test_base); + + $tr_1 = ibase_trans(IBASE_DEFAULT, $link_2); /* this default transaction also */ + $tr_2 = ibase_trans(IBASE_COMMITTED, $link_2); + + $res = ibase_query($tr_1, "select * from test5"); + + echo "three rows\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + ibase_query($tr_1, "insert into test5 (i) values (5)"); + + $res = ibase_query($tr_1, "select * from test5"); + + echo "four rows\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + ibase_commit($tr_1); + + $res = ibase_query($tr_2, "select * from test5"); + + echo "four rows again\n"; + out_result($res,"test5"); + + ibase_free_result($res); + + ibase_close($link_1); + ibase_close($link_2); + + echo "end of test\n"; +?> +--EXPECT-- +default transaction: +empty table +--- test5 --- +--- +one row +--- test5 --- +1 +--- +after rollback table empty again +--- test5 --- +--- +one row +--- test5 --- +2 +--- +one row +--- test5 --- +2 +--- +one row... again. +--- test5 --- +2 +--- +one row. +--- test5 --- +2 +--- +one row +--- test5 --- +2 +--- +two rows +--- test5 --- +2 +3 +--- +two rows again +--- test5 --- +2 +4 +--- +one row in second transaction +--- test5 --- +2 +--- +three rows in third transaction +--- test5 --- +2 +3 +4 +--- +three rows in fourth transaction with deadlock +--- test5 --- +2 +3 +4 +errmsg [lock conflict on no wait transaction deadlock ] +--- +three rows +--- test5 --- +2 +3 +4 +--- +four rows +--- test5 --- +2 +3 +4 +5 +--- +four rows again +--- test5 --- +2 +3 +4 +5 +--- +end of test + diff --git a/ext/interbase/tests/006.phpt b/ext/interbase/tests/006.phpt new file mode 100644 index 0000000..ad6120f --- /dev/null +++ b/ext/interbase/tests/006.phpt @@ -0,0 +1,301 @@ +--TEST-- +InterBase: binding (may take a while) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + + ibase_connect($test_base); + + ibase_query( + "create table test6 ( + iter integer, + v_char char(1000), + v_date timestamp, + v_decimal decimal(12,3), + v_double double precision, + v_float float, + v_integer integer, + v_numeric numeric(4,2), + v_smallint smallint, + v_varchar varchar(10000) + )"); + ibase_query( + "create procedure add1 (arg integer) + returns (result integer) + as + begin + result = arg +1; + end"); + ibase_commit(); + + /* if timefmt not supported, hide error */ + ini_set('ibase.timestampformat',"%m/%d/%Y %H:%M:%S"); + + echo "insert\n"; + + for($iter = 0; $iter < 3; $iter++) { + /* prepare data */ + $v_char = rand_str(1000); + $v_date = rand_datetime(); + $v_decimal = rand_number(12,3); + $v_double = rand_number(20); + $v_float = rand_number(7); + $v_integer = rand_number(9,0); + $v_numeric = rand_number(4,2); + $v_smallint = rand_number(5) % 32767; + $v_varchar = rand_str(10000); + + ibase_query("insert into test6 + (iter,v_char,v_date,v_decimal,v_double,v_float, + v_integer,v_numeric,v_smallint,v_varchar) + values (?,?,?,?,?,?,?,?,?,?)", + $iter, $v_char, $v_date, $v_decimal, $v_double, $v_float, + $v_integer, $v_numeric, $v_smallint, $v_varchar); + $sel = ibase_query("select * from test6 where iter = ?", $iter); + + $row = ibase_fetch_object($sel); + if(substr($row->V_CHAR,0,strlen($v_char)) != $v_char) { + echo " CHAR fail:\n"; + echo " in: $v_char\n"; + echo " out: $row->V_CHAR\n"; + } + if($row->V_DATE != $v_date) { + echo " DATE fail\n"; + echo " in: $v_date\n"; + echo " out: $row->V_DATE\n"; + } + if($row->V_DECIMAL != $v_decimal) { + echo " DECIMAL fail\n"; + echo " in: $v_decimal\n"; + echo " out: $row->V_DECIMAL\n"; + } + if(abs($row->V_DOUBLE - $v_double) > abs($v_double / 1E15)) { + echo " DOUBLE fail\n"; + echo " in: $v_double\n"; + echo " out: $row->V_DOUBLE\n"; + } + if(abs($row->V_FLOAT - $v_float) > abs($v_float / 1E7)) { + echo " FLOAT fail\n"; + echo " in: $v_float\n"; + echo " out: $row->V_FLOAT\n"; + } + if($row->V_INTEGER != $v_integer) { + echo " INTEGER fail\n"; + echo " in: $v_integer\n"; + echo " out: $row->V_INTEGER\n"; + } + if ($row->V_NUMERIC != $v_numeric) { + echo " NUMERIC fail\n"; + echo " in: $v_numeric\n"; + echo " out: $row->V_NUMERIC\n"; + } + if ($row->V_SMALLINT != $v_smallint) { + echo " SMALLINT fail\n"; + echo " in: $v_smallint\n"; + echo " out: $row->V_SMALLINT\n"; + } + if ($row->V_VARCHAR != $v_varchar) { + echo " VARCHAR fail:\n"; + echo " in: $v_varchar\n"; + echo " out: $row->V_VARCHAR\n"; + } + ibase_free_result($sel); + }/* for($iter)*/ + + echo "select\n"; + for($iter = 0; $iter < 3; $iter++) { + /* prepare data */ + $v_char = rand_str(1000); + $v_date = (int)rand_number(10,0,0); + $v_decimal = rand_number(12,3); + $v_double = rand_number(20); + $v_float = rand_number(7); + $v_integer = rand_number(9,0); + $v_numeric = rand_number(4,2); + $v_smallint = rand_number(5) % 32767; + $v_varchar = rand_str(10000); + + /* clear table*/ + ibase_query("delete from test6"); + + /* make one record */ + ibase_query("insert into test6 + (iter, v_char,v_date,v_decimal, + v_integer,v_numeric,v_smallint,v_varchar) + values (666, '$v_char',?,$v_decimal, $v_integer, + $v_numeric, $v_smallint, '$v_varchar')",$v_date); + + /* test all types */ + if(!($sel = ibase_query( + "select iter from test6 where v_char = ?", $v_char)) || + !ibase_fetch_row($sel)) { + echo "CHAR fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_date = ?", $v_date)) || + !ibase_fetch_row($sel)) { + echo "DATE fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_decimal = ?", $v_decimal)) || + !ibase_fetch_row($sel)) { + echo "DECIMAL fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_integer = ?", $v_integer)) || + !ibase_fetch_row($sel)) { + echo "INTEGER fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_numeric = ?", $v_numeric)) || + !ibase_fetch_row($sel)) { + echo "NUMERIC fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_smallint = ?", $v_smallint)) || + !ibase_fetch_row($sel)) { + echo "SMALLINT fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test6 where v_varchar = ?", $v_varchar)) || + !ibase_fetch_row($sel)) { + echo "VARCHAR fail\n"; + } + ibase_free_result($sel); + + } /*for iter*/ + + echo "prepare and exec insert\n"; + + /* prepare table */ + ibase_query("delete from test6"); + + /* prepare query */ + $query = ibase_prepare( + "insert into test6 (v_integer) values (?)"); + + for($i = 0; $i < 10; $i++) { + ibase_execute($query, $i); + } + + out_table("test6"); + + ibase_free_query($query); + + echo "prepare and exec select\n"; + + /* prepare query */ + $query = ibase_prepare("select * from test6 + where v_integer between ? and ?"); + + $low_border = 2; + $high_border = 6; + + $res = ibase_execute($query, $low_border, $high_border); + out_result($res, "test6"); + ibase_free_result($res); + + $low_border = 0; + $high_border = 4; + $res = ibase_execute($query, $low_border, $high_border); + out_result($res, "test6"); + ibase_free_result($res); + + $res = ibase_execute($query, "5", 7.499); + out_result($res, "test6"); + ibase_free_result($res); + + ibase_free_query($query); + + /* test execute procedure */ + $query = ibase_prepare("execute procedure add1(?)"); + $res = array(); + for ($i = 0; $i < 10; $i++) { + $res[] = ibase_execute($query,$i); + } + ibase_free_query($query); + foreach ($res as $r) { + out_result($r, "proc add1"); + ibase_free_result($r); + } + + ibase_close(); + echo "end of test\n"; +?> +--EXPECT-- +insert +select +prepare and exec insert +--- test6 --- + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 +--- +prepare and exec select +--- test6 --- + 2 + 3 + 4 + 5 + 6 +--- +--- test6 --- + 0 + 1 + 2 + 3 + 4 +--- +--- test6 --- + 5 + 6 + 7 +--- +--- proc add1 --- +1 +--- +--- proc add1 --- +2 +--- +--- proc add1 --- +3 +--- +--- proc add1 --- +4 +--- +--- proc add1 --- +5 +--- +--- proc add1 --- +6 +--- +--- proc add1 --- +7 +--- +--- proc add1 --- +8 +--- +--- proc add1 --- +9 +--- +--- proc add1 --- +10 +--- +end of test + diff --git a/ext/interbase/tests/007.phpt b/ext/interbase/tests/007.phpt new file mode 100644 index 0000000..069b7ed --- /dev/null +++ b/ext/interbase/tests/007.phpt @@ -0,0 +1,183 @@ +--TEST-- +InterBase: array handling +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php /* $Id$ */ + + require("interbase.inc"); + + ibase_connect($test_base); + + ibase_query( + "create table test7 ( + iter integer, + v_multi integer[10,10,10], + v_char char(100)[10], + v_date timestamp[10], + v_decimal decimal(18,3)[10], + v_double double precision[10], + v_float float[10], + v_integer integer[10], + v_numeric numeric(9,2)[10], + v_smallint smallint[10], + v_varchar varchar(1000)[10] + )"); + ibase_commit(); + + /* if timefmt not supported, hide error */ + ini_set('ibase.timestampformat',"%m/%d/%Y %H:%M:%S"); + + echo "insert\n"; + + for ($i = 1; $i <= 10; ++$i) { + for ($j = 1; $j <= 10; ++$j) { + for ($k = 1; $k <= 10; ++$k) { + $v_multi[$i][$j][$k] = $i * $j * $k; + } + } + } + + for($iter = 0; $iter < 3; $iter++) { + + /* prepare data */ + $v_char = array(); + $v_date = array(); + $v_decimal = array(); + $v_double = array(); + $v_float = array(); + $v_integer = array(); + $v_numeric = array(); + $v_smallint = array(); + $v_varchar = array(); + + for ($i = 1; $i <= 10; ++$i) { + $v_char[$i] = rand_str(100); + $v_date[$i] = rand_datetime(); + $v_decimal[$i] = rand_number(18,3); + $v_double[$i] = rand_number(20); + $v_float[$i] = rand_number(7); + $v_integer[$i] = rand_number(9,0); + $v_numeric[$i] = rand_number(9,2); + $v_smallint[$i] = rand_number(5) % 32767; + $v_varchar[$i] = rand_str(1000); + } + + ibase_query("insert into test7 + (iter,v_multi,v_char,v_date,v_decimal,v_double,v_float, + v_integer,v_numeric,v_smallint,v_varchar) + values (?,?,?,?,?,?,?,?,?,?,?)", + $iter, $v_multi, $v_char, $v_date, $v_decimal, $v_double, $v_float, + $v_integer, $v_numeric, $v_smallint, $v_varchar); + $sel = ibase_query("select * from test7 where iter = $iter"); + + $row = ibase_fetch_object($sel,IBASE_FETCH_ARRAYS); + for ($i = 1; $i <= 10; ++$i) { + + if(strncmp($row->V_CHAR[$i],$v_char[$i],strlen($v_char[$i])) != 0) { + echo " CHAR[$i] fail:\n"; + echo " in: ".$v_char[$i]."\n"; + echo " out: ".$row->V_CHAR[$i]."\n"; + } + if($row->V_DATE[$i] != $v_date[$i]) { + echo " DATE[$i] fail\n"; + echo " in: ".$v_date[$i]."\n"; + echo " out: ".$row->V_DATE[$i]."\n"; + } + if($row->V_DECIMAL[$i] != $v_decimal[$i]) { + echo " DECIMAL[$i] fail\n"; + echo " in: ".$v_decimal[$i]."\n"; + echo " out: ".$row->V_DECIMAL[$i]."\n"; + } + if(abs($row->V_DOUBLE[$i] - $v_double[$i]) > abs($v_double[$i] / 1E15)) { + echo " DOUBLE[$i] fail\n"; + echo " in: ".$v_double[$i]."\n"; + echo " out: ".$row->V_DOUBLE[$i]."\n"; + } + if(abs($row->V_FLOAT[$i] - $v_float[$i]) > abs($v_float[$i] / 1E7)) { + echo " FLOAT[$i] fail\n"; + echo " in: ".$v_float[$i]."\n"; + echo " out: ".$row->V_FLOAT[$i]."\n"; + } + if($row->V_INTEGER[$i] != $v_integer[$i]) { + echo " INTEGER[$i] fail\n"; + echo " in: ".$v_integer[$i]."\n"; + echo " out: ".$row->V_INTEGER[$i]."\n"; + } + if ($row->V_NUMERIC[$i] != $v_numeric[$i]) { + echo " NUMERIC[$i] fail\n"; + echo " in: ".$v_numeric[$i]."\n"; + echo " out: ".$row->V_NUMERIC[$i]."\n"; + } + if ($row->V_SMALLINT[$i] != $v_smallint[$i]) { + echo " SMALLINT[$i] fail\n"; + echo " in: ".$v_smallint[$i]."\n"; + echo " out: ".$row->V_SMALLINT[$i]."\n"; + } + if ($row->V_VARCHAR[$i] != $v_varchar[$i]) { + echo " VARCHAR[$i] fail:\n"; + echo " in: ".$v_varchar[$i]."\n"; + echo " out: ".$row->V_VARCHAR[$i]."\n"; + } + } + ibase_free_result($sel); + }/* for($iter) */ + + echo "select\n"; + + $sel = ibase_query("SELECT v_multi[5,5,5],v_multi[10,10,10] FROM test7 WHERE iter = 0"); + print_r(ibase_fetch_row($sel)); + ibase_free_result($sel); + + for($iter = 1; $iter <= 3; $iter++) { + + if(!($sel = ibase_query( + "select iter from test7 where v_char[$iter] LIKE ?", $v_char[$iter]."%")) || + !ibase_fetch_row($sel)) { + echo "CHAR fail\n"; + } + ibase_free_result($sel); + + if(!($sel = ibase_query( + "select iter from test7 where v_date[$iter] = ?", $v_date[$iter])) || + !ibase_fetch_row($sel)) { + echo "DATE fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test7 where v_decimal[$iter] = ?", $v_decimal[$iter])) || + !ibase_fetch_row($sel)) { + echo "DECIMAL fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test7 where v_integer[$iter] = ?", $v_integer[$iter])) || + !ibase_fetch_row($sel)) { + echo "INTEGER fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test7 where v_numeric[$iter] = ?", $v_numeric[$iter])) || + !ibase_fetch_row($sel)) { + echo "NUMERIC fail\n"; + } + ibase_free_result($sel); + if(!($sel = ibase_query( + "select iter from test7 where v_smallint[$iter] = ?", $v_smallint[$iter])) || + !ibase_fetch_row($sel)) { + echo "SMALLINT fail\n"; + } + ibase_free_result($sel); + } + ibase_close(); + echo "end of test\n"; +?> +--EXPECT-- +insert +select +Array +( + [0] => 125 + [1] => 1000 +) +end of test diff --git a/ext/interbase/tests/008.phpt b/ext/interbase/tests/008.phpt new file mode 100644 index 0000000..8292fc3 --- /dev/null +++ b/ext/interbase/tests/008.phpt @@ -0,0 +1,48 @@ +--TEST-- +InterBase: event handling +--SKIPIF-- +<?php +if (PHP_OS == "WINNT") echo "skip"; +include("skipif.inc"); +?> +--FILE-- +<?php /* $Id$ */ + +require("interbase.inc"); + +$count = 0; + +function event_callback($event) +{ + global $count; + if ($event == 'TEST1') echo "FAIL TEST1\n"; + return (++$count < 5); /* cancel event */ +} + +$link = ibase_connect($test_base); + +ibase_query("CREATE PROCEDURE pevent AS BEGIN POST_EVENT 'TEST1'; POST_EVENT 'TEST2'; END"); +ibase_commit(); + +$e = ibase_set_event_handler('event_callback','TEST1'); +ibase_free_event_handler($e); + +ibase_set_event_handler('event_callback','TEST2'); + +usleep(5E+5); + +for ($i = 0; $i < 8; ++$i) { + ibase_query("EXECUTE PROCEDURE pevent"); + ibase_commit(); + + usleep(3E+5); +} + +usleep(5E+5); + +if (!$count || $count > 5) echo "FAIL ($count)\n"; +echo "end of test\n"; + +?> +--EXPECT-- +end of test diff --git a/ext/interbase/tests/bug45373.phpt b/ext/interbase/tests/bug45373.phpt new file mode 100644 index 0000000..8b16ef0 --- /dev/null +++ b/ext/interbase/tests/bug45373.phpt @@ -0,0 +1,47 @@ +--TEST-- +Bug #45373 (php crash on query with errors in params) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + + require("interbase.inc"); + + $db = ibase_connect($test_base); + + + $sql = "select * from test1 where i = ? and c = ?"; + + $q = ibase_prepare($db, $sql); + $r = ibase_execute($q, 1, 'test table not created with isql'); + var_dump(ibase_fetch_assoc($r)); + ibase_free_result($r); + + $r = ibase_execute($q, 1, 'test table not created with isql', 1); + var_dump(ibase_fetch_assoc($r)); + ibase_free_result($r); + + $r = ibase_execute($q, 1); + var_dump(ibase_fetch_assoc($r)); + +?> +--EXPECTF-- +array(2) { + ["I"]=> + int(1) + ["C"]=> + string(32) "test table not created with isql" +} + +Notice: ibase_execute(): Statement expects 2 arguments, 3 given in %s on line %d +array(2) { + ["I"]=> + int(1) + ["C"]=> + string(32) "test table not created with isql" +} + +Warning: ibase_execute(): Statement expects 2 arguments, 1 given in %s on line %d + +Warning: ibase_fetch_assoc() expects parameter 1 to be resource, boolean given in %s on line %d +NULL diff --git a/ext/interbase/tests/bug45575.phpt b/ext/interbase/tests/bug45575.phpt new file mode 100644 index 0000000..ca0fa83 --- /dev/null +++ b/ext/interbase/tests/bug45575.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #45575 (Segfault with invalid non-string as event handler callback) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$db = ibase_connect($test_base); + +function foobar($var) { var_dump($var); return true; } + +ibase_set_event_handler($db, null, 'TEST1'); +ibase_set_event_handler($db, 1, 'TEST1'); +ibase_set_event_handler('foobar', 'TEST1'); + +?> +--EXPECTF-- +Warning: ibase_set_event_handler(): Callback argument is not a callable function in %s on line %d + +Warning: ibase_set_event_handler(): Callback argument 1 is not a callable function in %s on line %d diff --git a/ext/interbase/tests/bug46247.phpt b/ext/interbase/tests/bug46247.phpt new file mode 100644 index 0000000..ffd153b --- /dev/null +++ b/ext/interbase/tests/bug46247.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug #46247 (ibase_set_event_handler() is allowing to pass callback without event) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$db = ibase_connect($test_base); + +function test() { } + +ibase_set_event_handler(); + +ibase_set_event_handler('test', 1); +ibase_set_event_handler($db, 'test', 1); +ibase_set_event_handler(NULL, 'test', 1); + + +ibase_set_event_handler('foo', 1); +ibase_set_event_handler($db, 'foo', 1); +ibase_set_event_handler(NULL, 'foo', 1); + +?> +--EXPECTF-- + +Warning: Wrong parameter count for ibase_set_event_handler() in %s on line %d + +Warning: ibase_set_event_handler(): supplied argument is not a valid InterBase link resource in %s on line %d + +Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d + +Warning: ibase_set_event_handler(): Callback argument foo is not a callable function in %s on line %d + +Warning: ibase_set_event_handler(): supplied argument is not a valid InterBase link resource in %s on line %d diff --git a/ext/interbase/tests/bug46543.phpt b/ext/interbase/tests/bug46543.phpt new file mode 100644 index 0000000..59e088c --- /dev/null +++ b/ext/interbase/tests/bug46543.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #46543 (ibase_trans() memory leaks when using wrong parameters) +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +@ibase_close(); + +ibase_trans(1); +ibase_trans(); +ibase_trans('foo'); +ibase_trans(fopen(__FILE__, 'r')); + +$x = ibase_connect($test_base); +ibase_trans(1, 2, $x, $x, 3); + +?> +--EXPECTF-- +Warning: ibase_trans(): no Firebird/InterBase link resource supplied in %s on line %d + +Warning: ibase_trans(): no Firebird/InterBase link resource supplied in %s on line %d + +Warning: ibase_trans(): no Firebird/InterBase link resource supplied in %s on line %d + +Warning: ibase_trans(): supplied resource is not a valid Firebird/InterBase link resource in %s on line %d diff --git a/ext/interbase/tests/ibase_affected_rows_001.phpt b/ext/interbase/tests/ibase_affected_rows_001.phpt new file mode 100644 index 0000000..398a84c --- /dev/null +++ b/ext/interbase/tests/ibase_affected_rows_001.phpt @@ -0,0 +1,32 @@ +--TEST-- +ibase_affected_rows(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +ibase_query($x, 'INSERT INTO test1 VALUES (1, 100)'); +ibase_query($x, 'INSERT INTO test1 VALUES (10000, 100)'); + +ibase_query($x, 'UPDATE test1 SET i = 10000'); +var_dump(ibase_affected_rows($x)); + + +ibase_query($x, 'UPDATE test1 SET i = 10000 WHERE i = 2.0'); +var_dump(ibase_affected_rows($x)); + +ibase_query($x, 'UPDATE test1 SET i ='); +var_dump(ibase_affected_rows($x)); + + +?> +--EXPECTF-- +int(3) +int(0) + +Warning: ibase_query(): Dynamic SQL Error SQL error code = -104 %s on line %d +int(0) diff --git a/ext/interbase/tests/ibase_close_001.phpt b/ext/interbase/tests/ibase_close_001.phpt new file mode 100644 index 0000000..6e31916 --- /dev/null +++ b/ext/interbase/tests/ibase_close_001.phpt @@ -0,0 +1,25 @@ +--TEST-- +ibase_close(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); +var_dump(ibase_close($x)); +var_dump(ibase_close($x)); +var_dump(ibase_close()); +var_dump(ibase_close('foo')); + +?> +--EXPECTF-- +bool(true) +bool(true) + +Warning: ibase_close(): %d is not a valid Firebird/InterBase link resource in %s on line %d +bool(false) + +Warning: ibase_close() expects parameter 1 to be resource,%string given in %s on line %d +NULL diff --git a/ext/interbase/tests/ibase_drop_db_001.phpt b/ext/interbase/tests/ibase_drop_db_001.phpt new file mode 100644 index 0000000..7526e13 --- /dev/null +++ b/ext/interbase/tests/ibase_drop_db_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +ibase_drop_db(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +unlink($file = tempnam('/tmp',"php_ibase_test")); + + +$db = ibase_query(IBASE_CREATE, + sprintf("CREATE SCHEMA '%s' USER '%s' PASSWORD '%s' DEFAULT CHARACTER SET %s",$file, + $user, $password, ($charset = ini_get('ibase.default_charset')) ? $charset : 'NONE')); + +var_dump($db); +var_dump(ibase_drop_db($db)); +var_dump(ibase_drop_db(1)); +var_dump(ibase_drop_db(NULL)); + +?> +--EXPECTF-- +resource(%d) of type (Firebird/InterBase link) +bool(true) + +Warning: ibase_drop_db() expects parameter 1 to be resource, integer given in %s on line %d +NULL + +Warning: ibase_drop_db() expects parameter 1 to be resource, null given in %s on line %d +NULL diff --git a/ext/interbase/tests/ibase_errmsg_001.phpt b/ext/interbase/tests/ibase_errmsg_001.phpt new file mode 100644 index 0000000..ad0e827 --- /dev/null +++ b/ext/interbase/tests/ibase_errmsg_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +ibase_errmsg(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +ibase_query('SELECT Foobar'); +var_dump(ibase_errmsg()); + +ibase_close($x); +var_dump(ibase_errmsg()); + +?> +--EXPECTF-- +Warning: ibase_query(): Dynamic SQL Error SQL error code = -104 %s on line %d +string(%d) "Dynamic SQL Error SQL error code = -104 %s" +bool(false) diff --git a/ext/interbase/tests/ibase_free_query_001.phpt b/ext/interbase/tests/ibase_free_query_001.phpt new file mode 100644 index 0000000..bedec71 --- /dev/null +++ b/ext/interbase/tests/ibase_free_query_001.phpt @@ -0,0 +1,28 @@ +--TEST-- +ibase_free_query(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +$q =ibase_prepare($x, 'SELECT 1 FROM test1 WHERE i = ?'); +$q =ibase_prepare($x, 'SELECT 1 FROM test1 WHERE i = ?'); +$q = ibase_prepare($x, 'SELECT 1 FROM test1 WHERE i = ?'); + +var_dump(ibase_free_query($q)); +var_dump(ibase_free_query($q)); +var_dump(ibase_free_query($x)); + +?> +--EXPECTF-- +bool(true) + +Warning: ibase_free_query(): 11 is not a valid Firebird/InterBase query resource in %s on line %d +bool(false) + +Warning: ibase_free_query(): supplied resource is not a valid Firebird/InterBase query resource in %s on line %d +bool(false) diff --git a/ext/interbase/tests/ibase_num_fields_001.phpt b/ext/interbase/tests/ibase_num_fields_001.phpt new file mode 100644 index 0000000..f1e0e4a --- /dev/null +++ b/ext/interbase/tests/ibase_num_fields_001.phpt @@ -0,0 +1,25 @@ +--TEST-- +ibase_num_fields(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +var_dump(ibase_num_fields(ibase_query('SELECT * FROM test1'))); + +var_dump(ibase_num_fields(1)); +var_dump(ibase_num_fields()); + +?> +--EXPECTF-- +int(2) + +Warning: ibase_num_fields() expects parameter 1 to be resource, integer given in %s on line %d +NULL + +Warning: ibase_num_fields() expects exactly 1 parameter, 0 given in %s on line %d +NULL diff --git a/ext/interbase/tests/ibase_num_params_001.phpt b/ext/interbase/tests/ibase_num_params_001.phpt new file mode 100644 index 0000000..655cae1 --- /dev/null +++ b/ext/interbase/tests/ibase_num_params_001.phpt @@ -0,0 +1,32 @@ +--TEST-- +ibase_num_params(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +$rs = ibase_prepare('SELECT * FROM test1 WHERE 1 = ? AND 2 = ?'); +var_dump(ibase_num_params($rs)); + +$rs = ibase_prepare('SELECT * FROM test1 WHERE 1 = ? AND 2 = ?'); +var_dump(ibase_num_params()); + +$rs = ibase_prepare('SELECT * FROM test1 WHERE 1 = ? AND 2 = ? AND 3 = :x'); +var_dump(ibase_num_params($rs)); + + +?> +--EXPECTF-- +int(2) + +Warning: ibase_num_params() expects exactly 1 parameter, 0 given in %s on line %d +NULL + +Warning: ibase_prepare(): Dynamic SQL Error SQL error code = -206 %s in %s on line %d + +Warning: ibase_num_params() expects parameter 1 to be resource, boolean given in %s on line %d +NULL diff --git a/ext/interbase/tests/ibase_param_info_001.phpt b/ext/interbase/tests/ibase_param_info_001.phpt new file mode 100644 index 0000000..31fe196 --- /dev/null +++ b/ext/interbase/tests/ibase_param_info_001.phpt @@ -0,0 +1,53 @@ +--TEST-- +ibase_param_info(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +$rs = ibase_prepare('SELECT * FROM test1 WHERE 1 = ? AND 2 = ?'); +var_dump(ibase_param_info($rs, 1)); + +print "---\n"; + +var_dump(ibase_param_info($rs, 100)); + +print "---\n"; + +var_dump(ibase_param_info(100)); + + +?> +--EXPECTF-- +array(10) { + [0]=> + string(0) "" + ["name"]=> + string(0) "" + [1]=> + string(0) "" + ["alias"]=> + string(0) "" + [2]=> + string(0) "" + ["relation"]=> + string(0) "" + [3]=> + string(1) "4" + ["length"]=> + string(1) "4" + [4]=> + string(7) "INTEGER" + ["type"]=> + string(7) "INTEGER" +} +--- +bool(false) +--- + +Warning: ibase_param_info() expects exactly 2 parameters, 1 given in %s on line %d +NULL diff --git a/ext/interbase/tests/ibase_rollback_001.phpt b/ext/interbase/tests/ibase_rollback_001.phpt new file mode 100644 index 0000000..3cde5e9 --- /dev/null +++ b/ext/interbase/tests/ibase_rollback_001.phpt @@ -0,0 +1,41 @@ +--TEST-- +ibase_rollback(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +ibase_query('INSERT INTO test1 VALUES (100, 2)'); +ibase_query('INSERT INTO test1 VALUES (100, 2)'); +ibase_query('INSERT INTO test1 VALUES (100, 2)'); + +$rs = ibase_query('SELECT COUNT(*) FROM test1 WHERE i = 100'); +var_dump(ibase_fetch_row($rs)); + +var_dump(ibase_rollback($x)); + +$rs = ibase_query('SELECT COUNT(*) FROM test1 WHERE i = 100'); +var_dump(ibase_fetch_row($rs)); + +var_dump(ibase_rollback($x)); +var_dump(ibase_rollback()); + +?> +--EXPECTF-- +array(1) { + [0]=> + int(3) +} +bool(true) +array(1) { + [0]=> + int(0) +} +bool(true) + +Warning: ibase_rollback(): invalid transaction handle (expecting explicit transaction start) in %s on line %d +bool(false) diff --git a/ext/interbase/tests/ibase_trans_001.phpt b/ext/interbase/tests/ibase_trans_001.phpt new file mode 100644 index 0000000..cceb60e --- /dev/null +++ b/ext/interbase/tests/ibase_trans_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +ibase_trans(): Basic test +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); +var_dump(ibase_trans($x)); +var_dump(ibase_trans(1)); +var_dump(ibase_close()); +var_dump(ibase_close($x)); + +?> +--EXPECTF-- +resource(%d) of type (Firebird/InterBase transaction) +resource(%d) of type (Firebird/InterBase transaction) +bool(true) +bool(true) diff --git a/ext/interbase/tests/ibase_trans_002.phpt b/ext/interbase/tests/ibase_trans_002.phpt new file mode 100644 index 0000000..be7c073 --- /dev/null +++ b/ext/interbase/tests/ibase_trans_002.phpt @@ -0,0 +1,34 @@ +--TEST-- +ibase_trans(): Basic operations +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +require("interbase.inc"); + +$x = ibase_connect($test_base); + +$trans = ibase_trans(IBASE_DEFAULT, $x); +$sth = ibase_prepare($trans, 'INSERT INTO test1 VALUES (?, ?)'); + +$res = ibase_execute($sth, 100, 100); +var_dump($res); + +ibase_commit($trans); + +$rs = ibase_query($x, 'SELECT * FROM test1 WHERE i = 100'); +var_dump(ibase_fetch_assoc($rs)); + +ibase_free_query($sth); +unset($res); + +?> +--EXPECT-- +int(1) +array(2) { + ["I"]=> + int(100) + ["C"]=> + string(3) "100" +} diff --git a/ext/interbase/tests/interbase.inc b/ext/interbase/tests/interbase.inc new file mode 100644 index 0000000..42eb6e5 --- /dev/null +++ b/ext/interbase/tests/interbase.inc @@ -0,0 +1,120 @@ +<?php /* $Id$ */ + +srand((double)microtime()*1000000); + +$user = 'SYSDBA'; +$password = 'masterkey'; +ini_set('ibase.default_user',$user); +ini_set('ibase.default_password',$password); + +/* we need just the generated name, not the file itself */ +unlink($test_base = tempnam('/tmp',"php_ibase_test")); + +function init_db() +{ + global $test_base, $user, $password; + + $test_db = ibase_query(IBASE_CREATE, + sprintf("CREATE SCHEMA '%s' USER '%s' PASSWORD '%s' DEFAULT CHARACTER SET %s",$test_base, + $user, $password, ($charset = ini_get('ibase.default_charset')) ? $charset : 'NONE')); + $tr = ibase_trans($test_db); + ibase_query($tr,"create table test1 (i integer, c varchar(100))"); + ibase_commit_ret($tr); + ibase_query($tr,"insert into test1(i, c) values(1, 'test table not created with isql')"); + ibase_commit($tr); + ibase_close($test_db); +} + +function cleanup_db() +{ + global $test_base; + + $r = ibase_connect($test_base); + ibase_drop_db($r); +} + +register_shutdown_function('cleanup_db'); +init_db(); + +function out_table($table_name) +{ + echo "--- $table_name ---\n"; + $res = ibase_query("select * from $table_name"); + while ($r = ibase_fetch_row($res)) { + echo join("\t",$r)."\t\n"; + } + ibase_free_result($res); + echo "---\n"; +} + +function out_result($result, $table_name = "") +{ + echo "--- $table_name ---\n"; + while ($r = ibase_fetch_row($result)) { + echo join("\t",$r)."\t\n"; + } + echo "---\n"; +} + +function out_result_trap_error($result, $table_name = "") +{ + echo "--- $table_name ---\n"; + while ($r = @ibase_fetch_row($result)) { + echo join("\t",$r)."\t\n"; + } + echo "errmsg [" . ibase_errmsg() . "]\t\n"; + echo "---\n"; +} + +/* M/D/Y H:M:S */ +function rand_datetime() +{ + return sprintf("%02d/%02d/%4d %02d:%02d:%02d", + rand()%12+1, rand()%28+1, rand()%100+1910, + rand()%24, rand()%60, rand()%60); +} + +/* random binary string */ +function rand_binstr($max_len) +{ + $len = rand() % $max_len; + $s = ""; + while($len--) { + $s .= sprintf("%c", rand() % 256); + } + return $s; +} + +function rand_str($max_len) +{ + $len = rand() % $max_len; + $s = ""; + while ($len--) { + $s .= sprintf("%c", rand() % 26 + 65); + } + return $s; +} + +function rand_number($len , $prec = -1, $sign = 1) +{ + if ($prec == -1) { + $n = substr(rand() . rand(), 0, rand() % $len + 1); + if (strlen($n) < $len) { + $n .= "." . substr(rand(), 0, rand() % ($len - strlen($n)) + 1); + } + } else if ($prec == 0) { + $n = substr(rand() . rand(), 0, rand() % $len + 1); + } else if (($prec - $len) == 0) { + $n = substr(rand() . rand(), 0, 1); + $n .= "." . substr(rand(), 0, $prec); + } else { + $n = substr(rand() . rand(), 0, rand() % ($len - $prec) + 1); + $n .= "." . substr(rand(), 0, $prec); + } + if ($sign && (rand() % 3 == 0)) { + $n = "-" .$n; + } + return $n; +} + +?> diff --git a/ext/interbase/tests/skipif.inc b/ext/interbase/tests/skipif.inc new file mode 100644 index 0000000..79813f6 --- /dev/null +++ b/ext/interbase/tests/skipif.inc @@ -0,0 +1,9 @@ +<?php /* $Id$ */ + +if (!extension_loaded("interbase")) print "skip interbase extension not available"; +require("interbase.inc"); +if(!@ibase_connect($test_base)){ + die("skip cannot connnect"); +} + +?> |