Skip to content

Commit f5aaa8f

Browse files
committed
Fix GH-9372: HY010 when binding overlong parameter
If `SQLPutData()` *fails*, we should not call `SQLParamData()` again, because that yields the confusing `HY010` (Function sequence error). Instead we properly handle `SQLPutData()` errors. For the given case (paramter length > column length), some drivers let `SQLPutData()` fail, while others do not. Either behavior seems to conform to the ODBC specification. Anyhow, we do not want to silently truncate the given parameter, since that would break the behavior for drivers which do not fail, but still don't simply truncate the given parameter. So it is finally up to userland to avoid passing overlong parameters – with this patch they at least get useful information about the actual issue. Closes GH-9541.
1 parent 2e6b317 commit f5aaa8f

File tree

3 files changed

+69
-5
lines changed

3 files changed

+69
-5
lines changed

NEWS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ PHP NEWS
99
- MySQLnd:
1010
. Fixed potential heap corruption due to alignment mismatch. (cmb)
1111

12+
- PDO_ODBC:
13+
. Fixed bug GH-9372 (HY010 when binding overlong parameter). (cmb)
14+
1215
- SOAP:
13-
. Fixed GH-9720 (Null pointer dereference while serializing the response).
16+
. Fixed bug GH-9720 (Null pointer dereference while serializing the response).
1417
(cmb)
1518

1619
13 Oct 2022, PHP 8.2.0RC4

ext/pdo_odbc/odbc_stmt.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ static int odbc_stmt_dtor(pdo_stmt_t *stmt)
155155

156156
static int odbc_stmt_execute(pdo_stmt_t *stmt)
157157
{
158-
RETCODE rc;
158+
RETCODE rc, rc1;
159159
pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data;
160160
char *buf = NULL;
161161
SQLLEN row_count = -1;
@@ -192,11 +192,17 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt)
192192
Z_STRLEN_P(parameter),
193193
&ulen)) {
194194
case PDO_ODBC_CONV_NOT_REQUIRED:
195-
SQLPutData(S->stmt, Z_STRVAL_P(parameter),
195+
rc1 = SQLPutData(S->stmt, Z_STRVAL_P(parameter),
196196
Z_STRLEN_P(parameter));
197+
if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
198+
rc = rc1;
199+
}
197200
break;
198201
case PDO_ODBC_CONV_OK:
199-
SQLPutData(S->stmt, S->convbuf, ulen);
202+
rc1 = SQLPutData(S->stmt, S->convbuf, ulen);
203+
if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
204+
rc = rc1;
205+
}
200206
break;
201207
case PDO_ODBC_CONV_FAIL:
202208
pdo_odbc_stmt_error("error converting input string");
@@ -233,7 +239,10 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt)
233239
if (len == 0) {
234240
break;
235241
}
236-
SQLPutData(S->stmt, buf, len);
242+
rc1 = SQLPutData(S->stmt, buf, len);
243+
if (rc1 != SQL_SUCCESS && rc1 != SQL_SUCCESS_WITH_INFO) {
244+
rc = rc1;
245+
}
237246
} while (1);
238247
}
239248
}

ext/pdo_odbc/tests/gh9372.phpt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
Bug GH-9372 (HY010 when binding overlong parameter)
3+
--EXTENSIONS--
4+
pdo_odbc
5+
--SKIPIF--
6+
<?php
7+
require 'ext/pdo/tests/pdo_test.inc';
8+
PDOTest::skip();
9+
?>
10+
--FILE--
11+
<?php
12+
// Executing the statements fails with some drivers, but not others.
13+
// The test is written in a way to always pass, unless the execution
14+
// fails with a different code than 22001 (String data, right truncation).
15+
16+
require 'ext/pdo/tests/pdo_test.inc';
17+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
18+
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
19+
20+
$db->exec("CREATE TABLE gh9372 (col VARCHAR(10))");
21+
$db->exec("INSERT INTO gh9372 VALUES ('something')");
22+
23+
$stmt = $db->prepare("SELECT * FROM gh9372 WHERE col = ?");
24+
$stmt->bindValue(1, 'something else');
25+
try {
26+
$stmt->execute();
27+
} catch (PDOException $ex) {
28+
if ($ex->getCode() !== "22001") {
29+
var_dump($ex->getMessage());
30+
}
31+
}
32+
33+
$stmt = $db->prepare("SELECT * FROM gh9372 WHERE col = ?");
34+
$stream = fopen("php://memory", "w+");
35+
fwrite($stream, 'something else');
36+
rewind($stream);
37+
$stmt->bindvalue(1, $stream, PDO::PARAM_LOB);
38+
try {
39+
$stmt->execute();
40+
} catch (PDOException $ex) {
41+
if ($ex->getCode() !== "22001") {
42+
var_dump($ex->getMessage());
43+
}
44+
}
45+
?>
46+
--CLEAN--
47+
<?php
48+
require 'ext/pdo/tests/pdo_test.inc';
49+
$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
50+
$db->exec("DROP TABLE gh9372");
51+
?>
52+
--EXPECT--

0 commit comments

Comments
 (0)