Skip to content

Commit 9e3ba77

Browse files
committed
Fixed bug #72368
Generate a param count mismatch error even if the query contains no placeholders. Additionally we shouldn't HANDLE errors from pdo_parse_params, which are always reported via raise_impl_error. Doing so results in duplicate error messages.
1 parent a552757 commit 9e3ba77

11 files changed

+113
-69
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ PHP NEWS
5454
. Fixed bug #79872 (Can't execute query with pending result sets). (Nikita)
5555
. Fixed bug #79131 (PDO does not throw an exception when parameter values are
5656
missing). (Nikita)
57+
. Fixed bug #72368 (PdoStatement->execute() fails but does not throw an
58+
exception). (Nikita)
5759

5860
- Phar:
5961
. Fixed bug #73809 (Phar Zip parse crash - mmap fail). (cmb)

ext/pdo/pdo_sql_parser.re

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,6 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, const char *inquery, size_t inque
143143
}
144144
}
145145

146-
if (!placeholders) {
147-
/* nothing to do; good! */
148-
return 0;
149-
}
150-
151146
/* did the query make sense to me? */
152147
if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
153148
/* they mixed both types; punt */
@@ -156,6 +151,31 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, const char *inquery, size_t inque
156151
goto clean_up;
157152
}
158153

154+
params = stmt->bound_params;
155+
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements(params)) {
156+
/* extra bit of validation for instances when same params are bound more than once */
157+
if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
158+
int ok = 1;
159+
for (plc = placeholders; plc; plc = plc->next) {
160+
if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
161+
ok = 0;
162+
break;
163+
}
164+
}
165+
if (ok) {
166+
goto safe;
167+
}
168+
}
169+
pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
170+
ret = -1;
171+
goto clean_up;
172+
}
173+
174+
if (!placeholders) {
175+
/* nothing to do; good! */
176+
return 0;
177+
}
178+
159179
if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
160180
/* query matches native syntax */
161181
if (escapes) {
@@ -176,26 +196,6 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, const char *inquery, size_t inque
176196
query_type = PDO_PLACEHOLDER_POSITIONAL;
177197
}
178198

179-
params = stmt->bound_params;
180-
181-
if (bindno && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements(params)) {
182-
/* extra bit of validation for instances when same params are bound more than once */
183-
if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
184-
int ok = 1;
185-
for (plc = placeholders; plc; plc = plc->next) {
186-
if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
187-
ok = 0;
188-
break;
189-
}
190-
}
191-
if (ok) {
192-
goto safe;
193-
}
194-
}
195-
pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
196-
ret = -1;
197-
goto clean_up;
198-
}
199199
safe:
200200
/* what are we going to do ? */
201201
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {

ext/pdo/pdo_stmt.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,6 @@ PHP_METHOD(PDOStatement, execute)
458458
ret = 1;
459459
} else if (ret == -1) {
460460
/* something broke */
461-
PDO_HANDLE_STMT_ERR();
462461
RETURN_FALSE;
463462
}
464463
} else if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE)) {

ext/pdo/tests/bug_43130.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,4 @@ var_dump($stmt->fetch(PDO::FETCH_COLUMN));
3636
?>
3737
--EXPECTF--
3838
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in %s on line %d
39-
40-
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line %d
4139
bool(false)

ext/pdo/tests/bug_72368.phpt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
PDO Common: Bug #72368 (PdoStatement->execute() fails but does not throw an exception)
3+
--SKIPIF--
4+
<?php # vim:ft=php
5+
if (!extension_loaded('pdo')) die('skip');
6+
$dir = getenv('REDIR_TEST_DIR');
7+
if (false == $dir) die('skip no driver');
8+
require_once $dir . 'pdo_test.inc';
9+
PDOTest::skip();
10+
?>
11+
--FILE--
12+
<?php
13+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.dirname(__FILE__) . '/../../pdo/tests/');
14+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
15+
$db = PDOTest::factory();
16+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
17+
18+
$params = [":bar" => 1];
19+
$sql = "SELECT 1";
20+
21+
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
22+
try {
23+
$stmt = $db->prepare($sql);
24+
var_dump($stmt->execute($params));
25+
} catch (PDOException $e) {
26+
var_dump('ERR');
27+
}
28+
29+
30+
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
31+
try {
32+
$stmt = $db->prepare($sql);
33+
var_dump($stmt->execute($params));
34+
} catch (PDOException $e) {
35+
var_dump('ERR');
36+
}
37+
38+
?>
39+
===DONE===
40+
--EXPECT--
41+
string(3) "ERR"
42+
string(3) "ERR"
43+
===DONE===

ext/pdo_mysql/tests/bug41125.phpt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ foreach ($queries as $k => $query) {
8383
}
8484

8585
?>
86-
--EXPECT--
86+
--EXPECTF--
8787
1
8888
00000 - -
8989
-------------------------------------------------------
90+
91+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
9092
[1] Query: [[SELECT 1 FROM DUAL WHERE 1 = '?\'\'']]
9193

9294
00000 - -
@@ -123,18 +125,24 @@ O'\0
123125
1
124126
00000 - -
125127
--------
128+
129+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
126130
[5] Query: [[SELECT 'a', 'b\'' FROM DUAL WHERE '''' LIKE '\'' AND 1]]
127-
a - b'
131+
128132
00000 - -
129133
--------
134+
135+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
130136
[6] Query: [[SELECT 'a''', '\'b\'' FROM DUAL WHERE '''' LIKE '\'' AND 1]]
131-
a' - 'b'
137+
132138
00000 - -
133139
--------
134140
[7] Query: [[SELECT UPPER(:id) FROM DUAL WHERE '1']]
135141
1
136142
00000 - -
137143
--------
144+
145+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
138146
[8] Query: [[SELECT 1 FROM DUAL WHERE '\'']]
139147

140148
00000 - -
@@ -147,13 +155,16 @@ a' - 'b'
147155

148156
00000 - -
149157
--------
158+
159+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
150160
[11] Query: [[SELECT 1 FROM DUAL WHERE '\'' = '''']]
151-
1
161+
152162
00000 - -
153163
--------
164+
165+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
154166
[12] Query: [[SELECT '\n' '1 FROM DUAL WHERE '''' and :id']]
155167

156-
1 FROM DUAL WHERE '' and :id
157168
00000 - -
158169
--------
159170
[13] Query: [[SELECT 1 'FROM DUAL WHERE :id AND '''' = '''' OR 1 = 1 AND ':id]]

ext/pdo_mysql/tests/pdo_mysql_prepare_emulated.phpt

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ require __DIR__ . '/mysql_pdo_test.inc';
326326
$db = MySQLPDOTest::factory();
327327
$db->exec('DROP TABLE IF EXISTS test');
328328
?>
329-
--EXPECT--
329+
--EXPECTF--
330330
PDO::prepare(): Argument #1 ($query) cannot be empty
331331
array(1) {
332332
["one"]=>
@@ -339,12 +339,9 @@ array(1) {
339339
string(12) ":placeholder"
340340
}
341341
}
342-
array(1) {
343-
[0]=>
344-
array(1) {
345-
["label"]=>
346-
string(12) ":placeholder"
347-
}
342+
343+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
344+
array(0) {
348345
}
349346
array(2) {
350347
[0]=>
@@ -381,12 +378,9 @@ array(1) {
381378
string(1) "?"
382379
}
383380
}
384-
array(1) {
385-
[0]=>
386-
array(1) {
387-
["label"]=>
388-
string(1) "?"
389-
}
381+
382+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
383+
array(0) {
390384
}
391385
array(2) {
392386
[0]=>

ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_anonymous.phpt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,13 @@ $db = MySQLPDOTest::factory();
6565
$db->exec('DROP TABLE IF EXISTS test');
6666
?>
6767
--EXPECTF--
68-
array(1) {
69-
[0]=>
70-
array(2) {
71-
["id"]=>
72-
string(1) "1"
73-
["label"]=>
74-
string(1) "?"
75-
}
68+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
69+
[003] Execute has failed, 'HY093' array (
70+
0 => 'HY093',
71+
1 => NULL,
72+
2 => NULL,
73+
)
74+
array(0) {
7675
}
7776
now the same with native PS
7877

ext/pdo_mysql/tests/pdo_mysql_prepare_emulated_placeholder_everywhere.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ array(0) {
7272
now the same with emulated PS
7373

7474
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
75-
76-
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %s on line 33
7775
[005] Execute has failed, 'HY093' array (
7876
0 => 'HY093',
7977
1 => NULL,

ext/pdo_mysql/tests/pdo_mysql_prepare_native_named_placeholder.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in %
8181
)
8282
array(0) {
8383
}
84-
array(1) {
85-
[0]=>
86-
array(2) {
87-
["id"]=>
88-
string(3) "101"
89-
["label"]=>
90-
string(12) ":placeholder"
91-
}
84+
85+
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in %s on line %d
86+
[005] Execute has failed, 'HY093' array (
87+
0 => 'HY093',
88+
1 => NULL,
89+
2 => NULL,
90+
)
91+
array(0) {
9292
}
9393
done!

ext/pdo_pgsql/tests/bug70313.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ try {
1919

2020
$stmt->execute([1]);
2121
} catch (PDOException $e) {
22-
var_dump($e->getCode());
22+
echo $e->getMessage(), "\n";
2323
}
2424

2525
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
@@ -28,10 +28,10 @@ try {
2828

2929
$stmt->execute([1]);
3030
} catch (PDOException $e) {
31-
var_dump($e->getCode());
31+
echo $e->getMessage(), "\n";
3232
}
3333

3434
?>
35-
--EXPECT--
36-
string(5) "42601"
37-
string(5) "42601"
35+
--EXPECTF--
36+
SQLSTATE[42601]: Syntax error: %A
37+
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

0 commit comments

Comments
 (0)