Commit 2a44c94b authored by Benjamin Eberlei's avatar Benjamin Eberlei

DBAL-78 - Fix SQLParserUtils List Parameter expansion to be zero based for positional parameters.

parent e007fec5
......@@ -35,8 +35,8 @@ class SQLParserUtils
/**
* Get an array of the placeholders in an sql statements as keys and their positions in the query string.
*
* Returns an integer => integer pair for a positional statement and a string => int[] pair for
* a named statement.
* Returns an integer => integer pair (indexed from zero) for a positional statement
* and a string => int[] pair for a named statement.
*
* @param string $statement
* @param bool $isPositional
......@@ -49,7 +49,7 @@ class SQLParserUtils
return array();
}
$count = 1;
$count = 0;
$inLiteral = false; // a valid query never starts with quotes
$stmtLen = strlen($statement);
$paramMap = array();
......@@ -85,22 +85,24 @@ class SQLParserUtils
*/
static public function expandListParameters($query, $params, $types)
{
$isPositional = false;
$isPositional = is_int(key($params));
$arrayPositions = array();
$bindIndex = -1;
foreach ($types AS $name => $type) {
++$bindIndex;
if ($type === Connection::PARAM_INT_ARRAY || $type == Connection::PARAM_STR_ARRAY) {
if ($isPositional) {
$name = $bindIndex;
}
$arrayPositions[$name] = false;
$isPositional = (is_numeric($name));
}
}
if (!$arrayPositions) {
if (!$arrayPositions || count($params) != count($types)) {
return array($query, $params, $types);
}
ksort($params);
ksort($types);
$paramPos = self::getPlaceholderPositions($query, $isPositional);
if ($isPositional) {
$paramOffset = 0;
......@@ -115,25 +117,21 @@ class SQLParserUtils
$len = count($params[$needle]);
$params = array_merge(
array_slice($params, 0, $needle-1),
array_slice($params, 0, $needle),
$params[$needle],
array_slice($params, $needle)
array_slice($params, $needle + 1)
);
array_unshift($params, -1); // temporary to shift keys
unset($params[0]);
$types = array_merge(
array_slice($types, 0, $needle-1),
array_slice($types, 0, $needle),
array_fill(0, $len, $types[$needle] - Connection::ARRAY_PARAM_OFFSET), // array needles are at PDO::PARAM_* + 100
array_slice($types, $needle)
array_slice($types, $needle + 1)
);
array_unshift($types, -1);
unset($types[0]);
$expandStr = implode(", ", array_fill(0, $len, "?"));
$query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1);
$paramOffset += ($len -1);
$paramOffset += ($len - 1); // Grows larger by number of parameters minus the replaced needle.
$queryOffset += (strlen($expandStr) - 1);
}
} else {
......
......@@ -231,14 +231,14 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase
}
$stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_int IN (?)',
array(1 => array(100, 101, 102, 103, 104)), array(1 => Connection::PARAM_INT_ARRAY));
array(array(100, 101, 102, 103, 104)), array(Connection::PARAM_INT_ARRAY));
$data = $stmt->fetchAll(PDO::FETCH_NUM);
$this->assertEquals(5, count($data));
$this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $data);
$stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_string IN (?)',
array(1 => array('foo100', 'foo101', 'foo102', 'foo103', 'foo104')), array(1 => Connection::PARAM_STR_ARRAY));
array(array('foo100', 'foo101', 'foo102', 'foo103', 'foo104')), array(Connection::PARAM_STR_ARRAY));
$data = $stmt->fetchAll(PDO::FETCH_NUM);
$this->assertEquals(5, count($data));
......
......@@ -20,13 +20,13 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase
array('SELECT * FROM Foo', false, array()),
// Positionals
array('SELECT ?', true, array(1 => 7)),
array('SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, array(1 => 32, 2 => 35, 3 => 38)),
array('SELECT ? FROM ?', true, array(1 => 7, 2 => 14)),
array('SELECT ?', true, array(7)),
array('SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, array(32, 35, 38)),
array('SELECT ? FROM ?', true, array(7, 14)),
array('SELECT "?" FROM foo', true, array()),
array("SELECT '?' FROM foo", true, array()),
array('SELECT "?" FROM foo WHERE bar = ?', true, array(1 => 32)),
array("SELECT '?' FROM foo WHERE bar = ?", true, array(1 => 32)),
array('SELECT "?" FROM foo WHERE bar = ?', true, array(32)),
array("SELECT '?' FROM foo WHERE bar = ?", true, array(32)),
// named
array('SELECT :foo FROM :bar', false, array(':foo' => array(7), ':bar' => array(17))),
......@@ -54,56 +54,47 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase
// Positional: Very simple with one needle
array(
"SELECT * FROM Foo WHERE foo IN (?)",
array(1 => array(1, 2, 3)),
array(1 => Connection::PARAM_INT_ARRAY),
array(array(1, 2, 3)),
array(Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
array(1 => 1, 2 => 2, 3 => 3),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT)
array(1, 2, 3),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
),
// Positional: One non-list before d one after list-needle
array(
"SELECT * FROM Foo WHERE foo = ? AND bar IN (?)",
array(1 => "string", 2 => array(1, 2, 3)),
array(1 => \PDO::PARAM_STR, 2 => Connection::PARAM_INT_ARRAY),
array("string", array(1, 2, 3)),
array(\PDO::PARAM_STR, Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
array(1 => "string", 2 => 1, 3 => 2, 4 => 3),
array(1 => \PDO::PARAM_STR, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT)
),
// Positional: One non-list before d one after list-needle, parameters and types reversed.
array(
"SELECT * FROM Foo WHERE foo = ? AND bar IN (?)",
array(2 => array(1, 2, 3), 1 => "string"),
array(2 => Connection::PARAM_INT_ARRAY, 1 => \PDO::PARAM_STR),
'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
array(1 => "string", 2 => 1, 3 => 2, 4 => 3),
array(1 => \PDO::PARAM_STR, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT)
array("string", 1, 2, 3),
array(\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
),
// Positional: One non-list after list-needle
array(
"SELECT * FROM Foo WHERE bar IN (?) AND baz = ?",
array(1 => array(1, 2, 3), 3 => "foo"),
array(1 => Connection::PARAM_INT_ARRAY, 3 => \PDO::PARAM_STR),
array(array(1, 2, 3), "foo"),
array(Connection::PARAM_INT_ARRAY, \PDO::PARAM_STR),
'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
array(1 => 1, 2 => 2, 3 => 3, 4 => "foo"),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_STR)
array(1, 2, 3, "foo"),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
),
// Positional: One non-list before and one after list-needle
array(
"SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?",
array(1 => 1, 2 => array(1, 2, 3), 3 => 4),
array(1 => \PDO::PARAM_INT, 2 => Connection::PARAM_INT_ARRAY, 3 => \PDO::PARAM_INT),
array(1, array(1, 2, 3), 4),
array(\PDO::PARAM_INT, Connection::PARAM_INT_ARRAY, \PDO::PARAM_INT),
'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
array(1 => 1, 2 => 1, 3 => 2, 4 => 3, 5 => 4),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT, 5 => \PDO::PARAM_INT)
array(1, 1, 2, 3, 4),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
),
// Positional: Two lists
array(
"SELECT * FROM Foo WHERE foo IN (?, ?)",
array(1 => array(1, 2, 3), 2 => array(4, 5)),
array(1 => Connection::PARAM_INT_ARRAY, 2 => Connection::PARAM_INT_ARRAY),
array(array(1, 2, 3), array(4, 5)),
array(Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
array(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT, 5 => \PDO::PARAM_INT)
array(1, 2, 3, 4, 5),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
),
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment