diff options
| author | Wez Furlong <wez@php.net> | 2003-04-18 12:22:43 +0000 | 
|---|---|---|
| committer | Wez Furlong <wez@php.net> | 2003-04-18 12:22:43 +0000 | 
| commit | 8ed14ff16f81dde37804e399aae523c3b1e8f4bd (patch) | |
| tree | 692c22fa19d851858cf87a2752a243cb649d7b10 /ext/sqlite | |
| parent | bd77233ee3ed622b11bb3bef6b35f2672e1c65ac (diff) | |
| download | php-git-8ed14ff16f81dde37804e399aae523c3b1e8f4bd.tar.gz | |
Implement sqlite_unbuffered_query(), which works similarly to the mysql
function with a similar name.
Change sqlite_query() to use the same mechanism as the unbuffered query; this
moves the bulk of the memory allocations into the ZE memory manager, and will
hopefully be more efficient and less at risk of leaks.
Diffstat (limited to 'ext/sqlite')
| -rw-r--r-- | ext/sqlite/config.m4 | 4 | ||||
| -rw-r--r-- | ext/sqlite/php_sqlite.h | 1 | ||||
| -rw-r--r-- | ext/sqlite/sqlite.c | 262 | 
3 files changed, 235 insertions, 32 deletions
diff --git a/ext/sqlite/config.m4 b/ext/sqlite/config.m4 index a74bafab4c..cd4289888f 100644 --- a/ext/sqlite/config.m4 +++ b/ext/sqlite/config.m4 @@ -63,7 +63,6 @@ if test "$PHP_SQLITE" != "no"; then  	AC_DEFINE(SQLITE_PTR_SZ, SIZEOF_CHAR_P, [Size of a pointer])  	AC_DEFINE(OS_UNIX, 1, [if this is unix])  	AC_DEFINE(OS_WIN, 0, [if this is windows]) -	AC_CHECK_FUNCS(usleep)  	dnl use latin 1 for now; the utf-8 handling in funcs.c uses assert(),  	dnl which is a bit silly and something we want to avoid  	SQLITE_ENCODING="iso8859" @@ -82,4 +81,7 @@ if test "$PHP_SQLITE" != "no"; then  	PHP_ADD_MAKEFILE_FRAGMENT    fi +  AC_CHECK_FUNCS(usleep nanosleep) + +  AC_CHECK_HEADERS(time.h)  fi diff --git a/ext/sqlite/php_sqlite.h b/ext/sqlite/php_sqlite.h index fd4b723c80..87a33228c6 100644 --- a/ext/sqlite/php_sqlite.h +++ b/ext/sqlite/php_sqlite.h @@ -43,6 +43,7 @@ PHP_MINFO_FUNCTION(sqlite);  PHP_FUNCTION(sqlite_open);  PHP_FUNCTION(sqlite_close);  PHP_FUNCTION(sqlite_query); +PHP_FUNCTION(sqlite_unbuffered_query);  PHP_FUNCTION(sqlite_fetch_array);  PHP_FUNCTION(sqlite_num_rows); diff --git a/ext/sqlite/sqlite.c b/ext/sqlite/sqlite.c index dfd995407a..b41605537c 100644 --- a/ext/sqlite/sqlite.c +++ b/ext/sqlite/sqlite.c @@ -29,6 +29,11 @@  #include "ext/standard/info.h"  #include "php_sqlite.h" +#if HAVE_TIME_H +# include <time.h> +#endif +#include <unistd.h> +  #include <sqlite.h>  static unsigned char arg3_force_ref[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE }; @@ -36,10 +41,14 @@ static unsigned char arg3_force_ref[] = {3, BYREF_NONE, BYREF_NONE, BYREF_FORCE  static int le_sqlite_db, le_sqlite_result;  struct php_sqlite_result { -	char **table; -	int nrows; +	sqlite_vm *vm; +	int buffered;  	int ncolumns; +	int nrows;  	int curr_row; +	char **col_names; +	int alloc_rows; +	char **table;  };  struct php_sqlite_db { @@ -67,6 +76,7 @@ function_entry sqlite_functions[] = {  	PHP_FE(sqlite_busy_timeout, NULL)  	PHP_FE(sqlite_last_error, NULL)  	PHP_FE(sqlite_error_string, NULL) +	PHP_FE(sqlite_unbuffered_query, NULL)  	{NULL, NULL, NULL}  }; @@ -93,6 +103,19 @@ zend_module_entry sqlite_module_entry = {  ZEND_GET_MODULE(sqlite)  #endif +static void short_sleep(void) +{ +#if HAVE_NANOSLEEP +	struct timespec req = { 0, 500 * 1000 }; +	nanosleep(&req, NULL); +#elif HAVE_USLEEP +	usleep(500 * 1000); +#elif PHP_WIN32 +	Sleep(500); +#else +	php_sleep(1); +#endif +}  static ZEND_RSRC_DTOR_FUNC(php_sqlite_db_dtor)  { @@ -100,17 +123,42 @@ static ZEND_RSRC_DTOR_FUNC(php_sqlite_db_dtor)  	sqlite_close(db->db); -	efree(db); +	pefree(db, db->is_persistent);  } -static ZEND_RSRC_DTOR_FUNC(php_sqlite_result_dtor) +static void real_result_dtor(struct php_sqlite_result *res)  { -	struct php_sqlite_result *res = (struct php_sqlite_result *)rsrc->ptr; +	int i, j, base; + +	if (res->vm) { +		sqlite_finalize(res->vm, NULL); +	} +	 +	for (i = 0; i < res->nrows; i++) { +		base = i * res->ncolumns; +		for (j = 0; j < res->ncolumns; j++) { +			efree(res->table[base + j]); +		} +	} +	if (res->table) { +		efree(res->table); +	} +	if (res->col_names) { +		for (j = 0; j < res->ncolumns; j++) { +			efree(res->col_names[j]); +		} +		efree(res->col_names); +	} -	sqlite_free_table(res->table);  	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); +} +  /* {{{ PHP Function interface */  static void php_sqlite_function_callback(sqlite_func *func, int argc, const char **argv)  { @@ -359,6 +407,48 @@ PHP_FUNCTION(sqlite_close)  }  /* }}} */ +/* {{{ proto resource sqlite_unbuffered_query(string query, resource db) +   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; +	struct php_sqlite_result res, *rres; +	int ret, i, base; +	char *errtext = NULL; +	const char *tail; +	const char **rowdata, **colnames; + +	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &sql, &sql_len, &zdb)) { +		return; +	} + +	ZEND_FETCH_RESOURCE(db, struct php_sqlite_db *, &zdb, -1, "sqlite database", le_sqlite_db); + +	memset(&res, 0, sizeof(res)); + +	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; +	} + +	rres = (struct php_sqlite_result*)emalloc(sizeof(*rres)); +	memcpy(rres, &res, sizeof(*rres)); + +	/* now the result set is ready for stepping */ +	rres->curr_row = 0; + +	ZEND_REGISTER_RESOURCE(return_value, rres, le_sqlite_result); +} +/* }}} */ +  /* {{{ proto resource sqlite_query(string query, resource db)     Executes a query against a given database and returns a result handle */  PHP_FUNCTION(sqlite_query) @@ -368,8 +458,10 @@ PHP_FUNCTION(sqlite_query)  	char *sql;  	long sql_len;  	struct php_sqlite_result res, *rres; -	int ret; +	int ret, i, base;  	char *errtext = NULL; +	const char *tail; +	const char **rowdata, **colnames;  	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &sql, &sql_len, &zdb)) {  		return; @@ -378,8 +470,9 @@ PHP_FUNCTION(sqlite_query)  	ZEND_FETCH_RESOURCE(db, struct php_sqlite_db *, &zdb, -1, "sqlite database", le_sqlite_db);  	memset(&res, 0, sizeof(res)); +	res.buffered = 1; -	ret = sqlite_get_table(db->db, sql, &res.table, &res.nrows, &res.ncolumns, &errtext); +	ret = sqlite_compile(db->db, sql, &tail, &res.vm, &errtext);  	db->last_err_code = ret;  	if (ret != SQLITE_OK) { @@ -392,7 +485,59 @@ PHP_FUNCTION(sqlite_query)  	rres = (struct php_sqlite_result*)emalloc(sizeof(*rres));  	memcpy(rres, &res, sizeof(*rres)); -	rres->curr_row = 1;	/* 0 holds the column header names */ +next_row: +	ret = sqlite_step(rres->vm, &rres->ncolumns, &rowdata, &colnames); +	db->last_err_code = ret; + +	switch (ret) { +		case SQLITE_ROW: +			/* 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; + +		case SQLITE_DONE: +			/* no more rows - lets copy the column names */ +			rres->col_names = emalloc(rres->ncolumns * sizeof(char *)); +			for (i = 0; i < rres->ncolumns; i++) { +				rres->col_names[i] = estrdup(colnames[i]); +			} +			break; + +		case SQLITE_BUSY: +		case SQLITE_ERROR: +		case SQLITE_MISUSE: +		default: +			/* fall through to finalize */ +	} + +	ret = sqlite_finalize(rres->vm, &errtext); +	db->last_err_code = ret; +	rres->vm = NULL; + +	if (ret != SQLITE_OK) { + +		real_result_dtor(rres); +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); +		sqlite_freemem(errtext); +		 +		RETURN_FALSE; +	} +	 +	rres->curr_row = 0;  	ZEND_REGISTER_RESOURCE(return_value, rres, le_sqlite_result);  } @@ -405,7 +550,9 @@ PHP_FUNCTION(sqlite_fetch_array)  	zval *zres;  	struct php_sqlite_result *res;  	int mode = PHPSQLITE_BOTH; -	int i, j; +	int j, ret; +	const char **rowdata, **colnames; +	char *errtext = NULL;  	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zres, &mode)) {  		return; @@ -413,31 +560,65 @@ PHP_FUNCTION(sqlite_fetch_array)  	ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); -	/* check range of the row */ -	if (res->curr_row > res->nrows) { -		/* no more */ -		RETURN_FALSE; +	if (res->buffered) { +		/* check range of the row */ +		if (res->curr_row >= res->nrows) { +			/* no more */ +			RETURN_FALSE; +		} + +		rowdata = (const char**)&res->table[res->curr_row * res->ncolumns]; +		colnames = (const char**)res->col_names; +		 +	} else { +		/* unbuffered; we need to manually fetch the row now */ + +		if (res->vm == NULL) { +			/* sanity check */ +			RETURN_FALSE; +		} +		 +		ret = sqlite_step(res->vm, &res->ncolumns, &rowdata, &colnames); +		switch (ret) { +			case SQLITE_ROW: +				/* safe to fall through */ +				break; +				 +			case SQLITE_DONE: +				/* no more rows */ +				RETURN_FALSE; +				 +			case SQLITE_ERROR: +			case SQLITE_MISUSE: +			case SQLITE_BUSY: +			default: +				/* error; lets raise the error now */ +				ret = sqlite_finalize(res->vm, &errtext); +				res->vm = NULL; +				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext); +				sqlite_freemem(errtext); +				RETURN_FALSE; +		} + +		/* we got another row */  	} +	/* now populate the result */  	array_init(return_value); -	 -	/* calculate the starting slot for the row */ -	i = res->curr_row * res->ncolumns; -	/* now populate the result */  	for (j = 0; j < res->ncolumns; j++) {  		if (mode & PHPSQLITE_NUM) { -			if (res->table[i + j] == NULL) { +			if (rowdata[j] == NULL) {  				add_index_null(return_value, j);  			} else { -				add_index_string(return_value, j, res->table[i + j], 1); +				add_index_string(return_value, j, (char*)rowdata[j], 1);  			}  		}  		if (mode & PHPSQLITE_ASSOC) { -			if (res->table[i + j] == NULL) { -				add_assoc_null(return_value, res->table[j]); +			if (rowdata[j] == NULL) { +				add_assoc_null(return_value, (char*)colnames[j]);  			} else { -				add_assoc_string(return_value, res->table[j], res->table[i + j], 1); +				add_assoc_string(return_value, (char*)colnames[j], (char*)rowdata[j], 1);  			}  		}  	} @@ -516,8 +697,12 @@ PHP_FUNCTION(sqlite_num_rows)  	ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); -	RETURN_LONG(res->nrows); -	 +	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; +	}  }  /* }}} */ @@ -534,7 +719,12 @@ PHP_FUNCTION(sqlite_num_fields)  	ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result); -	RETURN_LONG(res->ncolumns); +	if (res->buffered) { +		RETURN_LONG(res->ncolumns); +	} else { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of fields not available for unbuffered queries"); +		RETURN_FALSE; +	}  }  /* }}} */ @@ -553,12 +743,17 @@ PHP_FUNCTION(sqlite_field_name)  	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); +	if (res->buffered) { +		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->table[field], 1); +	} else { +		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field name not available for unbuffered queries");  		RETURN_FALSE;  	} -	 -	RETURN_STRING(res->table[field], 1);	  }  /* }}} */ @@ -576,12 +771,17 @@ PHP_FUNCTION(sqlite_seek)  	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; +	res->curr_row = row - 1;  	RETURN_TRUE;  }  /* }}} */  | 
