From f339c6c2d3aad14433fa987668d0da58fa85d44b Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Sat, 16 Nov 2019 23:11:59 +0300 Subject: [PATCH 1/5] preprocessing sql --- ext/pdo_firebird/firebird_driver.c | 439 ++++++++++++++++++++++++++--- 1 file changed, 403 insertions(+), 36 deletions(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index f3e71e8313252..9f2b471e971c8 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -32,6 +32,399 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t*, const char*, size_t, XSQLDA*, isc_stmt_handle*, HashTable*); +const char CHR_LETTER = 1; +const char CHR_DIGIT = 2; +const char CHR_IDENT = 4; +const char CHR_QUOTE = 8; +const char CHR_WHITE = 16; +const char CHR_HEX = 32; +const char CHR_INTRODUCER = 64; + +static const char classes_array[] = { + /* 000 */ 0, + /* 001 */ 0, + /* 002 */ 0, + /* 003 */ 0, + /* 004 */ 0, + /* 005 */ 0, + /* 006 */ 0, + /* 007 */ 0, + /* 008 */ 0, + /* 009 */ 16, /* CHR_WHITE */ + /* 010 */ 16, /* CHR_WHITE */ + /* 011 */ 0, + /* 012 */ 0, + /* 013 */ 16, /* CHR_WHITE */ + /* 014 */ 0, + /* 015 */ 0, + /* 016 */ 0, + /* 017 */ 0, + /* 018 */ 0, + /* 019 */ 0, + /* 020 */ 0, + /* 021 */ 0, + /* 022 */ 0, + /* 023 */ 0, + /* 024 */ 0, + /* 025 */ 0, + /* 026 */ 0, + /* 027 */ 0, + /* 028 */ 0, + /* 029 */ 0, + /* 030 */ 0, + /* 031 */ 0, + /* 032 */ 16, /* CHR_WHITE */ + /* 033 ! */ 0, + /* 034 " */ 8, /* CHR_QUOTE */ + /* 035 # */ 0, + /* 036 $ */ 4, /* CHR_IDENT */ + /* 037 % */ 0, + /* 038 & */ 0, + /* 039 ' */ 8, /* CHR_QUOTE */ + /* 040 ( */ 0, + /* 041 ) */ 0, + /* 042 * */ 0, + /* 043 + */ 0, + /* 044 , */ 0, + /* 045 - */ 0, + /* 046 . */ 0, + /* 047 / */ 0, + /* 048 0 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 049 1 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 050 2 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 051 3 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 052 4 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 053 5 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 054 6 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 055 7 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 056 8 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 057 9 */ 38, /* CHR_DIGIT | CHR_IDENT | CHR_HEX */ + /* 058 : */ 0, + /* 059 ; */ 0, + /* 060 < */ 0, + /* 061 = */ 0, + /* 062 > */ 0, + /* 063 ? */ 0, + /* 064 @ */ 0, + /* 065 A */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 066 B */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 067 C */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 068 D */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 069 E */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 070 F */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 071 G */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 072 H */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 073 I */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 074 J */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 075 K */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 076 L */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 077 M */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 078 N */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 079 O */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 080 P */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 081 Q */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 082 R */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 083 S */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 084 T */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 085 U */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 086 V */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 087 W */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 088 X */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 089 Y */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 090 Z */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 091 [ */ 0, + /* 092 \ */ 0, + /* 093 ] */ 0, + /* 094 ^ */ 0, + /* 095 _ */ 65, /* CHR_IDENT | CHR_INTRODUCER */ + /* 096 ` */ 0, + /* 097 a */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 098 b */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 099 c */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 100 d */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 101 e */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 102 f */ 37, /* CHR_LETTER | CHR_IDENT | CHR_HEX */ + /* 103 g */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 104 h */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 105 i */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 106 j */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 107 k */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 108 l */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 109 m */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 110 n */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 111 o */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 112 p */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 113 q */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 114 r */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 115 s */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 116 t */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 117 u */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 118 v */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 119 w */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 120 x */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 121 y */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 122 z */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 123 { */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 124 | */ 0, + /* 125 } */ 5, /* CHR_LETTER | CHR_IDENT */ + /* 126 ~ */ 0, + /* 127 */ 0 +}; + +inline char classes(char idx) +{ + if (idx > 127) return 0; + return classes_array[idx]; +} + +typedef enum { + ttNone, + ttWhite, + ttComment, + ttBrokenComment, + ttString, + ttParamMark, + ttIdent, + ttOther +} FbTokenType; + +static FbTokenType getToken(const char** begin, const char* end) +{ + FbTokenType ret = ttNone; + const char* p = *begin; + + char c = *p++; + switch (c) + { + case ':': + case '?': + ret = ttParamMark; + break; + + case '\'': + case '"': + while (p < end) + { + if (*p++ == c) + { + ret = ttString; + break; + } + } + break; + + case '/': + if (p < end && *p == '*') + { + ret = ttBrokenComment; + p++; + while (p < end) + { + if (*p++ == '*' && p < end && *p == '/') + { + p++; + ret = ttComment; + break; + } + } + } + else { + ret = ttOther; + } + break; + + case '-': + if (p < end && *p == '-') + { + while (++p < end) + { + if (*p == '\r') + { + p++; + if (p < end && *p == '\n') + p++; + break; + } + else if (*p == '\n') + break; + } + + ret = ttComment; + } + else + ret = ttOther; + break; + + default: + if (classes(c) & CHR_DIGIT) + { + while (p < end && (classes(*p) & CHR_DIGIT)) + p++; + ret = ttOther; + } + else if (classes(c) & CHR_IDENT) + { + while (p < end && (classes(*p) & CHR_IDENT)) + p++; + ret = ttIdent; + } + else if (classes(c) & CHR_WHITE) + { + while (p < end && (classes(*p) & CHR_WHITE)) + p++; + ret = ttWhite; + } + else + { + while (p < end && !(classes(*p) & (CHR_DIGIT | CHR_IDENT | CHR_WHITE)) && + (*p != '/') && (*p != '-') && (*p != ':') && (*p != '?') && + (*p != '\'') && (*p != '"')) + { + p++; + } + ret = ttOther; + } + } + + *begin = p; + return ret; +} + +int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_params) +{ + zend_bool passAsIs = 1, execBlock = 0; + zend_long pindex = -1; + char pname[254]; + const char* p = sql, * end = sql + sql_len; + const char* start = p; + FbTokenType tok = getToken(&p, end); + + const char* i = start; + while (p < end && (tok == ttComment || tok == ttWhite)) + { + i = p; + tok = getToken(&p, end); + } + + if (p >= end || tok != ttIdent) + { + /* Execute statement preprocess SQL error */ + /* Statement expected */ + return 0; + } + /* skip leading comments ?? */ + start = i; + + if (!strncasecmp(i, "EXECUTE", 7)) + { + const char* i2 = p; + tok = getToken(&p, end); + while (p < end && (tok == ttComment || tok == ttWhite)) + { + i2 = p; + tok = getToken(&p, end); + } + if (p >= end || tok != ttIdent) + { + /* Execute statement preprocess SQL error */ + /* Statement expected */ + return 0; + } + + execBlock = !strncasecmp(i2, "BLOCK", 5); + passAsIs = 0; + } + else + { + passAsIs = strncasecmp(i, "INSERT", 5) && strncasecmp(i, "UPDATE", 6) && + strncasecmp(i, "DELETE", 6) && strncasecmp(i, "MERGE", 5) && + strncasecmp(i, "SELECT", 6) && strncasecmp(i, "WITH", 4); + } + + if (passAsIs) + { + strcpy(sql_out, sql); + return 1; + } + + strncat(sql_out, start, p - start); + + while (p < end) + { + start = p; + tok = getToken(&p, end); + switch (tok) + { + case ttParamMark: + tok = getToken(&p, end); + if (tok == ttIdent /*|| tok == ttString*/) + { + ++pindex; + unsigned int l = p - start; + /* check the length of the identifier */ + /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ + /* + symbol ":" */ + if (l > 253) { + return 0; + } + strncpy(pname, start, l); + pname[l] = '\0'; + + if (named_params) { + zval tmp; + ZVAL_LONG(&tmp, pindex); + zend_hash_str_update(named_params, pname, l, &tmp); + } + + strcat(sql_out, "?"); + } + else + { + if (strncmp(start, "?", 1)) { + /* Execute statement preprocess SQL error */ + /* Parameter name expected */ + return 0; + } + ++pindex; + strncat(sql_out, start, p - start); + } + break; + + case ttIdent: + if (execBlock) + { + if (!strncasecmp(start, "AS", 2)) + { + strncat(sql_out, start, end - start); + return 1; + } + } + + case ttWhite: + case ttComment: + case ttString: + case ttOther: + strncat(sql_out, start, p - start); + break; + + case ttBrokenComment: + { + /* Execute statement preprocess SQL error */ + /* Unclosed comment found near ''@1'' */ + return 0; + } + break; + + + case ttNone: + /* Execute statement preprocess SQL error */ + return 0; + break; + } + } + return 1; +} + /* map driver specific error message to PDO error */ void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_long line) /* {{{ */ { @@ -43,6 +436,8 @@ void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_lo #define RECORD_ERROR(dbh) _firebird_error(dbh, NULL, __FILE__, __LINE__) + + /* called by PDO to close a db handle */ static int firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */ { @@ -352,8 +747,7 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const char *sql, size_t s XSQLDA *out_sqlda, isc_stmt_handle *s, HashTable *named_params) { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; - char *c, *new_sql, in_quote, in_param, pname[64], *ppname; - zend_long l, pindex = -1; + char *new_sql; /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */ if (sql_len > 65536) { @@ -379,39 +773,12 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const char *sql, size_t s /* in order to support named params, which Firebird itself doesn't, we need to replace :foo by ?, and store the name we just replaced */ - new_sql = c = emalloc(sql_len+1); - - for (l = in_quote = in_param = 0; l <= sql_len; ++l) { - if ( !(in_quote ^= (sql[l] == '\''))) { - if (!in_param) { - switch (sql[l]) { - case ':': - in_param = 1; - ppname = pname; - *ppname++ = sql[l]; - case '?': - *c++ = '?'; - ++pindex; - continue; - } - } else { - if ((in_param &= ((sql[l] >= 'A' && sql[l] <= 'Z') || (sql[l] >= 'a' && sql[l] <= 'z') - || (sql[l] >= '0' && sql[l] <= '9') || sql[l] == '_' || sql[l] == '-'))) { - - - *ppname++ = sql[l]; - continue; - } else { - *ppname++ = 0; - if (named_params) { - zval tmp; - ZVAL_LONG(&tmp, pindex); - zend_hash_str_update(named_params, pname, (unsigned int)(ppname - pname - 1), &tmp); - } - } - } - } - *c++ = sql[l]; + new_sql = emalloc(sql_len+1); + new_sql[0] = '\0'; + if (!preprocess(sql, sql_len, new_sql, named_params)) { + strcpy(dbh->error_code, "07000"); + efree(new_sql); + return 0; } /* prepare the statement */ @@ -658,7 +1025,7 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* buf_len -= dpb_len; } } - + H->sql_dialect = PDO_FB_DIALECT; if (vars[3].optval) { H->sql_dialect = atoi(vars[3].optval); From 0cffaf4a7eb1e2bdb28c9ad1aa544cf189d3a957 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Sun, 17 Nov 2019 17:20:38 +0300 Subject: [PATCH 2/5] code formatting --- ext/pdo_firebird/firebird_driver.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 9f2b471e971c8..3d5a306d3c050 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -362,7 +362,7 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par ++pindex; unsigned int l = p - start; /* check the length of the identifier */ - /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ + /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ /* + symbol ":" */ if (l > 253) { return 0; @@ -436,8 +436,6 @@ void _firebird_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, char const *file, zend_lo #define RECORD_ERROR(dbh) _firebird_error(dbh, NULL, __FILE__, __LINE__) - - /* called by PDO to close a db handle */ static int firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */ { From 1cc36bf53bda3053993e49f8cfe6a87aa08e00c7 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Sun, 17 Nov 2019 17:39:49 +0300 Subject: [PATCH 3/5] add tests --- ext/pdo_firebird/tests/execute_block.phpt | 41 ++++++++++++++++++ ext/pdo_firebird/tests/ignore_parammarks.phpt | 42 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 ext/pdo_firebird/tests/execute_block.phpt create mode 100644 ext/pdo_firebird/tests/ignore_parammarks.phpt diff --git a/ext/pdo_firebird/tests/execute_block.phpt b/ext/pdo_firebird/tests/execute_block.phpt new file mode 100644 index 0000000000000..fbcd4bfff7991 --- /dev/null +++ b/ext/pdo_firebird/tests/execute_block.phpt @@ -0,0 +1,41 @@ +--TEST-- +PDO_Firebird: support 1 sql dialect +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $sql = ' +execute block (a int = :e, b int = :d) +returns (N int, M int) +as +declare z int; +begin + select 10 + from rdb$database + into :z; + + n = a + b + z; + m = z * a; + suspend; +end +'; + $query = $dbh->prepare($sql); + $query->execute(['d' => 1, 'e' => 2]); + $row = $query->fetch(\PDO::FETCH_OBJ); + var_dump($row->N); + var_dump($row->M); + + unset($query); + unset($dbh); + echo "done\n"; + +?> +--EXPECT-- +int(13) +int(20) +done diff --git a/ext/pdo_firebird/tests/ignore_parammarks.phpt b/ext/pdo_firebird/tests/ignore_parammarks.phpt new file mode 100644 index 0000000000000..f0e0560000a3e --- /dev/null +++ b/ext/pdo_firebird/tests/ignore_parammarks.phpt @@ -0,0 +1,42 @@ +--TEST-- +PDO_Firebird: support 1 sql dialect +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + + $sql = ' +select 1 as n +-- :f +from rdb$database +where 1=:d and 2=:e +'; + $query = $dbh->prepare($sql); + $query->execute(['d' => 1, 'e' => 2]); + $row = $query->fetch(\PDO::FETCH_OBJ); + var_dump($row->N); + unset($query); + + $sql = ' +select 1 as n +from rdb$database +where 1=:d /* and :f = 5 */ and 2=:e +'; + $query = $dbh->prepare($sql); + $query->execute(['d' => 1, 'e' => 2]); + $row = $query->fetch(\PDO::FETCH_OBJ); + var_dump($row->N); + unset($query); + + unset($dbh); + echo "done\n"; + +?> +--EXPECT-- +int(1) +int(1) +done From e7b9a79ee64b680a85d4ed85434c3f9d9e6bc4d6 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Mon, 18 Nov 2019 19:52:43 +0300 Subject: [PATCH 4/5] mixed --- ext/pdo_firebird/tests/execute_block.phpt | 2 +- ext/pdo_firebird/tests/ignore_parammarks.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/pdo_firebird/tests/execute_block.phpt b/ext/pdo_firebird/tests/execute_block.phpt index fbcd4bfff7991..fe01acb0a83b4 100644 --- a/ext/pdo_firebird/tests/execute_block.phpt +++ b/ext/pdo_firebird/tests/execute_block.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO_Firebird: support 1 sql dialect +PDO_Firebird: support EXECUTE BLOCK --SKIPIF-- diff --git a/ext/pdo_firebird/tests/ignore_parammarks.phpt b/ext/pdo_firebird/tests/ignore_parammarks.phpt index f0e0560000a3e..01764492047cd 100644 --- a/ext/pdo_firebird/tests/ignore_parammarks.phpt +++ b/ext/pdo_firebird/tests/ignore_parammarks.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO_Firebird: support 1 sql dialect +PDO_Firebird: ingnore parameter marks in comments --SKIPIF-- From 890d7868679680a782684e3277c52e1ea039b507 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Wed, 18 Dec 2019 22:42:01 +0300 Subject: [PATCH 5/5] Fixed verification of identifiers. Fixed verification of identifiers. Comments are added. --- ext/pdo_firebird/firebird_driver.c | 51 +++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 3d5a306d3c050..c7cfe6f992d29 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -294,7 +294,8 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par { zend_bool passAsIs = 1, execBlock = 0; zend_long pindex = -1; - char pname[254]; + char pname[254], ident[253], ident2[253]; + unsigned int l; const char* p = sql, * end = sql + sql_len; const char* start = p; FbTokenType tok = getToken(&p, end); @@ -313,10 +314,19 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par return 0; } /* skip leading comments ?? */ - start = i; - - if (!strncasecmp(i, "EXECUTE", 7)) + start = i; + l = p - i; + /* check the length of the identifier */ + /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ + if (l > 252) { + return 0; + } + strncpy(ident, i, l); + ident[l] = '\0'; + if (!strcasecmp(ident, "EXECUTE")) { + /* For EXECUTE PROCEDURE and EXECUTE BLOCK statements, named parameters must be processed. */ + /* However, in EXECUTE BLOCK this is done in a special way. */ const char* i2 = p; tok = getToken(&p, end); while (p < end && (tok == ttComment || tok == ttWhite)) @@ -330,15 +340,24 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par /* Statement expected */ return 0; } - - execBlock = !strncasecmp(i2, "BLOCK", 5); + l = p - i2; + /* check the length of the identifier */ + /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ + if (l > 252) { + return 0; + } + strncpy(ident2, i2, l); + ident2[l] = '\0'; + execBlock = !strcasecmp(ident2, "BLOCK"); passAsIs = 0; } else { - passAsIs = strncasecmp(i, "INSERT", 5) && strncasecmp(i, "UPDATE", 6) && - strncasecmp(i, "DELETE", 6) && strncasecmp(i, "MERGE", 5) && - strncasecmp(i, "SELECT", 6) && strncasecmp(i, "WITH", 4); + /* Named parameters must be processed in the INSERT, UPDATE, DELETE, MERGE statements. */ + /* If CTEs are present in the query, they begin with the WITH keyword. */ + passAsIs = strcasecmp(ident, "INSERT") && strcasecmp(ident, "UPDATE") && + strcasecmp(ident, "DELETE") && strcasecmp(ident, "MERGE") && + strcasecmp(ident, "SELECT") && strcasecmp(ident, "WITH"); } if (passAsIs) @@ -360,7 +379,7 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par if (tok == ttIdent /*|| tok == ttString*/) { ++pindex; - unsigned int l = p - start; + l = p - start; /* check the length of the identifier */ /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ /* + symbol ":" */ @@ -393,7 +412,17 @@ int preprocess(const char* sql, int sql_len, char* sql_out, HashTable* named_par case ttIdent: if (execBlock) { - if (!strncasecmp(start, "AS", 2)) + /* In the EXECUTE BLOCK statement, processing must be */ + /* carried out up to the keyword AS. */ + l = p - start; + /* check the length of the identifier */ + /* in Firebird 4.0 it is 63 characters, in previous versions 31 bytes */ + if (l > 252) { + return 0; + } + strncpy(ident, start, l); + ident[l] = '\0'; + if (!strcasecmp(ident, "AS")) { strncat(sql_out, start, end - start); return 1;