1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 08:12:21 +01:00

pdo_pgsql: add support for dollar-quotes

RFC: http://wiki.php.net/rfc/pdo_driver_specific_parsers
This commit is contained in:
Matteo Beccati
2024-05-02 10:39:56 +02:00
parent e82c486918
commit 01879ec254
4 changed files with 67 additions and 9 deletions

View File

@@ -18,7 +18,8 @@
#define PDO_PARSER_BIND 2
#define PDO_PARSER_BIND_POS 3
#define PDO_PARSER_ESCAPED_QUESTION 4
#define PDO_PARSER_EOI 5
#define PDO_PARSER_CUSTOM_QUOTE 5
#define PDO_PARSER_EOI 6
#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1

View File

@@ -53,6 +53,11 @@ struct placeholder {
struct placeholder *next;
};
struct custom_quote {
const char *pos;
size_t len;
};
static void free_param_name(zval *el) {
zend_string_release(Z_PTR_P(el));
}
@@ -70,6 +75,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
int query_type = PDO_PLACEHOLDER_NONE;
struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
int (*scan)(pdo_scanner_t *s);
struct custom_quote custom_quote = {NULL, 0};
scan = stmt->dbh->methods->scanner ? stmt->dbh->methods->scanner : default_scanner;
@@ -78,6 +84,25 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
/* phase 1: look for args */
while((t = scan(&s)) != PDO_PARSER_EOI) {
if (custom_quote.pos) {
/* Inside a custom quote */
if (t == PDO_PARSER_CUSTOM_QUOTE && custom_quote.len == s.cur - s.tok && !strncmp(s.tok, custom_quote.pos, custom_quote.len)) {
/* Matching closing quote found, end custom quoting */
custom_quote.pos = NULL;
custom_quote.len = 0;
}
continue;
}
if (t == PDO_PARSER_CUSTOM_QUOTE) {
/* Start of a custom quote, keep a reference to search for the matching closing quote */
custom_quote.pos = s.tok;
custom_quote.len = s.cur - s.tok;
continue;
}
if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS || t == PDO_PARSER_ESCAPED_QUESTION) {
if (t == PDO_PARSER_ESCAPED_QUESTION && stmt->supports_placeholders == PDO_PLACEHOLDER_POSITIONAL) {
/* escaped question marks unsupported, treat as text */

View File

@@ -29,8 +29,10 @@ int pdo_pgsql_scanner(pdo_scanner_t *s)
BINDCHR = [:][a-zA-Z0-9_]+;
QUESTION = [?];
ESCQUESTION = [?][?];
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
SPECIALS = [eE:?"'/-];
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--".*);
DOLQ_START = [A-Za-z\200-\377_];
DOLQ_CONT = [A-Za-z\200-\377_0-9];
SPECIALS = [$eE:?"'/-];
MULTICHAR = [:]{2,};
ANYNOEOF = [\001-\377];
*/
@@ -39,6 +41,7 @@ int pdo_pgsql_scanner(pdo_scanner_t *s)
[eE](['](([']['])|([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
(["]((["]["])|ANYNOEOF\["])*["]) { RET(PDO_PARSER_TEXT); }
(['](([']['])|ANYNOEOF\['])*[']) { RET(PDO_PARSER_TEXT); }
[$](DOLQ_START DOLQ_CONT*)?[$] { RET(PDO_PARSER_CUSTOM_QUOTE); }
MULTICHAR { RET(PDO_PARSER_TEXT); }
ESCQUESTION { RET(PDO_PARSER_ESCAPED_QUESTION); }
BINDCHR { RET(PDO_PARSER_BIND); }

View File

@@ -17,20 +17,49 @@ require_once __DIR__ . "/config.inc";
$db = new PdoPgsql($config['ENV']['PDOTEST_DSN']);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = <<<EOF
SELECT e'He''ll\'o ' || ? AS b -- '
$query = <<<'EOF'
SELECT e'He''ll\'o? ' || ? AS b -- '
UNION ALL
SELECT 'He''ll''o\' || ? AS b -- '
SELECT 'He''ll''o?\' || ? AS b -- '
UNION ALL
SELECT U&'d\0061t\+000061? ' || ? AS b /* :name */
UNION ALL
SELECT $__$Is this a $$dollar$$ 'escaping'? $__$ || ? AS b -- ?
EOF;
$stmt = $db->prepare($query);
$stmt->execute(['World', 'World']);
$stmt->execute(['World', 'World', 'base', 'Yes']);
while ($row = $stmt->fetchColumn()) {
var_dump($row);
}
// Nested comments are incompatible: PDO parses the "?" inside the comment, Postgres doesn't
$query = <<<'EOF'
SELECT 'Hello' || ? /* is this a /* nested */ 'comment' ? */
EOF;
$stmt = $db->prepare($query);
try {
$stmt->execute(['X', 'Y', 'Z']);
} catch (PDOException $e) {
// PDO error: 3 parameters vs 2 expected
var_dump('HY093' === $e->getCode());
}
try {
$stmt->execute(['X', 'Y']);
} catch (PDOException $e) {
// PgSQL error: Indeterminate datatype (1 extra parameter)
var_dump('42P18' === $e->getCode());
}
?>
--EXPECT--
string(13) "He'll'o World"
string(13) "He'll'o\World"
string(14) "He'll'o? World"
string(14) "He'll'o?\World"
string(10) "data? base"
string(36) "Is this a $$dollar$$ 'escaping'? Yes"
bool(true)
bool(true)