Browse Source

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.
migration/unlabaled-1.3.2
Wez Furlong 24 years ago
parent
commit
8ed14ff16f
  1. 4
      ext/sqlite/config.m4
  2. 1
      ext/sqlite/php_sqlite.h
  3. 262
      ext/sqlite/sqlite.c

4
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

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

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

Loading…
Cancel
Save