Skip to content

Commit b546ae9

Browse files
rkopacknikic
authored andcommitted
Implement SQLite extended result code functionality
1 parent 615ce0b commit b546ae9

8 files changed

+219
-21
lines changed

UPGRADING

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,14 @@ PHP 7.4 UPGRADE NOTES
265265
. PDOStatement::getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT) allows to
266266
check whether this statement is read-only, i.e. whether it doesn't modify
267267
the database.
268+
. PDO::setAttribute(PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES, true) enables the
269+
use of SQLite3 extended result codes in errorInfo().
270+
271+
r SQLite3:
272+
. Added SQLite3::lastExtendedErrorCode() to fetch the last extended result
273+
code.
274+
. Added SQLite3::enableExtendedResultCodes($enable = true), which will make
275+
SQLite3::lastErrorCode() return extended result codes.
268276

269277
- Standard:
270278
. strip_tags() now also accepts an array of allowed tags: Instead of

ext/pdo_sqlite/pdo_sqlite.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ PHP_MINIT_FUNCTION(pdo_sqlite)
7676
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long)SQLITE_OPEN_READWRITE);
7777
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long)SQLITE_OPEN_CREATE);
7878
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT);
79+
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);
7980

8081
return php_pdo_register_driver(&pdo_sqlite_driver);
8182
}

ext/pdo_sqlite/php_pdo_sqlite_int.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ extern const struct pdo_stmt_methods sqlite_stmt_methods;
7676

7777
enum {
7878
PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC,
79-
PDO_SQLITE_ATTR_READONLY_STATEMENT
79+
PDO_SQLITE_ATTR_READONLY_STATEMENT,
80+
PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
8081
};
8182

8283
#endif

ext/pdo_sqlite/sqlite_driver.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
305305
case PDO_ATTR_TIMEOUT:
306306
sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000);
307307
return 1;
308+
case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES:
309+
sqlite3_extended_result_codes(H->db, zval_get_long(val));
310+
return 1;
308311
}
309312
return 0;
310313
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
PDO_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
3+
--SKIPIF--
4+
<?php if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ?>
5+
--FILE--
6+
<?php
7+
8+
echo "Creating new PDO" . PHP_EOL;
9+
$db = new PDO('sqlite::memory:');
10+
11+
$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
12+
13+
echo "Inserting first time which should succeed" . PHP_EOL;
14+
$db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
15+
$errorInfo = $db->errorInfo();
16+
echo sprintf("First Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
17+
18+
echo "Inserting second time which should fail" . PHP_EOL;
19+
$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
20+
$errorInfo = $db->errorInfo();
21+
echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
22+
23+
24+
echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL;
25+
$db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]);
26+
27+
$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
28+
29+
echo "Inserting first time which should succeed" . PHP_EOL;
30+
$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
31+
$errorInfo = $db->errorInfo();
32+
echo sprintf("First (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
33+
34+
echo "Inserting second time which should fail" . PHP_EOL;
35+
$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
36+
$errorInfo = $db->errorInfo();
37+
echo sprintf("Second (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
38+
39+
?>
40+
--EXPECT--
41+
Creating new PDO
42+
Inserting first time which should succeed
43+
First Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()
44+
Inserting second time which should fail
45+
Second Error Info: SQLSTATE Error Code: (23000), Driver Specific Error Code: (19)
46+
Creating new PDO with Extended Result Codes turned on
47+
Inserting first time which should succeed
48+
First (Extended) Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()
49+
Inserting second time which should fail
50+
Second (Extended) Error Info: SQLSTATE Error Code: (HY000), Driver Specific Error Code: (1555)

ext/sqlite3/sqlite3.c

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,56 @@ PHP_METHOD(sqlite3, lastErrorCode)
289289
}
290290
/* }}} */
291291

292+
/* {{{ proto int SQLite3::lastExtendedErrorCode()
293+
Returns the numeric extended result code of the most recent failed sqlite API call for the database connection. */
294+
PHP_METHOD(sqlite3, lastExtendedErrorCode)
295+
{
296+
php_sqlite3_db_object *db_obj;
297+
zval *object = ZEND_THIS;
298+
db_obj = Z_SQLITE3_DB_P(object);
299+
300+
SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->db, SQLite3)
301+
302+
if (zend_parse_parameters_none() == FAILURE) {
303+
return;
304+
}
305+
306+
if (db_obj->initialised) {
307+
RETURN_LONG(sqlite3_extended_errcode(db_obj->db));
308+
} else {
309+
RETURN_LONG(0);
310+
}
311+
}
312+
/* }}} */
313+
314+
/* {{{ proto bool SQLite3::enableExtendedResultCodes([bool enable = true])
315+
Turns on or off the extended result codes feature of SQLite. */
316+
PHP_METHOD(sqlite3, enableExtendedResultCodes)
317+
{
318+
php_sqlite3_db_object *db_obj;
319+
zval *object = ZEND_THIS;
320+
zend_bool enable = 1;
321+
db_obj = Z_SQLITE3_DB_P(object);
322+
int ret;
323+
324+
SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->db, SQLite3)
325+
326+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable) == FAILURE) {
327+
return;
328+
}
329+
330+
if (db_obj->initialised) {
331+
ret = sqlite3_extended_result_codes(db_obj->db, enable ? 1 : 0);
332+
if (ret == SQLITE_OK)
333+
{
334+
RETURN_TRUE;
335+
}
336+
}
337+
338+
RETURN_FALSE;
339+
}
340+
/* }}} */
341+
292342
/* {{{ proto string SQLite3::lastErrorMsg()
293343
Returns english text describing the most recent failed sqlite API call for the database connection. */
294344
PHP_METHOD(sqlite3, lastErrorMsg)
@@ -2157,35 +2207,41 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3result_fetcharray, 0, 0, 0)
21572207
ZEND_ARG_INFO(0, mode)
21582208
ZEND_END_ARG_INFO()
21592209

