From 20307f41a7e257d5e6fe23f16556f2f813efab85 Mon Sep 17 00:00:00 2001 From: Matteo Beccati Date: Sun, 18 Dec 2016 13:13:31 +0100 Subject: [PATCH] Fix FR #71885 (Allow escaping question mark placeholders) --- ext/pdo/pdo_sql_parser.c | 135 +++++++++++++++++----------- ext/pdo/pdo_sql_parser.re | 76 +++++++++++----- ext/pdo/tests/bug_71885.phpt | 53 +++++++++++ ext/pdo_pgsql/pgsql_driver.c | 46 +++++----- ext/pdo_pgsql/tests/bug71885.phpt | 46 ++++++++++ ext/pdo_pgsql/tests/bug71885_2.phpt | 57 ++++++++++++ 6 files changed, 314 insertions(+), 99 deletions(-) create mode 100644 ext/pdo/tests/bug_71885.phpt create mode 100644 ext/pdo_pgsql/tests/bug71885.phpt create mode 100644 ext/pdo_pgsql/tests/bug71885_2.phpt diff --git a/ext/pdo/pdo_sql_parser.c b/ext/pdo/pdo_sql_parser.c index 8545861585615..d39f8d30fce56 100644 --- a/ext/pdo/pdo_sql_parser.c +++ b/ext/pdo/pdo_sql_parser.c @@ -27,7 +27,10 @@ #define PDO_PARSER_TEXT 1 #define PDO_PARSER_BIND 2 #define PDO_PARSER_BIND_POS 3 -#define PDO_PARSER_EOI 4 +#define PDO_PARSER_ESCAPED_QUESTION 4 +#define PDO_PARSER_EOI 5 + +#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1 #define RET(i) {s->cur = cursor; return i; } #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } @@ -47,11 +50,11 @@ static int scan(Scanner *s) char *cursor = s->cur; s->tok = cursor; - #line 55 "ext/pdo/pdo_sql_parser.re" + #line 59 "ext/pdo/pdo_sql_parser.re" -#line 55 "ext/pdo/pdo_sql_parser.c" +#line 58 "ext/pdo/pdo_sql_parser.c" { YYCTYPE yych; @@ -80,9 +83,9 @@ static int scan(Scanner *s) yych = *(YYMARKER = ++YYCURSOR); if (yych >= 0x01) goto yy37; yy4: -#line 63 "ext/pdo/pdo_sql_parser.re" +#line 68 "ext/pdo/pdo_sql_parser.re" { SKIP_ONE(PDO_PARSER_TEXT); } -#line 86 "ext/pdo/pdo_sql_parser.c" +#line 89 "ext/pdo/pdo_sql_parser.c" yy5: yych = *(YYMARKER = ++YYCURSOR); if (yych <= 0x00) goto yy4; @@ -152,8 +155,8 @@ static int scan(Scanner *s) case 'w': case 'x': case 'y': - case 'z': goto yy26; - case ':': goto yy29; + case 'z': goto yy25; + case ':': goto yy28; default: goto yy4; } yy7: @@ -163,9 +166,9 @@ static int scan(Scanner *s) default: goto yy8; } yy8: -#line 62 "ext/pdo/pdo_sql_parser.re" +#line 67 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_BIND_POS); } -#line 169 "ext/pdo/pdo_sql_parser.c" +#line 172 "ext/pdo/pdo_sql_parser.c" yy9: yych = *++YYCURSOR; goto yy4; @@ -202,9 +205,9 @@ static int scan(Scanner *s) default: goto yy12; } yy14: -#line 65 "ext/pdo/pdo_sql_parser.re" +#line 70 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 208 "ext/pdo/pdo_sql_parser.c" +#line 211 "ext/pdo/pdo_sql_parser.c" yy15: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -225,9 +228,9 @@ static int scan(Scanner *s) yy19: ++YYCURSOR; yy20: -#line 64 "ext/pdo/pdo_sql_parser.re" +#line 69 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 231 "ext/pdo/pdo_sql_parser.c" +#line 234 "ext/pdo/pdo_sql_parser.c" yy21: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -239,17 +242,10 @@ static int scan(Scanner *s) } yy23: ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - switch (yych) { - case '?': goto yy23; - default: goto yy25; - } +#line 65 "ext/pdo/pdo_sql_parser.re" + { RET(PDO_PARSER_ESCAPED_QUESTION); } +#line 248 "ext/pdo/pdo_sql_parser.c" yy25: -#line 60 "ext/pdo/pdo_sql_parser.re" - { RET(PDO_PARSER_TEXT); } -#line 252 "ext/pdo/pdo_sql_parser.c" -yy26: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; @@ -316,21 +312,25 @@ static int scan(Scanner *s) case 'w': case 'x': case 'y': - case 'z': goto yy26; - default: goto yy28; + case 'z': goto yy25; + default: goto yy27; } -yy28: -#line 61 "ext/pdo/pdo_sql_parser.re" +yy27: +#line 66 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_BIND); } -#line 326 "ext/pdo/pdo_sql_parser.c" -yy29: +#line 322 "ext/pdo/pdo_sql_parser.c" +yy28: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; switch (yych) { - case ':': goto yy29; - default: goto yy25; + case ':': goto yy28; + default: goto yy30; } +yy30: +#line 64 "ext/pdo/pdo_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 334 "ext/pdo/pdo_sql_parser.c" yy31: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); @@ -350,7 +350,7 @@ static int scan(Scanner *s) goto yy31; yy34: ++YYCURSOR; -#line 59 "ext/pdo/pdo_sql_parser.re" +#line 63 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } #line 356 "ext/pdo/pdo_sql_parser.c" yy36: @@ -372,11 +372,11 @@ static int scan(Scanner *s) goto yy36; yy39: ++YYCURSOR; -#line 58 "ext/pdo/pdo_sql_parser.re" +#line 62 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } #line 378 "ext/pdo/pdo_sql_parser.c" } -#line 66 "ext/pdo/pdo_sql_parser.re" +#line 71 "ext/pdo/pdo_sql_parser.re" } @@ -401,7 +401,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len char *ptr, *newbuffer; int t; uint32_t bindno = 0; - int ret = 0; + int ret = 0, escapes = 0; size_t newbuffer_len; HashTable *params; struct pdo_bound_param_data *param; @@ -414,14 +414,19 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len /* phase 1: look for args */ while((t = scan(&s)) != PDO_PARSER_EOI) { - if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) { + 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 */ + continue; + } + if (t == PDO_PARSER_BIND) { int len = s.cur - s.tok; if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) { continue; } query_type |= PDO_PLACEHOLDER_NAMED; - } else { + } else if (t == PDO_PARSER_BIND_POS) { query_type |= PDO_PLACEHOLDER_POSITIONAL; } @@ -430,7 +435,16 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len plc->next = NULL; plc->pos = s.tok; plc->len = s.cur - s.tok; - plc->bindno = bindno++; + + if (t == PDO_PARSER_ESCAPED_QUESTION) { + plc->bindno = PDO_PARSER_BINDNO_ESCAPED_CHAR; + plc->quoted = "?"; + plc->qlen = 1; + plc->freeq = 0; + escapes++; + } else { + plc->bindno = bindno++; + } if (placetail) { placetail->next = plc; @@ -441,7 +455,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len } } - if (bindno == 0) { + if (!placeholders) { /* nothing to do; good! */ return 0; } @@ -456,11 +470,11 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) { /* query matches native syntax */ - ret = 0; - goto clean_up; + newbuffer_len = inquery_len; + goto rewrite; } - if (stmt->named_rewrite_template) { + if (query_type == PDO_PLACEHOLDER_NAMED && stmt->named_rewrite_template) { /* magic/hack. * We we pretend that the query was positional even if * it was named so that we fall into the @@ -471,14 +485,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len params = stmt->bound_params; - /* Do we have placeholders but no bound params */ - if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { - pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound"); - ret = -1; - goto clean_up; - } - - if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { + if (bindno && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements(params)) { /* extra bit of validation for instances when same params are bound more than once */ if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) { int ok = 1; @@ -504,7 +511,16 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len newbuffer_len = inquery_len; /* let's quote all the values */ - for (plc = placeholders; plc; plc = plc->next) { + for (plc = placeholders; plc && params; plc = plc->next) { + if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) { + /* escaped character */ + continue; + } + + if (query_type == PDO_PLACEHOLDER_NONE) { + continue; + } + if (query_type == PDO_PLACEHOLDER_POSITIONAL) { param = zend_hash_index_find_ptr(params, plc->bindno); } else { @@ -618,7 +634,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len rewrite: /* allocate output buffer */ - newbuffer = emalloc(newbuffer_len + 1); + newbuffer = emalloc(newbuffer_len - escapes + 1); *outquery = newbuffer; /* and build the query */ @@ -631,8 +647,13 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len memcpy(newbuffer, ptr, t); newbuffer += t; } - memcpy(newbuffer, plc->quoted, plc->qlen); - newbuffer += plc->qlen; + if (plc->quoted) { + memcpy(newbuffer, plc->quoted, plc->qlen); + newbuffer += plc->qlen; + } else { + memcpy(newbuffer, plc->pos, plc->len); + newbuffer += plc->len; + } ptr = plc->pos + plc->len; plc = plc->next; @@ -665,6 +686,11 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len for (plc = placeholders; plc; plc = plc->next) { int skip_map = 0; char *p; + + if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) { + continue; + } + name = estrndup(plc->pos, plc->len); /* check if bound parameter is already available */ @@ -710,6 +736,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len efree(name); plc->quoted = "?"; plc->qlen = 1; + newbuffer_len -= plc->len - 1; } goto rewrite; diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index 20b1dbdcf5e86..5c3e8ab5e3a56 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -25,7 +25,10 @@ #define PDO_PARSER_TEXT 1 #define PDO_PARSER_BIND 2 #define PDO_PARSER_BIND_POS 3 -#define PDO_PARSER_EOI 4 +#define PDO_PARSER_ESCAPED_QUESTION 4 +#define PDO_PARSER_EOI 5 + +#define PDO_PARSER_BINDNO_ESCAPED_CHAR -1 #define RET(i) {s->cur = cursor; return i; } #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } @@ -48,9 +51,10 @@ static int scan(Scanner *s) /*!re2c BINDCHR = [:][a-zA-Z0-9_]+; QUESTION = [?]; + ESCQUESTION = [?][?]; COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*); SPECIALS = [:?"'-/]; - MULTICHAR = ([:]{2,}|[?]{2,}); + MULTICHAR = [:]{2,}; ANYNOEOF = [\001-\377]; */ @@ -58,6 +62,7 @@ static int scan(Scanner *s) (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); } (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); } MULTICHAR { RET(PDO_PARSER_TEXT); } + ESCQUESTION { RET(PDO_PARSER_ESCAPED_QUESTION); } BINDCHR { RET(PDO_PARSER_BIND); } QUESTION { RET(PDO_PARSER_BIND_POS); } SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); } @@ -87,7 +92,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len char *ptr, *newbuffer; int t; uint32_t bindno = 0; - int ret = 0; + int ret = 0, escapes = 0; size_t newbuffer_len; HashTable *params; struct pdo_bound_param_data *param; @@ -100,14 +105,19 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len /* phase 1: look for args */ while((t = scan(&s)) != PDO_PARSER_EOI) { - if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) { + 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 */ + continue; + } + if (t == PDO_PARSER_BIND) { int len = s.cur - s.tok; if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) { continue; } query_type |= PDO_PLACEHOLDER_NAMED; - } else { + } else if (t == PDO_PARSER_BIND_POS) { query_type |= PDO_PLACEHOLDER_POSITIONAL; } @@ -116,7 +126,16 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len plc->next = NULL; plc->pos = s.tok; plc->len = s.cur - s.tok; - plc->bindno = bindno++; + + if (t == PDO_PARSER_ESCAPED_QUESTION) { + plc->bindno = PDO_PARSER_BINDNO_ESCAPED_CHAR; + plc->quoted = "?"; + plc->qlen = 1; + plc->freeq = 0; + escapes++; + } else { + plc->bindno = bindno++; + } if (placetail) { placetail->next = plc; @@ -127,7 +146,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len } } - if (bindno == 0) { + if (!placeholders) { /* nothing to do; good! */ return 0; } @@ -142,11 +161,11 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) { /* query matches native syntax */ - ret = 0; - goto clean_up; + newbuffer_len = inquery_len; + goto rewrite; } - if (stmt->named_rewrite_template) { + if (query_type == PDO_PLACEHOLDER_NAMED && stmt->named_rewrite_template) { /* magic/hack. * We we pretend that the query was positional even if * it was named so that we fall into the @@ -157,14 +176,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len params = stmt->bound_params; - /* Do we have placeholders but no bound params */ - if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { - pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound"); - ret = -1; - goto clean_up; - } - - if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) { + if (bindno && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements(params)) { /* extra bit of validation for instances when same params are bound more than once */ if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) { int ok = 1; @@ -190,7 +202,16 @@ safe: newbuffer_len = inquery_len; /* let's quote all the values */ - for (plc = placeholders; plc; plc = plc->next) { + for (plc = placeholders; plc && params; plc = plc->next) { + if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) { + /* escaped character */ + continue; + } + + if (query_type == PDO_PLACEHOLDER_NONE) { + continue; + } + if (query_type == PDO_PLACEHOLDER_POSITIONAL) { param = zend_hash_index_find_ptr(params, plc->bindno); } else { @@ -304,7 +325,7 @@ safe: rewrite: /* allocate output buffer */ - newbuffer = emalloc(newbuffer_len + 1); + newbuffer = emalloc(newbuffer_len - escapes + 1); *outquery = newbuffer; /* and build the query */ @@ -317,8 +338,13 @@ rewrite: memcpy(newbuffer, ptr, t); newbuffer += t; } - memcpy(newbuffer, plc->quoted, plc->qlen); - newbuffer += plc->qlen; + if (plc->quoted) { + memcpy(newbuffer, plc->quoted, plc->qlen); + newbuffer += plc->qlen; + } else { + memcpy(newbuffer, plc->pos, plc->len); + newbuffer += plc->len; + } ptr = plc->pos + plc->len; plc = plc->next; @@ -351,6 +377,11 @@ rewrite: for (plc = placeholders; plc; plc = plc->next) { int skip_map = 0; char *p; + + if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) { + continue; + } + name = estrndup(plc->pos, plc->len); /* check if bound parameter is already available */ @@ -396,6 +427,7 @@ rewrite: efree(name); plc->quoted = "?"; plc->qlen = 1; + newbuffer_len -= plc->len - 1; } goto rewrite; diff --git a/ext/pdo/tests/bug_71885.phpt b/ext/pdo/tests/bug_71885.phpt new file mode 100644 index 0000000000000..e392c08c4991a --- /dev/null +++ b/ext/pdo/tests/bug_71885.phpt @@ -0,0 +1,53 @@ +--TEST-- +PDO Common: FR #71885 (Allow escaping question mark placeholders) +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->exec("CREATE TABLE test (a int)"); + +$sql = "SELECT * FROM test WHERE a ?? 1"; + +try { + $db->exec($sql); +} catch (PDOException $e) { + var_dump(strpos($e->getMessage(), "?") !== false); +} + +try { + $stmt = $db->prepare($sql); + $stmt->execute(); +} catch (PDOException $e) { + var_dump(strpos($e->getMessage(), "?") !== false); +} + +if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql') { + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); +} + +try { + $stmt = $db->prepare($sql); + $stmt->execute(); +} catch (PDOException $e) { + var_dump(strpos($e->getMessage(), "?") !== false); +} + +?> +===DONE=== +--EXPECTF-- +bool(true) +bool(true) +bool(true) +===DONE=== diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 52a9b8f285e4a..e50a6a65f6944 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -266,36 +266,36 @@ static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len execute_only = H->disable_prepares; } - if (!emulate && PQprotocolVersion(H->server) > 2) { + if (!emulate && PQprotocolVersion(H->server) <= 2) { + emulate = 1; + } + + if (emulate) { + stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; + } else { stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; stmt->named_rewrite_template = "$%d"; - ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - - if (ret == 1) { - /* query was re-written */ - sql = nsql; - } else if (ret == -1) { - /* couldn't grok it */ - strcpy(dbh->error_code, stmt->error_code); - return 0; - } + } - if (!execute_only) { - /* prepared query: set the query name and defer the - actual prepare until the first execute call */ - spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); - } + ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len); - if (nsql) { - S->query = nsql; - } else { - S->query = estrdup(sql); - } + if (ret == -1) { + /* couldn't grok it */ + strcpy(dbh->error_code, stmt->error_code); + return 0; + } else if (ret == 1) { + /* query was re-written */ + S->query = nsql; + } else { + S->query = estrdup(sql); + } - return 1; + if (!emulate && !execute_only) { + /* prepared query: set the query name and defer the + actual prepare until the first execute call */ + spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); } - stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; return 1; } diff --git a/ext/pdo_pgsql/tests/bug71885.phpt b/ext/pdo_pgsql/tests/bug71885.phpt new file mode 100644 index 0000000000000..ea5b1882ffaef --- /dev/null +++ b/ext/pdo_pgsql/tests/bug71885.phpt @@ -0,0 +1,46 @@ +--TEST-- +Request #71855 (PDO placeholder escaping) +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM); + +foreach ([false, true] as $emulate) { + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate); + + try { + $stmt = $db->prepare('select ?- lseg \'((-1,0),(1,0))\''); + $stmt->execute(); + } catch (PDOException $e) { + var_dump('ERR'); + } + + $stmt = $db->prepare('select ??- lseg \'((-1,0),(1,0))\''); + $stmt->execute(); + + var_dump($stmt->fetch()); +} + +?> +==OK== +--EXPECT-- +string(3) "ERR" +array(1) { + [0]=> + bool(true) +} +array(1) { + [0]=> + bool(true) +} +==OK== diff --git a/ext/pdo_pgsql/tests/bug71885_2.phpt b/ext/pdo_pgsql/tests/bug71885_2.phpt new file mode 100644 index 0000000000000..f98e9ef785a95 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug71885_2.phpt @@ -0,0 +1,57 @@ +--TEST-- +Request #71855 (PDO placeholder escaping, part 2) +--SKIPIF-- +getAttribute(PDO::ATTR_SERVER_VERSION), '9.4.0') < 0) { + die("skip Requires 9.4+"); +} + +?> +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM); + +$jsonb = $db->quote(json_encode(['a' => 1])); + +foreach ([false, true] as $emulate) { + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate); + + $stmt = $db->prepare("SELECT {$jsonb}::jsonb ?? ?"); + $stmt->execute(['b']); + var_dump($stmt->fetch()); + + $stmt = $db->prepare("SELECT {$jsonb}::jsonb ???"); + $stmt->execute(['a']); + var_dump($stmt->fetch()); +} + +?> +==OK== +--EXPECT-- +array(1) { + [0]=> + bool(false) +} +array(1) { + [0]=> + bool(true) +} +array(1) { + [0]=> + bool(false) +} +array(1) { + [0]=> + bool(true) +} +==OK==