-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #73234: Emulated statements let value dictate parameter type
The prepared statement emulator (pdo_sql_parser.*) figures out how to quote each query parameter. The intended type is specified by the PDO::PARAM_* consts, but this direction wasn't always followed. In practice, queries could work as expected, but subtle errors could result. For example, a numeric string bound as PDO::PARAM_INT would be sent to a driver's quote function. While these functions are told which type is expected, they generally assume values are being quoted as strings. This can result in implicit casts, which are bad for performance. This commit includes the following changes: - Cast values marked as bool/int/null to the appropriate type and bypass the driver's quote function. - Save some memory by dropping the temporary zval used for casting. - Avoid a memory leak if the driver's quote function produces an error. - Appropriate test suite updates.
- Loading branch information
1 parent
f51405c
commit 32b6154
Showing
7 changed files
with
109 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--TEST-- | ||
PDO Common: Bug #73234 (Emulated statements let value dictate parameter type) | ||
--SKIPIF-- | ||
<?php | ||
if (!extension_loaded('pdo')) die('skip'); | ||
$dir = getenv('REDIR_TEST_DIR'); | ||
if (false == $dir) die('skip no driver'); | ||
require_once $dir . 'pdo_test.inc'; | ||
PDOTest::skip(); | ||
?> | ||
--FILE-- | ||
<?php | ||
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.dirname(__FILE__) . '/../../pdo/tests/'); | ||
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; | ||
|
||
$db = PDOTest::factory(); | ||
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); | ||
$db->exec('CREATE TABLE test(id INT NULL)'); | ||
|
||
$stmt = $db->prepare('INSERT INTO test VALUES(:value)'); | ||
|
||
$stmt->bindValue(':value', 0, PDO::PARAM_NULL); | ||
$stmt->execute(); | ||
|
||
$stmt->bindValue(':value', null, PDO::PARAM_NULL); | ||
$stmt->execute(); | ||
|
||
$stmt = $db->query('SELECT * FROM test'); | ||
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); | ||
?> | ||
--EXPECT-- | ||
array(2) { | ||
[0]=> | ||
array(1) { | ||
["id"]=> | ||
NULL | ||
} | ||
[1]=> | ||
array(1) { | ||
["id"]=> | ||
NULL | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Adam,
First:
I think PDO::PARAM_NULL should be removed/deprecated at all, because NULL is a state not a type as expected here nor a value.
It is wrong to write $stmt->bindParam(':name', $value, PDO::PARAM_NULL); along with PDO::PARAM_INT.
Second:
Your bug test #73234 is not SQL standard compliant and many DBMS that does not emulate parameters fail this test. Parameter type in that query is unknown at prepare stage.
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, this change has been committed to master, but I'll certainly clean up the test as needed. I'll leave the PR open until we resolve this point.
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adam,
Then why to improve this fail?
Expecting $stmt->bindValue(':value', 0, PDO::PARAM_NULL); to put a NULL there is not OK.
0 is not NULL. What it should put there if we pass 1 or "A"? put there also NULL?
Here, 18th line:
32b6154#diff-3470eab08d851fd4571a17610980860aR18
Should be: $db->exec('CREATE TABLE test(id INT)');
By default, field can be null, just NOT NULL is required, when it is the case.
And it of course Firebird implementation fail it. ZERO != NULL.
========OUT========
array(2) {
[0]=>
array(1) {
["id"]=>
string(1) "0"
}
[1]=>
array(1) {
["id"]=>
NULL
}
}
========DONE========
========EXP========
array(2) {
[0]=>
array(1) {
["id"]=>
NULL
}
[1]=>
array(1) {
["id"]=>
NULL
}
}
========DONE========
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PDO::PARAM_NULL
is given as the type, but the value !== null?An easy fix would be to add a special case so these tests use different SQL with pdo_firebird. If that works for you, I can put the patch together.
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adam,
Because NULL means unknown, missing value. 0 is a KNOWN value. It is wrong to cast it as NULL.
0 may be cased as false for a boolean field, but not as null. False is not null as true is not null.
What's the problem, really? PHP allow NULLs. You may do bindValue('param', NULL, PDO::PARAM_INT);
As I said PDO::PARAM_NULL is NOT a type. And my suggestion is to deprecate it.
For now i'd put a NULL for any value you pass there if PDO::PARAM_NULL is passed.
I think we should follow SQL standard in a multiple dbmses condition, and adjust it for specific DBs that does not support it.
http://www.w3schools.com/sql/sql_notnull.asp
By default, a table column can hold NULL values.
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, about this 9fb94f0, I fixed 018, 024 pdo tests in master some days ago because they fail on Firebird :)
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should've been more specific with my commit comment. See this part of that (long) doc:
https://msdn.microsoft.com/en-us/library/ms174979.aspx#Nullability Rules Within a Table Definition
Environment settings can influence how the table is created if you're not specific.
I'll patch pdo_018.phpt so it continues to run reliably with MSSQL. Please be on the lookout for DB-specific nuances in these tests. I understand they might not play nice with all DBs, but please try to preserve them for the DBs that need them.
32b6154
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adam, it seems then you have "ANSI_NULL_DEFAULT_OFF = ON" or (ANSI_NULL_DEFAULT_ON = OFF") in your test database, right? Which seems to be a non-standard behavior, at least for most DBMSes. Sorry, i'm not an expert in T-SQL.