php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #31342 SQLite OO interface with Exceptions (patch included)
Submitted: 2004-12-30 05:10 UTC Modified: 2010-05-03 15:59 UTC
From: hugo dot pl at gmail dot com Assigned: helly (profile)
Status: Not a bug Package: *General Issues
PHP Version: 5.1.0-dev OS: *
Private report: No CVE-ID: None
 [2004-12-30 05:10 UTC] hugo dot pl at gmail dot com
Description:
------------
Hi, (english is not my natural language)

I'm sending a patch to SQLite module (is here the right place to do it?).

It's add Exceptions to OO interface of SQLite module when a error occur executing a query.

Affected methods:
query()
singleQuery()
unbufferedQuery()
arrayQuery()

If any error occur when executing the query a exception of type SQLiteException is throw. The message of the exeception is the same returned from SQLite, library (description of the sql error).

I made this patch because I'm doing a frontend to SQLite and I need descriptive message about errors on SQL queries and I can't find how to catch the query syntax error messages trowed by SQLite library (actually this messages are only show in warning messages).

Ex.:

If a do the query:
$db->query("SELECT FROM anytable");

I cant catch the SQLite message saying that exists a syntax error near "FROM anytable"... with tha actual interface I can get only the generic message "SQL logic error or missing database" returned by sqlite_error_string(sqlite_last_error()).


I think that the function sqlite_last_error() should return a error string like mysql_error(), not a error code, because the error string returned by sqlite_error_string is not too usefull,

I'm send the fle ext/sqlite/sqlite.c with modification based on PHP-5.0.2, if anyone accept this patch, nice, will be my first patch, otherwise I wish at least hear another ideia to solve the "problem".

Reproduce code:
---------------
<pre>
<?php

// Patch test-case

// our bugged query
$query = 'SELASDF sdaF SDaf ';

// Test OO interface (all *query methods must trow a exception)
$db = new SQLiteDatabase("teste.db");
$aMethods = array('query', 'singleQuery', 'unbufferedQuery', 'arrayQuery');
$n = count($aMethods);

echo "Testing OO interface...\n";
foreach ($aMethods as $method)
{
	try
	{
		eval('@$db->'.$method.'($query);');
		echo '$db::', $method, "(); // fail!\n";
	}
	catch (SQLiteException $e)
	{
		echo '$db::', $method, "(); // Ok!\n";
	}
	try
	{
		eval('$r = @$db->'.$method.'($query);');
		echo '$r = $db::', $method, "(); // fail!\n";
	}
	catch (SQLiteException $e)
	{
		echo '$r = $db::', $method, "(); // Ok!\n";
	}
}
echo "All done.... :)\n";

?>

Expected result:
----------------
Testing OO interface...
$db::query(); // Ok!
$r = $db::query(); // Ok!
$db::singleQuery(); // Ok!
$r = $db::singleQuery(); // Ok!
$db::unbufferedQuery(); // Ok!
$r = $db::unbufferedQuery(); // Ok!
$db::arrayQuery(); // Ok!
$r = $db::arrayQuery(); // Ok!
All done.... :)


Actual result:
--------------
Testing OO interface...
$db::query(); // fail!
$r = $db::query(); // fail!
$db::singleQuery(); // fail!
$r = $db::singleQuery(); // fail!
$db::unbufferedQuery(); // fail!
$r = $db::unbufferedQuery(); // fail!
$db::arrayQuery(); // fail!
$r = $db::arrayQuery(); // fail!
All done.... :)


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-12-30 05:18 UTC] hugo dot pl at gmail dot com
Ops, I forgot the patch!
It can be found for download at http://sqlite.sourceforge.net/temp/sqlite.c

or above:



/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2004 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 <wez@thebrainroom.com>                          |
   |          Tal Peer <tal@php.net>                                      |
   |          Marcus Boerger <helly@php.net>                              |
   +----------------------------------------------------------------------+

   $Id: sqlite.c,v 1.146.2.2 2004/08/02 22:43:42 iliaa Exp $ 
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define PHP_SQLITE_MODULE_VERSION	"2.0-dev"

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/session/php_session.h"
#include "php_sqlite.h"

#if HAVE_TIME_H
# include <time.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sqlite.h>

#include "zend_exceptions.h"

#ifndef safe_emalloc
# define safe_emalloc(a,b,c) emalloc((a)*(b)+(c))
#endif

ZEND_DECLARE_MODULE_GLOBALS(sqlite)

#if HAVE_PHP_SESSION
extern ps_module ps_mod_sqlite;
#define ps_sqlite_ptr &ps_mod_sqlite
#endif

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);

#define php_sqlite_encode_binary(in, n, out) sqlite_encode_binary((const unsigned char *)in, n, (unsigned char *)out)
#define php_sqlite_decode_binary(in, out)    sqlite_decode_binary((const unsigned char *)in, (unsigned char *)out)

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)

#define DB_FROM_OBJECT(db, object) \
	{ \
		sqlite_object *obj = (sqlite_object*) zend_object_store_get_object(object TSRMLS_CC); \
		db = obj->u.db; \
		if (!db) { \
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "The database wasn't opened"); \
			RETURN_NULL(); \
		} \
	}

#define RES_FROM_OBJECT(res, object) \
	{ \
		sqlite_object *obj = (sqlite_object*) zend_object_store_get_object(object TSRMLS_CC); \
		res = obj->u.res; \
		if (!res) { \
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "No result set available"); \
			RETURN_NULL(); \
		} \
	}

