Commit 38f0d510 authored by Lars Strojny's avatar Lars Strojny

Fixing parameter parsing for DBAL-496 and #301

parent af20e348
...@@ -80,7 +80,8 @@ class SQLParserUtils ...@@ -80,7 +80,8 @@ class SQLParserUtils
* @param string $query The SQL query to execute. * @param string $query The SQL query to execute.
* @param array $params The parameters to bind to the query. * @param array $params The parameters to bind to the query.
* @param array $types The types the previous parameters are in. * @param array $types The types the previous parameters are in.
* *
* @throws SQLParserUtilsException
* @return array * @return array
*/ */
static public function expandListParameters($query, $params, $types) static public function expandListParameters($query, $params, $types)
...@@ -103,7 +104,7 @@ class SQLParserUtils ...@@ -103,7 +104,7 @@ class SQLParserUtils
$arrayPositions[$name] = false; $arrayPositions[$name] = false;
} }
if (( ! $arrayPositions && $isPositional) || (count($params) != count($types))) { if (( ! $arrayPositions && $isPositional)) {
return array($query, $params, $types); return array($query, $params, $types);
} }
...@@ -130,9 +131,9 @@ class SQLParserUtils ...@@ -130,9 +131,9 @@ class SQLParserUtils
$types = array_merge( $types = array_merge(
array_slice($types, 0, $needle), array_slice($types, 0, $needle),
$count ? $count ?
array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100 array_fill(0, $count, $types[$needle] - Connection::ARRAY_PARAM_OFFSET) : // array needles are at PDO::PARAM_* + 100
array(), array(),
array_slice($types, $needle + 1) array_slice($types, $needle + 1)
); );
...@@ -152,16 +153,16 @@ class SQLParserUtils ...@@ -152,16 +153,16 @@ class SQLParserUtils
$paramsOrd = array(); $paramsOrd = array();
foreach ($paramPos as $pos => $paramName) { foreach ($paramPos as $pos => $paramName) {
$paramLen = strlen($paramName) + 1; $paramLen = strlen($paramName) + 1;
$value = $params[$paramName]; $value = static::extractParam($paramName, $params, true);
if ( ! isset($arrayPositions[$paramName])) { if ( ! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) {
$pos += $queryOffset; $pos += $queryOffset;
$queryOffset -= ($paramLen - 1); $queryOffset -= ($paramLen - 1);
$paramsOrd[] = $value; $paramsOrd[] = $value;
$typesOrd[] = $types[$paramName]; $typesOrd[] = static::extractParam($paramName, $types, false, \PDO::PARAM_STR);
$query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen)); $query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen));
continue; continue;
} }
...@@ -170,7 +171,7 @@ class SQLParserUtils ...@@ -170,7 +171,7 @@ class SQLParserUtils
foreach ($value as $val) { foreach ($value as $val) {
$paramsOrd[] = $val; $paramsOrd[] = $val;
$typesOrd[] = $types[$paramName] - Connection::ARRAY_PARAM_OFFSET; $typesOrd[] = static::extractParam($paramName, $types, false) - Connection::ARRAY_PARAM_OFFSET;
} }
$pos += $queryOffset; $pos += $queryOffset;
...@@ -199,4 +200,35 @@ class SQLParserUtils ...@@ -199,4 +200,35 @@ class SQLParserUtils
return $fragments[1]; return $fragments[1];
} }
/**
* @param string $paramName The name of the parameter (without a colon in front)
* @param array $paramsOrTypes A hash of parameters or types
* @param bool $isParam
* @param mixed $defaultValue An optional default value. If omitted, an exception is thrown
*
* @throws SQLParserUtilsException
* @return mixed
*/
static private function extractParam($paramName, $paramsOrTypes, $isParam, $defaultValue = null)
{
if (isset($paramsOrTypes[$paramName])) {
return $paramsOrTypes[$paramName];
}
// Hash keys can be prefixed with a colon for compatibility
if (isset($paramsOrTypes[':' . $paramName])) {
return $paramsOrTypes[':' . $paramName];
}
if (null !== $defaultValue) {
return $defaultValue;
}
if ($isParam) {
throw SQLParserUtilsException::missingParam($paramName);
} else {
throw SQLParserUtilsException::missingType($paramName);
}
}
} }
<?php
/*
* $Id: $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL;
/**
* Doctrine\DBAL\ConnectionException
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.4
* @author Lars Strojny <lars@strojny.net>
*/
class SQLParserUtilsException extends DBALException
{
public static function missingParam($paramName)
{
return new self(sprintf('Value for :%1$s not found in params array. Params array key should be "%1$s"', $paramName));
}
public static function missingType($typeName)
{
return new self(sprintf('Value for :%1$s not found in types array. Types array key should be "%1$s"', $typeName));
}
}
...@@ -237,6 +237,55 @@ SQLDATA ...@@ -237,6 +237,55 @@ SQLDATA
array(), array(),
array() array()
), ),
array(
"SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar OR baz = :baz",
array('foo' => array(1, 2), 'bar' => 'bar', 'baz' => 'baz'),
array('foo' => Connection::PARAM_INT_ARRAY, 'baz' => 'string'),
'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ? OR baz = ?',
array(1, 2, 'bar', 'baz'),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR, 'string')
),
array(
"SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar",
array('foo' => array(1, 2), 'bar' => 'bar'),
array('foo' => Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
array(1, 2, 'bar'),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
),
// Params/types with colons
array(
"SELECT * FROM Foo WHERE foo = :foo OR bar = :bar",
array(':foo' => 'foo', ':bar' => 'bar'),
array(':foo' => \PDO::PARAM_INT),
'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
array('foo', 'bar'),
array(\PDO::PARAM_INT, \PDO::PARAM_STR)
),
array(
"SELECT * FROM Foo WHERE foo = :foo OR bar = :bar",
array(':foo' => 'foo', ':bar' => 'bar'),
array(':foo' => \PDO::PARAM_INT, 'bar' => \PDO::PARAM_INT),
'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
array('foo', 'bar'),
array(\PDO::PARAM_INT, \PDO::PARAM_INT)
),
array(
"SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar",
array(':foo' => array(1, 2), ':bar' => 'bar'),
array('foo' => Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
array(1, 2, 'bar'),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
),
array(
"SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar",
array('foo' => array(1, 2), 'bar' => 'bar'),
array(':foo' => Connection::PARAM_INT_ARRAY),
'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
array(1, 2, 'bar'),
array(\PDO::PARAM_INT, \PDO::PARAM_INT, \PDO::PARAM_STR)
),
); );
} }
...@@ -257,4 +306,53 @@ SQLDATA ...@@ -257,4 +306,53 @@ SQLDATA
$this->assertEquals($expectedParams, $params, "Params dont match"); $this->assertEquals($expectedParams, $params, "Params dont match");
$this->assertEquals($expectedTypes, $types, "Types dont match"); $this->assertEquals($expectedTypes, $types, "Types dont match");
} }
public static function dataQueryWithMissingParameters()
{
return array(
array(
"SELECT * FROM foo WHERE bar = :param",
array('other' => 'val'),
array(),
),
array(
"SELECT * FROM foo WHERE bar = :param",
array(),
array(),
),
array(
"SELECT * FROM foo WHERE bar = :param",
array(),
array('param' => Connection::PARAM_INT_ARRAY),
),
array(
"SELECT * FROM foo WHERE bar = :param",
array(),
array(':param' => Connection::PARAM_INT_ARRAY),
),
array(
"SELECT * FROM foo WHERE bar = :param",
array(),
array('bar' => Connection::PARAM_INT_ARRAY),
),
array(
"SELECT * FROM foo WHERE bar = :param",
array('bar' => 'value'),
array('bar' => Connection::PARAM_INT_ARRAY),
),
);
}
/**
* @dataProvider dataQueryWithMissingParameters
*/
public function testExceptionIsThrownForMissingParam($query, $params, $types = array())
{
$this->setExpectedException(
'Doctrine\DBAL\SQLParserUtilsException',
'Value for :param not found in params array. Params array key should be "param"'
);
SQLParserUtils::expandListParameters($query, $params, $types);
}
} }
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