23
23
#define PDO_PARSER_TEXT 1
24
24
#define PDO_PARSER_BIND 2
25
25
#define PDO_PARSER_BIND_POS 3
26
- #define PDO_PARSER_EOI 4
26
+ #define PDO_PARSER_ESCAPED_QUESTION 4
27
+ #define PDO_PARSER_EOI 5
28
+
29
+ #define PDO_PARSER_BINDNO_ESCAPED_CHAR -1
27
30
28
31
#define RET (i ) {s->cur = cursor; return i; }
29
32
#define SKIP_ONE (i ) {s->cur = s->tok + 1 ; return i; }
@@ -46,16 +49,18 @@ static int scan(Scanner *s)
46
49
/* !re2c
47
50
BINDCHR = [:][a-zA-Z0-9_]+;
48
51
QUESTION = [?];
52
+ ESCQUESTION = [?][?];
49
53
COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/ " |" --" [^\r\n ]*);
50
54
SPECIALS = [:?" ' -/];
51
- MULTICHAR = ( [:]{2,}|[?]{2,}) ;
55
+ MULTICHAR = [:]{2,};
52
56
ANYNOEOF = [\001 -\377 ];
53
57
*/
54
58
55
59
/*!re2c
56
60
(["](([\\ ]ANYNOEOF)|ANYNOEOF\[ "\\ ])*["]) { RET(PDO_PARSER_TEXT); }
57
61
([' ](([\\]ANYNOEOF)|ANYNOEOF\[' \\ ])*[' ]) { RET (PDO_PARSER_TEXT); }
58
62
MULTICHAR { RET (PDO_PARSER_TEXT); }
63
+ ESCQUESTION { RET (PDO_PARSER_ESCAPED_QUESTION); }
59
64
BINDCHR { RET (PDO_PARSER_BIND); }
60
65
QUESTION { RET (PDO_PARSER_BIND_POS); }
61
66
SPECIALS { SKIP_ONE (PDO_PARSER_TEXT); }
@@ -85,7 +90,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
85
90
char *ptr, *newbuffer;
86
91
ptrdiff_t t;
87
92
uint32_t bindno = 0 ;
88
- int ret = 0 ;
93
+ int ret = 0 , escapes = 0 ;
89
94
size_t newbuffer_len;
90
95
HashTable *params;
91
96
struct pdo_bound_param_data *param;
@@ -98,14 +103,19 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
98
103
99
104
/* phase 1: look for args */
100
105
while ((t = scan (&s)) != PDO_PARSER_EOI) {
101
- if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
106
+ if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS || t == PDO_PARSER_ESCAPED_QUESTION) {
107
+ if (t == PDO_PARSER_ESCAPED_QUESTION && stmt->supports_placeholders == PDO_PLACEHOLDER_POSITIONAL) {
108
+ /* escaped question marks unsupported, treat as text */
109
+ continue ;
110
+ }
111
+
102
112
if (t == PDO_PARSER_BIND) {
103
113
ptrdiff_t len = s.cur - s.tok ;
104
114
if ((inquery < (s.cur - len)) && isalnum (*(s.cur - len - 1 ))) {
105
115
continue ;
106
116
}
107
117
query_type |= PDO_PLACEHOLDER_NAMED;
108
- } else {
118
+ } else if (t == PDO_PARSER_BIND_POS) {
109
119
query_type |= PDO_PLACEHOLDER_POSITIONAL;
110
120
}
111
121
@@ -114,7 +124,16 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
114
124
plc->next = NULL ;
115
125
plc->pos = s.tok ;
116
126
plc->len = s.cur - s.tok ;
117
- plc->bindno = bindno++;
127
+
128
+ if (t == PDO_PARSER_ESCAPED_QUESTION) {
129
+ plc->bindno = PDO_PARSER_BINDNO_ESCAPED_CHAR;
130
+ plc->quoted = " ?" ;
131
+ plc->qlen = 1 ;
132
+ plc->freeq = 0 ;
133
+ escapes++;
134
+ } else {
135
+ plc->bindno = bindno++;
136
+ }
118
137
119
138
if (placetail) {
120
139
placetail->next = plc;
@@ -125,7 +144,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
125
144
}
126
145
}
127
146
128
- if (bindno == 0 ) {
147
+ if (!placeholders ) {
129
148
/* nothing to do; good! */
130
149
return 0 ;
131
150
}
@@ -140,11 +159,16 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
140
159
141
160
if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template ) {
142
161
/* query matches native syntax */
162
+ if (escapes) {
163
+ newbuffer_len = inquery_len;
164
+ goto rewrite;
165
+ }
166
+
143
167
ret = 0 ;
144
168
goto clean_up;
145
169
}
146
170
147
- if (stmt->named_rewrite_template ) {
171
+ if (query_type == PDO_PLACEHOLDER_NAMED && stmt->named_rewrite_template ) {
148
172
/* magic/hack.
149
173
* We we pretend that the query was positional even if
150
174
* it was named so that we fall into the
@@ -155,14 +179,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
155
179
156
180
params = stmt->bound_params ;
157
181
158
- /* Do we have placeholders but no bound params */
159
- if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
160
- pdo_raise_impl_error (stmt->dbh , stmt, " HY093" , " no parameters were bound" );
161
- ret = -1 ;
162
- goto clean_up;
163
- }
164
-
165
- if (params && bindno != zend_hash_num_elements (params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
182
+ if (bindno && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements (params)) {
166
183
/* extra bit of validation for instances when same params are bound more than once */
167
184
if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements (params)) {
168
185
int ok = 1 ;
@@ -188,7 +205,16 @@ safe:
188
205
newbuffer_len = inquery_len;
189
206
190
207
/* let's quote all the values */
191
- for (plc = placeholders; plc; plc = plc->next ) {
208
+ for (plc = placeholders; plc && params; plc = plc->next ) {
209
+ if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
210
+ /* escaped character */
211
+ continue ;
212
+ }
213
+
214
+ if (query_type == PDO_PLACEHOLDER_NONE) {
215
+ continue ;
216
+ }
217
+
192
218
if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
193
219
param = zend_hash_index_find_ptr (params, plc->bindno );
194
220
} else {
@@ -316,8 +342,13 @@ rewrite:
316
342
memcpy (newbuffer, ptr, t);
317
343
newbuffer += t;
318
344
}
319
- memcpy (newbuffer, plc->quoted , plc->qlen );
320
- newbuffer += plc->qlen ;
345
+ if (plc->quoted ) {
346
+ memcpy (newbuffer, plc->quoted , plc->qlen );
347
+ newbuffer += plc->qlen ;
348
+ } else {
349
+ memcpy (newbuffer, plc->pos , plc->len );
350
+ newbuffer += plc->len ;
351
+ }
321
352
ptr = plc->pos + plc->len ;
322
353
323
354
plc = plc->next ;
@@ -350,6 +381,11 @@ rewrite:
350
381
for (plc = placeholders; plc; plc = plc->next ) {
351
382
int skip_map = 0 ;
352
383
char *p;
384
+
385
+ if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
386
+ continue ;
387
+ }
388
+
353
389
name = estrndup (plc->pos , plc->len );
354
390
355
391
/* check if bound parameter is already available */
@@ -395,6 +431,7 @@ rewrite:
395
431
efree (name);
396
432
plc->quoted = " ?" ;
397
433
plc->qlen = 1 ;
434
+ newbuffer_len -= plc->len - 1 ;
398
435
}
399
436
400
437
goto rewrite;
0 commit comments