#define SQLITE_THROW(message) \
	PG(suppress_errors) = 0; \
	EG(exception) = zend_throw_exception(sqlite_ce_exception, message, 0 TSRMLS_CC);

#define PHP_SQLITE_EMPTY_QUERY \
	if (!sql_len) { \
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute empty query."); \
		RETURN_FALSE; \
	}

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;
	int mode;
	char **table;
};

struct php_sqlite_db {
	sqlite *db;
	int last_err_code;
	zend_bool is_persistent;
	long rsrc_id;

	HashTable callbacks;
};

struct php_sqlite_agg_functions {
	struct php_sqlite_db *db;
	int is_valid;
	zval *step;
	zval *fini;
};

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);
static int php_sqlite_fetch(struct php_sqlite_result *rres TSRMLS_DC);

enum { PHPSQLITE_ASSOC = 1, PHPSQLITE_NUM = 2, PHPSQLITE_BOTH = PHPSQLITE_ASSOC|PHPSQLITE_NUM };

function_entry sqlite_functions[] = {
	PHP_FE(sqlite_open, third_arg_force_ref)
	PHP_FE(sqlite_popen, third_arg_force_ref)
	PHP_FE(sqlite_close, NULL)
	PHP_FE(sqlite_query, NULL)
	PHP_FE(sqlite_exec, NULL)
	PHP_FE(sqlite_array_query, NULL)
	PHP_FE(sqlite_single_query, NULL)
	PHP_FE(sqlite_fetch_array, NULL)
	PHP_FE(sqlite_fetch_object, NULL)
	PHP_FE(sqlite_fetch_single, NULL)
	PHP_FALIAS(sqlite_fetch_string, sqlite_fetch_single, 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_prev, NULL)
	PHP_FE(sqlite_valid, NULL)
	PHP_FALIAS(sqlite_has_more, sqlite_valid, NULL)
	PHP_FE(sqlite_has_prev, 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_factory, third_arg_force_ref)
	PHP_FE(sqlite_udf_encode_binary, NULL)
	PHP_FE(sqlite_udf_decode_binary, NULL)
	PHP_FE(sqlite_fetch_column_types, NULL)
	{NULL, NULL, NULL}
};

function_entry sqlite_funcs_db[] = {
	PHP_ME_MAPPING(__construct, sqlite_open, NULL)
/*	PHP_ME_MAPPING(close, sqlite_close, NULL)*/
	PHP_ME_MAPPING(query, sqlite_query, NULL)
	PHP_ME_MAPPING(queryExec, sqlite_exec, NULL)
	PHP_ME_MAPPING(arrayQuery, sqlite_array_query, NULL)
	PHP_ME_MAPPING(singleQuery, sqlite_single_query, NULL)
	PHP_ME_MAPPING(unbufferedQuery, sqlite_unbuffered_query, NULL)
	PHP_ME_MAPPING(lastInsertRowid, sqlite_last_insert_rowid, NULL)
	PHP_ME_MAPPING(changes, sqlite_changes, NULL)
	PHP_ME_MAPPING(createAggregate, sqlite_create_aggregate, NULL)
	PHP_ME_MAPPING(createFunction, sqlite_create_function, NULL)
	PHP_ME_MAPPING(busyTimeout, sqlite_busy_timeout, NULL)
	PHP_ME_MAPPING(lastError, sqlite_last_error, NULL)
	PHP_ME_MAPPING(fetchColumnTypes, sqlite_fetch_column_types, NULL)
/*	PHP_ME_MAPPING(error_string, sqlite_error_string, NULL) static */
/*	PHP_ME_MAPPING(escape_string, sqlite_escape_string, NULL) static */
	{NULL, NULL, NULL}
};

function_entry sqlite_funcs_query[] = {
	PHP_ME_MAPPING(fetch, sqlite_fetch_array, NULL)
	PHP_ME_MAPPING(fetchObject, sqlite_fetch_object, NULL)
	PHP_ME_MAPPING(fetchSingle, sqlite_fetch_single, NULL)
	PHP_ME_MAPPING(fetchAll, sqlite_fetch_all, NULL)
	PHP_ME_MAPPING(column, sqlite_column, NULL)
	PHP_ME_MAPPING(numFields, sqlite_num_fields, NULL)
	PHP_ME_MAPPING(fieldName, sqlite_field_name, NULL)
	/* spl_forward */
	PHP_ME_MAPPING(current, sqlite_current, NULL)
	PHP_ME_MAPPING(next, sqlite_next, NULL)
	PHP_ME_MAPPING(valid, sqlite_valid, NULL)
	/* spl_sequence */
	PHP_ME_MAPPING(rewind, sqlite_rewind, NULL)
	/* additional */
	PHP_ME_MAPPING(prev, sqlite_prev, NULL)
	PHP_ME_MAPPING(hasPrev, sqlite_has_prev, NULL)
	PHP_ME_MAPPING(numRows, sqlite_num_rows, NULL)
	PHP_ME_MAPPING(seek, sqlite_seek, NULL)
	{NULL, NULL, NULL}
};

function_entry sqlite_funcs_ub_query[] = {
	PHP_ME_MAPPING(fetch, sqlite_fetch_array, NULL)
	PHP_ME_MAPPING(fetchObject, sqlite_fetch_object, NULL)
	PHP_ME_MAPPING(fetchSingle, sqlite_fetch_single, NULL)
	PHP_ME_MAPPING(fetchAll, sqlite_fetch_all, NULL)
	PHP_ME_MAPPING(column, sqlite_column, NULL)
	PHP_ME_MAPPING(numFields, sqlite_num_fields, NULL)
	PHP_ME_MAPPING(fieldName, sqlite_field_name, NULL)
	/* spl_forward */
	PHP_ME_MAPPING(current, sqlite_current, NULL)
	PHP_ME_MAPPING(next, sqlite_next, NULL)
	PHP_ME_MAPPING(valid, sqlite_valid, NULL)
	{NULL, NULL, NULL}
};