2210+
ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_enableextended, 0, 0, 1)
2211+
ZEND_ARG_INFO(0, enable)
2212+
ZEND_END_ARG_INFO()
2213+
21602214
ZEND_BEGIN_ARG_INFO(arginfo_sqlite3_void, 0)
21612215
ZEND_END_ARG_INFO()
21622216
/* }}} */
21632217

21642218
/* {{{ php_sqlite3_class_methods */
21652219
static const zend_function_entry php_sqlite3_class_methods[] = {
2166-
PHP_ME(sqlite3, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
2167-
PHP_ME(sqlite3, close, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2168-
PHP_ME(sqlite3, exec, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2169-
PHP_ME(sqlite3, version, arginfo_sqlite3_void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
2170-
PHP_ME(sqlite3, lastInsertRowID, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2171-
PHP_ME(sqlite3, lastErrorCode, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2172-
PHP_ME(sqlite3, lastErrorMsg, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2173-
PHP_ME(sqlite3, busyTimeout, arginfo_sqlite3_busytimeout, ZEND_ACC_PUBLIC)
2220+
PHP_ME(sqlite3, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
2221+
PHP_ME(sqlite3, close, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2222+
PHP_ME(sqlite3, exec, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2223+
PHP_ME(sqlite3, version, arginfo_sqlite3_void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
2224+
PHP_ME(sqlite3, lastInsertRowID, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2225+
PHP_ME(sqlite3, lastErrorCode, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2226+
PHP_ME(sqlite3, lastExtendedErrorCode, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2227+
PHP_ME(sqlite3, enableExtendedResultCodes, arginfo_sqlite3_enableextended, ZEND_ACC_PUBLIC)
2228+
PHP_ME(sqlite3, lastErrorMsg, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2229+
PHP_ME(sqlite3, busyTimeout, arginfo_sqlite3_busytimeout, ZEND_ACC_PUBLIC)
21742230
#ifndef SQLITE_OMIT_LOAD_EXTENSION
2175-
PHP_ME(sqlite3, loadExtension, arginfo_sqlite3_loadextension, ZEND_ACC_PUBLIC)
2231+
PHP_ME(sqlite3, loadExtension, arginfo_sqlite3_loadextension, ZEND_ACC_PUBLIC)
21762232
#endif
2177-
PHP_ME(sqlite3, changes, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2178-
PHP_ME(sqlite3, escapeString, arginfo_sqlite3_escapestring, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
2179-
PHP_ME(sqlite3, prepare, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2180-
PHP_ME(sqlite3, query, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2181-
PHP_ME(sqlite3, querySingle, arginfo_sqlite3_querysingle, ZEND_ACC_PUBLIC)
2182-
PHP_ME(sqlite3, createFunction, arginfo_sqlite3_createfunction, ZEND_ACC_PUBLIC)
2183-
PHP_ME(sqlite3, createAggregate, arginfo_sqlite3_createaggregate, ZEND_ACC_PUBLIC)
2184-
PHP_ME(sqlite3, createCollation, arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
2185-
PHP_ME(sqlite3, openBlob, arginfo_sqlite3_openblob, ZEND_ACC_PUBLIC)
2186-
PHP_ME(sqlite3, enableExceptions, arginfo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
2233+
PHP_ME(sqlite3, changes, arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
2234+
PHP_ME(sqlite3, escapeString, arginfo_sqlite3_escapestring, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
2235+
PHP_ME(sqlite3, prepare, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2236+
PHP_ME(sqlite3, query, arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
2237+
PHP_ME(sqlite3, querySingle, arginfo_sqlite3_querysingle, ZEND_ACC_PUBLIC)
2238+
PHP_ME(sqlite3, createFunction, arginfo_sqlite3_createfunction, ZEND_ACC_PUBLIC)
2239+
PHP_ME(sqlite3, createAggregate, arginfo_sqlite3_createaggregate, ZEND_ACC_PUBLIC)
2240+
PHP_ME(sqlite3, createCollation, arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
2241+
PHP_ME(sqlite3, openBlob, arginfo_sqlite3_openblob, ZEND_ACC_PUBLIC)
2242+
PHP_ME(sqlite3, enableExceptions, arginfo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
21872243
#if SQLITE_VERSION_NUMBER >= 3006011
2188-
PHP_ME(sqlite3, backup, arginfo_sqlite3_backup, ZEND_ACC_PUBLIC)
2244+
PHP_ME(sqlite3, backup, arginfo_sqlite3_backup, ZEND_ACC_PUBLIC)
21892245
#endif
21902246
/* Aliases */
21912247
PHP_MALIAS(sqlite3, __construct, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
SQLite3 extended error code Function
3+
--SKIPIF--
4+
<?php require_once(__DIR__ . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
require_once(__DIR__ . '/new_db.inc');
9+
10+
$db->query("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
11+
12+
echo "Inserting first time which should succeed" . PHP_EOL;
13+
$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
14+
echo "First Error Code: " . $db->lastErrorCode() . PHP_EOL;
15+
16+
echo "Inserting second time which should fail" . PHP_EOL;
17+
$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
18+
echo "Second Error Code: " . $db->lastErrorCode() . PHP_EOL;
19+
echo "Second Extended Error Code: " . $db->lastExtendedErrorCode() . PHP_EOL;
20+
21+
echo "Closing database\n";
22+
var_dump($db->close());
23+
echo "Done" . PHP_EOL;
24+
?>
25+
--EXPECTF--
26+
Inserting first time which should succeed
27+
First Error Code: 0
28+
Inserting second time which should fail
29+
30+
Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
31+
Second Error Code: 19
32+
Second Extended Error Code: 1555
33+
Closing database
34+
bool(true)
35+
Done
36+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
SQLite3 enable Extended Error Result Codes
3+
--SKIPIF--
4+
<?php require_once(__DIR__ . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
require_once(__DIR__ . '/new_db.inc');
9+
10+
$db->query("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
11+
12+
echo "Inserting first time which should succeed" . PHP_EOL;
13+
$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
14+
echo "First Error Code: " . $db->lastErrorCode() . PHP_EOL;
15+
16+
echo "Inserting second time which should fail" . PHP_EOL;
17+
$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
18+
echo "Second Error Code: " . $db->lastErrorCode() . PHP_EOL;
19+
20+
echo "Toggling extended error codes and re-inserting a third time" . PHP_EOL;
21+
$db->enableExtendedResultCodes(true);
22+
$result = $db->query("INSERT INTO DOG VALUES (1, 'Annoying Dog', 1)");
23+
echo "Third (Extended) Error Code: " . $db->lastErrorCode() . PHP_EOL;
24+
25+
echo "Closing database\n";
26+
var_dump($db->close());
27+
echo "Done" . PHP_EOL;
28+
?>
29+
--EXPECTF--
30+
Inserting first time which should succeed
31+
First Error Code: 0
32+
Inserting second time which should fail
33+
34+
Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
35+
Second Error Code: 19
36+
Toggling extended error codes and re-inserting a third time
37+
38+
Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
39+
Third (Extended) Error Code: 1555
40+
Closing database
41+
bool(true)
42+
Done
43+

0 commit comments

Comments
 (0)