Skip to content

Commit 1487dd0

Browse files
kaja47nikic
authored andcommitted
Speed up SQLite3Result::fetchArray() by caching column names
Closes phpGH-7505.
1 parent 1ea5883 commit 1487dd0

File tree

3 files changed

+80
-4
lines changed

3 files changed

+80
-4
lines changed

ext/sqlite3/php_sqlite3_structs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ struct _php_sqlite3_result_object {
107107
php_sqlite3_stmt *stmt_obj;
108108
zval stmt_obj_zval;
109109

110+
/* Cache of column names to speed up repeated fetchArray(SQLITE3_ASSOC) calls.
111+
* Cache is cleared on reset() and finalize() calls. */
112+
int column_count;
113+
zend_string **column_names;
114+
110115
int is_prepared_statement;
111116
zend_object zo;
112117
};

ext/sqlite3/sqlite3.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,8 @@ PHP_METHOD(SQLite3, query)
586586
result = Z_SQLITE3_RESULT_P(return_value);
587587
result->db_obj = db_obj;
588588
result->stmt_obj = stmt_obj;
589+
result->column_names = NULL;
590+
result->column_count = -1;
589591
ZVAL_OBJ(&result->stmt_obj_zval, Z_OBJ(stmt));
590592

591593
return_code = sqlite3_step(result->stmt_obj->stmt);
@@ -1792,6 +1794,8 @@ PHP_METHOD(SQLite3Stmt, execute)
17921794
result->is_prepared_statement = 1;
17931795
result->db_obj = stmt_obj->db_obj;
17941796
result->stmt_obj = stmt_obj;
1797+
result->column_names = NULL;
1798+
result->column_count = -1;
17951799
ZVAL_OBJ_COPY(&result->stmt_obj_zval, Z_OBJ_P(object));
17961800

17971801
break;
@@ -1945,11 +1949,25 @@ PHP_METHOD(SQLite3Result, fetchArray)
19451949
RETURN_FALSE;
19461950
}
19471951

1952+
if (result_obj->column_count == -1) {
1953+
result_obj->column_count = sqlite3_column_count(result_obj->stmt_obj->stmt);
1954+
}
1955+
1956+
int n_cols = result_obj->column_count;
1957+
1958+
/* Cache column names to speed up repeated fetchArray calls. */
1959+
if (mode & PHP_SQLITE3_ASSOC && !result_obj->column_names) {
1960+
result_obj->column_names = emalloc(n_cols * sizeof(zend_string*));
1961+
1962+
for (int i = 0; i < n_cols; i++) {
1963+
const char *column = sqlite3_column_name(result_obj->stmt_obj->stmt, i);
1964+
result_obj->column_names[i] = zend_string_init(column, strlen(column), 0);
1965+
}
1966+
}
1967+
19481968
array_init(return_value);
1949-
1950-
int column_count = sqlite3_data_count(result_obj->stmt_obj->stmt);
19511969

1952-
for (i = 0; i < column_count; i++) {
1970+
for (i = 0; i < n_cols; i++) {
19531971
zval data;
19541972

19551973
sqlite_value_to_zval(result_obj->stmt_obj->stmt, i, &data);
@@ -1964,7 +1982,7 @@ PHP_METHOD(SQLite3Result, fetchArray)
19641982
Z_ADDREF(data);
19651983
}
19661984
}
1967-
add_assoc_zval(return_value, (char*)sqlite3_column_name(result_obj->stmt_obj->stmt, i), &data);
1985+
zend_symtable_add_new(Z_ARR_P(return_value), result_obj->column_names[i], &data);
19681986
}
19691987
}
19701988
break;
@@ -1979,6 +1997,17 @@ PHP_METHOD(SQLite3Result, fetchArray)
19791997
}
19801998
/* }}} */
19811999

2000+
static void sqlite3result_clear_column_names_cache(php_sqlite3_result *result) {
2001+
if (result->column_names) {
2002+
for (int i = 0; i < result->column_count; i++) {
2003+
zend_string_release(result->column_names[i]);
2004+
}
2005+
efree(result->column_names);
2006+
}
2007+
result->column_names = NULL;
2008+
result->column_count = -1;
2009+
}
2010+
19822011
/* {{{ Resets the result set back to the first row. */
19832012
PHP_METHOD(SQLite3Result, reset)
19842013
{
@@ -1990,6 +2019,8 @@ PHP_METHOD(SQLite3Result, reset)
19902019

19912020
SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result)
19922021

2022+
sqlite3result_clear_column_names_cache(result_obj);
2023+
19932024
if (sqlite3_reset(result_obj->stmt_obj->stmt) != SQLITE_OK) {
19942025
RETURN_FALSE;
19952026
}
@@ -2009,6 +2040,8 @@ PHP_METHOD(SQLite3Result, finalize)
20092040

20102041
SQLITE3_CHECK_INITIALIZED(result_obj->db_obj, result_obj->stmt_obj->initialised, SQLite3Result)
20112042

2043+
sqlite3result_clear_column_names_cache(result_obj);
2044+
20122045
/* We need to finalize an internal statement */
20132046
if (result_obj->is_prepared_statement == 0) {
20142047
zend_llist_del_element(&(result_obj->db_obj->free_list), &result_obj->stmt_obj_zval,
@@ -2235,6 +2268,8 @@ static void php_sqlite3_result_object_free_storage(zend_object *object) /* {{{ *
22352268
return;
22362269
}
22372270

2271+
sqlite3result_clear_column_names_cache(intern);
2272+
22382273
if (!Z_ISNULL(intern->stmt_obj_zval)) {
22392274
if (intern->stmt_obj && intern->stmt_obj->initialised) {
22402275
sqlite3_reset(intern->stmt_obj->stmt);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
SQLite3 - rename column while SQLite3Result is open
3+
--EXTENSIONS--
4+
sqlite3
5+
--SKIPIF--
6+
<?php
7+
if (SQLite3::version()['versionNumber'] < 3025000) {
8+
die("skip: sqlite3 library version < 3.25: no support for rename column");
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
$db = new SQLite3(':memory:');
15+
16+
$db->exec('CREATE TABLE tbl (orig text)');
17+
$db->exec('insert into tbl values ("one"), ("two")');
18+
19+
$res1 = $db->prepare('select * from tbl')->execute();
20+
$res2 = $db->prepare('select * from tbl')->execute();
21+
22+
var_dump(array_key_first($res1->fetchArray(SQLITE3_ASSOC)));
23+
var_dump(array_key_first($res2->fetchArray(SQLITE3_ASSOC)));
24+
25+
$db->exec('alter table tbl rename column orig to changed');
26+
27+
$res1->reset();
28+
var_dump(array_key_first($res1->fetchArray(SQLITE3_ASSOC)));
29+
var_dump(array_key_first($res2->fetchArray(SQLITE3_ASSOC)));
30+
31+
?>
32+
--EXPECT--
33+
string(4) "orig"
34+
string(4) "orig"
35+
string(7) "changed"
36+
string(4) "orig"

0 commit comments

Comments
 (0)