function_entry sqlite_funcs_exception[] = {
	{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),
	PHP_MSHUTDOWN(sqlite),
	PHP_RINIT(sqlite),
	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)
# ifdef PHP_WIN32
# include "zend_arg_defs.c"
# endif
#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);
	}

	if (res->db) {
		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 = (struct php_sqlite_db*)rsrc->ptr;

	if (Z_TYPE_P(rsrc) != le_sqlite_pdb) {
		return 0;
	}

	/* 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);

	db->rsrc_id = FAILURE;

	/* don't leave pending commits hanging around */
	sqlite_exec(db->db, "ROLLBACK", NULL, NULL, NULL);
	
	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], 1);

	if (!zend_make_callable(&funcname, &callable TSRMLS_CC)) {
		spprintf(&errbuf, 0, "function `%s' is not a function name", callable);
		sqlite_set_result_error(func, errbuf, -1);
		efree(errbuf);
		efree(callable);
		zval_dtor(&funcname);
		return;
	}
	
	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);

	zval_dtor(&funcname);

	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 {
		char *errbuf;
		spprintf(&errbuf, 0, "call_user_function_ex failed for function %s()", callable);
		sqlite_set_result_error(func, errbuf, -1);
		efree(errbuf);
	}

	efree(callable);

	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:
			if (strncmp(arg4, ":memory:", sizeof(":memory:") - 1)) {
				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:
			if (strncmp(arg3, ":memory:", sizeof(":memory:") - 1)) {
				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;
	}
}
/* }}} */

/* {{{ OO init/structure stuff */
#define REGISTER_SQLITE_CLASS(name, c_name, parent) \
	{ \
		zend_class_entry ce; \
		INIT_CLASS_ENTRY(ce, "SQLite" # name, sqlite_funcs_ ## c_name); \
		ce.create_object = sqlite_object_new_ ## c_name; \
		sqlite_ce_ ## c_name = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \
		memcpy(&sqlite_object_handlers_ ## c_name, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
		sqlite_object_handlers_ ## c_name.clone_obj = NULL; \
		sqlite_ce_ ## c_name->ce_flags |= ZEND_ACC_FINAL_CLASS; \
	}

zend_class_entry *sqlite_ce_db, *sqlite_ce_exception;
zend_class_entry *sqlite_ce_query, *sqlite_ce_ub_query;

static zend_object_handlers sqlite_object_handlers_db;
static zend_object_handlers sqlite_object_handlers_query;
static zend_object_handlers sqlite_object_handlers_ub_query;
static zend_object_handlers sqlite_object_handlers_exception;

typedef enum {
	is_db,
	is_result
} sqlite_obj_type;

typedef struct _sqlite_object {
	zend_object       std;
	sqlite_obj_type   type;
	union {
		struct php_sqlite_db     *db;
		struct php_sqlite_result *res;
		void *ptr;
	} u;
} sqlite_object;

static int sqlite_free_persistent(list_entry *le, void *ptr TSRMLS_DC)
{
	return le->ptr == ptr ? ZEND_HASH_APPLY_REMOVE : ZEND_HASH_APPLY_KEEP;
}

static void sqlite_object_free_storage(void *object TSRMLS_DC)
{
	sqlite_object *intern = (sqlite_object *)object;

	zend_hash_destroy(intern->std.properties);
	FREE_HASHTABLE(intern->std.properties);

	if (intern->u.ptr) {
		if (intern->type == is_db) {
			if (intern->u.db->rsrc_id) {
				zend_list_delete(intern->u.db->rsrc_id);
				zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) sqlite_free_persistent, &intern->u.ptr TSRMLS_CC);
			}
		} else {
			real_result_dtor(intern->u.res TSRMLS_CC);
		}
	}

	efree(object);
}

static void sqlite_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)
{
	sqlite_object *intern;
	zval *tmp;

	intern = emalloc(sizeof(sqlite_object));
	memset(intern, 0, sizeof(sqlite_object));
	intern->std.ce = class_type;

	ALLOC_HASHTABLE(intern->std.properties);
	zend_hash_init(intern->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
	zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));

	retval->handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) sqlite_object_free_storage, NULL TSRMLS_CC);
	retval->handlers = handlers;
}

static zend_object_value sqlite_object_new_db(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	
	sqlite_object_new(class_type, &sqlite_object_handlers_db, &retval TSRMLS_CC);
	return retval;
}

static zend_object_value sqlite_object_new_query(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	
	sqlite_object_new(class_type, &sqlite_object_handlers_query, &retval TSRMLS_CC);
	return retval;
}

static zend_object_value sqlite_object_new_ub_query(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	
	sqlite_object_new(class_type, &sqlite_object_handlers_ub_query, &retval TSRMLS_CC);
	return retval;
}

static zend_object_value sqlite_object_new_exception(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	
	sqlite_object_new(class_type, &sqlite_object_handlers_exception, &retval TSRMLS_CC);
	return retval;
}

#define SQLITE_REGISTER_OBJECT(_type, _object, _ptr) \
	{ \
		sqlite_object *obj; \
		obj = (sqlite_object*)zend_object_store_get_object(_object TSRMLS_CC); \
		obj->type = is_ ## _type; \
		obj->u._type = _ptr; \
	}

