summaryrefslogtreecommitdiff
path: root/databases/php85-pdo/files/patch-pdo__sql__parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'databases/php85-pdo/files/patch-pdo__sql__parser.c')
-rw-r--r--databases/php85-pdo/files/patch-pdo__sql__parser.c729
1 files changed, 729 insertions, 0 deletions
diff --git a/databases/php85-pdo/files/patch-pdo__sql__parser.c b/databases/php85-pdo/files/patch-pdo__sql__parser.c
new file mode 100644
index 000000000000..191eea16a3bd
--- /dev/null
+++ b/databases/php85-pdo/files/patch-pdo__sql__parser.c
@@ -0,0 +1,729 @@
+--- pdo_sql_parser.c.orig 2025-07-02 12:59:51 UTC
++++ pdo_sql_parser.c
+@@ -0,0 +1,726 @@
++/* Generated by re2c 3.1 */
++/*
++ +----------------------------------------------------------------------+
++ | Copyright (c) The PHP Group |
++ +----------------------------------------------------------------------+
++ | This source file is subject to version 3.01 of the PHP license, |
++ | that is bundled with this package in the file LICENSE, and is |
++ | available through the world-wide-web at the following url: |
++ | https://www.php.net/license/3_01.txt |
++ | If you did not receive a copy of the PHP license and are unable to |
++ | obtain it through the world-wide-web, please send a note to |
++ | license@php.net so we can mail you a copy immediately. |
++ +----------------------------------------------------------------------+
++ | Author: George Schlossnagle <george@omniti.com> |
++ +----------------------------------------------------------------------+
++*/
++
++#include "php.h"
++#include "php_pdo_driver.h"
++#include "pdo_sql_parser.h"
++
++static int default_scanner(pdo_scanner_t *s)
++{
++ const char *cursor = s->cur;
++
++ s->tok = cursor;
++
++
++
++{
++ YYCTYPE yych;
++ unsigned int yyaccept = 0;
++ if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case 0x00: goto yy1;
++ case '"': goto yy4;
++ case '\'': goto yy6;
++ case '-': goto yy7;
++ case '/': goto yy8;
++ case ':': goto yy9;
++ case '?': goto yy10;
++ default: goto yy2;
++ }
++yy1:
++ YYCURSOR = YYMARKER;
++ switch (yyaccept) {
++ case 0: goto yy5;
++ case 1: goto yy15;
++ default: goto yy19;
++ }
++yy2:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case 0x00:
++ case '"':
++ case '\'':
++ case '-':
++ case '/':
++ case ':':
++ case '?': goto yy3;
++ default: goto yy2;
++ }
++yy3:
++ { RET(PDO_PARSER_TEXT); }
++yy4:
++ yyaccept = 0;
++ yych = *(YYMARKER = ++YYCURSOR);
++ if (yych >= 0x01) goto yy13;
++yy5:
++ { SKIP_ONE(PDO_PARSER_TEXT); }
++yy6:
++ yyaccept = 0;
++ yych = *(YYMARKER = ++YYCURSOR);
++ if (yych <= 0x00) goto yy5;
++ goto yy17;
++yy7:
++ yych = *++YYCURSOR;
++ switch (yych) {
++ case '-': goto yy20;
++ default: goto yy5;
++ }
++yy8:
++ yych = *++YYCURSOR;
++ switch (yych) {
++ case '*': goto yy22;
++ default: goto yy5;
++ }
++yy9:
++ yych = *++YYCURSOR;
++ switch (yych) {
++ case '0':
++ case '1':
++ case '2':
++ case '3':
++ case '4':
++ case '5':
++ case '6':
++ case '7':
++ case '8':
++ case '9':
++ case 'A':
++ case 'B':
++ case 'C':
++ case 'D':
++ case 'E':
++ case 'F':
++ case 'G':
++ case 'H':
++ case 'I':
++ case 'J':
++ case 'K':
++ case 'L':
++ case 'M':
++ case 'N':
++ case 'O':
++ case 'P':
++ case 'Q':
++ case 'R':
++ case 'S':
++ case 'T':
++ case 'U':
++ case 'V':
++ case 'W':
++ case 'X':
++ case 'Y':
++ case 'Z':
++ case '_':
++ case 'a':
++ case 'b':
++ case 'c':
++ case 'd':
++ case 'e':
++ case 'f':
++ case 'g':
++ case 'h':
++ case 'i':
++ case 'j':
++ case 'k':
++ case 'l':
++ case 'm':
++ case 'n':
++ case 'o':
++ case 'p':
++ case 'q':
++ case 'r':
++ case 's':
++ case 't':
++ case 'u':
++ case 'v':
++ case 'w':
++ case 'x':
++ case 'y':
++ case 'z': goto yy23;
++ case ':': goto yy25;
++ default: goto yy5;
++ }
++yy10:
++ yych = *++YYCURSOR;
++ switch (yych) {
++ case '?': goto yy27;
++ default: goto yy11;
++ }
++yy11:
++ { RET(PDO_PARSER_BIND_POS); }
++yy12:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++yy13:
++ switch (yych) {
++ case 0x00: goto yy1;
++ case '"': goto yy14;
++ default: goto yy12;
++ }
++yy14:
++ yyaccept = 1;
++ YYMARKER = ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '"': goto yy12;
++ default: goto yy15;
++ }
++yy15:
++ { RET(PDO_PARSER_TEXT); }
++yy16:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++yy17:
++ switch (yych) {
++ case 0x00: goto yy1;
++ case '\'': goto yy18;
++ default: goto yy16;
++ }
++yy18:
++ yyaccept = 2;
++ YYMARKER = ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '\'': goto yy16;
++ default: goto yy19;
++ }
++yy19:
++ { RET(PDO_PARSER_TEXT); }
++yy20:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '\n': goto yy21;
++ default: goto yy20;
++ }
++yy21:
++ { RET(PDO_PARSER_TEXT); }
++yy22:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '*': goto yy28;
++ default: goto yy22;
++ }
++yy23:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '0':
++ case '1':
++ case '2':
++ case '3':
++ case '4':
++ case '5':
++ case '6':
++ case '7':
++ case '8':
++ case '9':
++ case 'A':
++ case 'B':
++ case 'C':
++ case 'D':
++ case 'E':
++ case 'F':
++ case 'G':
++ case 'H':
++ case 'I':
++ case 'J':
++ case 'K':
++ case 'L':
++ case 'M':
++ case 'N':
++ case 'O':
++ case 'P':
++ case 'Q':
++ case 'R':
++ case 'S':
++ case 'T':
++ case 'U':
++ case 'V':
++ case 'W':
++ case 'X':
++ case 'Y':
++ case 'Z':
++ case '_':
++ case 'a':
++ case 'b':
++ case 'c':
++ case 'd':
++ case 'e':
++ case 'f':
++ case 'g':
++ case 'h':
++ case 'i':
++ case 'j':
++ case 'k':
++ case 'l':
++ case 'm':
++ case 'n':
++ case 'o':
++ case 'p':
++ case 'q':
++ case 'r':
++ case 's':
++ case 't':
++ case 'u':
++ case 'v':
++ case 'w':
++ case 'x':
++ case 'y':
++ case 'z': goto yy23;
++ default: goto yy24;
++ }
++yy24:
++ { RET(PDO_PARSER_BIND); }
++yy25:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case ':': goto yy25;
++ default: goto yy26;
++ }
++yy26:
++ { RET(PDO_PARSER_TEXT); }
++yy27:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '?': goto yy27;
++ default: goto yy26;
++ }
++yy28:
++ ++YYCURSOR;
++ if (YYLIMIT <= YYCURSOR) YYFILL(1);
++ yych = *YYCURSOR;
++ switch (yych) {
++ case '*': goto yy28;
++ case '/': goto yy29;
++ default: goto yy22;
++ }
++yy29:
++ ++YYCURSOR;
++ goto yy21;
++}
++
++}
++
++struct placeholder {
++ const char *pos;
++ size_t len;
++ zend_string *quoted; /* quoted value */
++ int bindno;
++ 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));
++}
++
++PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string **outquery)
++{
++ pdo_scanner_t s;
++ char *newbuffer;
++ ptrdiff_t t;
++ uint32_t bindno = 0;
++ int ret = 0, escapes = 0;
++ size_t newbuffer_len;
++ HashTable *params;
++ struct pdo_bound_param_data *param;
++ 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;
++
++ s.cur = ZSTR_VAL(inquery);
++ s.end = s.cur + ZSTR_LEN(inquery) + 1;
++
++ /* 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;
++ } else if (t == PDO_PARSER_ESCAPED_QUESTION) {
++ /* An escaped question mark has been used inside a dollar quoted string, most likely as a workaround
++ * as a single "?" would have been parsed as placeholder, due to the lack of support for dollar quoted
++ * strings. For now, we emit a deprecation notice, but still process it */
++ php_error_docref(NULL, E_DEPRECATED, "Escaping question marks inside dollar quoted strings is not required anymore and is deprecated");
++
++ goto placeholder;
++ }
++
++ 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 */
++ continue;
++ }
++
++ if (t == PDO_PARSER_BIND) {
++ ptrdiff_t len = s.cur - s.tok;
++ if ((ZSTR_VAL(inquery) < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
++ continue;
++ }
++ query_type |= PDO_PLACEHOLDER_NAMED;
++ } else if (t == PDO_PARSER_BIND_POS) {
++ query_type |= PDO_PLACEHOLDER_POSITIONAL;
++ }
++
++placeholder:
++ plc = emalloc(sizeof(*plc));
++ memset(plc, 0, sizeof(*plc));
++ plc->next = NULL;
++ plc->pos = s.tok;
++ plc->len = s.cur - s.tok;
++
++ if (t == PDO_PARSER_ESCAPED_QUESTION) {
++ plc->bindno = PDO_PARSER_BINDNO_ESCAPED_CHAR;
++ plc->quoted = ZSTR_CHAR('?');
++ escapes++;
++ } else {
++ plc->bindno = bindno++;
++ }
++
++ if (placetail) {
++ placetail->next = plc;
++ } else {
++ placeholders = plc;
++ }
++ placetail = plc;
++ }
++ }
++
++ /* did the query make sense to me? */
++ if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
++ /* they mixed both types; punt */
++ pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters");
++ ret = -1;
++ goto clean_up;
++ }
++
++ params = stmt->bound_params;
++ if (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;
++ for (plc = placeholders; plc; plc = plc->next) {
++ if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
++ ok = 0;
++ break;
++ }
++ }
++ if (ok) {
++ goto safe;
++ }
++ }
++ pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
++ ret = -1;
++ goto clean_up;
++ }
++
++ if (!placeholders) {
++ /* nothing to do; good! */
++ return 0;
++ }
++
++ if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
++ /* query matches native syntax */
++ if (escapes) {
++ newbuffer_len = ZSTR_LEN(inquery);
++ goto rewrite;
++ }
++
++ ret = 0;
++ goto clean_up;
++ }
++
++ 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
++ * named rewrite case below. Not too pretty,
++ * but it works. */
++ query_type = PDO_PLACEHOLDER_POSITIONAL;
++ }
++
++safe:
++ /* what are we going to do ? */
++ if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
++ /* query generation */
++
++ newbuffer_len = ZSTR_LEN(inquery);
++
++ /* let's quote all the values */
++ 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 {
++ param = zend_hash_str_find_ptr(params, plc->pos, plc->len);
++ }
++ if (param == NULL) {
++ /* parameter was not defined */
++ ret = -1;
++ pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
++ goto clean_up;
++ }
++ if (stmt->dbh->methods->quoter) {
++ zval *parameter;
++ if (Z_ISREF(param->parameter)) {
++ parameter = Z_REFVAL(param->parameter);
++ } else {
++ parameter = &param->parameter;
++ }
++ if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {
++ php_stream *stm;
++
++ php_stream_from_zval_no_verify(stm, parameter);
++ if (stm) {
++ zend_string *buf;
++
++ buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
++ if (!buf) {
++ buf = ZSTR_EMPTY_ALLOC();
++ }
++
++ plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param->param_type);
++
++ if (buf) {
++ zend_string_release_ex(buf, 0);
++ }
++ if (plc->quoted == NULL) {
++ /* bork */
++ ret = -1;
++ strncpy(stmt->error_code, stmt->dbh->error_code, 6);
++ goto clean_up;
++ }
++
++ } else {
++ pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
++ ret = -1;
++ goto clean_up;
++ }
++ } else {
++ enum pdo_param_type param_type = param->param_type;
++ zend_string *buf = NULL;
++
++ /* assume all types are nullable */
++ if (Z_TYPE_P(parameter) == IS_NULL) {
++ param_type = PDO_PARAM_NULL;
++ }
++
++ switch (param_type) {
++ case PDO_PARAM_BOOL:
++ plc->quoted = zend_is_true(parameter) ? ZSTR_CHAR('1') : ZSTR_CHAR('0');
++ break;
++
++ case PDO_PARAM_INT:
++ plc->quoted = zend_long_to_str(zval_get_long(parameter));
++ break;
++
++ case PDO_PARAM_NULL:
++ plc->quoted = ZSTR_KNOWN(ZEND_STR_NULL);
++ break;
++
++ default: {
++ buf = zval_try_get_string(parameter);
++ /* parameter does not have a string representation, buf == NULL */
++ if (EG(exception)) {
++ /* bork */
++ ret = -1;
++ strncpy(stmt->error_code, stmt->dbh->error_code, 6);
++ goto clean_up;
++ }
++
++ plc->quoted = stmt->dbh->methods->quoter(stmt->dbh, buf, param_type);
++ }
++ }
++
++ if (buf) {
++ zend_string_release_ex(buf, 0);
++ }
++ }
++ } else {
++ zval *parameter;
++ if (Z_ISREF(param->parameter)) {
++ parameter = Z_REFVAL(param->parameter);
++ } else {
++ parameter = &param->parameter;
++ }
++ plc->quoted = zend_string_copy(Z_STR_P(parameter));
++ }
++ newbuffer_len += ZSTR_LEN(plc->quoted);
++ }
++
++rewrite:
++ /* allocate output buffer */
++ *outquery = zend_string_alloc(newbuffer_len, 0);
++ newbuffer = ZSTR_VAL(*outquery);
++
++ /* and build the query */
++ const char *ptr = ZSTR_VAL(inquery);
++ plc = placeholders;
++
++ do {
++ t = plc->pos - ptr;
++ if (t) {
++ memcpy(newbuffer, ptr, t);
++ newbuffer += t;
++ }
++ if (plc->quoted) {
++ memcpy(newbuffer, ZSTR_VAL(plc->quoted), ZSTR_LEN(plc->quoted));
++ newbuffer += ZSTR_LEN(plc->quoted);
++ } else {
++ memcpy(newbuffer, plc->pos, plc->len);
++ newbuffer += plc->len;
++ }
++ ptr = plc->pos + plc->len;
++
++ plc = plc->next;
++ } while (plc);
++
++ t = ZSTR_VAL(inquery) + ZSTR_LEN(inquery) - ptr;
++ if (t) {
++ memcpy(newbuffer, ptr, t);
++ newbuffer += t;
++ }
++ *newbuffer = '\0';
++ ZSTR_LEN(*outquery) = newbuffer - ZSTR_VAL(*outquery);
++
++ ret = 1;
++ goto clean_up;
++
++ } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
++ /* rewrite ? to :pdoX */
++ const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
++ int bind_no = 1;
++
++ newbuffer_len = ZSTR_LEN(inquery);
++
++ if (stmt->bound_param_map == NULL) {
++ ALLOC_HASHTABLE(stmt->bound_param_map);
++ zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
++ }
++
++ for (plc = placeholders; plc; plc = plc->next) {
++ int skip_map = 0;
++ zend_string *p;
++ zend_string *idxbuf;
++
++ if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
++ continue;
++ }
++
++ zend_string *name = zend_string_init(plc->pos, plc->len, 0);
++
++ /* check if bound parameter is already available */
++ if (zend_string_equals_literal(name, "?") || (p = zend_hash_find_ptr(stmt->bound_param_map, name)) == NULL) {
++ idxbuf = zend_strpprintf(0, tmpl, bind_no++);
++ } else {
++ idxbuf = zend_string_copy(p);
++ skip_map = 1;
++ }
++
++ plc->quoted = idxbuf;
++ newbuffer_len += ZSTR_LEN(plc->quoted);
++
++ if (!skip_map && stmt->named_rewrite_template) {
++ /* create a mapping */
++ zend_hash_update_ptr(stmt->bound_param_map, name, zend_string_copy(plc->quoted));
++ }
++
++ /* map number to name */
++ zend_hash_index_update_ptr(stmt->bound_param_map, plc->bindno, zend_string_copy(plc->quoted));
++
++ zend_string_release(name);
++ }
++
++ goto rewrite;
++
++ } else {
++ /* rewrite :name to ? */
++
++ newbuffer_len = ZSTR_LEN(inquery);
++
++ if (stmt->bound_param_map == NULL) {
++ ALLOC_HASHTABLE(stmt->bound_param_map);
++ zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
++ }
++
++ for (plc = placeholders; plc; plc = plc->next) {
++ zend_string *name = zend_string_init(plc->pos, plc->len, 0);
++ zend_hash_index_update_ptr(stmt->bound_param_map, plc->bindno, name);
++ plc->quoted = ZSTR_CHAR('?');
++ newbuffer_len -= plc->len - 1;
++ }
++
++ goto rewrite;
++ }
++
++clean_up:
++
++ while (placeholders) {
++ plc = placeholders;
++ placeholders = plc->next;
++ if (plc->quoted) {
++ zend_string_release_ex(plc->quoted, 0);
++ }
++ efree(plc);
++ }
++
++ return ret;
++}