Skip to content

Commit

Permalink
Implemented automatic identifier quoting for all drivers, this features
Browse files Browse the repository at this point in the history
can be both controlled by configuration and using the Driver API. This
features is, nevertheless, very cpu expensive to have as a default, thus
it needs to be explicitly enabled.
  • Loading branch information
lorenzo committed Nov 6, 2013
1 parent d1c3f57 commit 8c103b3
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 81 deletions.
25 changes: 4 additions & 21 deletions Cake/Database/Dialect/PostgresDialectTrait.php
Expand Up @@ -17,8 +17,6 @@
namespace Cake\Database\Dialect;

use Cake\Database\Expression\FunctionExpression;
use Cake\Database\Expression\Comparison;
use Cake\Database\Expression\OrderByExpression;
use Cake\Database\Expression\UnaryExpression;
use Cake\Database\Query;
use Cake\Database\SqlDialectTrait;
Expand All @@ -29,7 +27,9 @@
*/
trait PostgresDialectTrait {

use SqlDialectTrait;
use SqlDialectTrait {
_expressionTranslators as private _quotingTranslators;
}

/**
* String used to start a database identifier quoting to make it safe
Expand Down Expand Up @@ -77,28 +77,11 @@ protected function _insertQueryTranslator($query) {
*/
protected function _expressionTranslators() {
$namespace = 'Cake\Database\Expression';
return [
$namespace . '\Comparison' => '_transformComparison',
$namespace . '\UnaryExpression' => '_transformUnary',
return $this->_quotingTranslators() + [
$namespace . '\FunctionExpression' => '_transformFunctionExpression'
];
}

protected function _transformComparison(Comparison $expression) {
$field = $expression->getField();
if (is_string($field)) {
$expression->field($this->quoteIdentifier($field));
}
}

protected function _transformUnary(UnaryExpression $expression) {
$expression->iterateParts(function($part) {
if (is_string($part)) {
return $this->quoteIdentifier($part);
}
});
}

/**
* Receives a FunctionExpression and changes it so that it conforms to this
* SQL dialect.
Expand Down
6 changes: 4 additions & 2 deletions Cake/Database/Dialect/SqliteDialectTrait.php
Expand Up @@ -23,7 +23,9 @@

trait SqliteDialectTrait {

use SqlDialectTrait;
use SqlDialectTrait {
_expressionTranslators as private _quotingTranslators;
}

/**
* String used to start a database identifier quoting to make it safe
Expand All @@ -47,7 +49,7 @@ trait SqliteDialectTrait {
*/
protected function _expressionTranslators() {
$namespace = 'Cake\Database\Expression';
return [
return $this->_quotingTranslators() + [
$namespace . '\FunctionExpression' => '_transformFunctionExpression'
];
}
Expand Down
28 changes: 28 additions & 0 deletions Cake/Database/Driver.php
Expand Up @@ -38,6 +38,14 @@ abstract class Driver {
*/
protected $_baseConfig = [];

/**
* Indicates whether or not the driver is doing automatic identifier quoting
* for all queries
*
* @var bool
*/
protected $_autoQuoting = false;

/**
* Constructor
*
Expand All @@ -47,6 +55,9 @@ abstract class Driver {
public function __construct($config = []) {
$config += $this->_baseConfig;
$this->_config = $config;
if (!empty($config['quoteIdentifiers'])) {
$this->autoQuoting(true);
}
}

/**
Expand Down Expand Up @@ -180,6 +191,23 @@ public function isConnected() {
return $this->_connection !== null;
}

/**
* Returns whether or not this driver should automatically quote identifiers
* in queries
*
* If called with a boolean argument, it will toggle the auto quoting setting
* to the passed value
*
* @param boolean $enable whether to enable auto quoting
* @return boolean
*/
public function autoQuoting($enable = null) {
if ($enable === null) {
return $this->_autoQuoting;
}
return $this->_autoQuoting = (bool)$enable;
}

/**
* Destructor
*
Expand Down
58 changes: 58 additions & 0 deletions Cake/Database/Expression/FieldExpression.php
@@ -0,0 +1,58 @@
<?php
/**
* PHP Version 5.4
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Database\Expression;

use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;

class FieldExpression implements ExpressionInterface {

protected $_field;

public function __construct($field) {
$this->field($field);
}

public function field($field) {
$this->_field = $field;
}

public function getField() {
return $this->_field;
}

/**
* Converts the expression to its string representation
*
* @param Cake\Database\ValueBinder $generator Placeholder generator object
* @return string
*/
public function sql(ValueBinder $generator) {
return $this->_field;
}

/**
* This method is a no-op, this is a leaf type of expression,
* hence there is nothing to traverse
*
* @param callable $visitor
* @return void
*/
public function traverse(callable $callable) {
}

}
14 changes: 12 additions & 2 deletions Cake/Database/Expression/OrderByExpression.php
Expand Up @@ -49,8 +49,18 @@ public function sql(ValueBinder $generator) {
return sprintf('ORDER BY %s', implode(', ', $order));
}

protected function _addConditions(array $conditions, array $types) {
$this->_conditions = array_merge($this->_conditions, $conditions);
/**
* Auxiliary function used for decomposing a nested array of conditions and build
* a tree structure inside this object to represent the full SQL expression.
*
* New order by expressions are merged to existing ones
*
* @param array $orders list of order by expressions
* @param array $types list of types associated on fields referenced in $conditions
* @return void
*/
protected function _addConditions(array $orders, array $types) {
$this->_conditions = array_merge($this->_conditions, $orders);
}

}
12 changes: 9 additions & 3 deletions Cake/Database/Expression/QueryExpression.php
Expand Up @@ -341,7 +341,7 @@ public function sql(ValueBinder $generator) {
*/
public function traverse(callable $callable) {
foreach ($this->_conditions as $c) {
if ($c instanceof self) {
if ($c instanceof ExpressionInterface) {
$callable($c);
$c->traverse($callable);
}
Expand All @@ -354,15 +354,21 @@ public function traverse(callable $callable) {
* which the currently visited part will be replaced. If the callable function
* returns null then the part will be discarded completely from this expression
*
* The callback function will receive each of the conditions as first param and
* the key as second param. It is possible to declare the second parameter as
* passed by reference, this will enable you to change the key under which the
* modified part is stored.
*
* @param callable $callable
* @return QueryExpression
*/
public function iterateParts(callable $callable) {
$parts = [];
foreach ($this->_conditions as $k => $c) {
$part = $callable($c);
$key =& $k;
$part = $callable($c, $key);
if ($part !== null) {
$parts[] = $part;
$parts[$key] = $part;
}
}
$this->_conditions = $parts;
Expand Down

0 comments on commit 8c103b3

Please sign in to comment.