]> granicus.if.org Git - php/commitdiff
Implement SQLite extended result code functionality
authorRobert Kopack <rkopack@tenable.com>
Tue, 2 Jul 2019 10:59:01 +0000 (12:59 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 2 Jul 2019 11:03:35 +0000 (13:03 +0200)
UPGRADING
ext/pdo_sqlite/pdo_sqlite.c
ext/pdo_sqlite/php_pdo_sqlite_int.h
ext/pdo_sqlite/sqlite_driver.c
ext/pdo_sqlite/tests/pdo_sqlite_extendederror_attr.phpt [new file with mode: 0644]
ext/sqlite3/sqlite3.c
ext/sqlite3/tests/sqlite3_38_extended_error.phpt [new file with mode: 0644]
ext/sqlite3/tests/sqlite3_39_toggleExtended.phpt [new file with mode: 0644]

index 6ce27cef86f6dc52b545fd66b618e4b31d27a2bf..3fd2caa0f67500cf95914f6cfdcbe96f317420bf 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -265,6 +265,14 @@ PHP 7.4 UPGRADE NOTES
   . PDOStatement::getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT) allows to
     check whether this statement is read-only, i.e. whether it doesn't modify
     the database.
+  . PDO::setAttribute(PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES, true) enables the
+    use of SQLite3 extended result codes in errorInfo().
+
+r SQLite3:
+  . Added SQLite3::lastExtendedErrorCode() to fetch the last extended result
+    code.
+  . Added SQLite3::enableExtendedResultCodes($enable = true), which will make
+    SQLite3::lastErrorCode() return extended result codes.
 
 - Standard:
   . strip_tags() now also accepts an array of allowed tags: Instead of
index 82e2d79b06b983164ec16666a734ab1e1387bc97..5c2fd63cdbd7839b67118bb09752a8f08ee2691b 100644 (file)
@@ -76,6 +76,7 @@ PHP_MINIT_FUNCTION(pdo_sqlite)
        REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long)SQLITE_OPEN_READWRITE);
        REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long)SQLITE_OPEN_CREATE);
        REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT);
+       REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);
 
        return php_pdo_register_driver(&pdo_sqlite_driver);
 }
index e784294dbce2edb96d4f32406c5df79a7126cbd3..c1d3a953a03b5f21b7fb75e0d6bec55a7b25c503 100644 (file)
@@ -76,7 +76,8 @@ extern const struct pdo_stmt_methods sqlite_stmt_methods;
 
 enum {
        PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC,
-       PDO_SQLITE_ATTR_READONLY_STATEMENT
+       PDO_SQLITE_ATTR_READONLY_STATEMENT,
+       PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
 };
 
 #endif
index ab65049a9695cfd539ef23b0a759d551fa0ddc31..120488e229655561e9f039e9182eca8129fc2a04 100644 (file)
@@ -305,6 +305,9 @@ static int pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
                case PDO_ATTR_TIMEOUT:
                        sqlite3_busy_timeout(H->db, zval_get_long(val) * 1000);
                        return 1;
+               case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES:
+                       sqlite3_extended_result_codes(H->db, zval_get_long(val));
+                       return 1;
        }
        return 0;
 }
diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_extendederror_attr.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_extendederror_attr.phpt
new file mode 100644 (file)
index 0000000..50b939b
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+PDO_sqlite: Testing PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES
+--SKIPIF--
+<?php if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ?>
+--FILE--
+<?php
+
+echo "Creating new PDO" . PHP_EOL;
+$db = new PDO('sqlite::memory:');
+
+$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
+
+echo "Inserting first time which should succeed" . PHP_EOL;
+$db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+$errorInfo = $db->errorInfo();
+echo sprintf("First Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
+
+echo "Inserting second time which should fail" . PHP_EOL;
+$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+$errorInfo = $db->errorInfo();
+echo sprintf("Second Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
+
+
+echo "Creating new PDO with Extended Result Codes turned on" . PHP_EOL;
+$db = new PDO('sqlite::memory:', '', '', [PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES => TRUE]);
+
+$db->exec("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
+
+echo "Inserting first time which should succeed" . PHP_EOL;
+$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+$errorInfo = $db->errorInfo();
+echo sprintf("First (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
+
+echo "Inserting second time which should fail" . PHP_EOL;
+$result = $db->exec("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+$errorInfo = $db->errorInfo();
+echo sprintf("Second (Extended) Error Info: SQLSTATE Error Code: (%s), Driver Specific Error Code: (%s)", $errorInfo[0], $errorInfo[1]) . PHP_EOL;
+
+?>
+--EXPECT--
+Creating new PDO
+Inserting first time which should succeed
+First Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()
+Inserting second time which should fail
+Second Error Info: SQLSTATE Error Code: (23000), Driver Specific Error Code: (19)
+Creating new PDO with Extended Result Codes turned on
+Inserting first time which should succeed
+First (Extended) Error Info: SQLSTATE Error Code: (00000), Driver Specific Error Code: ()
+Inserting second time which should fail
+Second (Extended) Error Info: SQLSTATE Error Code: (HY000), Driver Specific Error Code: (1555)
index e3474e4acba22bcc998eea5b62d66d1870b4e7da..429be63cb6874bbf60ce3fa1b56987740ef2c361 100644 (file)
@@ -289,6 +289,56 @@ PHP_METHOD(sqlite3, lastErrorCode)
 }
 /* }}} */
 
+/* {{{ proto int SQLite3::lastExtendedErrorCode()
+   Returns the numeric extended result code of the most recent failed sqlite API call for the database connection. */
+PHP_METHOD(sqlite3, lastExtendedErrorCode)
+{
+       php_sqlite3_db_object *db_obj;
+       zval *object = ZEND_THIS;
+       db_obj = Z_SQLITE3_DB_P(object);
+
+       SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->db, SQLite3)
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               return;
+       }
+
+       if (db_obj->initialised) {
+               RETURN_LONG(sqlite3_extended_errcode(db_obj->db));
+       } else {
+               RETURN_LONG(0);
+       }
+}
+/* }}} */
+
+/* {{{ proto bool SQLite3::enableExtendedResultCodes([bool enable = true])
+    Turns on or off the extended result codes feature of SQLite. */
+PHP_METHOD(sqlite3, enableExtendedResultCodes)
+{
+       php_sqlite3_db_object *db_obj;
+       zval *object = ZEND_THIS;
+       zend_bool enable = 1;
+       db_obj = Z_SQLITE3_DB_P(object);
+       int ret;
+
+       SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->db, SQLite3)
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable) == FAILURE) {
+               return;
+       }
+
+       if (db_obj->initialised) {
+               ret = sqlite3_extended_result_codes(db_obj->db, enable ? 1 : 0);
+               if (ret == SQLITE_OK)
+               {
+                       RETURN_TRUE;
+               }
+       }
+
+       RETURN_FALSE;
+}
+/* }}} */
+
 /* {{{ proto string SQLite3::lastErrorMsg()
    Returns english text describing the most recent failed sqlite API call for the database connection. */
 PHP_METHOD(sqlite3, lastErrorMsg)
@@ -2157,35 +2207,41 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3result_fetcharray, 0, 0, 0)
        ZEND_ARG_INFO(0, mode)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_enableextended, 0, 0, 1)
