]> granicus.if.org Git - php/commitdiff
Implement sqlite_unbuffered_query(), which works similarly to the mysql
authorWez Furlong <wez@php.net>
Fri, 18 Apr 2003 12:22:43 +0000 (12:22 +0000)
committerWez Furlong <wez@php.net>
Fri, 18 Apr 2003 12:22:43 +0000 (12:22 +0000)
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.

ext/sqlite/config.m4
ext/sqlite/php_sqlite.h
ext/sqlite/sqlite.c

index a74bafab4c14b8aebd25b2428d303641de73b913..cd4289888f3c626424736025fdb4fb4d9553fa8a 100644 (file)
@@ -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
index fd4b723c80f2772122f87f1cb19b0eb7e03332b5..87a33228c6a2fd31375d487fb6dacde10afba041 100644 (file)
@@ -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);
index dfd995407ad957ce8beefdfa0ab5641daecf6a6a..b41605537caf229b966a0f96542d4903a13bbf1f 100644 (file)
 #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;
 }
 /* }}} */