From: SVN Migration Date: Sat, 21 Jun 2003 13:50:44 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create branch 'PHP_4_3'. X-Git-Tag: php-4.3.3RC2~265 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ea33b2824e1f6c0af6626bf43ab1b2147fb23180;p=php This commit was manufactured by cvs2svn to create branch 'PHP_4_3'. --- diff --git a/ext/sqlite/package.xml b/ext/sqlite/package.xml new file mode 100644 index 0000000000..1a87198fd9 --- /dev/null +++ b/ext/sqlite/package.xml @@ -0,0 +1,141 @@ + + + + SQLite + SQLite database bindings + + + wez + Wez Furlong + wez@php.net + lead + + + tal + Tal Peer + tal@php.net + developer + + + helly + Marcus Börger + helly@php.net + lead + + + + SQLite is a C library that implements an embeddable SQL database engine. + Programs that link with the SQLite library can have SQL database access + without running a separate RDBMS process. + This extension allows you to access SQLite databases from within PHP. + + Windows binary available from: + http://snaps.php.net/win32/PECL_STABLE/php_sqlite.dll + + PHP + + stable + 1.0 + 2003-06-21 + + Added: + sqlite_udf_encode_binary() and sqlite_udf_decode_binary() for + handling binary data in UDF callbacks. + sqlite_popen() for persistent db connections. + sqlite_unbuffered_query() for high performance queries. + sqlite_last_error() returns error code from last operation. + sqlite_error_string() returns description of error. + sqlite_create_aggregate() for registering aggregating SQL functions. + sqlite_create_function() for registering regular SQL functions. + sqlite_fetch_string() for speedy access to first column of result sets. + sqlite_fetch_all() to receive all rowsets from a query as an array. + iteration interface + + sqlite_query() functions accept resource/query string in either order, + for compatibility with mysql and postgresql extensions. + + Fixed some build issues for thread-safe builds. + + Increase the default busy timeout interval to 60 seconds. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/sqlite/php_sqlite.h b/ext/sqlite/php_sqlite.h new file mode 100644 index 0000000000..3f937e4656 --- /dev/null +++ b/ext/sqlite/php_sqlite.h @@ -0,0 +1,103 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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: Wez Furlong | + | Tal Peer | + | Marcus Boerger | + +----------------------------------------------------------------------+ + + $Id$ +*/ + +#ifndef PHP_SQLITE_H +#define PHP_SQLITE_H + +extern zend_module_entry sqlite_module_entry; +#define phpext_sqlite_ptr &sqlite_module_entry + +#ifdef PHP_WIN32 +#define PHP_SQLITE_API __declspec(dllexport) +#else +#define PHP_SQLITE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(sqlite); +PHP_MSHUTDOWN_FUNCTION(sqlite); +PHP_RINIT_FUNCTION(sqlite); +PHP_RSHUTDOWN_FUNCTION(sqlite); +PHP_MINFO_FUNCTION(sqlite); + +PHP_FUNCTION(sqlite_open); +PHP_FUNCTION(sqlite_popen); +PHP_FUNCTION(sqlite_close); +PHP_FUNCTION(sqlite_query); +PHP_FUNCTION(sqlite_unbuffered_query); +PHP_FUNCTION(sqlite_array_query); + +PHP_FUNCTION(sqlite_fetch_array); +PHP_FUNCTION(sqlite_fetch_string); +PHP_FUNCTION(sqlite_fetch_all); +PHP_FUNCTION(sqlite_current); +PHP_FUNCTION(sqlite_column); + +PHP_FUNCTION(sqlite_num_rows); +PHP_FUNCTION(sqlite_num_fields); +PHP_FUNCTION(sqlite_field_name); +PHP_FUNCTION(sqlite_seek); +PHP_FUNCTION(sqlite_rewind); +PHP_FUNCTION(sqlite_next); +PHP_FUNCTION(sqlite_has_more); + +PHP_FUNCTION(sqlite_libversion); +PHP_FUNCTION(sqlite_libencoding); + +PHP_FUNCTION(sqlite_changes); +PHP_FUNCTION(sqlite_last_insert_rowid); + +PHP_FUNCTION(sqlite_escape_string); + +PHP_FUNCTION(sqlite_busy_timeout); + +PHP_FUNCTION(sqlite_last_error); +PHP_FUNCTION(sqlite_error_string); + +PHP_FUNCTION(sqlite_create_aggregate); +PHP_FUNCTION(sqlite_create_function); +PHP_FUNCTION(sqlite_udf_decode_binary); +PHP_FUNCTION(sqlite_udf_encode_binary); + +ZEND_BEGIN_MODULE_GLOBALS(sqlite) + long assoc_case; +ZEND_END_MODULE_GLOBALS(sqlite) + +#ifdef ZTS +#define SQLITE_G(v) TSRMG(sqlite_globals_id, zend_sqlite_globals *, v) +#else +#define SQLITE_G(v) (sqlite_globals.v) +#endif + +#endif + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/ext/sqlite/sqlite.c b/ext/sqlite/sqlite.c new file mode 100644 index 0000000000..fdc8abf909 --- /dev/null +++ b/ext/sqlite/sqlite.c @@ -0,0 +1,1879 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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: Wez Furlong | + | Tal Peer | + | Marcus Boerger | + +----------------------------------------------------------------------+ + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define PHP_SQLITE_MODULE_VERSION "1.0" + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_sqlite.h" + +#if HAVE_TIME_H +# include +#endif +#include + +#include + +#ifndef safe_emalloc +# define safe_emalloc(a,b,c) emalloc((a)*(b)+(c)) +#endif + +#ifndef ZEND_ENGINE_2 +# define OnUpdateLong OnUpdateInt +#endif + +ZEND_DECLARE_MODULE_GLOBALS(sqlite) + +extern int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out); +extern int sqlite_decode_binary(const unsigned char *in, unsigned char *out); + +static unsigned char arg3_force_ref[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE }; + +static int le_sqlite_db, le_sqlite_result, le_sqlite_pdb; + +static inline void php_sqlite_strtoupper(char *s) +{ + while (*s!='\0') { + *s = toupper(*s); + s++; + } +} + +static inline void php_sqlite_strtolower(char *s) +{ + while (*s!='\0') { + *s = tolower(*s); + s++; + } +} + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() +STD_PHP_INI_ENTRY_EX("sqlite.assoc_case", "0", PHP_INI_ALL, OnUpdateLong, assoc_case, zend_sqlite_globals, sqlite_globals, display_link_numbers) +PHP_INI_END() +/* }}} */ + + +#define DB_FROM_ZVAL(db, zv) ZEND_FETCH_RESOURCE2(db, struct php_sqlite_db *, zv, -1, "sqlite database", le_sqlite_db, le_sqlite_pdb) + +struct php_sqlite_result { + struct php_sqlite_db *db; + sqlite_vm *vm; + int buffered; + int ncolumns; + int nrows; + int curr_row; + char **col_names; + int alloc_rows; + char **table; + int mode; +}; + +struct php_sqlite_db { + sqlite *db; + int last_err_code; + int is_persistent; + int rsrc_id; + + HashTable callbacks; +}; + +struct php_sqlite_agg_functions { + struct php_sqlite_db *db; + int is_valid; + zval *step; + zval *fini; +}; + + +enum { PHPSQLITE_ASSOC = 1, PHPSQLITE_NUM = 2, PHPSQLITE_BOTH = PHPSQLITE_ASSOC|PHPSQLITE_NUM }; + +function_entry sqlite_functions[] = { + PHP_FE(sqlite_open, arg3_force_ref) + PHP_FE(sqlite_popen, arg3_force_ref) + PHP_FE(sqlite_close, NULL) + PHP_FE(sqlite_query, NULL) + PHP_FE(sqlite_array_query, NULL) + PHP_FE(sqlite_fetch_array, NULL) + PHP_FE(sqlite_fetch_string, NULL) + PHP_FE(sqlite_fetch_all, NULL) + PHP_FE(sqlite_current, NULL) + PHP_FE(sqlite_column, NULL) + PHP_FE(sqlite_libversion, NULL) + PHP_FE(sqlite_libencoding, NULL) + PHP_FE(sqlite_changes, NULL) + PHP_FE(sqlite_last_insert_rowid, NULL) + PHP_FE(sqlite_num_rows, NULL) + PHP_FE(sqlite_num_fields, NULL) + PHP_FE(sqlite_field_name, NULL) + PHP_FE(sqlite_seek, NULL) + PHP_FE(sqlite_rewind, NULL) + PHP_FE(sqlite_next, NULL) + PHP_FE(sqlite_has_more, NULL) + PHP_FE(sqlite_escape_string, NULL) + PHP_FE(sqlite_busy_timeout, NULL) + PHP_FE(sqlite_last_error, NULL) + PHP_FE(sqlite_error_string, NULL) + PHP_FE(sqlite_unbuffered_query, NULL) + PHP_FE(sqlite_create_aggregate, NULL) + PHP_FE(sqlite_create_function, NULL) + PHP_FE(sqlite_udf_encode_binary, NULL) + PHP_FE(sqlite_udf_decode_binary, NULL) + {NULL, NULL, NULL} +}; + + +zend_module_entry sqlite_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "sqlite", + sqlite_functions, + PHP_MINIT(sqlite), + NULL, + NULL, + PHP_RSHUTDOWN(sqlite), + PHP_MINFO(sqlite), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_SQLITE_MODULE_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; + + +#ifdef COMPILE_DL_SQLITE +ZEND_GET_MODULE(sqlite) +#endif + +static int php_sqlite_callback_invalidator(struct php_sqlite_agg_functions *funcs TSRMLS_DC) +{ + if (!funcs->is_valid) { + return 0; + } + + if (funcs->step) { + zval_ptr_dtor(&funcs->step); + funcs->step = NULL; + } + + if (funcs->fini) { + zval_ptr_dtor(&funcs->fini); + funcs->fini = NULL; + } + + funcs->is_valid = 0; + + return 0; +} + + +static void php_sqlite_callback_dtor(void *pDest) +{ + struct php_sqlite_agg_functions *funcs = (struct php_sqlite_agg_functions*)pDest; + + if (funcs->is_valid) { + TSRMLS_FETCH(); + + php_sqlite_callback_invalidator(funcs TSRMLS_CC); + } +} + +static ZEND_RSRC_DTOR_FUNC(php_sqlite_db_dtor) +{ + if (rsrc->ptr) { + struct php_sqlite_db *db = (struct php_sqlite_db*)rsrc->ptr; + sqlite_close(db->db); + + zend_hash_destroy(&db->callbacks); + + pefree(db, db->is_persistent); + + rsrc->ptr = NULL; + } +} + +static void real_result_dtor(struct php_sqlite_result *res TSRMLS_DC) +{ + int i, j, base; + + if (res->vm) { + sqlite_finalize(res->vm, NULL); + } + + if (res->table) { + if (!res->buffered && res->nrows) { + res->nrows = 1; /* only one row is stored */ + } + for (i = 0; i < res->nrows; i++) { + base = i * res->ncolumns; + for (j = 0; j < res->ncolumns; j++) { + if (res->table[base + j] != NULL) { + efree(res->table[base + j]); + } + } + } + efree(res->table); + } + if (res->col_names) { + for (j = 0; j < res->ncolumns; j++) { + efree(res->col_names[j]); + } + efree(res->col_names); + } + + zend_list_delete(res->db->rsrc_id); + efree(res); +} + +static ZEND_RSRC_DTOR_FUNC(php_sqlite_result_dtor) +{ + struct php_sqlite_result *res = (struct php_sqlite_result *)rsrc->ptr; + real_result_dtor(res TSRMLS_CC); +} + +static int php_sqlite_forget_persistent_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + struct php_sqlite_db *db; + + if (Z_TYPE_P(rsrc) != le_sqlite_pdb) { + return 0; + } + + db = (struct php_sqlite_db*)rsrc->ptr; + + db->rsrc_id = FAILURE; + + /* don't leave pending commits hanging around */ + sqlite_exec(db->db, "ROLLBACK", NULL, NULL, NULL); + + /* prevent bad mojo if someone tries to use a previously registered function in the next request */ + zend_hash_apply(&db->callbacks, (apply_func_t)php_sqlite_callback_invalidator TSRMLS_CC); + + return 0; +} + +PHP_RSHUTDOWN_FUNCTION(sqlite) +{ + zend_hash_apply(&EG(persistent_list), (apply_func_t)php_sqlite_forget_persistent_id_numbers TSRMLS_CC); + return SUCCESS; +} + +/* {{{ PHP Function interface */ +static void php_sqlite_generic_function_callback(sqlite_func *func, int argc, const char **argv) +{ + zval *retval = NULL; + zval ***zargs = NULL; + zval funcname; + int i, res; + char *callable = NULL, *errbuf=NULL; + TSRMLS_FETCH(); + + /* sanity check the args */ + if (argc == 0) { + sqlite_set_result_error(func, "not enough parameters", -1); + return; + } + + ZVAL_STRING(&funcname, (char*)argv[0], 0); + + if (!zend_is_callable(&funcname, 0, &callable)) { + spprintf(&errbuf, 0, "function `%s' is not callable", callable); + sqlite_set_result_error(func, errbuf, -1); + efree(errbuf); + efree(callable); + return; + } + efree(callable); + + if (argc > 1) { + zargs = (zval ***)safe_emalloc((argc - 1), sizeof(zval **), 0); + + for (i = 0; i < argc-1; i++) { + zargs[i] = emalloc(sizeof(zval *)); + MAKE_STD_ZVAL(*zargs[i]); + ZVAL_STRING(*zargs[i], (char*)argv[i+1], 1); + } + } + + res = call_user_function_ex(EG(function_table), + NULL, + &funcname, + &retval, + argc-1, + zargs, + 0, NULL TSRMLS_CC); + + if (res == SUCCESS) { + if (retval == NULL) { + sqlite_set_result_string(func, NULL, 0); + } else { + switch (Z_TYPE_P(retval)) { + case IS_STRING: + sqlite_set_result_string(func, Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + break; + case IS_LONG: + case IS_BOOL: + sqlite_set_result_int(func, Z_LVAL_P(retval)); + break; + case IS_DOUBLE: + sqlite_set_result_double(func, Z_DVAL_P(retval)); + break; + case IS_NULL: + default: + sqlite_set_result_string(func, NULL, 0); + } + } + } else { + sqlite_set_result_error(func, "call_user_function_ex failed", -1); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (zargs) { + for (i = 0; i < argc-1; i++) { + zval_ptr_dtor(zargs[i]); + efree(zargs[i]); + } + efree(zargs); + } +} +/* }}} */ + +/* {{{ callback for sqlite_create_function */ +static void php_sqlite_function_callback(sqlite_func *func, int argc, const char **argv) +{ + zval *retval = NULL; + zval ***zargs = NULL; + int i, res; + struct php_sqlite_agg_functions *funcs = sqlite_user_data(func); + TSRMLS_FETCH(); + + if (!funcs->is_valid) { + sqlite_set_result_error(func, "this function has not been correctly defined for this request", -1); + return; + } + + if (argc > 0) { + zargs = (zval ***)safe_emalloc(argc, sizeof(zval **), 0); + + for (i = 0; i < argc; i++) { + zargs[i] = emalloc(sizeof(zval *)); + MAKE_STD_ZVAL(*zargs[i]); + + if (argv[i] == NULL) { + ZVAL_NULL(*zargs[i]); + } else { + ZVAL_STRING(*zargs[i], (char*)argv[i], 1); + } + } + } + + res = call_user_function_ex(EG(function_table), + NULL, + funcs->step, + &retval, + argc, + zargs, + 0, NULL TSRMLS_CC); + + if (res == SUCCESS) { + if (retval == NULL) { + sqlite_set_result_string(func, NULL, 0); + } else { + switch (Z_TYPE_P(retval)) { + case IS_STRING: + /* TODO: for binary results, need to encode the string */ + sqlite_set_result_string(func, Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + break; + case IS_LONG: + case IS_BOOL: + sqlite_set_result_int(func, Z_LVAL_P(retval)); + break; + case IS_DOUBLE: + sqlite_set_result_double(func, Z_DVAL_P(retval)); + break; + case IS_NULL: + default: + sqlite_set_result_string(func, NULL, 0); + } + } + } else { + sqlite_set_result_error(func, "call_user_function_ex failed", -1); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (zargs) { + for (i = 0; i < argc; i++) { + zval_ptr_dtor(zargs[i]); + efree(zargs[i]); + } + efree(zargs); + } +} +/* }}} */ + +/* {{{ callback for sqlite_create_aggregate: step function */ +static void php_sqlite_agg_step_function_callback(sqlite_func *func, int argc, const char **argv) +{ + zval *retval = NULL; + zval ***zargs; + zval **context_p; + int i, res, zargc; + struct php_sqlite_agg_functions *funcs = sqlite_user_data(func); + TSRMLS_FETCH(); + + if (!funcs->is_valid) { + sqlite_set_result_error(func, "this function has not been correctly defined for this request", -1); + return; + } + + /* sanity check the args */ + if (argc < 1) { + return; + } + + zargc = argc + 1; + zargs = (zval ***)safe_emalloc(zargc, sizeof(zval **), 0); + + /* first arg is always the context zval */ + context_p = (zval **)sqlite_aggregate_context(func, sizeof(*context_p)); + + if (*context_p == NULL) { + MAKE_STD_ZVAL(*context_p); + (*context_p)->is_ref = 1; + Z_TYPE_PP(context_p) = IS_NULL; + } + + zargs[0] = context_p; + + /* copy the other args */ + for (i = 0; i < argc; i++) { + zargs[i+1] = emalloc(sizeof(zval *)); + MAKE_STD_ZVAL(*zargs[i+1]); + if (argv[i] == NULL) { + ZVAL_NULL(*zargs[i+1]); + } else { + ZVAL_STRING(*zargs[i+1], (char*)argv[i], 1); + } + } + + res = call_user_function_ex(EG(function_table), + NULL, + funcs->step, + &retval, + zargc, + zargs, + 0, NULL TSRMLS_CC); + + if (res != SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "call_user_function_ex failed"); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (zargs) { + for (i = 1; i < zargc; i++) { + zval_ptr_dtor(zargs[i]); + efree(zargs[i]); + } + efree(zargs); + } +} +/* }}} */ + +/* {{{ callback for sqlite_create_aggregate: finalize function */ +static void php_sqlite_agg_fini_function_callback(sqlite_func *func) +{ + zval *retval = NULL; + int res; + struct php_sqlite_agg_functions *funcs = sqlite_user_data(func); + zval **context_p; + TSRMLS_FETCH(); + + if (!funcs->is_valid) { + sqlite_set_result_error(func, "this function has not been correctly defined for this request", -1); + return; + } + + context_p = (zval **)sqlite_aggregate_context(func, sizeof(*context_p)); + + res = call_user_function_ex(EG(function_table), + NULL, + funcs->fini, + &retval, + 1, + &context_p, + 0, NULL TSRMLS_CC); + + if (res == SUCCESS) { + if (retval == NULL) { + sqlite_set_result_string(func, NULL, 0); + } else { + switch (Z_TYPE_P(retval)) { + case IS_STRING: + /* TODO: for binary results, need to encode the string */ + sqlite_set_result_string(func, Z_STRVAL_P(retval), Z_STRLEN_P(retval)); + break; + case IS_LONG: + case IS_BOOL: + sqlite_set_result_int(func, Z_LVAL_P(retval)); + break; + case IS_DOUBLE: + sqlite_set_result_double(func, Z_DVAL_P(retval)); + break; + case IS_NULL: + default: + sqlite_set_result_string(func, NULL, 0); + } + } + } else { + sqlite_set_result_error(func, "call_user_function_ex failed", -1); + } + + if (retval) { + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(context_p); +} +/* }}} */ + +/* {{{ Authorization Callback */ +static int php_sqlite_authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, + const char *arg5, const char *arg6) +{ + switch (access_type) { + case SQLITE_COPY: + { + TSRMLS_FETCH(); + if (PG(safe_mode) && (!php_checkuid(arg4, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return SQLITE_DENY; + } + + if (php_check_open_basedir(arg4 TSRMLS_CC)) { + return SQLITE_DENY; + } + } + return SQLITE_OK; +#ifdef SQLITE_ATTACH + case SQLITE_ATTACH: + { + TSRMLS_FETCH(); + if (PG(safe_mode) && (!php_checkuid(arg3, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + return SQLITE_DENY; + } + + if (php_check_open_basedir(arg3 TSRMLS_CC)) { + return SQLITE_DENY; + } + } + return SQLITE_OK; +#endif + + default: + /* access allowed */ + return SQLITE_OK; + } +} +/* }}} */ + +static int init_sqlite_globals(zend_sqlite_globals *g) +{ + g->assoc_case = 0; + return SUCCESS; +} + +PHP_MINIT_FUNCTION(sqlite) +{ + ZEND_INIT_MODULE_GLOBALS(sqlite, init_sqlite_globals, NULL); + + REGISTER_INI_ENTRIES(); + + le_sqlite_db = zend_register_list_destructors_ex(php_sqlite_db_dtor, NULL, "sqlite database", module_number); + le_sqlite_pdb = zend_register_list_destructors_ex(NULL, php_sqlite_db_dtor, "sqlite database (persistent)", module_number); + le_sqlite_result = zend_register_list_destructors_ex(php_sqlite_result_dtor, NULL, "sqlite result", module_number); + + REGISTER_LONG_CONSTANT("SQLITE_BOTH", PHPSQLITE_BOTH, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_NUM", PHPSQLITE_NUM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_ASSOC", PHPSQLITE_ASSOC, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SQLITE_OK", SQLITE_OK, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_ERROR", SQLITE_ERROR, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_INTERNAL", SQLITE_INTERNAL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_PERM", SQLITE_PERM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_ABORT", SQLITE_ABORT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_BUSY", SQLITE_BUSY, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_LOCKED", SQLITE_LOCKED, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_NOMEM", SQLITE_NOMEM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_READONLY", SQLITE_READONLY, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_INTERRUPT", SQLITE_INTERRUPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_IOERR", SQLITE_IOERR, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_CORRUPT", SQLITE_CORRUPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_NOTFOUND", SQLITE_NOTFOUND, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_FULL", SQLITE_FULL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_CANTOPEN", SQLITE_CANTOPEN, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_PROTOCOL", SQLITE_PROTOCOL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_EMPTY", SQLITE_EMPTY, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_SCHEMA", SQLITE_SCHEMA, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_TOOBIG", SQLITE_TOOBIG, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_CONSTRAINT", SQLITE_CONSTRAINT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_MISMATCH", SQLITE_MISMATCH, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_MISUSE", SQLITE_MISUSE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_NOLFS", SQLITE_NOLFS, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_AUTH", SQLITE_AUTH, CONST_CS|CONST_PERSISTENT); +#ifdef SQLITE_FORMAT + REGISTER_LONG_CONSTANT("SQLITE_FORMAT", SQLITE_FORMAT, CONST_CS|CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SQLITE_ROW", SQLITE_ROW, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SQLITE_DONE", SQLITE_DONE, CONST_CS|CONST_PERSISTENT); + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(sqlite) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SQLite support", "enabled"); + php_info_print_table_row(2, "PECL Module version", PHP_SQLITE_MODULE_VERSION " $Id$"); + php_info_print_table_row(2, "SQLite Library", sqlite_libversion()); + php_info_print_table_row(2, "SQLite Encoding", sqlite_libencoding()); + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} + +static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *persistent_id, zval *return_value, zval *errmsg TSRMLS_DC) +{ + char *errtext = NULL; + sqlite *sdb = NULL; + struct php_sqlite_db *db = NULL; + + sdb = sqlite_open(filename, mode, &errtext); + + if (sdb == NULL) { + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + + if (errmsg) { + ZVAL_STRING(errmsg, errtext, 1); + } + + sqlite_freemem(errtext); + + RETVAL_FALSE; + return NULL; + } + + db = (struct php_sqlite_db *)pemalloc(sizeof(struct php_sqlite_db), persistent_id ? 1 : 0); + db->is_persistent = persistent_id ? 1 : 0; + db->last_err_code = SQLITE_OK; + db->db = sdb; + + zend_hash_init(&db->callbacks, 0, NULL, php_sqlite_callback_dtor, db->is_persistent); + + /* register the PHP functions */ + sqlite_create_function(sdb, "php", -1, php_sqlite_generic_function_callback, 0); + + /* set default busy handler; keep retrying up until 1 minute has passed, + * then fail with a busy status code */ + sqlite_busy_timeout(sdb, 60000); + + /* authorizer hook so we can enforce safe mode + * Note: the declaration of php_sqlite_authorizer is correct for 2.8.2 of libsqlite, + * and IS backwards binary compatible with earlier versions */ + sqlite_set_authorizer(sdb, php_sqlite_authorizer, NULL); + + db->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, db, persistent_id ? le_sqlite_pdb : le_sqlite_db); + + if (persistent_id) { + list_entry le; + + Z_TYPE(le) = le_sqlite_pdb; + le.ptr = db; + + if (FAILURE == zend_hash_update(&EG(persistent_list), persistent_id, + strlen(persistent_id)+1, + (void *)&le, sizeof(le), NULL)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to register persistent resource"); + } + } + + return db; +} + +/* {{{ proto resource sqlite_popen(string filename [, int mode, string &errmessage]) + Opens a persistent handle to an SQLite database. Will create the database if it does not exist */ +PHP_FUNCTION(sqlite_popen) +{ + int mode = 0666; + char *filename, *fullpath, *hashkey; + long filename_len, hashkeylen; + zval *errmsg = NULL; + struct php_sqlite_db *db = NULL; + list_entry *le; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/", + &filename, &filename_len, &mode, &errmsg)) { + return; + } + + if (strncmp(filename, ":memory:", sizeof(":memory:") - 1)) { + /* resolve the fully-qualified path name to use as the hash key */ + fullpath = expand_filepath(filename, NULL TSRMLS_CC); + + if (PG(safe_mode) && (!php_checkuid(fullpath, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + RETURN_FALSE; + } + + if (php_check_open_basedir(fullpath TSRMLS_CC)) { + RETURN_FALSE; + } + } else { + fullpath = estrndup(filename, filename_len); + } + + hashkeylen = spprintf(&hashkey, 0, "sqlite_pdb_%s:%d", fullpath, mode); + + /* do we have an existing persistent connection ? */ + if (SUCCESS == zend_hash_find(&EG(persistent_list), hashkey, hashkeylen+1, (void*)&le)) { + if (Z_TYPE_P(le) == le_sqlite_pdb) { + db = (struct php_sqlite_db*)le->ptr; + + if (db->rsrc_id == FAILURE) { + /* give it a valid resource id for this request */ + db->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, db, le_sqlite_pdb); + } else { + /* already accessed this request; map it */ + ZVAL_RESOURCE(return_value, db->rsrc_id); + } + + /* all set */ + efree(fullpath); + efree(hashkey); + return; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Some other type of persistent resource is using this hash key!?"); + RETURN_FALSE; + } + + /* now we need to open the database */ + php_sqlite_open(fullpath, mode, hashkey, return_value, errmsg TSRMLS_CC); + + efree(fullpath); + efree(hashkey); +} +/* }}} */ + +/* {{{ proto resource sqlite_open(string filename [, int mode, string &errmessage]) + Opens an SQLite database. Will create the database if it does not exist */ +PHP_FUNCTION(sqlite_open) +{ + int mode = 0666; + char *filename; + long filename_len; + zval *errmsg = NULL; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/", + &filename, &filename_len, &mode, &errmsg)) { + return; + } + + if (strncmp(filename, ":memory:", sizeof(":memory:") - 1)) { + if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { + RETURN_FALSE; + } + + if (php_check_open_basedir(filename TSRMLS_CC)) { + RETURN_FALSE; + } + } + + php_sqlite_open(filename, mode, NULL, return_value, errmsg TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto void sqlite_busy_timeout(resource db, int ms) + Set busy timeout duration. If ms <= 0, all busy handlers are disabled */ +PHP_FUNCTION(sqlite_busy_timeout) +{ + zval *zdb; + struct php_sqlite_db *db; + long ms; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zdb, &ms)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + sqlite_busy_timeout(db->db, ms); +} +/* }}} */ + +/* {{{ proto void sqlite_close(resource db) + Closes an open sqlite database */ +PHP_FUNCTION(sqlite_close) +{ + zval *zdb; + struct php_sqlite_db *db; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) { + return; + } + DB_FROM_ZVAL(db, &zdb); + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + zend_list_delete(Z_RESVAL_P(zdb)); +} +/* }}} */ + +/* {{{ php_sqlite_fetch */ +int php_sqlite_fetch(struct php_sqlite_result *rres TSRMLS_DC) +{ + const char **rowdata, **colnames; + int ret, i, base; + char *errtext = NULL, *colname; + +next_row: + ret = sqlite_step(rres->vm, &rres->ncolumns, &rowdata, &colnames); + if (!rres->nrows) { + /* first row - lets copy the column names */ + rres->col_names = safe_emalloc(rres->ncolumns, sizeof(char *), 0); + for (i = 0; i < rres->ncolumns; i++) { + colname = strchr(colnames[i], '.'); + if (!colname++) { + colname = (char*)colnames[i]; + } + if (SQLITE_G(assoc_case) == 1) { + php_sqlite_strtoupper(colname); + } else if (SQLITE_G(assoc_case) == 2) { + php_sqlite_strtolower(colname); + } + rres->col_names[i] = estrdup(colname); + } + if (!rres->buffered) { + /* non buffered mode - also fetch memory for on single row */ + rres->table = safe_emalloc(rres->ncolumns, sizeof(char *), 0); + } + } + + switch (ret) { + case SQLITE_ROW: + if (rres->buffered) { + /* add the row to our collection */ + if (rres->nrows + 1 >= rres->alloc_rows) { + rres->alloc_rows = rres->alloc_rows ? rres->alloc_rows * 2 : 16; + rres->table = erealloc(rres->table, rres->alloc_rows * rres->ncolumns * sizeof(char *)); + } + base = rres->nrows * rres->ncolumns; + for (i = 0; i < rres->ncolumns; i++) { + if (rowdata[i]) { + rres->table[base + i] = estrdup(rowdata[i]); + } else { + rres->table[base + i] = NULL; + } + } + rres->nrows++; + goto next_row; + } else { + /* non buffered: only fetch one row but first free data if not first row */ + if (rres->nrows++) { + for (i = 0; i < rres->ncolumns; i++) { + if (rres->table[i]) { + efree(rres->table[i]); + } + } + } + for (i = 0; i < rres->ncolumns; i++) { + if (rowdata[i]) { + rres->table[i] = estrdup(rowdata[i]); + } else { + rres->table[i] = NULL; + } + } + } + ret = SQLITE_OK; + break; + + case SQLITE_BUSY: + case SQLITE_ERROR: + case SQLITE_MISUSE: + case SQLITE_DONE: + default: + if (rres->vm) { + ret = sqlite_finalize(rres->vm, &errtext); + } + rres->vm = NULL; + if (ret != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + sqlite_freemem(errtext); + } + break; + } + rres->db->last_err_code = ret; + + return ret; +} +/* }}} */ + +/* {{{ sqlite_query */ +void sqlite_query(struct php_sqlite_db *db, char *sql, long sql_len, int mode, int buffered, zval *return_value, struct php_sqlite_result *rres TSRMLS_DC) +{ + struct php_sqlite_result res; + int ret; + char *errtext = NULL; + const char *tail; + + memset(&res, 0, sizeof(res)); + res.buffered = buffered; + res.mode = mode; + + ret = sqlite_compile(db->db, sql, &tail, &res.vm, &errtext); + db->last_err_code = ret; + + if (ret != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + sqlite_freemem(errtext); + + RETURN_FALSE; + } + + if (!rres) { + rres = (struct php_sqlite_result*)emalloc(sizeof(*rres)); + } + memcpy(rres, &res, sizeof(*rres)); + rres->db = db; + zend_list_addref(db->rsrc_id); + + + /* now the result set is ready for stepping: get first row */ + if (php_sqlite_fetch(rres TSRMLS_CC) != SQLITE_OK) { + real_result_dtor(rres TSRMLS_CC); + RETURN_FALSE; + } + + rres->curr_row = 0; + + if (return_value) { + ZEND_REGISTER_RESOURCE(return_value, rres, le_sqlite_result); + } +} +/* }}} */ + +/* {{{ proto resource sqlite_unbuffered_query(string query, resource db [ , int result_type ]) + Execute a query that does not prefetch and buffer all data */ +PHP_FUNCTION(sqlite_unbuffered_query) +{ + zval *zdb; + struct php_sqlite_db *db; + char *sql; + long sql_len; + int mode = PHPSQLITE_BOTH; + char *errtext = NULL; + + if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode) && + FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + /* avoid doing work if we can */ + if (!return_value_used) { + db->last_err_code = sqlite_exec(db->db, sql, NULL, NULL, &errtext); + + if (db->last_err_code != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + sqlite_freemem(errtext); + } + return; + } + + sqlite_query(db, sql, sql_len, mode, 0, return_value, NULL TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto resource sqlite_query(string query, resource db [ , int result_type ]) + Executes a query against a given database and returns a result handle */ +PHP_FUNCTION(sqlite_query) +{ + zval *zdb; + struct php_sqlite_db *db; + char *sql; + long sql_len; + int mode = PHPSQLITE_BOTH; + char *errtext = NULL; + + if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode) && + FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode)) { + return; + } + DB_FROM_ZVAL(db, &zdb); + + /* avoid doing work if we can */ + if (!return_value_used) { + db->last_err_code = sqlite_exec(db->db, sql, NULL, NULL, &errtext); + + if (db->last_err_code != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + sqlite_freemem(errtext); + } + return; + } + + sqlite_query(db, sql, sql_len, mode, 1, return_value, NULL TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_sqlite_fetch_array */ +static void php_sqlite_fetch_array(struct php_sqlite_result *res, int mode, zend_bool decode_binary, int move_next, zval *return_value TSRMLS_DC) +{ + int j, buffered = res->buffered; + const char **rowdata, **colnames; + + /* check range of the row */ + if (res->curr_row >= res->nrows) { + /* no more */ + RETURN_FALSE; + } + colnames = (const char**)res->col_names; + if (res->buffered) { + rowdata = (const char**)&res->table[res->curr_row * res->ncolumns]; + } else { + rowdata = (const char**)res->table; + } + + /* now populate the result */ + array_init(return_value); + + for (j = 0; j < res->ncolumns; j++) { + zval *decoded; + MAKE_STD_ZVAL(decoded); + + if (rowdata[j] == NULL) { + ZVAL_NULL(decoded); + } else if (decode_binary && rowdata[j][0] == '\x01') { + Z_STRVAL_P(decoded) = emalloc(strlen(rowdata[j])); + Z_STRLEN_P(decoded) = sqlite_decode_binary(rowdata[j]+1, Z_STRVAL_P(decoded)); + Z_STRVAL_P(decoded)[Z_STRLEN_P(decoded)] = '\0'; + Z_TYPE_P(decoded) = IS_STRING; + if (!buffered) { + efree((char*)rowdata[j]); + rowdata[j] = NULL; + } + } else { + ZVAL_STRING(decoded, (char*)rowdata[j], buffered); + if (!buffered) { + rowdata[j] = NULL; + } + } + + if (mode & PHPSQLITE_NUM) { + if (mode & PHPSQLITE_ASSOC) { + add_index_zval(return_value, j, decoded); + ZVAL_ADDREF(decoded); + add_assoc_zval(return_value, (char*)colnames[j], decoded); + } else { + add_next_index_zval(return_value, decoded); + } + } else { + add_assoc_zval(return_value, (char*)colnames[j], decoded); + } + } + + if (move_next) { + if (!res->buffered) { + /* non buffered: fetch next row */ + php_sqlite_fetch(res TSRMLS_CC); + } + /* advance the row pointer */ + res->curr_row++; + } +} +/* }}} */ + +/* {{{ php_sqlite_fetch_column */ +static void php_sqlite_fetch_column(struct php_sqlite_result *res, zval *which, zend_bool decode_binary, zval *return_value TSRMLS_DC) +{ + int j; + const char **rowdata, **colnames; + + /* check range of the row */ + if (res->curr_row >= res->nrows) { + /* no more */ + RETURN_FALSE; + } + colnames = (const char**)res->col_names; + + if (Z_TYPE_P(which) == IS_LONG) { + j = Z_LVAL_P(which); + } else { + convert_to_string_ex(&which); + for (j = 0; j < res->ncolumns; j++) { + if (!strcasecmp((char*)colnames[j], Z_STRVAL_P(which))) { + break; + } + } + } + if (j < 0 || j >= res->ncolumns) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such column %d", j); + RETURN_FALSE; + } + + if (res->buffered) { + rowdata = (const char**)&res->table[res->curr_row * res->ncolumns]; + } else { + rowdata = (const char**)res->table; + } + + if (rowdata[j] == NULL) { + RETURN_NULL(); + } else if (decode_binary && rowdata[j][0] == '\x01') { + int l = strlen(rowdata[j]); + char *decoded = emalloc(l); + l = sqlite_decode_binary(rowdata[j]+1, decoded); + decoded[l] = '\0'; + RETVAL_STRINGL(decoded, l, 0); + if (!res->buffered) { + efree((char*)rowdata[j]); + rowdata[j] = NULL; + } + } else { + RETVAL_STRING((char*)rowdata[j], res->buffered); + if (!res->buffered) { + rowdata[j] = NULL; + } + } +} +/* }}} */ + +/* {{{ proto array sqlite_fetch_all(resource result [, int result_type, bool decode_binary]) + Fetches all rows from a result set as an array */ +PHP_FUNCTION(sqlite_fetch_all) +{ + zval *zres, *ent; + int mode = PHPSQLITE_BOTH; + zend_bool decode_binary = 1; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + if (ZEND_NUM_ARGS() < 2) { + mode = res->mode; + } + + if (res->curr_row >= res->nrows && res->nrows) { + if (!res->buffered) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "One or more rowsets were already returned"); + } else { + res->curr_row = 0; + } + } + + array_init(return_value); + + while (res->curr_row < res->nrows) { + MAKE_STD_ZVAL(ent); + php_sqlite_fetch_array(res, mode, decode_binary, 1, ent TSRMLS_CC); + add_next_index_zval(return_value, ent); + } +} +/* }}} */ + +/* {{{ proto array sqlite_fetch_array(resource result [, int result_type, bool decode_binary]) + Fetches the next row from a result set as an array */ +PHP_FUNCTION(sqlite_fetch_array) +{ + zval *zres; + int mode = PHPSQLITE_BOTH; + zend_bool decode_binary = 1; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + if (ZEND_NUM_ARGS() < 2) { + mode = res->mode; + } + + php_sqlite_fetch_array(res, mode, decode_binary, 1, return_value TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto array sqlite_array_query(resource db, string query [ , int result_type, bool decode_binary ]) + Executes a query against a given database and returns an array */ +PHP_FUNCTION(sqlite_array_query) +{ + zval *zdb, *ent; + struct php_sqlite_db *db; + struct php_sqlite_result *rres; + char *sql; + long sql_len; + int mode = PHPSQLITE_BOTH; + char *errtext = NULL; + zend_bool decode_binary = 1; + + if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode, &decode_binary) && + FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode, &decode_binary)) { + return; + } + DB_FROM_ZVAL(db, &zdb); + + /* avoid doing work if we can */ + if (!return_value_used) { + db->last_err_code = sqlite_exec(db->db, sql, NULL, NULL, &errtext); + + if (db->last_err_code != SQLITE_OK) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); + sqlite_freemem(errtext); + } + return; + } + + rres = (struct php_sqlite_result *)emalloc(sizeof(*rres)); + sqlite_query(db, sql, sql_len, mode, 0, NULL, rres TSRMLS_CC); + + array_init(return_value); + + while (rres->curr_row < rres->nrows) { + MAKE_STD_ZVAL(ent); + php_sqlite_fetch_array(rres, mode, decode_binary, 1, ent TSRMLS_CC); + add_next_index_zval(return_value, ent); + } + real_result_dtor(rres TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto string sqlite_fetch_array(resource result [, bool decode_binary]) + Fetches first column of a result set as a string */ +PHP_FUNCTION(sqlite_fetch_string) +{ + zval *zres; + zend_bool decode_binary = 1; + struct php_sqlite_result *res; + char *decoded = NULL; + int decoded_len; + const char **rowdata; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|b", &zres, &decode_binary)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + /* check if there are any more rows on the cursor */ + if (res->curr_row >= res->nrows) { + RETURN_FALSE; + } + + if (res->buffered) { + rowdata = (const char**)&res->table[res->curr_row * res->ncolumns]; + } else { + rowdata = (const char**)res->table; + } + + if (decode_binary && rowdata[0] != NULL && rowdata[0][0] == '\x01') { + decoded = emalloc(strlen(rowdata[0])); + decoded_len = sqlite_decode_binary(rowdata[0]+1, decoded); + if (!res->buffered) { + efree((char*)rowdata[0]); + rowdata[0] = NULL; + } + } else { + if (rowdata[0]) { + decoded_len = strlen((char*)rowdata[0]); + if (res->buffered) { + decoded = estrndup((char*)rowdata[0], decoded_len); + } else { + decoded = (char*)rowdata[0]; + rowdata[0] = NULL; + } + } else { + decoded_len = 0; + decoded = NULL; + } + } + + if (!res->buffered) { + /* non buffered: fetch next row */ + php_sqlite_fetch(res TSRMLS_CC); + } + /* advance the row pointer */ + res->curr_row++; + + if (decoded == NULL) { + RETURN_NULL(); + } else { + RETURN_STRINGL(decoded, decoded_len, 0); + } +} +/* }}} */ + +/* {{{ proto array sqlite_fetch_array(resource result [, int result_type, bool decode_binary]) + Fetches the current row from a result set as an array */ +PHP_FUNCTION(sqlite_current) +{ + zval *zres; + int mode = PHPSQLITE_BOTH; + zend_bool decode_binary = 1; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + if (ZEND_NUM_ARGS() < 2) { + mode = res->mode; + } + + php_sqlite_fetch_array(res, mode, decode_binary, 0, return_value TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto mixed sqlite_column(resource result, mixed index_or_name [, bool decode_binary]) + Fetches a column from the current row of a result set */ +PHP_FUNCTION(sqlite_column) +{ + zval *zres; + zval *which; + zend_bool decode_binary = 1; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zres, &which, &decode_binary)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + php_sqlite_fetch_column(res, which, decode_binary, return_value TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto string sqlite_libversion() + Returns the version of the linked SQLite library */ +PHP_FUNCTION(sqlite_libversion) +{ + if (ZEND_NUM_ARGS() != 0) { + WRONG_PARAM_COUNT; + } + RETURN_STRING((char*)sqlite_libversion(), 1); +} +/* }}} */ + +/* {{{ proto string sqlite_libencoding() + Returns the encoding (iso8859 or UTF-8) of the linked SQLite library */ +PHP_FUNCTION(sqlite_libencoding) +{ + if (ZEND_NUM_ARGS() != 0) { + WRONG_PARAM_COUNT; + } + RETURN_STRING((char*)sqlite_libencoding(), 1); +} +/* }}} */ + +/* {{{ proto int sqlite_changes(resource db) + Returns the number of rows that were changed by the most recent SQL statement */ +PHP_FUNCTION(sqlite_changes) +{ + zval *zdb; + struct php_sqlite_db *db; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + RETURN_LONG(sqlite_changes(db->db)); +} +/* }}} */ + +/* {{{ proto int sqlite_last_insert_rowid(resource db) + Returns the rowid of the most recently inserted row */ +PHP_FUNCTION(sqlite_last_insert_rowid) +{ + zval *zdb; + struct php_sqlite_db *db; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + RETURN_LONG(sqlite_last_insert_rowid(db->db)); +} +/* }}} */ + +/* {{{ proto int sqlite_num_rows(resource result) + Returns the number of rows in a result set */ +PHP_FUNCTION(sqlite_num_rows) +{ + zval *zres; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) { + return; + } + + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + if (res->buffered) { + RETURN_LONG(res->nrows); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Row count is not available for unbuffered queries"); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool sqlite_has_more(resource result) + Returns whether or not more rows are available */ +PHP_FUNCTION(sqlite_has_more) +{ + zval *zres; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + RETURN_BOOL(res->nrows && res->curr_row < res->nrows); /* curr_row may be -1 */ +} +/* }}} */ + +/* {{{ proto int sqlite_num_fields(resource result) + Returns the number of fields in a result set */ +PHP_FUNCTION(sqlite_num_fields) +{ + zval *zres; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) { + return; + } + + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + RETURN_LONG(res->ncolumns); +} +/* }}} */ + +/* {{{ proto string sqlite_field_name(resource result, int field) + Returns the name of a particular field */ +PHP_FUNCTION(sqlite_field_name) +{ + zval *zres; + struct php_sqlite_result *res; + int field; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &field)) { + return; + } + + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + if (field < 0 || field >= res->ncolumns) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "field %d out of range", field); + RETURN_FALSE; + } + + RETURN_STRING(res->col_names[field], 1); +} +/* }}} */ + +/* {{{ proto bool sqlite_seek(resource result, int row) + Seek to a particular row number */ +PHP_FUNCTION(sqlite_seek) +{ + zval *zres; + struct php_sqlite_result *res; + int row; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &row)) { + return; + } + + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + if (!res->buffered) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot seek an unbuffered result set"); + RETURN_FALSE; + } + + if (row < 1 || row >= res->nrows) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "row %d out of range", row); + RETURN_FALSE; + } + + res->curr_row = row; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool sqlite_rewind(resource result) + Seek to first row number */ +PHP_FUNCTION(sqlite_rewind) +{ + zval *zres; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + if (!res->buffered) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot seek an unbuffered result set"); + RETURN_FALSE; + } + + if (!res->nrows) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "no rows received"); + RETURN_FALSE; + } + + res->curr_row = 0; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool sqlite_next(resource result) + Seek to next row number */ +PHP_FUNCTION(sqlite_next) +{ + zval *zres; + struct php_sqlite_result *res; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) { + return; + } + ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); + + if (!res->buffered && res->vm) { + php_sqlite_fetch(res TSRMLS_CC); + } + + if (res->curr_row >= res->nrows) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "no more rows available"); + RETURN_FALSE; + } + + res->curr_row++; + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string sqlite_escape_string(string item) + Escapes a string for use as a query parameter */ +PHP_FUNCTION(sqlite_escape_string) +{ + char *string = NULL; + long stringlen; + char *ret; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &stringlen)) { + return; + } + + if (stringlen && (string[0] == '\x01' || memchr(string, '\0', stringlen) != NULL)) { + /* binary string */ + int enclen; + + ret = emalloc( 1 + ((256 * stringlen + 1262) / 253) ); + ret[0] = '\x01'; + enclen = sqlite_encode_binary((const unsigned char*)string, stringlen, ret+1); + RETVAL_STRINGL(ret, enclen+1, 0); + + } else { + ret = sqlite_mprintf("%q", string); + if (ret) { + RETVAL_STRING(ret, 1); + sqlite_freemem(ret); + } + } +} +/* }}} */ + +/* {{{ proto int sqlite_last_error(resource db) + Returns the error code of the last error for a database */ +PHP_FUNCTION(sqlite_last_error) +{ + zval *zdb; + struct php_sqlite_db *db; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) { + return; + } + + DB_FROM_ZVAL(db, &zdb); + + RETURN_LONG(db->last_err_code); +} +/* }}} */ + +/* {{{ proto string sqlite_error_string(int error_code) + Returns the textual description of an error code */ +PHP_FUNCTION(sqlite_error_string) +{ + long code; + const char *msg; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { + return; + } + + msg = sqlite_error_string(code); + + if (msg) { + RETURN_STRING((char*)msg, 1); + } else { + RETURN_NULL(); + } +} +/* }}} */ + +/* manages duplicate registrations of a particular function, and + * also handles the case where the db is using a persistent connection */ +enum callback_prep_t { DO_REG, SKIP_REG, ERR }; + +static enum callback_prep_t prep_callback_struct(struct php_sqlite_db *db, int is_agg, + char *funcname, + zval *step, zval *fini, struct php_sqlite_agg_functions **funcs) +{ + struct php_sqlite_agg_functions *alloc_funcs, func_tmp; + char *hashkey; + int hashkeylen; + enum callback_prep_t ret; + + hashkeylen = spprintf(&hashkey, 0, "%s-%s", is_agg ? "agg" : "reg", funcname); + + /* is it already registered ? */ + if (SUCCESS == zend_hash_find(&db->callbacks, hashkey, hashkeylen+1, (void*)&alloc_funcs)) { + /* override the previous definition */ + + if (alloc_funcs->is_valid) { + /* release these */ + + if (alloc_funcs->step) { + zval_ptr_dtor(&alloc_funcs->step); + alloc_funcs->step = NULL; + } + + if (alloc_funcs->fini) { + zval_ptr_dtor(&alloc_funcs->fini); + alloc_funcs->fini = NULL; + } + } + + ret = SKIP_REG; + } else { + /* add a new one */ + func_tmp.db = db; + + ret = SUCCESS == zend_hash_update(&db->callbacks, hashkey, hashkeylen+1, + (void*)&func_tmp, sizeof(func_tmp), (void**)&alloc_funcs) ? DO_REG : ERR; + } + + efree(hashkey); + + MAKE_STD_ZVAL(alloc_funcs->step); + *(alloc_funcs->step) = *step; + zval_copy_ctor(alloc_funcs->step); + + if (is_agg) { + MAKE_STD_ZVAL(alloc_funcs->fini); + *(alloc_funcs->fini) = *fini; + zval_copy_ctor(alloc_funcs->fini); + } else { + alloc_funcs->fini = NULL; + } + alloc_funcs->is_valid = 1; + *funcs = alloc_funcs; + + return ret; +} + + +/* {{{ proto bool sqlite_create_aggregate(resource db, string funcname, mixed step_func, mixed finalize_func[, long num_args]) + Registers an aggregated function for queries*/ +PHP_FUNCTION(sqlite_create_aggregate) +{ + char *funcname = NULL; + long funcname_len; + zval *zstep, *zfinal, *zdb; + struct php_sqlite_db *db; + struct php_sqlite_agg_functions *funcs; + char *callable = NULL; + long num_args = -1; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rszz|l", &zdb, &funcname, &funcname_len, &zstep, &zfinal, &num_args)) { + return; + } + DB_FROM_ZVAL(db, &zdb); + + if (!zend_is_callable(zstep, 0, &callable)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "step function `%s' is not callable", callable); + efree(callable); + return; + } + efree(callable); + + if (!zend_is_callable(zfinal, 0, &callable)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "finalize function `%s' is not callable", callable); + efree(callable); + return; + } + efree(callable); + + if (prep_callback_struct(db, 1, funcname, zstep, zfinal, &funcs) == DO_REG) { + sqlite_create_aggregate(db->db, funcname, num_args, + php_sqlite_agg_step_function_callback, + php_sqlite_agg_fini_function_callback, funcs); + } + + +} +/* }}} */ + +/* {{{ proto bool sqlite_create_function(resource db, string funcname, mixed callback[, long num_args]) + Registers a "regular" function for queries */ +PHP_FUNCTION(sqlite_create_function) +{ + char *funcname = NULL; + long funcname_len; + zval *zcall, *zdb; + struct php_sqlite_db *db; + struct php_sqlite_agg_functions *funcs; + char *callable = NULL; + long num_args = -1; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsz|l", &zdb, &funcname, &funcname_len, &zcall, &num_args)) { + return; + } + DB_FROM_ZVAL(db, &zdb); + + if (!zend_is_callable(zcall, 0, &callable)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "function `%s' is not callable", callable); + efree(callable); + return; + } + efree(callable); + + if (prep_callback_struct(db, 0, funcname, zcall, NULL, &funcs) == DO_REG) { + sqlite_create_function(db->db, funcname, num_args, php_sqlite_function_callback, funcs); + } +} +/* }}} */ + +/* {{{ proto string sqlite_udf_encode_binary(string data) + Apply binary encoding (if required) to a string to return from an UDF */ +PHP_FUNCTION(sqlite_udf_encode_binary) +{ + char *data = NULL; + long datalen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &data, &datalen)) { + return; + } + + if (data == NULL) { + RETURN_NULL(); + } + if (datalen && (data[0] == '\x01' || memchr(data, '\0', datalen) != NULL)) { + /* binary string */ + int enclen; + char *ret; + + ret = emalloc( 1 + ((256 * datalen + 1262) / 253) ); + ret[0] = '\x01'; + enclen = sqlite_encode_binary((const unsigned char*)data, datalen, ret+1); + RETVAL_STRINGL(ret, enclen+1, 0); + } else { + RETVAL_STRINGL(data, datalen, 1); + } +} +/* }}} */ + +/* {{{ proto string sqlite_udf_decode_binary(string data) + Decode binary encoding on a string parameter passed to an UDF */ +PHP_FUNCTION(sqlite_udf_decode_binary) +{ + char *data = NULL; + long datalen; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &data, &datalen)) { + return; + } + + if (data == NULL) { + RETURN_NULL(); + } + if (datalen && data[0] == '\x01') { + /* encoded string */ + int enclen; + char *ret; + + ret = emalloc(datalen); + enclen = sqlite_decode_binary((const unsigned char*)data+1, ret); + ret[enclen] = '\0'; + RETVAL_STRINGL(ret, enclen, 0); + } else { + RETVAL_STRINGL(data, datalen, 1); + } +} +/* }}} */ + + +/* + * 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/sqlite/tests/sqlite_017.phpt b/ext/sqlite/tests/sqlite_017.phpt new file mode 100755 index 0000000000..9058622f99 --- /dev/null +++ b/ext/sqlite/tests/sqlite_017.phpt @@ -0,0 +1,33 @@ +--TEST-- +sqlite: UDF binary handling functions +--SKIPIF-- + +--FILE-- + +--EXPECT-- +68656c6c6f207468657265 +0101736768721f6760721f601fff1f626760711f686d1f7367641f6c6863636b64 +0102ff1e726667711e665f711e5f6c1e2e762e2f1e5f721e7266631e71725f7072 +7468697320686173200120696e20746865206d6964646c65 +OK! diff --git a/pear/packages/Mail-1.1.0.tar b/pear/packages/Mail-1.1.0.tar new file mode 100644 index 0000000000..081feb836f Binary files /dev/null and b/pear/packages/Mail-1.1.0.tar differ diff --git a/pear/packages/Net_SMTP-1.2.3.tar b/pear/packages/Net_SMTP-1.2.3.tar new file mode 100644 index 0000000000..3cdea14427 Binary files /dev/null and b/pear/packages/Net_SMTP-1.2.3.tar differ