Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ PHP NEWS

- PDO_SQLite:
. Fixed bug GH-24990 (Statement unusable after a failed execute()). (lazerg)
. Added Pdo\Sqlite::ATTR_DQS to disable double-quoted string literals
(GH-13597). (Juan Versolato Lopes)

- PGSQL:
. Fixed pg_lo_write() rejecting data containing null bytes. (Ilia Alshanetsky)
Expand Down
6 changes: 6 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ PHP 8.7 UPGRADE NOTES
A ValueError is thrown if the array is empty or contains an invalid
locale, and a TypeError if it contains a non-string value.

- PDO_SQLite:
. Added the Pdo\Sqlite::ATTR_DQS connection attribute. Setting it to false
makes SQLite reject double-quoted string literals, so "abc" is only read
as an identifier, as in the sqlite3 shell since SQLite 3.41.0. It is
available when PHP is built against SQLite 3.29.0 or later.

- Streams:
. The so_linger, so_rcvbuf and so_sndbuf stream socket context options are
now also applied to unix and unix datagram sockets.
Expand Down
5 changes: 5 additions & 0 deletions ext/pdo_sqlite/pdo_sqlite.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class Sqlite extends \PDO
/** @cvalue PDO_SQLITE_ATTR_TRANSACTION_MODE */
public const int ATTR_TRANSACTION_MODE = UNKNOWN;

#ifdef SQLITE_DBCONFIG_DQS_DML
/** @cvalue PDO_SQLITE_ATTR_DQS */
public const int ATTR_DQS = UNKNOWN;
#endif

public const int TRANSACTION_MODE_DEFERRED = 0;
public const int TRANSACTION_MODE_IMMEDIATE = 1;
public const int TRANSACTION_MODE_EXCLUSIVE = 2;
Expand Down
10 changes: 9 additions & 1 deletion ext/pdo_sqlite/pdo_sqlite_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ext/pdo_sqlite/php_pdo_sqlite_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ enum {
PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES,
PDO_SQLITE_ATTR_BUSY_STATEMENT,
PDO_SQLITE_ATTR_EXPLAIN_STATEMENT,
PDO_SQLITE_ATTR_TRANSACTION_MODE
PDO_SQLITE_ATTR_TRANSACTION_MODE,
PDO_SQLITE_ATTR_DQS
};

typedef int pdo_sqlite_create_collation_callback(void*, int, const void*, int, const void*);
Expand Down
25 changes: 25 additions & 0 deletions ext/pdo_sqlite/sqlite_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,15 @@ static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return
case PDO_SQLITE_ATTR_TRANSACTION_MODE:
ZVAL_LONG(return_value, H->transaction_mode);
break;
#ifdef SQLITE_DBCONFIG_DQS_DML
case PDO_SQLITE_ATTR_DQS: {
int dml = 0, ddl = 0;
sqlite3_db_config(H->db, SQLITE_DBCONFIG_DQS_DML, -1, &dml);
sqlite3_db_config(H->db, SQLITE_DBCONFIG_DQS_DDL, -1, &ddl);
ZVAL_BOOL(return_value, dml || ddl);
break;
}
#endif

default:
return 0;
Expand Down Expand Up @@ -351,6 +360,22 @@ static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
default:
return false;
}
#ifdef SQLITE_DBCONFIG_DQS_DML
case PDO_SQLITE_ATTR_DQS: {
bool bval;
if (!pdo_get_bool_param(&bval, val)) {
return false;
}
/* Double-quoted string literals are controlled separately for DML
* and DDL statements; toggle both so the attribute has a single
* meaning. */
if (sqlite3_db_config(H->db, SQLITE_DBCONFIG_DQS_DML, (int) bval, NULL) != SQLITE_OK
|| sqlite3_db_config(H->db, SQLITE_DBCONFIG_DQS_DDL, (int) bval, NULL) != SQLITE_OK) {
return false;
}
return true;
}
#endif
}
return false;
}
Expand Down
86 changes: 86 additions & 0 deletions ext/pdo_sqlite/tests/gh13597.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
--TEST--
GH-13597 (Allow disabling double-quoted string literals in SQLite)
--EXTENSIONS--
pdo_sqlite
--SKIPIF--
<?php
if (!defined('Pdo\Sqlite::ATTR_DQS')) die('skip SQLite < 3.29.0');
?>
--FILE--
<?php

function check(PDO $pdo, string $sql): void {
try {
var_dump($pdo->query($sql)->fetchColumn());
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}
}

$pdo = new Pdo\Sqlite('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

echo "Enabled:\n";
var_dump($pdo->setAttribute(Pdo\Sqlite::ATTR_DQS, true));
var_dump($pdo->getAttribute(Pdo\Sqlite::ATTR_DQS));
check($pdo, 'SELECT "abc"');
$pdo->exec('CREATE TABLE t1 (a TEXT)');
$pdo->exec('CREATE INDEX t1_idx ON t1("missing")');
echo "index created\n";

echo "Disabled:\n";
var_dump($pdo->setAttribute(Pdo\Sqlite::ATTR_DQS, false));
var_dump($pdo->getAttribute(Pdo\Sqlite::ATTR_DQS));
check($pdo, 'SELECT "abc"');
check($pdo, "SELECT 'abc'");
check($pdo, 'SELECT "a" FROM t1 UNION ALL SELECT 1');
try {
$pdo->exec('CREATE INDEX t1_idx2 ON t1("missing")');
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}

echo "Prepared statement:\n";
$pdo->setAttribute(Pdo\Sqlite::ATTR_DQS, true);
$stmt = $pdo->prepare('SELECT "abc"');
$stmt->execute();
var_dump($stmt->fetchColumn());
$pdo->setAttribute(Pdo\Sqlite::ATTR_DQS, false);
try {
$stmt->execute();
} catch (PDOException $e) {
echo $e->getMessage(), "\n";
}
$pdo->setAttribute(Pdo\Sqlite::ATTR_DQS, true);
$stmt->execute();
var_dump($stmt->fetchColumn());

echo "Constructor option:\n";
$pdo = new Pdo\Sqlite('sqlite::memory:', options: [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
Pdo\Sqlite::ATTR_DQS => false,
]);
var_dump($pdo->getAttribute(Pdo\Sqlite::ATTR_DQS));
check($pdo, 'SELECT "abc"');

?>
--EXPECTF--
Enabled:
bool(true)
bool(true)
string(3) "abc"
index created
Disabled:
bool(true)
bool(false)
SQLSTATE[HY000]: General error: 1 no such column: %Sabc%S
string(3) "abc"
int(1)
SQLSTATE[HY000]: General error: 1 no such column: %Smissing%S
Prepared statement:
string(3) "abc"
SQLSTATE[HY000]: General error: 1 no such column: %Sabc%S
string(3) "abc"
Constructor option:
bool(false)
SQLSTATE[HY000]: General error: 1 no such column: %Sabc%S
Loading