static zend_class_entry *sqlite_get_ce_query(zval *object TSRMLS_DC)
{
	return sqlite_ce_query;
}

static zend_class_entry *sqlite_get_ce_ub_query(zval *object TSRMLS_DC)
{
	return sqlite_ce_ub_query;
}

static zval * sqlite_instanciate(zend_class_entry *pce, zval *object TSRMLS_DC)
{
	if (!object) {
		ALLOC_ZVAL(object);
	}
	Z_TYPE_P(object) = IS_OBJECT;
	object_init_ex(object, pce);
	object->refcount = 1;
	object->is_ref = 1;
	return object;
}

typedef struct _sqlite_object_iterator {
	zend_object_iterator     it;
	struct php_sqlite_result *res;
	zval *value;
} sqlite_object_iterator;

void sqlite_iterator_dtor(zend_object_iterator *iter TSRMLS_DC)
{
	zval *object = (zval*)((sqlite_object_iterator*)iter)->it.data;

	if (((sqlite_object_iterator*)iter)->value) {
		zval_ptr_dtor(&((sqlite_object_iterator*)iter)->value);
		((sqlite_object_iterator*)iter)->value = NULL;
	}
	zval_ptr_dtor(&object);
	efree(iter);
}

void sqlite_iterator_rewind(zend_object_iterator *iter TSRMLS_DC)
{
	struct php_sqlite_result *res = ((sqlite_object_iterator*)iter)->res;

	if (((sqlite_object_iterator*)iter)->value) {
		zval_ptr_dtor(&((sqlite_object_iterator*)iter)->value);
		((sqlite_object_iterator*)iter)->value = NULL;
	}
	if (res) {
		res->curr_row = 0;
	}
}

int sqlite_iterator_valid(zend_object_iterator *iter TSRMLS_DC)
{
	struct php_sqlite_result *res = ((sqlite_object_iterator*)iter)->res;

	if (res && res->curr_row < res->nrows && res->nrows) { /* curr_row may be -1 */
		return SUCCESS;
	} else {
		return FAILURE;
	}
}

void sqlite_iterator_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
{
	struct php_sqlite_result *res = ((sqlite_object_iterator*)iter)->res;

	*data = &((sqlite_object_iterator*)iter)->value;
	if (res && !**data) {
		MAKE_STD_ZVAL(**data);
		php_sqlite_fetch_array(res, res->mode, 1, 0, **data TSRMLS_CC);
	}
	
}

int sqlite_iterator_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
{
	struct php_sqlite_result *res = ((sqlite_object_iterator*)iter)->res;

	*str_key = NULL;
	*str_key_len = 0;
	*int_key = res ? res->curr_row : 0;
	return HASH_KEY_IS_LONG;
}

void sqlite_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC)
{
	struct php_sqlite_result *res = ((sqlite_object_iterator*)iter)->res;

	if (((sqlite_object_iterator*)iter)->value) {
		zval_ptr_dtor(&((sqlite_object_iterator*)iter)->value);
		((sqlite_object_iterator*)iter)->value = NULL;
	}
	if (res) {
		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;
		}
	
		res->curr_row++;
	}
}

zend_object_iterator_funcs sqlite_ub_query_iterator_funcs = {
	sqlite_iterator_dtor,
	sqlite_iterator_valid,
	sqlite_iterator_get_current_data,
	sqlite_iterator_get_current_key,
	sqlite_iterator_move_forward,
	NULL
};

zend_object_iterator_funcs sqlite_query_iterator_funcs = {
	sqlite_iterator_dtor,
	sqlite_iterator_valid,
	sqlite_iterator_get_current_data,
	sqlite_iterator_get_current_key,
	sqlite_iterator_move_forward,
	sqlite_iterator_rewind
};

zend_object_iterator *sqlite_get_iterator(zend_class_entry *ce, zval *object TSRMLS_DC)
{
	sqlite_object_iterator *iterator = emalloc(sizeof(sqlite_object_iterator));

	sqlite_object *obj = (sqlite_object*) zend_object_store_get_object(object TSRMLS_CC);

	object->refcount++;
	iterator->it.data = (void*)object;
	iterator->it.funcs = ce->iterator_funcs.funcs;
	iterator->res = obj->u.res;
	iterator->value = NULL;
	return (zend_object_iterator*)iterator;
}
/* }}} */

static int init_sqlite_globals(zend_sqlite_globals *g)
{
	g->assoc_case = 0;
	return SUCCESS;
}

