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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user