Browse Source

...

tags/2.0.0-beta
Qiang Xue 13 years ago
parent
commit
702476d886
  1. 26
      framework/base/Model.php
  2. 8
      framework/base/ModelBehavior.php
  3. 300
      framework/db/dao/QueryBuilder.php
  4. 16
      framework/db/dao/mysql/QueryBuilder.php
  5. 14
      framework/db/dao/sqlite/QueryBuilder.php
  6. 4
      tests/unit/framework/db/dao/CommandTest.php

26
framework/base/Model.php

@ -22,9 +22,9 @@ namespace yii\base;
* *
* Model also provides a set of events for further customization: * Model also provides a set of events for further customization:
* *
* - [[onAfterConstruct]]: an event raised at the end of constructor * - [[onAfterInit]]: an event raised at the end of [[init()]]
* - [[onBeforeValidate]]: an event raised at the beginning of [[validate]] * - [[onBeforeValidate]]: an event raised at the beginning of [[validate()]]
* - [[onAfterValidate]]: an event raised at the end of [[validate]] * - [[onAfterValidate]]: an event raised at the end of [[validate()]]
* *
* You may directly use Model to store model data, or extend it with customization. * You may directly use Model to store model data, or extend it with customization.
* You may also customize Model by attaching [[ModelBehavior|model behaviors]]. * You may also customize Model by attaching [[ModelBehavior|model behaviors]].
@ -46,7 +46,6 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
public function __construct($scenario = '') public function __construct($scenario = '')
{ {
$this->_scenario = $scenario; $this->_scenario = $scenario;
$this->afterConstruct();
} }
/** /**
@ -62,6 +61,7 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
public function init() public function init()
{ {
$this->attachBehaviors($this->behaviors()); $this->attachBehaviors($this->behaviors());
$this->afterInit();
} }
/** /**
@ -136,7 +136,7 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
* - validator type: required, specifies the validator to be used. It can be the name of a model * - validator type: required, specifies the validator to be used. It can be the name of a model
* class method, the name of a built-in validator, or a validator class (or its path alias). * class method, the name of a built-in validator, or a validator class (or its path alias).
* - on: optional, specifies the [[scenario|scenarios]] (separated by commas) when the validation * - on: optional, specifies the [[scenario|scenarios]] (separated by commas) when the validation
* rule can be applied. If this option is not set, the rule will apply to any scenario. * rule can be applied. If this option is not set, the rule will apply to all scenarios.
* - additional name-value pairs can be specified to initialize the corresponding validator properties. * - additional name-value pairs can be specified to initialize the corresponding validator properties.
* Please refer to individual validator class API for possible properties. * Please refer to individual validator class API for possible properties.
* *
@ -232,15 +232,15 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
} }
/** /**
* This method is invoked at the end of model constructor. * This method is invoked at the end of [[init()]].
* The default implementation raises the [[onAfterConstruct]] event. * The default implementation raises the [[onAfterInit]] event.
* You may override this method to do postprocessing after model creation. * You may override this method to do postprocessing after model creation.
* Make sure you call the parent implementation so that the event is raised properly. * Make sure you call the parent implementation so that the event is raised properly.
*/ */
public function afterConstruct() public function afterInit()
{ {
if ($this->hasEventHandlers('onAfterConstruct')) { if ($this->hasEventHandlers('onAfterInit')) {
$this->onAfterConstruct(new Event($this)); $this->onAfterInit(new Event($this));
} }
} }
@ -285,10 +285,10 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
} }
/** /**
* This event is raised after the model instance is created by new operator. * This event is raised at the end of [[init()]].
* @param Event $event the event parameter * @param Event $event the event parameter
*/ */
public function onAfterConstruct($event) public function onAfterInit($event)
{ {
$this->raiseEvent(__FUNCTION__, $event); $this->raiseEvent(__FUNCTION__, $event);
} }
@ -584,7 +584,7 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
public function onUnsafeAttribute($name, $value) public function onUnsafeAttribute($name, $value)
{ {
if (YII_DEBUG) { if (YII_DEBUG) {
\Yii::warning(sprintf('Failed to set unsafe attribute "%s" in "%s".', $name, get_class($this))); \Yii::warning("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.");
} }
} }