PHP_MINIT_FUNCTION(sqlite)
{
	REGISTER_SQLITE_CLASS(Database,   db,        NULL);
	REGISTER_SQLITE_CLASS(Result,     query,     NULL);
	REGISTER_SQLITE_CLASS(Unbuffered, ub_query,  NULL);
	REGISTER_SQLITE_CLASS(Exception,  exception, zend_exception_get_default());
	sqlite_object_handlers_query.get_class_entry = sqlite_get_ce_query;
	sqlite_object_handlers_ub_query.get_class_entry = sqlite_get_ce_ub_query;
	
	sqlite_ce_ub_query->get_iterator = sqlite_get_iterator;
	sqlite_ce_ub_query->iterator_funcs.funcs = &sqlite_ub_query_iterator_funcs;

	sqlite_ce_query->get_iterator = sqlite_get_iterator;
	sqlite_ce_query->iterator_funcs.funcs = &sqlite_query_iterator_funcs;

	ZEND_INIT_MODULE_GLOBALS(sqlite, init_sqlite_globals, NULL);

	REGISTER_INI_ENTRIES();

#if HAVE_PHP_SESSION
	php_session_register_module(ps_sqlite_ptr);
#endif
	
	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_MSHUTDOWN_FUNCTION(sqlite)
{
	UNREGISTER_INI_ENTRIES();
	return SUCCESS;
}

PHP_RINIT_FUNCTION(sqlite)
{
	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: sqlite.c,v 1.146.2.2 2004/08/02 22:43:42 iliaa Exp $");
	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, zval *object 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);

		/* if object is not an object then we're called from the factory() function */
		if (object && Z_TYPE_P(object) != IS_OBJECT) {
			RETVAL_NULL();
		} else {
			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(object ? NULL : return_value, db, persistent_id ? le_sqlite_pdb : le_sqlite_db);
	if (object) {
		/* if object is not an object then we're called from the factory() function */
		if (Z_TYPE_P(object) != IS_OBJECT) {
			sqlite_instanciate(sqlite_ce_db, object TSRMLS_CC);
		}
		/* and now register the object */
		SQLITE_REGISTER_OBJECT(db, object, 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 &error_message]])
   Opens a persistent handle to a SQLite database. Will create the database if it does not exist. */
PHP_FUNCTION(sqlite_popen)
{
	long mode = 0666;
	char *filename, *fullpath, *hashkey;
	int 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 (errmsg) {
		zval_dtor(errmsg);
	}

	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:%ld", 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 {
				int type;
				/* sanity check to ensure that the resource is still a valid regular resource
				 * number */
				if (zend_list_find(db->rsrc_id, &type) == db) {
					/* already accessed this request; map it */
					zend_list_addref(db->rsrc_id);
					ZVAL_RESOURCE(return_value, db->rsrc_id);
				} else {
					db->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, db, le_sqlite_pdb);
				}
			}

			/* all set */
			goto done;
		}

		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Some other type of persistent resource is using this hash key!?");
		RETVAL_FALSE;
		goto done;
	}

	/* now we need to open the database */
	php_sqlite_open(fullpath, (int)mode, hashkey, return_value, errmsg, NULL TSRMLS_CC);
done:
	efree(fullpath);
	efree(hashkey);
}
/* }}} */

/* {{{ proto resource sqlite_open(string filename [, int mode [, string &error_message]])
   Opens a SQLite database. Will create the database if it does not exist. */
PHP_FUNCTION(sqlite_open)
{
	long mode = 0666;
	char *filename, *fullpath = NULL;
	int filename_len;
	zval *errmsg = NULL;
	zval *object = getThis();

	php_set_error_handling(object ? EH_THROW : EH_NORMAL, sqlite_ce_exception TSRMLS_CC);
	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/",
				&filename, &filename_len, &mode, &errmsg)) {
		php_std_error_handling();
		return;
	}
	if (errmsg) {
		zval_dtor(errmsg);
	}

	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))) {
			php_std_error_handling();
			efree(fullpath);
			if (object) {
				RETURN_NULL();
			} else {
				RETURN_FALSE;
			}
		}

		if (php_check_open_basedir(fullpath TSRMLS_CC)) {
			php_std_error_handling();
			efree(fullpath);
			if (object) {
				RETURN_NULL();
			} else {
				RETURN_FALSE;
			}
		}

	}

	php_sqlite_open(fullpath ? fullpath : filename, (int)mode, NULL, return_value, errmsg, object TSRMLS_CC);

	if (fullpath) {
		efree(fullpath);
	}
	php_std_error_handling();
}
/* }}} */

/* {{{ proto object sqlite_factory(string filename [, int mode [, string &error_message]])
   Opens a SQLite database and creates an object for it. Will create the database if it does not exist. */
PHP_FUNCTION(sqlite_factory)
{
	long mode = 0666;
	char *filename, *fullpath = NULL;
	int filename_len;
	zval *errmsg = NULL;

	php_set_error_handling(EH_THROW, sqlite_ce_exception TSRMLS_CC);
	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/",
				&filename, &filename_len, &mode, &errmsg)) {
		php_std_error_handling();
		RETURN_NULL();
	}
	if (errmsg) {
		zval_dtor(errmsg);
	}

	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))) {
			efree(fullpath);
			php_std_error_handling();
			RETURN_NULL();
		}
	
		if (php_check_open_basedir(fullpath TSRMLS_CC)) {
			efree(fullpath);
			php_std_error_handling();
			RETURN_NULL();
		}
	}

	php_sqlite_open(fullpath ? fullpath : filename, (int)mode, NULL, return_value, errmsg, return_value TSRMLS_CC);
	if (fullpath) {
		efree(fullpath);
	}
	php_std_error_handling();
}
/* }}} */

/* {{{ 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;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ms)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		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;
	zval *object = getThis();

	if (object) {
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Ignored, you must destruct the object instead");
	} else {
		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 */