+       ZEND_ARG_INFO(0, enable)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO(arginfo_sqlite3_void, 0)
 ZEND_END_ARG_INFO()
 /* }}} */
 
 /* {{{ php_sqlite3_class_methods */
 static const zend_function_entry php_sqlite3_class_methods[] = {
-       PHP_ME(sqlite3,         open,                           arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         close,                          arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         exec,                           arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         version,                        arginfo_sqlite3_void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(sqlite3,         lastInsertRowID,        arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         lastErrorCode,          arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         lastErrorMsg,           arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         busyTimeout,            arginfo_sqlite3_busytimeout, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         open,                                           arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         close,                                          arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         exec,                                           arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         version,                                        arginfo_sqlite3_void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(sqlite3,         lastInsertRowID,                        arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         lastErrorCode,                          arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         lastExtendedErrorCode,          arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         enableExtendedResultCodes,      arginfo_sqlite3_enableextended, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         lastErrorMsg,                           arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         busyTimeout,                            arginfo_sqlite3_busytimeout, ZEND_ACC_PUBLIC)
 #ifndef SQLITE_OMIT_LOAD_EXTENSION
-       PHP_ME(sqlite3,         loadExtension,          arginfo_sqlite3_loadextension, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         loadExtension,                          arginfo_sqlite3_loadextension, ZEND_ACC_PUBLIC)
 #endif
-       PHP_ME(sqlite3,         changes,                        arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         escapeString,           arginfo_sqlite3_escapestring, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
-       PHP_ME(sqlite3,         prepare,                        arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         query,                          arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         querySingle,            arginfo_sqlite3_querysingle, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         createFunction,         arginfo_sqlite3_createfunction, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         createAggregate,        arginfo_sqlite3_createaggregate, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         createCollation,        arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         openBlob,                       arginfo_sqlite3_openblob, ZEND_ACC_PUBLIC)
-       PHP_ME(sqlite3,         enableExceptions,       arginfo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         changes,                                        arginfo_sqlite3_void, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         escapeString,                           arginfo_sqlite3_escapestring, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(sqlite3,         prepare,                                        arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         query,                                          arginfo_sqlite3_query, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         querySingle,                            arginfo_sqlite3_querysingle, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         createFunction,                         arginfo_sqlite3_createfunction, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         createAggregate,                        arginfo_sqlite3_createaggregate, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         createCollation,                        arginfo_sqlite3_createcollation, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         openBlob,                                       arginfo_sqlite3_openblob, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         enableExceptions,                       arginfo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC)
 #if SQLITE_VERSION_NUMBER >= 3006011
-       PHP_ME(sqlite3,         backup,                         arginfo_sqlite3_backup, ZEND_ACC_PUBLIC)
+       PHP_ME(sqlite3,         backup,                                         arginfo_sqlite3_backup, ZEND_ACC_PUBLIC)
 #endif
        /* Aliases */
        PHP_MALIAS(sqlite3,     __construct, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC)
diff --git a/ext/sqlite3/tests/sqlite3_38_extended_error.phpt b/ext/sqlite3/tests/sqlite3_38_extended_error.phpt
new file mode 100644 (file)
index 0000000..8c16889
--- /dev/null
@@ -0,0 +1,36 @@
+--TEST--
+SQLite3 extended error code Function
+--SKIPIF--
+<?php require_once(__DIR__ . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(__DIR__ . '/new_db.inc');
+
+$db->query("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
+
+echo "Inserting first time which should succeed" . PHP_EOL;
+$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+echo "First Error Code: " . $db->lastErrorCode() . PHP_EOL;
+
+echo "Inserting second time which should fail" . PHP_EOL;
+$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+echo "Second Error Code: " . $db->lastErrorCode() . PHP_EOL;
+echo "Second Extended Error Code: " . $db->lastExtendedErrorCode() . PHP_EOL;
+
+echo "Closing database\n";
+var_dump($db->close());
+echo "Done" . PHP_EOL;
+?>
+--EXPECTF--
+Inserting first time which should succeed
+First Error Code: 0
+Inserting second time which should fail
+
+Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
+Second Error Code: 19
+Second Extended Error Code: 1555
+Closing database
+bool(true)
+Done
+
diff --git a/ext/sqlite3/tests/sqlite3_39_toggleExtended.phpt b/ext/sqlite3/tests/sqlite3_39_toggleExtended.phpt
new file mode 100644 (file)
index 0000000..6c2240e
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+SQLite3 enable Extended Error Result Codes 
+--SKIPIF--
+<?php require_once(__DIR__ . '/skipif.inc'); ?>
+--FILE--
+<?php
+
+require_once(__DIR__ . '/new_db.inc');
+
+$db->query("CREATE TABLE dog ( id INTEGER PRIMARY KEY, name TEXT, annoying INTEGER )");
+
+echo "Inserting first time which should succeed" . PHP_EOL;
+$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+echo "First Error Code: " . $db->lastErrorCode() . PHP_EOL;
+
+echo "Inserting second time which should fail" . PHP_EOL;
+$result = $db->query("INSERT INTO dog VALUES (1, 'Annoying Dog', 1)");
+echo "Second Error Code: " . $db->lastErrorCode() . PHP_EOL;
+
+echo "Toggling extended error codes and re-inserting a third time" . PHP_EOL;
+$db->enableExtendedResultCodes(true);
+$result = $db->query("INSERT INTO DOG VALUES (1, 'Annoying Dog', 1)");
+echo "Third (Extended) Error Code: " . $db->lastErrorCode() . PHP_EOL;
+
+echo "Closing database\n";
+var_dump($db->close());
+echo "Done" . PHP_EOL;
+?>
+--EXPECTF--
+Inserting first time which should succeed
+First Error Code: 0
+Inserting second time which should fail
+
+Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
+Second Error Code: 19
+Toggling extended error codes and re-inserting a third time
+
+Warning: SQLite3::query(): Unable to execute statement: UNIQUE constraint failed: dog.id in %s on line %d
+Third (Extended) Error Code: 1555
+Closing database
+bool(true)
+Done
+