8
framework/base/ModelBehavior.php

@ -24,7 +24,7 @@ class ModelBehavior extends Behavior
* Declares event handlers for owner's events. * Declares event handlers for owner's events.
* The default implementation returns the following event handlers: * The default implementation returns the following event handlers:
* *
* - `onAfterConstruct` event: [[afterConstruct]] * - `onAfterInit` event: [[afterInit]]
* - `onBeforeValidate` event: [[beforeValidate]] * - `onBeforeValidate` event: [[beforeValidate]]
* - `onAfterValidate` event: [[afterValidate]] * - `onAfterValidate` event: [[afterValidate]]
* *
@ -34,18 +34,18 @@ class ModelBehavior extends Behavior
public function events() public function events()
{ {
return array( return array(
'onAfterConstruct' => 'afterConstruct', 'onAfterInit' => 'afterInit',
'onBeforeValidate' => 'beforeValidate', 'onBeforeValidate' => 'beforeValidate',
'onAfterValidate' => 'afterValidate', 'onAfterValidate' => 'afterValidate',
); );
} }
/** /**
* Responds to [[Model::onAfterConstruct]] event. * Responds to [[Model::onAfterInit]] event.
* Override this method if you want to handle the corresponding event of the [[owner]]. * Override this method if you want to handle the corresponding event of the [[owner]].
* @param Event $event event parameter * @param Event $event event parameter
*/ */
public function afterConstruct($event) public function afterInit($event)
{ {
} }

300
framework/db/dao/QueryBuilder.php