static 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 = (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(zval *object, struct php_sqlite_db *db, char *sql, long sql_len, int mode, int buffered, zval *return_value, struct php_sqlite_result **prres TSRMLS_DC)
{
	struct php_sqlite_result res, *rres;
	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) {
		if (object)
			zend_throw_exception(sqlite_ce_exception, errtext, ret TSRMLS_CC);
		else
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
		sqlite_freemem(errtext);
		goto terminate;
	} else if (!res.vm) { /* empty query */
terminate:
		if (return_value) {
			RETURN_FALSE;
		} else {
			return;
		}
	}

	if (!prres) {
		rres = NULL;
		prres = &rres;
	}
	if (!*prres) {
		*prres = (struct php_sqlite_result*)emalloc(sizeof(**prres));
	}
	memcpy(*prres, &res, sizeof(**prres));
	(*prres)->db = db;
	zend_list_addref(db->rsrc_id);
	

	/* now the result set is ready for stepping: get first row */
	if (php_sqlite_fetch((*prres) TSRMLS_CC) != SQLITE_OK) {
		real_result_dtor((*prres) TSRMLS_CC);
		*prres = NULL;
		if (return_value) {
			RETURN_FALSE;
		} else {
			return;	
		}
	}
	
	(*prres)->curr_row = 0;

	if (object) {
		sqlite_object *obj;
		if (buffered) {
			sqlite_instanciate(sqlite_ce_query, return_value TSRMLS_CC);
		} else {
			sqlite_instanciate(sqlite_ce_ub_query, return_value TSRMLS_CC);
		}
		obj = (sqlite_object *) zend_object_store_get_object(return_value TSRMLS_CC);
		obj->type = is_result;
		obj->u.res = (*prres);
	} else if (return_value) {
		ZEND_REGISTER_RESOURCE(object ? NULL : return_value, (*prres), le_sqlite_result);
	}
}
/* }}} */

/* {{{ proto resource sqlite_unbuffered_query(string query, resource db [ , int result_type ])
   Executes a query that does not prefetch and buffer all data. */
PHP_FUNCTION(sqlite_unbuffered_query)
{
	zval *zdb;
	struct php_sqlite_db *db;
	char *sql;
	int sql_len;
	long mode = PHPSQLITE_BOTH;
	char *errtext = NULL;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &sql, &sql_len, &mode)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		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);
	}

	PHP_SQLITE_EMPTY_QUERY;

	/* 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) {
			if (object)
				zend_throw_exception(sqlite_ce_exception, errtext, db->last_err_code TSRMLS_CC);
			else		
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
			sqlite_freemem(errtext);
		}
		return;
	}

	sqlite_query(object, db, sql, sql_len, (int)mode, 0, return_value, NULL TSRMLS_CC);
}
/* }}} */

/* {{{ proto resource sqlite_fetch_column_types(string table_name, resource db)
   Return an array of column types from a particular table. */
PHP_FUNCTION(sqlite_fetch_column_types)
{
	zval *zdb;
	struct php_sqlite_db *db;
	char *tbl, *sql;
	int tbl_len;
	char *errtext = NULL;
	zval *object = getThis();
	struct php_sqlite_result res;
	const char **rowdata, **colnames, *tail;
	int i, ncols;

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &tbl, &tbl_len)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
				ZEND_NUM_ARGS() TSRMLS_CC, "sr", &tbl, &tbl_len, &zdb) && 
			FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zdb, &tbl, &tbl_len)) {
			return;
		}
		DB_FROM_ZVAL(db, &zdb);
	}

	if (!(sql = sqlite_mprintf("SELECT * FROM '%q' LIMIT 1", tbl))) {
		RETURN_FALSE;
	}

	sqlite_exec(db->db, "PRAGMA show_datatypes = ON", NULL, NULL, NULL);

	db->last_err_code = sqlite_compile(db->db, sql, &tail, &res.vm, &errtext);

	sqlite_freemem(sql);

	if (db->last_err_code != SQLITE_OK) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
		sqlite_freemem(errtext);
		RETVAL_FALSE;
		goto done;
	}

	sqlite_step(res.vm, &ncols, &rowdata, &colnames);

	array_init(return_value);

	for (i = 0; i < ncols; i++) {
		char *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);
		}

		add_assoc_string(return_value, colname, colnames[ncols + i] ? (char *)colnames[ncols + i] : "", 1);
	}

done:
	sqlite_exec(db->db, "PRAGMA show_datatypes = OFF", NULL, NULL, NULL);
}
/* }}} */

/* {{{ 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;
	int sql_len;
	long mode = PHPSQLITE_BOTH;
	char *errtext = NULL;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &sql, &sql_len, &mode)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		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);
	}

	PHP_SQLITE_EMPTY_QUERY;

	/* 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) {
			if (object)
				zend_throw_exception(sqlite_ce_exception, errtext, db->last_err_code TSRMLS_CC);
			else
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
			sqlite_freemem(errtext);
		}
		return;
	}

	sqlite_query(object, db, sql, sql_len, (int)mode, 1, return_value, NULL TSRMLS_CC);
}
/* }}} */

/* {{{ proto boolean sqlite_exec(string query, resource db)
   Executes a result-less query against a given database */
PHP_FUNCTION(sqlite_exec)
{
	zval *zdb;
	struct php_sqlite_db *db;
	char *sql;
	int sql_len;
	char *errtext = NULL;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &sql, &sql_len)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		if(FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
			ZEND_NUM_ARGS() TSRMLS_CC, "sr", &sql, &sql_len, &zdb) && 
		   FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zdb, &sql, &sql_len)) {
			return;
		}
		DB_FROM_ZVAL(db, &zdb);
	}
	
	PHP_SQLITE_EMPTY_QUERY;

	db->last_err_code = sqlite_exec(db->db, sql, NULL, NULL, &errtext);

	if (db->last_err_code != SQLITE_OK) {
		if (object)
			zend_throw_exception(sqlite_ce_exception, errtext, db->last_err_code TSRMLS_CC);
		else
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
		sqlite_freemem(errtext);
		RETURN_FALSE;
	}

	RETURN_TRUE;
}
/* }}} */

/* {{{ 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, n = res->ncolumns, 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 < n; 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) = php_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] != NULL && rowdata[j][0] == '\x01') {
		int l = strlen(rowdata[j]);
		char *decoded = emalloc(l);
		l = php_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 of arrays. */
