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,11 @@ 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 */
143
- ret = 0 ;
144
- goto clean_up ;
162
+ newbuffer_len = inquery_len ;
163
+ goto rewrite ;
145
164
}
146
165
147
- if (stmt->named_rewrite_template ) {
166
+ if (query_type == PDO_PLACEHOLDER_NAMED && stmt->named_rewrite_template ) {
148
167
/* magic/hack.
149
168
* We we pretend that the query was positional even if
150
169
* it was named so that we fall into the
@@ -155,14 +174,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len
155
174
156
175
params = stmt->bound_params ;
157
176
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) {
177
+ if (bindno && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements (params)) {
166
178
/* extra bit of validation for instances when same params are bound more than once */
167
179
if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements (params)) {
168
180
int ok = 1 ;
@@ -188,7 +200,16 @@ safe:
188
200
newbuffer_len = inquery_len;
189
201
190
202
/* let's quote all the values */
191
- for (plc = placeholders; plc; plc = plc->next ) {
203
+ for (plc = placeholders; plc && params; plc = plc->next ) {
204
+ if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
205
+ /* escaped character */
206
+ continue ;
207
+ }
208
+
209
+ if (query_type == PDO_PLACEHOLDER_NONE) {
210
+ continue ;
211
+ }
212
+
192
213
if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
193
214
param = zend_hash_index_find_ptr (params, plc->bindno );
194
215
} else {
@@ -302,7 +323,7 @@ safe:
302
323
303
324
rewrite:
304
325
/* allocate output buffer */
305
- newbuffer = emalloc (newbuffer_len + 1 );
326
+ newbuffer = emalloc (newbuffer_len - escapes + 1 );
306
327
*outquery = newbuffer;
307
328
308
329
/* and build the query */
@@ -315,8 +336,13 @@ rewrite:
315
336
memcpy (newbuffer, ptr, t);
316
337
newbuffer += t;
317
338
}
318
- memcpy (newbuffer, plc->quoted , plc->qlen );
319
- newbuffer += plc->qlen ;
339
+ if (plc->quoted ) {
340
+ memcpy (newbuffer, plc->quoted , plc->qlen );
341
+ newbuffer += plc->qlen ;
342
+ } else {
343
+ memcpy (newbuffer, plc->pos , plc->len );
344
+ newbuffer += plc->len ;
345
+ }
320
346
ptr = plc->pos + plc->len ;
321
347
322
348
plc = plc->next ;
@@ -349,6 +375,11 @@ rewrite:
349
375
for (plc = placeholders; plc; plc = plc->next ) {
350
376
int skip_map = 0 ;
351
377
char *p;
378
+
379
+ if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
380
+ continue ;
381
+ }
382
+
352
383
name = estrndup (plc->pos , plc->len );
353
384
354
385
/* check if bound parameter is already available */
@@ -394,6 +425,7 @@ rewrite:
394
425
efree (name);
395
426
plc->quoted = " ?" ;
396
427
plc->qlen = 1 ;
428
+ newbuffer_len -= plc->len - 1 ;
397
429
}
398
430
399
431
goto rewrite;
0 commit comments