@ -44,9 +44,13 @@ class QueryBuilder extends \yii\base\Object
public $separator = " "; public $separator = " ";
/** /**
* @var Query the Query object that is currently processed by the query builder to generate a SQL statement. * @var Query the Query object that is currently processed by the query builder to generate a SQL statement.
* After the SQL statement is generated by [[build()]], this property will be set null. * This property will be set null upon completion of [[build()]].
*/ */
public $query; public $query;
/**
* @var boolean whether to automatically quote table and column names when generating SQL statements.
*/
public $autoQuote = true;
/** /**
* Constructor. * Constructor.
@ -99,8 +103,8 @@ class QueryBuilder extends \yii\base\Object
* ~~~ * ~~~
* $params = array(); * $params = array();
* $sql = $queryBuilder->insert('tbl_user', array( * $sql = $queryBuilder->insert('tbl_user', array(
* 'name' => 'Sam', * 'name' => 'Sam',
* 'age' => 30, * 'age' => 30,
* ), $params); * ), $params);
* ~~~ * ~~~
* *
@ -116,7 +120,7 @@ class QueryBuilder extends \yii\base\Object
$placeholders = array(); $placeholders = array();
$count = 0; $count = 0;
foreach ($columns as $name => $value) { foreach ($columns as $name => $value) {
$names[] = $this->driver->quoteColumnName($name); $names[] = $this->quoteColumnName($name);
if ($value instanceof Expression) { if ($value instanceof Expression) {
$placeholders[] = $value->expression; $placeholders[] = $value->expression;
foreach ($value->params as $n => $v) { foreach ($value->params as $n => $v) {
@ -132,7 +136,7 @@ class QueryBuilder extends \yii\base\Object
$this->query->addParams($params); $this->query->addParams($params);
} }
return 'INSERT INTO ' . $this->driver->quoteTableName($table) return 'INSERT INTO ' . $this->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES (' . ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')'; . implode(', ', $placeholders) . ')';
} }
@ -145,7 +149,7 @@ class QueryBuilder extends \yii\base\Object
* ~~~ * ~~~
* $params = array(); * $params = array();
* $sql = $queryBuilder->update('tbl_user', array( * $sql = $queryBuilder->update('tbl_user', array(
* 'status' => 1, * 'status' => 1,
* ), 'age > 30', $params); * ), 'age > 30', $params);
* ~~~ * ~~~
* *
@ -163,12 +167,12 @@ class QueryBuilder extends \yii\base\Object
$count = 0; $count = 0;
foreach ($columns as $name => $value) { foreach ($columns as $name => $value) {
if ($value instanceof Expression) { if ($value instanceof Expression) {
$lines[] = $this->driver->quoteSimpleColumnName($name) . '=' . $value->expression; $lines[] = $this->quoteColumnName($name, true) . '=' . $value->expression;
foreach ($value->params as $n => $v) { foreach ($value->params as $n => $v) {
$params[$n] = $v; $params[$n] = $v;
} }
} else { } else {
$lines[] = $this->driver->quoteSimpleColumnName($name) . '=:p' . $count; $lines[] = $this->quoteColumnName($name, true) . '=:p' . $count;
$params[':p' . $count] = $value; $params[':p' . $count] = $value;
$count++; $count++;
} }
@ -176,7 +180,7 @@ class QueryBuilder extends \yii\base\Object
if ($this->query instanceof Query) { if ($this->query instanceof Query) {
$this->query->addParams($params); $this->query->addParams($params);
} }
$sql = 'UPDATE ' . $this->driver->quoteTableName($table) . ' SET ' . implode(', ', $lines); $sql = 'UPDATE ' . $this->quoteTableName($table) . ' SET ' . implode(', ', $lines);
if (($where = $this->buildCondition($condition)) != '') { if (($where = $this->buildCondition($condition)) != '') {
$sql .= ' WHERE ' . $where; $sql .= ' WHERE ' . $where;
} }
@ -199,7 +203,7 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function delete($table, $condition = '') public function delete($table, $condition = '')
{ {
$sql = 'DELETE FROM ' . $this->driver->quoteTableName($table); $sql = 'DELETE FROM ' . $this->quoteTableName($table);
if (($where = $this->buildCondition($condition)) != '') { if (($where = $this->buildCondition($condition)) != '') {
$sql .= ' WHERE ' . $where; $sql .= ' WHERE ' . $where;
} }
@ -221,9 +225,9 @@ class QueryBuilder extends \yii\base\Object
* *
* ~~~ * ~~~
* $sql = $queryBuilder->createTable('tbl_user', array( * $sql = $queryBuilder->createTable('tbl_user', array(
* 'id' => 'pk', * 'id' => 'pk',
* 'name' => 'string', * 'name' => 'string',
* 'age' => 'integer', * 'age' => 'integer',
* )); * ));
* ~~~ * ~~~
* *
@ -237,13 +241,12 @@ class QueryBuilder extends \yii\base\Object
$cols = array(); $cols = array();
foreach ($columns as $name => $type) { foreach ($columns as $name => $type) {
if (is_string($name)) { if (is_string($name)) {
$cols[] = "\t" . $this->driver->quoteColumnName($name) . ' ' . $this->getColumnType($type); $cols[] = "\t" . $this->quoteColumnName($name) . ' ' . $this->getColumnType($type);
} else } else {
{
$cols[] = "\t" . $type; $cols[] = "\t" . $type;
} }
} }
$sql = "CREATE TABLE " . $this->driver->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)"; $sql = "CREATE TABLE " . $this->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)";
return $options === null ? $sql : $sql . ' ' . $options; return $options === null ? $sql : $sql . ' ' . $options;
} }
@ -255,7 +258,7 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function renameTable($oldName, $newName) public function renameTable($oldName, $newName)
{ {
return 'RENAME TABLE ' . $this->driver->quoteTableName($oldName) . ' TO ' . $this->driver->quoteTableName($newName); return 'RENAME TABLE ' . $this->quoteTableName($oldName) . ' TO ' . $this->quoteTableName($newName);
} }
/** /**
@ -265,7 +268,7 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function dropTable($table) public function dropTable($table)
{ {
return "DROP TABLE " . $this->driver->quoteTableName($table); return "DROP TABLE " . $this->quoteTableName($table);
} }
/** /**
@ -275,7 +278,7 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function truncateTable($table) public function truncateTable($table)
{ {
return "TRUNCATE TABLE " . $this->driver->quoteTableName($table); return "TRUNCATE TABLE " . $this->quoteTableName($table);
} }
/** /**
@ -289,8 +292,8 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function addColumn($table, $column, $type) public function addColumn($table, $column, $type)
{ {
return 'ALTER TABLE ' . $this->driver->quoteTableName($table) return 'ALTER TABLE ' . $this->quoteTableName($table)
. ' ADD ' . $this->driver->quoteColumnName($column) . ' ' . ' ADD ' . $this->quoteColumnName($column) . ' '
. $this->getColumnType($type); . $this->getColumnType($type);
} }
@ -302,8 +305,8 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function dropColumn($table, $column) public function dropColumn($table, $column)
{ {
return "ALTER TABLE " . $this->driver->quoteTableName($table) return "ALTER TABLE " . $this->quoteTableName($table)
. " DROP COLUMN " . $this->driver->quoteSimpleColumnName($column); . " DROP COLUMN " . $this->quoteColumnName($column, true);
} }
/** /**
@ -315,9 +318,9 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function renameColumn($table, $oldName, $newName) public function renameColumn($table, $oldName, $newName)
{ {
return "ALTER TABLE " . $this->driver->quoteTableName($table) return "ALTER TABLE " . $this->quoteTableName($table)
. " RENAME COLUMN " . $this->driver->quoteSimpleColumnName($oldName) . " RENAME COLUMN " . $this->quoteColumnName($oldName, true)
. " TO " . $this->driver->quoteSimpleColumnName($newName); . " TO " . $this->quoteColumnName($newName, true);
} }
/** /**
@ -332,9 +335,9 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function alterColumn($table, $column, $type) public function alterColumn($table, $column, $type)
{ {
return 'ALTER TABLE ' . $this->driver->quoteTableName($table) . ' CHANGE ' return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE '
. $this->driver->quoteSimpleColumnName($column) . ' ' . $this->quoteColumnName($column, true) . ' '
. $this->driver->quoteSimpleColumnName($column) . ' ' . $this->quoteColumnName($column, true) . ' '
. $this->getColumnType($type); . $this->getColumnType($type);
} }
@ -354,23 +357,11 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
{ {
if (!is_array($columns)) { $sql = 'ALTER TABLE ' . $this->quoteTableName($table)
$columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY); . ' ADD CONSTRAINT ' . $this->quoteColumnName($name)
} . ' FOREIGN KEY (' . $this->buildColumns($columns) . ')'
foreach ($columns as $i => $col) { . ' REFERENCES ' . $this->quoteTableName($refTable)
$columns[$i] = $this->driver->quoteColumnName($col); . ' (' . $this->buildColumns($refColumns) . ')';
}
if (!is_array($refColumns)) {
$refColumns = preg_split('/\s*,\s*/', $refColumns, -1, PREG_SPLIT_NO_EMPTY);
}
foreach ($refColumns as $i => $col) {
$refColumns[$i] = $this->driver->quoteColumnName($col);
}
$sql = 'ALTER TABLE ' . $this->driver->quoteTableName($table)
. ' ADD CONSTRAINT ' . $this->driver->quoteColumnName($name)
. ' FOREIGN KEY (' . implode(', ', $columns) . ')'
. ' REFERENCES ' . $this->driver->quoteTableName($refTable)
. ' (' . implode(', ', $refColumns) . ')';
if ($delete !== null) { if ($delete !== null) {
$sql .= ' ON DELETE ' . $delete; $sql .= ' ON DELETE ' . $delete;
} }
@ -388,8 +379,8 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function dropForeignKey($name, $table) public function dropForeignKey($name, $table)
{ {
return 'ALTER TABLE ' . $this->driver->quoteTableName($table) return 'ALTER TABLE ' . $this->quoteTableName($table)
. ' DROP CONSTRAINT ' . $this->driver->quoteColumnName($name); . ' DROP CONSTRAINT ' . $this->quoteColumnName($name);
} }
/** /**
@ -404,19 +395,10 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function createIndex($name, $table, $columns, $unique = false) public function createIndex($name, $table, $columns, $unique = false)
{ {
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
}
foreach ($columns as $i => $column) {
if (strpos($column, '(') !== false) {
$columns[$i] = $column;
} else {
$columns[$i] = $this->driver->quoteColumnName($column);
}
}
return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')
. $this->driver->quoteTableName($name) . ' ON ' . $this->quoteTableName($name) . ' ON '
. $this->driver->quoteTableName($table) . ' (' . implode(', ', $columns) . ')'; . $this->quoteTableName($table)
. ' (' . $this->buildColumns($columns) . ')';
} }
/** /**
@ -427,7 +409,7 @@ class QueryBuilder extends \yii\base\Object
*/ */
public function dropIndex($name, $table) public function dropIndex($name, $table)
{ {
return 'DROP INDEX ' . $this->driver->quoteTableName($name) . ' ON ' . $this->driver->quoteTableName($table); return 'DROP INDEX ' . $this->quoteTableName($name) . ' ON ' . $this->quoteTableName($table);
} }
/** /**
@ -525,7 +507,7 @@ class QueryBuilder extends \yii\base\Object
$column = $condition[1]; $column = $condition[1];
if (strpos($column, '(') === false) { if (strpos($column, '(') === false) {
$column = $this->connection->quoteColumnName($column); $column = $this->quoteColumnName($column);
} }
if ($operator === 'BETWEEN' || $operator === 'NOT BETWEEN') { if ($operator === 'BETWEEN' || $operator === 'NOT BETWEEN') {
@ -592,26 +574,32 @@ class QueryBuilder extends \yii\base\Object
return $select . ' *'; return $select . ' *';
} }
if (is_string($columns)) { if ($this->autoQuote) {
if (strpos($columns, '(') !== false) { if (!is_array($columns)) {
return $select . ' ' . $columns; if (strpos($columns, '(') !== false) {
} return $select . ' ' . $columns;
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-\.])$/', $column, $matches)) {
$columns[$i] = $this->connection->quoteColumnName($matches[1]) . ' AS ' . $this->driver->quoteSimpleColumnName($matches[2]);
} else { } else {
$columns[$i] = $this->connection->quoteColumnName($column); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-\.])$/', $column, $matches)) {
$columns[$i] = $this->driver->quoteColumnName($matches[1]) . ' AS ' . $this->driver->quoteSimpleColumnName($matches[2]);
} else {
$columns[$i] = $this->driver->quoteColumnName($column);
}
} }
} }
} }
return $select . ' ' . implode(', ', $columns); if (is_array($columns)) {
$columns = implode(', ', $columns);
}
return $select . ' ' . $columns;
} }
/** /**
@ -624,24 +612,31 @@ class QueryBuilder extends \yii\base\Object
} }
$tables = $this->query->from; $tables = $this->query->from;
if (is_string($tables) && strpos($tables, '(') !== false) {
return 'FROM ' . $tables;
}
if (!is_array($tables)) { if ($this->autoQuote) {
$tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY); if (!is_array($tables)) {
} if (strpos($tables, '(') !== false) {
foreach ($tables as $i => $table) { return 'FROM ' . $tables;
if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
$tables[$i] = $this->connection->quoteTableName($matches[1]) . ' ' . $this->connection->quoteTableName($matches[2]);
} else { } else {
$tables[$i] = $this->connection->quoteTableName($table); $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
} }
} }
foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
$tables[$i] = $this->driver->quoteTableName($matches[1]) . ' ' . $this->driver->quoteTableName($matches[2]);
} else {
$tables[$i] = $this->driver->quoteTableName($table);
}
}
}
}
if (is_array($tables)) {
$tables = implode(', ', $tables);
} }
return 'FROM ' . implode(', ', $tables); return 'FROM ' . $tables;
} }
/** /**
@ -661,11 +656,11 @@ class QueryBuilder extends \yii\base\Object
if (is_array($join)) { // join type, table name, on-condition if (is_array($join)) { // join type, table name, on-condition
if (isset($join[0], $join[1])) { if (isset($join[0], $join[1])) {
$table = $join[1]; $table = $join[1];
if (strpos($table, '(') === false) { if ($this->autoQuote && strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
$table = $this->connection->quoteTableName($matches[1]) . ' ' . $this->connection->quoteTableName($matches[2]); $table = $this->driver->quoteTableName($matches[1]) . ' ' . $this->driver->quoteTableName($matches[2]);
} else { } else {
$table = $this->connection->quoteTableName($table); $table = $this->driver->quoteTableName($table);
} }
} }
$joins[$i] = strtoupper($join[0]) . ' ' . $table; $joins[$i] = strtoupper($join[0]) . ' ' . $table;
@ -695,25 +690,11 @@ class QueryBuilder extends \yii\base\Object
*/ */
protected function buildGroupBy() protected function buildGroupBy()
{ {
$columns = $this->query->groupBy; if (empty($this->query->groupBy)) {
if (empty($columns)) {
return ''; return '';
} else {
return 'GROUP BY ' . $this->buildColumns($this->query->groupBy);
} }
if (is_string($columns) && strpos($columns, '(') !== false) {
return 'GROUP BY ' . $columns;
}
if (!is_array($columns)) {
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
$columns[$i] = $this->connection->quoteColumnName($column);
}
}
return 'GROUP BY ' . implode(', ', $columns);
} }
/** /**
@ -730,29 +711,34 @@ class QueryBuilder extends \yii\base\Object
*/ */
protected function buildOrderBy() protected function buildOrderBy()
{ {
$columns = $this->query->orderBy; if (empty($this->query->orderBy)) {
if (empty($columns)) {
return ''; return '';
} }
if (is_string($columns) && strpos($columns, '(') !== false) { $columns = $this->query->orderBy;
return 'ORDER BY ' . $columns; if ($this->autoQuote) {
} if (!is_array($columns)) {
if (strpos($columns, '(') !== false) {
if (!is_array($columns)) { return 'ORDER BY ' . $columns;
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$columns[$i] = $this->connection->quoteColumnName($matches[1]) . ' ' . strtoupper($matches[2]);
} else { } else {
$columns[$i] = $this->connection->quoteColumnName($column); $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
} }
} }
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
$columns[$i] = $this->driver->quoteColumnName($matches[1]) . ' ' . $matches[2];
} else {
$columns[$i] = $this->driver->quoteColumnName($column);
}
}
}
}
if (is_array($columns)) {
$columns = implode(', ', $columns);
} }
return 'ORDER BY ' . implode(', ', $columns); return 'ORDER BY ' . $columns;
} }
/** /**
@ -789,4 +775,64 @@ class QueryBuilder extends \yii\base\Object
} }
return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)";
} }
/**
* Processes columns and properly quote them if necessary.
* This method will quote columns if [[autoQuote]] is true.
* It will join all columns into a string with comma as separators.
* @param string|array $columns the columns to be processed
* @return string the processing result
*/
protected function buildColumns($columns)
{
if ($this->autoQuote) {
if (!is_array($columns)) {
if (strpos($columns, '(') !== false) {
return $columns;
} else {
$columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
}
}
foreach ($columns as $i => $column) {
if (is_object($column)) {
$columns[$i] = (string)$column;
} elseif (strpos($column, '(') === false) {
$columns[$i] = $this->driver->quoteColumnName($column);
}
}
}
return is_array($columns) ? implode(', ', $columns) : $columns;
}
/**
* Quotes a table name for use in a query.
* This method will perform name quoting only when [[autoQuote]] is true.
* @param string $name table name
* @param boolean $simple whether the name should be treated as a simple table name without any prefix.
* @return string the properly quoted table name
*/
protected function quoteTableName($name, $simple = false)
{
if ($this->autoQuote) {
return $simple ? $this->driver->quoteSimpleTableName($name) : $this->driver->quoteTableName($name);
} else {
return $name;
}
}
/**
* Quotes a column name for use in a query.
* This method will perform name quoting only when [[autoQuote]] is true.
* @param string $name column name
* @param boolean $simple whether the name should be treated as a simple column name without any prefix.
* @return string the properly quoted column name
*/
protected function quoteColumnName($name, $simple = false)
{
if ($this->autoQuote) {
return $simple ? $this->driver->quoteSimpleColumnName($name) : $this->driver->quoteColumnName($name);
} else {
return $name;
}
}
} }

