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 ...@@ -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. * 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 * Returns an integer => integer pair (indexed from zero) for a positional statement
* a named statement. * and a string => int[] pair for a named statement.
* *
* @param string $statement * @param string $statement
* @param bool $isPositional * @param bool $isPositional
...@@ -49,7 +49,7 @@ class SQLParserUtils ...@@ -49,7 +49,7 @@ class SQLParserUtils
return array(); return array();
} }
$count = 1; $count = 0;
$inLiteral = false; // a valid query never starts with quotes $inLiteral = false; // a valid query never starts with quotes
$stmtLen = strlen($statement); $stmtLen = strlen($statement);
$paramMap = array(); $paramMap = array();
...@@ -85,22 +85,24 @@ class SQLParserUtils ...@@ -85,22 +85,24 @@ class SQLParserUtils
*/ */
static public function expandListParameters($query, $params, $types) static public function expandListParameters($query, $params, $types)
{ {
$isPositional = false; $isPositional = is_int(key($params));
$arrayPositions = array(); $arrayPositions = array();
$bindIndex = -1;
foreach ($types AS $name => $type) { foreach ($types AS $name => $type) {
++$bindIndex;
if ($type === Connection::PARAM_INT_ARRAY || $type == Connection::PARAM_STR_ARRAY) { if ($type === Connection::PARAM_INT_ARRAY || $type == Connection::PARAM_STR_ARRAY) {
if ($isPositional) {
$name = $bindIndex;
}
$arrayPositions[$name] = false; $arrayPositions[$name] = false;
$isPositional = (is_numeric($name));
} }
} }
if (!$arrayPositions) { if (!$arrayPositions || count($params) != count($types)) {
return array($query, $params, $types); return array($query, $params, $types);
} }
ksort($params);
ksort($types);
$paramPos = self::getPlaceholderPositions($query, $isPositional); $paramPos = self::getPlaceholderPositions($query, $isPositional);
if ($isPositional) { if ($isPositional) {
$paramOffset = 0; $paramOffset = 0;
...@@ -115,25 +117,21 @@ class SQLParserUtils ...@@ -115,25 +117,21 @@ class SQLParserUtils
$len = count($params[$needle]); $len = count($params[$needle]);
$params = array_merge( $params = array_merge(
array_slice($params, 0, $needle-1), array_slice($params, 0, $needle),
$params[$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( $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_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, "?")); $expandStr = implode(", ", array_fill(0, $len, "?"));
$query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); $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); $queryOffset += (strlen($expandStr) - 1);
} }
} else { } else {
......
...@@ -231,14 +231,14 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase ...@@ -231,14 +231,14 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase
} }
$stmt = $this->_conn->executeQuery('SELECT test_int FROM fetch_table WHERE test_int IN (?)', $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); $data = $stmt->fetchAll(PDO::FETCH_NUM);
$this->assertEquals(5, count($data)); $this->assertEquals(5, count($data));
$this->assertEquals(array(array(100), array(101), array(102), array(103), array(104)), $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 (?)', $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); $data = $stmt->fetchAll(PDO::FETCH_NUM);
$this->assertEquals(5, count($data)); $this->assertEquals(5, count($data));
......
...@@ -20,13 +20,13 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase ...@@ -20,13 +20,13 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase
array('SELECT * FROM Foo', false, array()), array('SELECT * FROM Foo', false, array()),
// Positionals // Positionals
array('SELECT ?', true, array(1 => 7)), array('SELECT ?', true, array(7)),
array('SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, array(1 => 32, 2 => 35, 3 => 38)), array('SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, array(32, 35, 38)),
array('SELECT ? FROM ?', true, array(1 => 7, 2 => 14)), array('SELECT ? FROM ?', true, array(7, 14)),
array('SELECT "?" FROM foo', true, array()), array('SELECT "?" FROM foo', true, array()),
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(32)),
array("SELECT '?' FROM foo WHERE bar = ?", true, array(1 => 32)), array("SELECT '?' FROM foo WHERE bar = ?", true, array(32)),
// named // named
array('SELECT :foo FROM :bar', false, array(':foo' => array(7), ':bar' => array(17))), array('SELECT :foo FROM :bar', false, array(':foo' => array(7), ':bar' => array(17))),
...@@ -54,56 +54,47 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase ...@@ -54,56 +54,47 @@ class SQLParserUtilsTest extends \Doctrine\Tests\DbalTestCase
// Positional: Very simple with one needle // Positional: Very simple with one needle
array( array(
"SELECT * FROM Foo WHERE foo IN (?)", "SELECT * FROM Foo WHERE foo IN (?)",
array(1 => array(1, 2, 3)), array(array(1, 2, 3)),
array(1 => Connection::PARAM_INT_ARRAY), array(Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?, ?)', 'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
array(1 => 1, 2 => 2, 3 => 3), array(1, 2, 3),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT) array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
), ),
// Positional: One non-list before d one after list-needle // Positional: One non-list before d one after list-needle
array( array(
"SELECT * FROM Foo WHERE foo = ? AND bar IN (?)", "SELECT * FROM Foo WHERE foo = ? AND bar IN (?)",
array(1 => "string", 2 => array(1, 2, 3)), array("string", array(1, 2, 3)),
array(1 => \PDO::PARAM_STR, 2 => Connection::PARAM_INT_ARRAY), array(\PDO::PARAM_STR, Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)', 'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
array(1 => "string", 2 => 1, 3 => 2, 4 => 3), array("string", 1, 2, 3),
array(1 => \PDO::PARAM_STR, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT) array(\PDO::PARAM_STR, \PDO::PARAM_INT, \PDO::PARAM_INT, \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)
), ),
// Positional: One non-list after list-needle // Positional: One non-list after list-needle
array( array(
"SELECT * FROM Foo WHERE bar IN (?) AND baz = ?", "SELECT * FROM Foo WHERE bar IN (?) AND baz = ?",
array(1 => array(1, 2, 3), 3 => "foo"), array(array(1, 2, 3), "foo"),
array(1 => Connection::PARAM_INT_ARRAY, 3 => \PDO::PARAM_STR), array(Connection::PARAM_INT_ARRAY, \PDO::PARAM_STR),
'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?', 'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
array(1 => 1, 2 => 2, 3 => 3, 4 => "foo"), array(1, 2, 3, "foo"),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_STR) array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
), ),
// Positional: One non-list before and one after list-needle // Positional: One non-list before and one after list-needle
array( array(
"SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?", "SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?",
array(1 => 1, 2 => array(1, 2, 3), 3 => 4), array(1, array(1, 2, 3), 4),
array(1 => \PDO::PARAM_INT, 2 => Connection::PARAM_INT_ARRAY, 3 => \PDO::PARAM_INT), array(\PDO::PARAM_INT, Connection::PARAM_INT_ARRAY, \PDO::PARAM_INT),
'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?', 'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
array(1 => 1, 2 => 1, 3 => 2, 4 => 3, 5 => 4), array(1, 1, 2, 3, 4),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT, 5 => \PDO::PARAM_INT) array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_INT)
), ),
// Positional: Two lists // Positional: Two lists
array( array(
"SELECT * FROM Foo WHERE foo IN (?, ?)", "SELECT * FROM Foo WHERE foo IN (?, ?)",
array(1 => array(1, 2, 3), 2 => array(4, 5)), array(array(1, 2, 3), array(4, 5)),
array(1 => Connection::PARAM_INT_ARRAY, 2 => Connection::PARAM_INT_ARRAY), array(Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)', 'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
array(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5), array(1, 2, 3, 4, 5),
array(1 => \PDO::PARAM_INT, 2 => \PDO::PARAM_INT, 3 => \PDO::PARAM_INT, 4 => \PDO::PARAM_INT, 5 => \PDO::PARAM_INT) 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