PHP_FUNCTION(sqlite_fetch_all)
{
	zval *zres, *ent;
	long mode = PHPSQLITE_BOTH;
	zend_bool decode_binary = 1;
	struct php_sqlite_result *res;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &mode, &decode_binary)) {
			return;
		}
		RES_FROM_OBJECT(res, object);
		if (!ZEND_NUM_ARGS()) {
			mode = res->mode;
		}
	} else {
		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_WARNING, "One or more rowsets were already returned; returning NULL this time");
		} 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;
	long mode = PHPSQLITE_BOTH;
	zend_bool decode_binary = 1;
	struct php_sqlite_result *res;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &mode, &decode_binary)) {
			return;
		}
		RES_FROM_OBJECT(res, object);
		if (!ZEND_NUM_ARGS()) {
			mode = res->mode;
		}
	} else {
		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 object sqlite_fetch_object(resource result [, string class_name [, NULL|array ctor_params [, bool decode_binary]]])
   Fetches the next row from a result set as an object. */
   /* note that you can do array(&$val) for param ctor_params */
PHP_FUNCTION(sqlite_fetch_object)
{
	zval *zres;
	zend_bool decode_binary = 1;
	struct php_sqlite_result *res;
	zval *object = getThis();
	char *class_name;
	int class_name_len;
	zend_class_entry *ce;
	zval dataset;
	zend_fcall_info fci;
	zend_fcall_info_cache fcc;
	zval *retval_ptr; 
	zval *ctor_params = NULL;

	php_set_error_handling(object ? EH_THROW : EH_NORMAL, sqlite_ce_exception TSRMLS_CC);
	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|szb", &class_name, &class_name_len, &ctor_params, &decode_binary)) {
			php_std_error_handling();
			return;
		}
		RES_FROM_OBJECT(res, object);
		if (!ZEND_NUM_ARGS()) {
			ce = zend_standard_class_def;
		} else {
			ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
		}
	} else {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|szb", &zres, &class_name, &class_name_len, &ctor_params, &decode_binary)) {
			php_std_error_handling();
			return;
		}
		ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
		if (ZEND_NUM_ARGS() < 2) {
			ce = zend_standard_class_def;
		} else {
			ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
		}
	}

	if (!ce) {
		zend_throw_exception_ex(sqlite_ce_exception, 0 TSRMLS_CC, "Could not find class '%s'", class_name);
		php_std_error_handling();
		return;
	}

	if (res->curr_row < res->nrows) {
		php_sqlite_fetch_array(res, PHPSQLITE_ASSOC, decode_binary, 1, &dataset TSRMLS_CC);
	} else {
		RETURN_FALSE;
	}

	object_and_properties_init(return_value, ce, NULL);
	zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC);

	php_std_error_handling(); /* before calling the ctor */

	if (ce->constructor) {
		fci.size = sizeof(fci);
		fci.function_table = &ce->function_table;
		fci.function_name = NULL;
		fci.symbol_table = NULL;
		fci.object_pp = &return_value;
		fci.retval_ptr_ptr = &retval_ptr;
		if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
			if (Z_TYPE_P(ctor_params) == IS_ARRAY) {
				HashTable *ht = Z_ARRVAL_P(ctor_params);
				Bucket *p;

				fci.param_count = 0;
				fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);
				p = ht->pListHead;
				while (p != NULL) {
					fci.params[fci.param_count++] = (zval**)p->pData;
					p = p->pListNext;
				}
			} else {
				/* Two problems why we throw exceptions here: PHP is typeless
				 * and hence passing one argument that's not an array could be
				 * by mistake and the other way round is possible, too. The 
				 * single value is an array. Also we'd have to make that one
				 * argument passed by reference.
				 */
				zend_throw_exception(sqlite_ce_exception, "Parameter ctor_params must be an array", 0 TSRMLS_CC);
				return;
			}
		} else {
			fci.param_count = 0;
			fci.params = NULL;
		}
		fci.no_separation = 1;

		fcc.initialized = 1;
		fcc.function_handler = ce->constructor;
		fcc.calling_scope = EG(scope);
		fcc.object_pp = &return_value;

		if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
			zend_throw_exception_ex(sqlite_ce_exception, 0 TSRMLS_CC, "Could not execute %s::%s()", class_name, ce->constructor->common.function_name);
		} else {
			if (retval_ptr) {
				zval_ptr_dtor(&retval_ptr);
			}
		}
		if (fci.params) {
			efree(fci.params);
		}
	} else if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
		zend_throw_exception_ex(sqlite_ce_exception, 0 TSRMLS_CC, "Class %s does not have a constructor, use NULL for parameter ctor_params or omit it", class_name);
	}
}
/* }}} */

/* {{{ 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 of arrays. */
PHP_FUNCTION(sqlite_array_query)
{
	zval *zdb, *ent;
	struct php_sqlite_db *db;
	struct php_sqlite_result *rres;
	char *sql;
	int sql_len;
	long mode = PHPSQLITE_BOTH;
	char *errtext = NULL;
	zend_bool decode_binary = 1;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lb", &sql, &sql_len, &mode, &decode_binary)) {
			return;
		}
		DB_FROM_OBJECT(db, object);
	} else {
		if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
				ZEND_NUM_ARGS() TSRMLS_CC, "sr|lb", &sql, &sql_len, &zdb, &mode, &decode_binary) && 
			FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lb", &zdb, &sql, &sql_len, &mode, &decode_binary)) {
			return;
		}
		DB_FROM_ZVAL(db, &zdb);
	}

	PHP_SQLITE_EMPTY_QUERY;

	/* 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) {
			if (object)
				zend_throw_exception(sqlite_ce_exception, errtext, db->last_err_code TSRMLS_CC);
			else
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
			sqlite_freemem(errtext);
		}
		return;
	}
	
	rres = (struct php_sqlite_result *)emalloc(sizeof(*rres));
	sqlite_query(object, db, sql, sql_len, (int)mode, 0, NULL, &rres TSRMLS_CC);
 	if (db->last_err_code != SQLITE_OK) {
 		if (rres) {
	 		efree(rres);
	 	}
 		RETURN_FALSE;
 	}

	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);
}
/* }}} */