16
framework/db/dao/mysql/QueryBuilder.php

@ -50,7 +50,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function renameColumn($table, $oldName, $newName) public function renameColumn($table, $oldName, $newName)
{ {
$quotedTable = $this->driver->quoteTableName($table); $quotedTable = $this->quoteTableName($table);
$row = $this->connection->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow(); $row = $this->connection->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryRow();
if ($row === false) { if ($row === false) {
throw new Exception("Unable to find '$oldName' in table '$table'."); throw new Exception("Unable to find '$oldName' in table '$table'.");
@ -64,13 +64,17 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) { if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
foreach ($matches[1] as $i => $c) { foreach ($matches[1] as $i => $c) {
if ($c === $oldName) { if ($c === $oldName) {
return "ALTER TABLE $quotedTable CHANGE " . $this->driver->quoteColumnName($oldName) return "ALTER TABLE $quotedTable CHANGE "
. ' ' . $this->driver->quoteColumnName($newName) . ' ' . $matches[2][$i]; . $this->quoteColumnName($oldName, true) . ' '
. $this->quoteColumnName($newName, true) . ' '
. $matches[2][$i];
} }
} }
} }
// try to give back a SQL anyway // try to give back a SQL anyway
return "ALTER TABLE $quotedTable CHANGE " . $this->driver->quoteColumnName($oldName) . ' ' . $newName; return "ALTER TABLE $quotedTable CHANGE "
. $this->quoteColumnName($oldName, true) . ' '
. $this->quoteColumnName($newName, true);
} }
/** /**
@ -81,7 +85,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function dropForeignKey($name, $table) public function dropForeignKey($name, $table)
{ {
return 'ALTER TABLE ' . $this->driver->quoteTableName($table) return 'ALTER TABLE ' . $this->quoteTableName($table)
. ' DROP FOREIGN KEY ' . $this->driver->quoteColumnName($name); . ' DROP FOREIGN KEY ' . $this->quoteColumnName($name);
} }
} }

14
framework/db/dao/sqlite/QueryBuilder.php

@ -83,7 +83,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function truncateTable($table) public function truncateTable($table)
{ {
return "DELETE FROM " . $this->driver->quoteTableName($table); return "DELETE FROM " . $this->quoteTableName($table);
} }
/** /**
@ -94,7 +94,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function dropIndex($name, $table) public function dropIndex($name, $table)
{ {
return 'DROP INDEX ' . $this->driver->quoteTableName($name); return 'DROP INDEX ' . $this->quoteTableName($name);
} }
/** /**
@ -105,7 +105,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function dropColumn($table, $column) public function dropColumn($table, $column)
{ {
throw new Exception('Dropping DB column is not supported by SQLite.'); throw new Exception(__METHOD__ . ' is not supported by SQLite.');
} }
/** /**
@ -117,7 +117,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function renameColumn($table, $oldName, $newName) public function renameColumn($table, $oldName, $newName)
{ {
throw new Exception('Renaming a DB column is not supported by SQLite.'); throw new Exception(__METHOD__ . ' is not supported by SQLite.');
} }
/** /**
@ -136,7 +136,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
{ {
throw new Exception('Adding a foreign key constraint to an existing table is not supported by SQLite.'); throw new Exception(__METHOD__ . ' is not supported by SQLite.');
} }
/** /**
@ -147,7 +147,7 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function dropForeignKey($name, $table) public function dropForeignKey($name, $table)
{ {
throw new Exception('Dropping a foreign key constraint is not supported by SQLite.'); throw new Exception(__METHOD__ . ' is not supported by SQLite.');
} }
/** /**
@ -162,6 +162,6 @@ class QueryBuilder extends \yii\db\dao\QueryBuilder
*/ */
public function alterColumn($table, $column, $type) public function alterColumn($table, $column, $type)
{ {
throw new Exception('Altering a DB column is not supported by SQLite.'); throw new Exception(__METHOD__ . ' is not supported by SQLite.');
} }
} }

4
tests/unit/framework/db/dao/CommandTest.php

@ -26,14 +26,14 @@ class CommandTest extends \yiiunit\MysqlTestCase
$query = new Query; $query = new Query;
$query->select('id')->from('tbl_user'); $query->select('id')->from('tbl_user');
$command = $db->createCommand($query); $command = $db->createCommand($query);
$this->assertEquals("SELECT `id`\nFROM `tbl_user`", $command->sql); $this->assertEquals("SELECT `id` FROM `tbl_user`", $command->sql);
// array // array
$command = $db->createCommand(array( $command = $db->createCommand(array(
'select' => 'name', 'select' => 'name',
'from' => 'tbl_user', 'from' => 'tbl_user',
)); ));
$this->assertEquals("SELECT `name`\nFROM `tbl_user`", $command->sql); $this->assertEquals("SELECT `name` FROM `tbl_user`", $command->sql);
} }
function testGetSetSql() function testGetSetSql()

Loading…
Cancel
Save