From 597082a11aa49b59826b2300fd90122bba1cf947 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 30 Mar 2013 18:28:54 -0400 Subject: [PATCH] Automatic table and column name quoting. --- framework/caching/DbCache.php | 6 +++--- framework/db/Command.php | 25 ++-------------------- framework/db/Connection.php | 34 +++++++++++++++++++----------- framework/db/Schema.php | 10 ++++----- framework/logging/DbTarget.php | 3 ++- framework/web/DbSession.php | 4 ++-- tests/unit/framework/db/ConnectionTest.php | 1 - 7 files changed, 36 insertions(+), 47 deletions(-) diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index 3952852..dee8c7a 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -99,7 +99,7 @@ class DbCache extends Cache $query = new Query; $query->select(array('data')) ->from($this->cacheTable) - ->where('id = :id AND (expire = 0 OR expire >' . time() . ')', array(':id' => $key)); + ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', array(':id' => $key)); if ($this->db->enableQueryCache) { // temporarily disable and re-enable query caching $this->db->enableQueryCache = false; @@ -125,7 +125,7 @@ class DbCache extends Cache $query->select(array('id', 'data')) ->from($this->cacheTable) ->where(array('id' => $keys)) - ->andWhere('(expire = 0 OR expire > ' . time() . ')'); + ->andWhere('([[expire]] = 0 OR [[expire]] > ' . time() . ')'); if ($this->db->enableQueryCache) { $this->db->enableQueryCache = false; @@ -227,7 +227,7 @@ class DbCache extends Cache { if ($force || mt_rand(0, 1000000) < $this->gcProbability) { $this->db->createCommand() - ->delete($this->cacheTable, 'expire > 0 AND expire < ' . time()) + ->delete($this->cacheTable, '[[expire]] > 0 AND [[expire]] < ' . time()) ->execute(); } } diff --git a/framework/db/Command.php b/framework/db/Command.php index 2a13a89..73a8346 100644 --- a/framework/db/Command.php +++ b/framework/db/Command.php @@ -84,42 +84,21 @@ class Command extends \yii\base\Component /** * Specifies the SQL statement to be executed. - * Any previous execution will be terminated or cancelled. + * The previous SQL execution (if any) will be cancelled, and [[params]] will be cleared as well. * @param string $sql the SQL statement to be set. * @return Command this command instance */ public function setSql($sql) { if ($sql !== $this->_sql) { - if ($this->db->enableAutoQuoting && $sql != '') { - $sql = $this->expandSql($sql); - } $this->cancel(); - $this->_sql = $sql; + $this->_sql = $this->db->quoteSql($sql); $this->_params = array(); } return $this; } /** - * Expands a SQL statement by quoting table and column names and replacing table prefixes. - * @param string $sql the SQL to be expanded - * @return string the expanded SQL - */ - protected function expandSql($sql) - { - $db = $this->db; - return preg_replace_callback('/(\\{\\{(.*?)\\}\\}|\\[\\[(.*?)\\]\\])/', function($matches) use($db) { - if (isset($matches[3])) { - return $db->quoteColumnName($matches[3]); - } else { - $name = str_replace('%', $db->tablePrefix, $matches[2]); - return $db->quoteTableName($name); - } - }, $sql); - } - - /** * Prepares the SQL statement to be executed. * For complex SQL statement that is to be executed multiple times, * this may improve performance. diff --git a/framework/db/Connection.php b/framework/db/Connection.php index 1be43eb..695034a 100644 --- a/framework/db/Connection.php +++ b/framework/db/Connection.php @@ -223,21 +223,10 @@ class Connection extends Component * @var string the common prefix or suffix for table names. If a table name is given * as `{{%TableName}}`, then the percentage character `%` will be replaced with this * property value. For example, `{{%post}}` becomes `{{tbl_post}}` if this property is - * set as `"tbl_"`. Note that this property is only effective when [[enableAutoQuoting]] - * is true. - * @see enableAutoQuoting + * set as `"tbl_"`. */ public $tablePrefix; /** - * @var boolean whether to enable automatic quoting of table names and column names. - * Defaults to true. When this property is true, any token enclosed within double curly brackets - * (e.g. `{{post}}`) in a SQL statement will be treated as a table name and will be quoted - * accordingly when the SQL statement is executed; and any token enclosed within double square - * brackets (e.g. `[[name]]`) will be treated as a column name and quoted accordingly. - * @see tablePrefix - */ - public $enableAutoQuoting = true; - /** * @var array mapping between PDO driver names and [[Schema]] classes. * The keys of the array are PDO driver names while the values the corresponding * schema class name or configuration. Please refer to [[\Yii::createObject()]] for @@ -518,6 +507,27 @@ class Connection extends Component } /** + * Processes a SQL statement by quoting table and column names that are enclosed within double brackets. + * Tokens enclosed within double curly brackets are treated as table names, while + * tokens enclosed within double square brackets are column names. They will be quoted accordingly. + * Also, the percentage character "%" in a table name will be replaced with [[tablePrefix]]. + * @param string $sql the SQL to be quoted + * @return string the quoted SQL + */ + public function quoteSql($sql) + { + $db = $this; + return preg_replace_callback('/(\\{\\{([\w\-\. ]+)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/', + function($matches) use($db) { + if (isset($matches[3])) { + return $db->quoteColumnName($matches[3]); + } else { + return str_replace('%', $this->tablePrefix, $db->quoteTableName($matches[2])); + } + }, $sql); + } + + /** * Returns the name of the DB driver for the current [[dsn]]. * @return string name of the DB driver */ diff --git a/framework/db/Schema.php b/framework/db/Schema.php index 71bc9a2..d8d89bc 100644 --- a/framework/db/Schema.php +++ b/framework/db/Schema.php @@ -248,7 +248,7 @@ abstract class Schema extends \yii\base\Object /** * Quotes a table name for use in a query. * If the table name contains schema prefix, the prefix will also be properly quoted. - * If the table name is already quoted or contains special characters including '(', '[[' and '{{', + * If the table name is already quoted or contains '(' or '{{', * then this method will do nothing. * @param string $name table name * @return string the properly quoted table name @@ -256,7 +256,7 @@ abstract class Schema extends \yii\base\Object */ public function quoteTableName($name) { - if (strpos($name, '(') !== false || strpos($name, '[[') !== false || strpos($name, '{{') !== false) { + if (strpos($name, '(') !== false || strpos($name, '{{') !== false) { return $name; } if (strpos($name, '.') === false) { @@ -273,7 +273,7 @@ abstract class Schema extends \yii\base\Object /** * Quotes a column name for use in a query. * If the column name contains prefix, the prefix will also be properly quoted. - * If the column name is already quoted or contains special characters including '(', '[[' and '{{', + * If the column name is already quoted or contains '(', '[[' or '{{', * then this method will do nothing. * @param string $name column name * @return string the properly quoted column name @@ -320,13 +320,13 @@ abstract class Schema extends \yii\base\Object /** * Returns the real name of a table name. * This method will strip off curly brackets from the given table name - * and replace the percentage character in the name with [[Connection::tablePrefix]]. + * and replace the percentage character '%' with [[Connection::tablePrefix]]. * @param string $name the table name to be converted * @return string the real name of the given table name */ public function getRealTableName($name) { - if ($this->db->enableAutoQuoting && strpos($name, '{{') !== false) { + if (strpos($name, '{{') !== false) { $name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name); return str_replace('%', $this->db->tablePrefix, $name); } else { diff --git a/framework/logging/DbTarget.php b/framework/logging/DbTarget.php index e4e30ce..ce9d843 100644 --- a/framework/logging/DbTarget.php +++ b/framework/logging/DbTarget.php @@ -78,7 +78,8 @@ class DbTarget extends Target public function export($messages) { $tableName = $this->db->quoteTableName($this->logTable); - $sql = "INSERT INTO $tableName (level, category, log_time, message) VALUES (:level, :category, :log_time, :message)"; + $sql = "INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[message]]) + VALUES (:level, :category, :log_time, :message)"; $command = $this->db->createCommand($sql); foreach ($messages as $message) { $command->bindValues(array( diff --git a/framework/web/DbSession.php b/framework/web/DbSession.php index d3afc76..2910b40 100644 --- a/framework/web/DbSession.php +++ b/framework/web/DbSession.php @@ -144,7 +144,7 @@ class DbSession extends Session $query = new Query; $data = $query->select(array('data')) ->from($this->sessionTable) - ->where('expire>:expire AND id=:id', array(':expire' => time(), ':id' => $id)) + ->where('[[expire]]>:expire AND [[id]]=:id', array(':expire' => time(), ':id' => $id)) ->createCommand($this->db) ->queryScalar(); return $data === false ? '' : $data; @@ -214,7 +214,7 @@ class DbSession extends Session public function gcSession($maxLifetime) { $this->db->createCommand() - ->delete($this->sessionTable, 'expire<:expire', array(':expire' => time())) + ->delete($this->sessionTable, '[[expire]]<:expire', array(':expire' => time())) ->execute(); return true; } diff --git a/tests/unit/framework/db/ConnectionTest.php b/tests/unit/framework/db/ConnectionTest.php index afb4f20..256c5a9 100644 --- a/tests/unit/framework/db/ConnectionTest.php +++ b/tests/unit/framework/db/ConnectionTest.php @@ -59,7 +59,6 @@ class ConnectionTest extends \yiiunit\MysqlTestCase $this->assertEquals('`table`', $connection->quoteTableName('`table`')); $this->assertEquals('`schema`.`table`', $connection->quoteTableName('schema.table')); $this->assertEquals('`schema`.`table`', $connection->quoteTableName('schema.`table`')); - $this->assertEquals('[[table]]', $connection->quoteTableName('[[table]]')); $this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}')); $this->assertEquals('(table)', $connection->quoteTableName('(table)')); }