/* +----------------------------------------------------------------------+ | 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 "0.9-c" #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 */