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