php-src/ext/sqlite/sqlite.c

990 lines
25 KiB
C
Raw Normal View History

/*
+----------------------------------------------------------------------+
| PHP Version 4 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.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. |
+----------------------------------------------------------------------+
| Author: Wez Furlong <wez@thebrainroom.com> |
+----------------------------------------------------------------------+
$Id$
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define PHP_SQLITE_MODULE_VERSION "0.7"
#include "php.h"
#include "php_ini.h"
#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 };
static int le_sqlite_db, le_sqlite_result, le_sqlite_pdb;
#define DB_FROM_ZVAL(db, zv) ZEND_FETCH_RESOURCE2(db, struct php_sqlite_db *, zv, -1, "sqlite database", le_sqlite_db, le_sqlite_pdb)
struct php_sqlite_result {
sqlite_vm *vm;
int buffered;
int ncolumns;
int nrows;
int curr_row;
char **col_names;
int alloc_rows;
char **table;
};
struct php_sqlite_db {
sqlite *db;
int last_err_code;
int is_persistent;
int rsrc_id;
};
enum { PHPSQLITE_ASSOC = 1, PHPSQLITE_NUM = 2, PHPSQLITE_BOTH = PHPSQLITE_ASSOC|PHPSQLITE_NUM };
function_entry sqlite_functions[] = {
PHP_FE(sqlite_open, arg3_force_ref)
PHP_FE(sqlite_popen, arg3_force_ref)
PHP_FE(sqlite_close, NULL)
PHP_FE(sqlite_query, NULL)
PHP_FE(sqlite_fetch_array, 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_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)
{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),
NULL,
NULL,
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)
#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)
{
if (rsrc->ptr) {
struct php_sqlite_db *db = (struct php_sqlite_db*)rsrc->ptr;
sqlite_close(db->db);
pefree(db, db->is_persistent);
rsrc->ptr = NULL;
}
}
static void real_result_dtor(struct php_sqlite_result *res)
{
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++) {
2003-04-19 10:28:45 +00:00
if (res->table[base + j] != NULL) {
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);
}
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);
}
static int php_sqlite_forget_persistent_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
struct php_sqlite_db *db;
if (Z_TYPE_P(rsrc) != le_sqlite_pdb) {
return 0;
}
db = (struct php_sqlite_db*)rsrc->ptr;
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;
}
2003-04-17 20:09:45 +00:00
/* {{{ PHP Function interface */
static void php_sqlite_function_callback(sqlite_func *func, int argc, const char **argv)
{
zval *retval = NULL;
zval ***zargs;
zval funcname;
int i, res;
char *callable = NULL, *errbuf=NULL;
2003-04-18 19:43:38 +00:00
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], 0);
if (!zend_is_callable(&funcname, 0, &callable)) {
spprintf(&errbuf, 0, "function `%s' is not callable", callable);
sqlite_set_result_error(func, errbuf, -1);
efree(errbuf);
efree(callable);
return;
}
efree(callable);
if (argc > 1) {
zargs = (zval ***)emalloc((argc - 1) * sizeof(zval **));
for (i = 0; i < argc-1; i++) {
zargs[i] = emalloc(sizeof(zval *));
MAKE_STD_ZVAL(*zargs[i]);
2003-04-17 16:36:59 +00:00
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);
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 {
sqlite_set_result_error(func, "call_user_function_ex failed", -1);
}
if (retval) {
zval_ptr_dtor(&retval);
}
if (zargs) {
for (i = 0; i < argc-1; i++) {
2003-04-17 16:36:59 +00:00
zval_ptr_dtor(zargs[i]);
efree(zargs[i]);
}
efree(zargs);
}
}
2003-04-17 20:09:45 +00:00
/* }}} */
2003-04-17 16:36:59 +00:00
2003-04-17 20:09:45 +00:00
/* {{{ Authorization Callback */
static int php_sqlite_authorizer(void *autharg, int access_type, const char *arg3, const char *arg4)
{
switch (access_type) {
case SQLITE_COPY:
{
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;
default:
/* access allowed */
return SQLITE_OK;
}
}
2003-04-17 20:09:45 +00:00
/* }}} */
2003-04-17 16:36:59 +00:00
PHP_MINIT_FUNCTION(sqlite)
{
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);
2003-04-17 20:17:54 +00:00
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);
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_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$");
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();
}
static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *persistent_id, zval *return_value, zval *errmsg)
{
char *errtext = NULL;
sqlite *sdb = NULL;
struct php_sqlite_db *db = NULL;
sdb = sqlite_open(filename, mode, &errtext);
if (sdb == NULL) {
2003-04-18 19:43:38 +00:00
TSRMLS_FETCH();
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
if (errmsg) {
ZVAL_STRING(errmsg, errtext, 1);
}
sqlite_freemem(errtext);
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;
2003-04-17 20:17:54 +00:00
db->last_err_code = SQLITE_OK;
db->db = sdb;
/* register the PHP functions */
sqlite_create_function(sdb, "php", -1, php_sqlite_function_callback, 0);
/* set default busy handler; keep retrying up until 1/2 second has passed,
* then fail with a busy status code */
sqlite_busy_timeout(sdb, 500);
/* authorizer hook so we can enforce safe mode */
sqlite_set_authorizer(sdb, php_sqlite_authorizer, NULL);
db->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, db, persistent_id ? le_sqlite_pdb : le_sqlite_db);
if (persistent_id) {
list_entry le;
2003-04-18 19:43:38 +00:00
TSRMLS_FETCH();
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 &errmessage])
Opens a persistent handle to an SQLite database. Will create the database if it does not exist */
PHP_FUNCTION(sqlite_popen)
{
int mode = 0666;
char *filename, *fullpath, *hashkey;
long filename_len, hashkeylen;
zval *errmsg = NULL;
char *errtext = 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;
}
/* 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;
}
hashkeylen = spprintf(&hashkey, 0, "sqlite_pdb_%s:%d", 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 {
/* already accessed this request; map it */
ZVAL_RESOURCE(return_value, db->rsrc_id);
}
/* all set */
efree(fullpath);
efree(hashkey);
return;
}
php_error_docref(NULL TSRMLS_CC, E_WARNING, "some other type of persistent resource is using this hash key!?");
RETURN_FALSE;
}
/* now we need to open the database */
php_sqlite_open(fullpath, mode, hashkey, return_value, errmsg);
efree(fullpath);
efree(hashkey);
}
/* }}} */
/* {{{ proto resource sqlite_open(string filename [, int mode, string &errmessage])
Opens an SQLite database. Will create the database if it does not exist */
PHP_FUNCTION(sqlite_open)
{
int mode = 0666;
char *filename;
long filename_len;
zval *errmsg = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/",
&filename, &filename_len, &mode, &errmsg)) {
return;
}
if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
RETURN_FALSE;
}
if (php_check_open_basedir(filename TSRMLS_CC)) {
RETURN_FALSE;
}
php_sqlite_open(filename, mode, NULL, return_value, errmsg);
}
/* }}} */
/* {{{ 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;
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;
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));
}
/* }}} */
/* {{{ 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;
}
DB_FROM_ZVAL(db, &zdb);
/* 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) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
sqlite_freemem(errtext);
}
return;
}
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)
{
zval *zdb;
struct php_sqlite_db *db;
char *sql;
long sql_len;
struct php_sqlite_result res, *rres;
int ret, i, base;
2003-04-17 16:36:59 +00:00
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;
}
DB_FROM_ZVAL(db, &zdb);
/* 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) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", errtext);
sqlite_freemem(errtext);
}
return;
}
memset(&res, 0, sizeof(res));
res.buffered = 1;
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));
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);
}
/* }}} */
/* {{{ proto array sqlite_fetch_array(resource result [, int result_type])
Fetches the next row from a result set as an array */
PHP_FUNCTION(sqlite_fetch_array)
{
zval *zres;
struct php_sqlite_result *res;
int mode = PHPSQLITE_BOTH;
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;
}
ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
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);
for (j = 0; j < res->ncolumns; j++) {
if (mode & PHPSQLITE_NUM) {
if (rowdata[j] == NULL) {
2003-04-17 17:09:59 +00:00
add_index_null(return_value, j);
} else {
add_index_string(return_value, j, (char*)rowdata[j], 1);
2003-04-17 17:09:59 +00:00
}
}
if (mode & PHPSQLITE_ASSOC) {
if (rowdata[j] == NULL) {
add_assoc_null(return_value, (char*)colnames[j]);
2003-04-17 17:09:59 +00:00
} else {
add_assoc_string(return_value, (char*)colnames[j], (char*)rowdata[j], 1);
2003-04-17 17:09:59 +00:00
}
}
}
/* advance the row pointer */
res->curr_row++;
}
/* }}} */
/* {{{ proto string sqlite_libversion()
Returns the version of the linked SQLite library */
PHP_FUNCTION(sqlite_libversion)
{
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
RETURN_STRING((char*)sqlite_libversion(), 1);
}
/* }}} */
/* {{{ proto string sqlite_libencoding()
Returns the encoding (iso8859 or UTF-8) of the linked SQLite library */
PHP_FUNCTION(sqlite_libencoding)
{
if (ZEND_NUM_ARGS() != 0) {
WRONG_PARAM_COUNT;
}
RETURN_STRING((char*)sqlite_libencoding(), 1);
}
/* }}} */
/* {{{ proto int sqlite_changes(resource db)
Returns the number of rows that were changed by the most recent SQL statement */
PHP_FUNCTION(sqlite_changes)
{
zval *zdb;
struct php_sqlite_db *db;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
return;
}
DB_FROM_ZVAL(db, &zdb);
RETURN_LONG(sqlite_changes(db->db));
}
/* }}} */
/* {{{ proto int sqlite_last_insert_rowid(resource db)
Returns the rowid of the most recently inserted row */
PHP_FUNCTION(sqlite_last_insert_rowid)
{
zval *zdb;
struct php_sqlite_db *db;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
return;
}
DB_FROM_ZVAL(db, &zdb);
RETURN_LONG(sqlite_last_insert_rowid(db->db));
}
/* }}} */
/* {{{ proto int sqlite_num_rows(resource result)
Returns the number of rows in a result set */
PHP_FUNCTION(sqlite_num_rows)
{
zval *zres;
struct php_sqlite_result *res;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
return;
}
ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
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;
}
}
/* }}} */
/* {{{ proto int sqlite_num_fields(resource result)
Returns the number of fields in a result set */
PHP_FUNCTION(sqlite_num_fields)
{
zval *zres;
struct php_sqlite_result *res;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
return;
}
ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
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;
}
}
/* }}} */
/* {{{ proto string sqlite_field_name(resource result, int field)
Returns the name of a particular field */
PHP_FUNCTION(sqlite_field_name)
{
zval *zres;
struct php_sqlite_result *res;
int field;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &field)) {
return;
}
ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
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;
}
2003-04-19 10:28:45 +00:00
RETURN_STRING(res->col_names[field], 1);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field name not available for unbuffered queries");
RETURN_FALSE;
}
}
/* }}} */
/* {{{ proto bool sqlite_seek(resource result, int row)
Seek to a particular row number */
PHP_FUNCTION(sqlite_seek)
{
zval *zres;
struct php_sqlite_result *res;
int row;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &row)) {
return;
}
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 - 1;
RETURN_TRUE;
}
/* }}} */
/* {{{ proto string sqlite_escape_string(string item)
Escapes a string for use as a query parameter */
PHP_FUNCTION(sqlite_escape_string)
{
char *string;
long stringlen;
char *ret;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &stringlen)) {
return;
}
ret = sqlite_mprintf("%q", string);
if (ret) {
RETVAL_STRING(ret, 1);
sqlite_freemem(ret);
}
}
/* }}} */
/* {{{ proto int sqlite_last_error(resource db)
Returns the error code of the last error for a database */
PHP_FUNCTION(sqlite_last_error)
{
zval *zdb;
struct php_sqlite_db *db;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
return;
}
DB_FROM_ZVAL(db, &zdb);
RETURN_LONG(db->last_err_code);
}
/* }}} */
/* {{{ proto string sqlite_error_string(int error_code)
Returns the textual description of an error code */
PHP_FUNCTION(sqlite_error_string)
{
long code;
const char *msg;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) {
return;
}
msg = sqlite_error_string(code);
if (msg) {
RETURN_STRING((char*)msg, 1);
} else {
RETURN_NULL();
}
}
/* }}} */