/* {{{ php_sqlite_fetch_single */
static void php_sqlite_fetch_single(struct php_sqlite_result *res, zend_bool decode_binary, zval *return_value TSRMLS_DC)
{
	const char **rowdata;
	char *decoded;
	int decoded_len;
	
	/* check range of the row */
	if (res->curr_row >= res->nrows) {
		/* no more */
		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 = php_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 = NULL;
		decoded_len = 0;
	}

	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_single_query(resource db, string query [, bool first_row_only [, bool decode_binary]])
   Executes a query and returns either an array for one single column or the value of the first row. */
PHP_FUNCTION(sqlite_single_query)
{
	zval *zdb, *ent;
	struct php_sqlite_db *db;
	struct php_sqlite_result *rres;
	char *sql;
	int sql_len;
	char *errtext = NULL;
	zend_bool decode_binary = 1;
	zend_bool srow = 1;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bb", &sql, &sql_len, &srow, &decode_binary)) {
			return;
		}
		RES_FROM_OBJECT(db, object);
	} else {
		if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
				ZEND_NUM_ARGS() TSRMLS_CC, "sr|bb", &sql, &sql_len, &zdb, &srow, &decode_binary) && 
			FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|bb", &zdb, &sql, &sql_len, &srow, &decode_binary)) {
			return;
		}
		DB_FROM_ZVAL(db, &zdb);
	}

	PHP_SQLITE_EMPTY_QUERY;

	/* 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) {
			if (object)
				zend_throw_exception(sqlite_ce_exception, errtext, db->last_err_code TSRMLS_CC);
			else
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
			sqlite_freemem(errtext);
		}
		return;
	}

	rres = (struct php_sqlite_result *)emalloc(sizeof(*rres));
	sqlite_query(object, db, sql, sql_len, PHPSQLITE_NUM, 0, NULL, &rres TSRMLS_CC);
	if (db->last_err_code != SQLITE_OK) {
		if (rres) {
			efree(rres);
		}
		RETURN_FALSE;
	}

	if (!srow) {
		array_init(return_value);
	}

	while (rres->curr_row < rres->nrows) {
		MAKE_STD_ZVAL(ent);
		php_sqlite_fetch_single(rres, decode_binary, ent TSRMLS_CC);

		/* if set and we only have 1 row in the result set, return the result as a string. */
		if (srow) {
			if (rres->curr_row == 1 && rres->curr_row >= rres->nrows) {
				*return_value = *ent;
				zval_copy_ctor(return_value);
				zval_dtor(ent);
				FREE_ZVAL(ent);
				break;
			} else {
				srow = 0;
				array_init(return_value);
			}
		}
		add_next_index_zval(return_value, ent);
	}

	real_result_dtor(rres TSRMLS_CC);
}
/* }}} */


/* {{{ proto string sqlite_fetch_single(resource result [, bool decode_binary])
   Fetches the first column of a result set as a string. */
PHP_FUNCTION(sqlite_fetch_single)
{
	zval *zres;
	zend_bool decode_binary = 1;
	struct php_sqlite_result *res;
	zval *object = getThis();

	if (object) {
		if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &decode_binary)) {
			return;
		}
		RES_FROM_OBJECT(res, object);
	} else {
		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, -
 [2004-12-30 05:29 UTC] hugo dot pl at gmail dot com
Sorry, wrong URL

The correct URL is:
http://sqlitefront.sourceforge.net/temp/sqlite.c
 [2004-12-31 01:31 UTC] iliaa@php.net
Please provide a unified patch, thanks.
 [2005-01-01 14:27 UTC] helly@php.net
unified diff == diff -u
 [2005-01-10 00:25 UTC] hugo dot pl at gmail dot com
Sorry for the delay, I was travelling.

The patch generated with "diff -u orig_file mod_file"

http://sqlitefront.sourceforge.net/temp/patch.diff
 [2005-01-10 08:46 UTC] helly@php.net
We cannot easily do this because it would change the api compared to 5.0. So what we need to do is adding a method set_throw_exceptions(bool) or adding a flag SQLITE_THROW_EXCEPTIONS for the result type parameter.

Once we have this we could easily extend all yout 'if (object)' checks to test for that flag also. But we must not extend the checks in the ctors because there we MUST throw exceptions.

 [2005-01-11 04:27 UTC] hugo dot pl at gmail dot com
Maybe adding another paramater in the constructor to set this flag?

SQLiteDatabase( file, mode, errmsg, flags )
 [2010-05-03 15:59 UTC] johannes@php.net
-Status: Open +Status: Bogus -Package: Feature/Change Request +Package: *General Issues
 [2010-05-03 15:59 UTC] johannes@php.net
Upstream won't do any more work on SQLite 2. For SQLite 3 we have a different extension so we won't do such changes for SQLite 2 anymore.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Apr 29 15:01:31 2024 UTC