Commit 64c5da53 by Qiang Xue

w

parent 7b31e27d
...@@ -50,12 +50,19 @@ class Command extends \yii\base\Component ...@@ -50,12 +50,19 @@ class Command extends \yii\base\Component
*/ */
public $params = array(); public $params = array();
private $_connection; public $connection;
private $_text; public $query;
private $_statement; public $pdoStatement;
private $_sql;
private $_paramLog = array(); private $_paramLog = array();
private $_query; /**
private $_fetchMode = array(PDO::FETCH_ASSOC); * Set the default fetch mode for this statement
* @param mixed $mode fetch mode
* @return Command
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
*/
public $fetchMode = \PDO::FETCH_ASSOC;
/** /**
* Constructor. * Constructor.
...@@ -80,38 +87,19 @@ class Command extends \yii\base\Component ...@@ -80,38 +87,19 @@ class Command extends \yii\base\Component
*/ */
public function __construct($connection, $query = null) public function __construct($connection, $query = null)
{ {
$this->_connection = $connection; $this->connection = $connection;
if (is_array($query)) if (is_object($query)) {
{ $this->query = $query;
foreach ($query as $name => $value)
$this->$name = $value;
} }
else else {
$this->setText($query); $this->query = new Query;
if (is_array($this->query)) {
$this->query->fromArray($this->query);
}
else {
$this->_sql = $query;
} }
/**
* Set the statement to null when serializing.
* @return array
*/
public function __sleep()
{
$this->_statement = null;
return array_keys(get_object_vars($this));
} }
/**
* Set the default fetch mode for this statement
* @param mixed $mode fetch mode
* @return Command
* @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php
* @since 1.1.7
*/
public function setFetchMode($mode)
{
$params = func_get_args();
$this->_fetchMode = $params;
return $this;
} }
/** /**
...@@ -124,9 +112,9 @@ class Command extends \yii\base\Component ...@@ -124,9 +112,9 @@ class Command extends \yii\base\Component
*/ */
public function reset() public function reset()
{ {
$this->_text = null; $this->_sql = null;
$this->_query = null; $this->query = new Query;
$this->_statement = null; $this->pdoStatement = null;
$this->_paramLog = array(); $this->_paramLog = array();
$this->params = array(); $this->params = array();
return $this; return $this;
...@@ -135,11 +123,12 @@ class Command extends \yii\base\Component ...@@ -135,11 +123,12 @@ class Command extends \yii\base\Component
/** /**
* @return string the SQL statement to be executed * @return string the SQL statement to be executed
*/ */
public function getText() public function getSql()
{ {
if ($this->_text == '' && !empty($this->_query)) if ($this->_sql == '' && is_object($this->query)) {
$this->setText($this->buildQuery($this->_query)); $this->_sql = $this->query->getSql($this->connection);
return $this->_text; }
return $this->_sql;
} }
/** /**
...@@ -148,31 +137,16 @@ class Command extends \yii\base\Component ...@@ -148,31 +137,16 @@ class Command extends \yii\base\Component
* @param string $value the SQL statement to be executed * @param string $value the SQL statement to be executed
* @return Command this command instance * @return Command this command instance
*/ */
public function setText($value) public function setSql($value)
{ {
if ($this->_connection->tablePrefix !== null && $value != '') if ($this->connection->tablePrefix !== null && strpos($value, '{') !== false) {
$this->_text = preg_replace('/{{(.*?)}}/', $this->_connection->tablePrefix . '\1', $value); $this->_sql = preg_replace('/{{(.*?)}}/', $this->connection->tablePrefix . '\1', $value);
else
$this->_text = $value;
$this->cancel();
return $this;
} }
else {
/** $this->_sql = $value;
* @return CDbConnection the connection associated with this command
*/
public function getConnection()
{
return $this->_connection;
} }
$this->cancel();
/** return $this;
* @return PDOStatement the underlying PDOStatement for this command
* It could be null if the statement is not prepared yet.
*/
public function getPdoStatement()
{
return $this->_statement;
} }
/** /**
...@@ -184,18 +158,15 @@ class Command extends \yii\base\Component ...@@ -184,18 +158,15 @@ class Command extends \yii\base\Component
*/ */
public function prepare() public function prepare()
{ {
if ($this->_statement == null) if ($this->pdoStatement == null) {
{ try {
try $this->pdoStatement = $this->connection->pdo->prepare($this->getSql());
{
$this->_statement = $this->getConnection()->getPdoInstance()->prepare($this->getText());
$this->_paramLog = array(); $this->_paramLog = array();
} }
catch(Exception $e) catch(Exception $e) {
{ Yii::log('Error in preparing SQL: ' . $this->getSql(), CLogger::LEVEL_ERROR, 'system.db.Command');
Yii::log('Error in preparing SQL: ' . $this->getText(), CLogger::LEVEL_ERROR, 'system.db.Command'); $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
$errorInfo = $e instanceof PDOException ? $e->errorInfo : null; throw new Exception('Unable to prepare the SQL statement: {error}',
throw new CDbException(Yii::t('yii', 'Command failed to prepare the SQL statement: {error}',
array('{error}' => $e->getMessage())), (int)$e->getCode(), $errorInfo); array('{error}' => $e->getMessage())), (int)$e->getCode(), $errorInfo);
} }
} }
...@@ -206,7 +177,7 @@ class Command extends \yii\base\Component ...@@ -206,7 +177,7 @@ class Command extends \yii\base\Component
*/ */
public function cancel() public function cancel()
{ {
$this->_statement = null; $this->pdoStatement = null;
} }
/** /**
...@@ -226,13 +197,13 @@ class Command extends \yii\base\Component ...@@ -226,13 +197,13 @@ class Command extends \yii\base\Component
{ {
$this->prepare(); $this->prepare();
if ($dataType === null) if ($dataType === null)
$this->_statement->bindParam($name, $value, $this->_connection->getPdoType(gettype($value))); $this->pdoStatement->bindParam($name, $value, $this->connection->getPdoType(gettype($value)));
elseif ($length === null) elseif ($length === null)
$this->_statement->bindParam($name, $value, $dataType); $this->pdoStatement->bindParam($name, $value, $dataType);
elseif ($driverOptions === null) elseif ($driverOptions === null)
$this->_statement->bindParam($name, $value, $dataType, $length); $this->pdoStatement->bindParam($name, $value, $dataType, $length);
else else
$this->_statement->bindParam($name, $value, $dataType, $length, $driverOptions); $this->pdoStatement->bindParam($name, $value, $dataType, $length, $driverOptions);
$this->_paramLog[$name] =& $value; $this->_paramLog[$name] =& $value;
return $this; return $this;
} }
...@@ -252,9 +223,9 @@ class Command extends \yii\base\Component ...@@ -252,9 +223,9 @@ class Command extends \yii\base\Component
{ {
$this->prepare(); $this->prepare();
if ($dataType === null) if ($dataType === null)
$this->_statement->bindValue($name, $value, $this->_connection->getPdoType(gettype($value))); $this->pdoStatement->bindValue($name, $value, $this->connection->getPdoType(gettype($value)));
else else
$this->_statement->bindValue($name, $value, $dataType); $this->pdoStatement->bindValue($name, $value, $dataType);
$this->_paramLog[$name] = $value; $this->_paramLog[$name] = $value;
return $this; return $this;
} }
...@@ -274,7 +245,7 @@ class Command extends \yii\base\Component ...@@ -274,7 +245,7 @@ class Command extends \yii\base\Component
$this->prepare(); $this->prepare();
foreach ($values as $name => $value) foreach ($values as $name => $value)
{ {
$this->_statement->bindValue($name, $value, $this->_connection->getPdoType(gettype($value))); $this->pdoStatement->bindValue($name, $value, $this->connection->getPdoType(gettype($value)));
$this->_paramLog[$name] = $value; $this->_paramLog[$name] = $value;
} }
return $this; return $this;
...@@ -295,7 +266,7 @@ class Command extends \yii\base\Component ...@@ -295,7 +266,7 @@ class Command extends \yii\base\Component
*/ */
public function execute($params = array()) public function execute($params = array())
{ {
if ($this->_connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) if ($this->connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array())
{ {
$p = array(); $p = array();
foreach ($pars as $name => $value) foreach ($pars as $name => $value)
...@@ -304,34 +275,34 @@ class Command extends \yii\base\Component ...@@ -304,34 +275,34 @@ class Command extends \yii\base\Component
} }
else else
$par = ''; $par = '';
Yii::trace('Executing SQL: ' . $this->getText() . $par, 'system.db.Command'); Yii::trace('Executing SQL: ' . $this->getSql() . $par, 'system.db.Command');
try try
{ {
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::beginProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); Yii::beginProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
$this->prepare(); $this->prepare();
if ($params === array()) if ($params === array())
$this->_statement->execute(); $this->pdoStatement->execute();
else else
$this->_statement->execute($params); $this->pdoStatement->execute($params);
$n = $this->_statement->rowCount(); $n = $this->pdoStatement->rowCount();
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::endProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
return $n; return $n;
} }
catch(Exception $e) catch(Exception $e)
{ {
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::endProfile('system.db.Command.execute(' . $this->getText() . ')', 'system.db.Command.execute'); Yii::endProfile('system.db.Command.execute(' . $this->getSql() . ')', 'system.db.Command.execute');
$errorInfo = $e instanceof PDOException ? $e->errorInfo : null; $errorInfo = $e instanceof PDOException ? $e->errorInfo : null;
$message = $e->getMessage(); $message = $e->getMessage();
Yii::log(Yii::t('yii', 'Command::execute() failed: {error}. The SQL statement executed was: {sql}.', Yii::log(Yii::t('yii', 'Command::execute() failed: {error}. The SQL statement executed was: {sql}.',
array('{error}' => $message, '{sql}' => $this->getText() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); array('{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command');
if (YII_DEBUG) if (YII_DEBUG)
$message .= '. The SQL statement executed was: ' . $this->getText() . $par; $message .= '. The SQL statement executed was: ' . $this->getSql() . $par;
throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}', throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
array('{error}' => $message)), (int)$e->getCode(), $errorInfo); array('{error}' => $message)), (int)$e->getCode(), $errorInfo);
} }
...@@ -351,7 +322,7 @@ class Command extends \yii\base\Component ...@@ -351,7 +322,7 @@ class Command extends \yii\base\Component
*/ */
public function query($params = array()) public function query($params = array())
{ {
return $this->queryInternal('', 0, $params); return $this->queryInternal('', $params);
} }
/** /**
...@@ -368,9 +339,9 @@ class Command extends \yii\base\Component ...@@ -368,9 +339,9 @@ class Command extends \yii\base\Component
* An empty array is returned if the query results in nothing. * An empty array is returned if the query results in nothing.
* @throws CException execution failed * @throws CException execution failed
*/ */
public function queryAll($fetchAssociative = true, $params = array()) public function queryAll($params = array(), $fetchMode = null)
{ {
return $this->queryInternal('fetchAll', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params); return $this->queryInternal('fetchAll', $params, $fetchMode);
} }
/** /**
...@@ -387,9 +358,9 @@ class Command extends \yii\base\Component ...@@ -387,9 +358,9 @@ class Command extends \yii\base\Component
* @return mixed the first row (in terms of an array) of the query result, false if no result. * @return mixed the first row (in terms of an array) of the query result, false if no result.
* @throws CException execution failed * @throws CException execution failed
*/ */
public function queryRow($fetchAssociative = true, $params = array()) public function queryRow($params = array(), $fetchMode = null)
{ {
return $this->queryInternal('fetch', $fetchAssociative ? $this->_fetchMode : PDO::FETCH_NUM, $params); return $this->queryInternal('fetch', $params, $fetchMode);
} }
/** /**
...@@ -407,12 +378,14 @@ class Command extends \yii\base\Component ...@@ -407,12 +378,14 @@ class Command extends \yii\base\Component
*/ */
public function queryScalar($params = array()) public function queryScalar($params = array())
{ {
$result = $this->queryInternal('fetchColumn', 0, $params); $result = $this->queryInternal('fetchColumn', $params);
if (is_resource($result) && get_resource_type($result) === 'stream') if (is_resource($result) && get_resource_type($result) === 'stream') {
return stream_get_contents($result); return stream_get_contents($result);
else }
else {
return $result; return $result;
} }
}
/** /**
* Executes the SQL statement and returns the first column of the result. * Executes the SQL statement and returns the first column of the result.
...@@ -429,7 +402,7 @@ class Command extends \yii\base\Component ...@@ -429,7 +402,7 @@ class Command extends \yii\base\Component
*/ */
public function queryColumn($params = array()) public function queryColumn($params = array())
{ {
return $this->queryInternal('fetchAll', PDO::FETCH_COLUMN, $params); return $this->queryInternal('fetchAll', $params, \PDO::FETCH_COLUMN);
} }
/** /**
...@@ -443,11 +416,11 @@ class Command extends \yii\base\Component ...@@ -443,11 +416,11 @@ class Command extends \yii\base\Component
* This parameter has been available since version 1.0.10. * This parameter has been available since version 1.0.10.
* @return mixed the method execution result * @return mixed the method execution result
*/ */
private function queryInternal($method, $mode, $params = array()) private function queryInternal($method, $params, $fetchMode = null)
{ {
$params = array_merge($this->params, $params); $params = array_merge($this->params, $params);
if ($this->_connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array()) if ($this->connection->enableParamLogging && ($pars = array_merge($this->_paramLog, $params)) !== array())
{ {
$p = array(); $p = array();
foreach ($pars as $name => $value) foreach ($pars as $name => $value)
...@@ -457,16 +430,16 @@ class Command extends \yii\base\Component ...@@ -457,16 +430,16 @@ class Command extends \yii\base\Component
else else
$par = ''; $par = '';
Yii::trace('Querying SQL: ' . $this->getText() . $par, 'system.db.Command'); Yii::trace('Querying SQL: ' . $this->getSql() . $par, 'system.db.Command');
if ($this->_connection->queryCachingCount > 0 && $method !== '' if ($this->connection->queryCachingCount > 0 && $method !== ''
&& $this->_connection->queryCachingDuration > 0 && $this->connection->queryCachingDuration > 0
&& $this->_connection->queryCacheID !== false && $this->connection->queryCacheID !== false
&& ($cache = Yii::app()->getComponent($this->_connection->queryCacheID)) !== null) && ($cache = Yii::app()->getComponent($this->connection->queryCacheID)) !== null)
{ {
$this->_connection->queryCachingCount--; $this->connection->queryCachingCount--;
$cacheKey = 'yii:dbquery' . $this->_connection->connectionString . ':' . $this->_connection->username; $cacheKey = 'yii:dbquery' . $this->connection->connectionString . ':' . $this->connection->username;
$cacheKey .= ':' . $this->getText() . ':' . serialize(array_merge($this->_paramLog, $params)); $cacheKey .= ':' . $this->getSql() . ':' . serialize(array_merge($this->_paramLog, $params));
if (($result = $cache->get($cacheKey)) !== false) if (($result = $cache->get($cacheKey)) !== false)
{ {
Yii::trace('Query result found in cache', 'system.db.Command'); Yii::trace('Query result found in cache', 'system.db.Command');
...@@ -476,93 +449,50 @@ class Command extends \yii\base\Component ...@@ -476,93 +449,50 @@ class Command extends \yii\base\Component
try try
{ {
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::beginProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); Yii::beginProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
$this->prepare(); $this->prepare();
if ($params === array()) if ($params === array())
$this->_statement->execute(); $this->pdoStatement->execute();
else else
$this->_statement->execute($params); $this->pdoStatement->execute($params);
if ($method === '') if ($method === '')
$result = new CDbDataReader($this); $result = new DataReader($this);
else else
{ {
$mode = (array)$mode; if ($fetchMode === null) {
$result = call_user_func_array(array($this->_statement, $method), $mode); $fetchMode = $this->fetchMode;
$this->_statement->closeCursor(); }
$result = call_user_func_array(array($this->pdoStatement, $method), (array)$fetchMode);
$this->pdoStatement->closeCursor();
} }
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::endProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
if (isset($cache, $cacheKey)) if (isset($cache, $cacheKey))
$cache->set($cacheKey, $result, $this->_connection->queryCachingDuration, $this->_connection->queryCachingDependency); $cache->set($cacheKey, $result, $this->connection->queryCachingDuration, $this->connection->queryCachingDependency);
return $result; return $result;
} }
catch(Exception $e) catch(Exception $e)
{ {
if ($this->_connection->enableProfiling) if ($this->connection->enableProfiling)
Yii::endProfile('system.db.Command.query(' . $this->getText() . $par . ')', 'system.db.Command.query'); Yii::endProfile('system.db.Command.query(' . $this->getSql() . $par . ')', 'system.db.Command.query');
$errorInfo = $e instanceof PDOException ? $e->errorInfo : null; $errorInfo = $e instanceof PDOException ? $e->errorInfo : null;
$message = $e->getMessage(); $message = $e->getMessage();
Yii::log(Yii::t('yii', 'Command::{method}() failed: {error}. The SQL statement executed was: {sql}.', Yii::log(Yii::t('yii', 'Command::{method}() failed: {error}. The SQL statement executed was: {sql}.',
array('{method}' => $method, '{error}' => $message, '{sql}' => $this->getText() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command'); array('{method}' => $method, '{error}' => $message, '{sql}' => $this->getSql() . $par)), CLogger::LEVEL_ERROR, 'system.db.Command');
if (YII_DEBUG) if (YII_DEBUG)
$message .= '. The SQL statement executed was: ' . $this->getText() . $par; $message .= '. The SQL statement executed was: ' . $this->getSql() . $par;
throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}', throw new CDbException(Yii::t('yii', 'Command failed to execute the SQL statement: {error}',
array('{error}' => $message)), (int)$e->getCode(), $errorInfo); array('{error}' => $message)), (int)$e->getCode(), $errorInfo);
} }
} }
/** /**
* Builds a SQL SELECT statement from the given query specification.
* @param array $query the query specification in name-value pairs. The following
* query options are supported: {@link select}, {@link distinct}, {@link from},
* {@link where}, {@link join}, {@link group}, {@link having}, {@link order},
* {@link limit}, {@link offset} and {@link union}.
* @return string the SQL statement
* @since 1.1.6
*/
public function buildQuery($query)
{
$sql = isset($query['distinct']) && $query['distinct'] ? 'SELECT DISTINCT' : 'SELECT';
$sql .= ' ' . (isset($query['select']) ? $query['select'] : '*');
if (isset($query['from']))
$sql .= "\nFROM " . $query['from'];
else
throw new CDbException(Yii::t('yii', 'The DB query must contain the "from" portion.'));
if (isset($query['join']))
$sql .= "\n" . (is_array($query['join']) ? implode("\n", $query['join']) : $query['join']);
if (isset($query['where']))
$sql .= "\nWHERE " . $query['where'];
if (isset($query['group']))
$sql .= "\nGROUP BY " . $query['group'];
if (isset($query['having']))
$sql .= "\nHAVING " . $query['having'];
if (isset($query['order']))
$sql .= "\nORDER BY " . $query['order'];
$limit = isset($query['limit']) ? (int)$query['limit'] : -1;
$offset = isset($query['offset']) ? (int)$query['offset'] : -1;
if ($limit >= 0 || $offset > 0)
$sql = $this->_connection->getCommandBuilder()->applyLimit($sql, $limit, $offset);
if (isset($query['union']))
$sql .= "\nUNION (\n" . (is_array($query['union']) ? implode("\n) UNION (\n", $query['union']) : $query['union']) . ')';
return $sql;
}
/**
* Sets the SELECT part of the query. * Sets the SELECT part of the query.
* @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns. * @param mixed $columns the columns to be selected. Defaults to '*', meaning all columns.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')). * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
...@@ -576,84 +506,22 @@ class Command extends \yii\base\Component ...@@ -576,84 +506,22 @@ class Command extends \yii\base\Component
*/ */
public function select($columns = '*', $option = '') public function select($columns = '*', $option = '')
{ {
if (is_string($columns) && strpos($columns, '(') !== false) $this->query->select = $columns;
$this->_query['select'] = $columns; $this->query->selectOption = $option;
else
{
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)
{
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $column, $matches))
$columns[$i] = $this->_connection->quoteColumnName($matches[1]) . ' AS ' . $this->_connection->quoteColumnName($matches[2]);
else
$columns[$i] = $this->_connection->quoteColumnName($column);
}
}
$this->_query['select'] = implode(', ', $columns);
}
if ($option != '')
$this->_query['select'] = $option . ' ' . $this->_query['select'];
return $this; return $this;
} }
/** /**
* Returns the SELECT part in the query.
* @return string the SELECT part (without 'SELECT') in the query.
* @since 1.1.6
*/
public function getSelect()
{
return isset($this->_query['select']) ? $this->_query['select'] : '';
}
/**
* Sets the SELECT part in the query.
* @param mixed $value the data to be selected. Please refer to {@link select()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setSelect($value)
{
$this->select($value);
}
/**
* Sets the SELECT part of the query with the DISTINCT flag turned on. * Sets the SELECT part of the query with the DISTINCT flag turned on.
* This is the same as {@link select} except that the DISTINCT flag is turned on. * This is the same as {@link select} except that the DISTINCT flag is turned on.
* @param mixed $columns the columns to be selected. See {@link select} for more details. * @param mixed $columns the columns to be selected. See {@link select} for more details.
* @return Command the command object itself * @return Command the command object itself
* @since 1.1.6 * @since 1.1.6
*/ */
public function selectDistinct($columns = '*') public function selectDistinct($columns = '*', $option = '')
{ {
$this->_query['distinct'] = true; $this->query->distinct = true;
return $this->select($columns); return $this->select($columns, $option);
}
/**
* Returns a value indicating whether SELECT DISTINCT should be used.
* @return boolean a value indicating whether SELECT DISTINCT should be used.
* @since 1.1.6
*/
public function getDistinct()
{
return isset($this->_query['distinct']) ? $this->_query['distinct'] : false;
}
/**
* Sets a value indicating whether SELECT DISTINCT should be used.
* @param boolean $value a value indicating whether SELECT DISTINCT should be used.
* @since 1.1.6
*/
public function setDistinct($value)
{
$this->_query['distinct'] = $value;
} }
/** /**
...@@ -668,49 +536,11 @@ class Command extends \yii\base\Component ...@@ -668,49 +536,11 @@ class Command extends \yii\base\Component
*/ */
public function from($tables) public function from($tables)
{ {
if (is_string($tables) && strpos($tables, '(') !== false) $this->query->from = $tables;
$this->_query['from'] = $tables;
else
{
if (!is_array($tables))
$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+)(.*)$/', $table, $matches)) // with alias
$tables[$i] = $this->_connection->quoteTableName($matches[1]) . ' ' . $this->_connection->quoteTableName($matches[2]);
else
$tables[$i] = $this->_connection->quoteTableName($table);
}
}
$this->_query['from'] = implode(', ', $tables);
}
return $this; return $this;
} }
/** /**
* Returns the FROM part in the query.
* @return string the FROM part (without 'FROM' ) in the query.
* @since 1.1.6
*/
public function getFrom()
{
return isset($this->_query['from']) ? $this->_query['from'] : '';
}
/**
* Sets the FROM part in the query.
* @param mixed $value the tables to be selected from. Please refer to {@link from()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setFrom($value)
{
$this->from($value);
}
/**
* Sets the WHERE part of the query. * Sets the WHERE part of the query.
* *
* The method requires a $conditions parameter, and optionally a $params parameter * The method requires a $conditions parameter, and optionally a $params parameter
...@@ -750,34 +580,12 @@ class Command extends \yii\base\Component ...@@ -750,34 +580,12 @@ class Command extends \yii\base\Component
*/ */
public function where($conditions, $params = array()) public function where($conditions, $params = array())
{ {
$this->_query['where'] = $this->processConditions($conditions); $this->query->where = $conditions;
foreach ($params as $name => $value) $this->query->addParams($params);
$this->params[$name] = $value;
return $this; return $this;
} }
/** /**
* Returns the WHERE part in the query.
* @return string the WHERE part (without 'WHERE' ) in the query.
* @since 1.1.6
*/
public function getWhere()
{
return isset($this->_query['where']) ? $this->_query['where'] : '';
}
/**
* Sets the WHERE part in the query.
* @param mixed $value the where part. Please refer to {@link where()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setWhere($value)
{
$this->where($value);
}
/**
* Appends an INNER JOIN part to the query. * Appends an INNER JOIN part to the query.
* @param string $table the table to be joined. * @param string $table the table to be joined.
* Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u').
...@@ -791,31 +599,7 @@ class Command extends \yii\base\Component ...@@ -791,31 +599,7 @@ class Command extends \yii\base\Component
*/ */
public function join($table, $conditions, $params = array()) public function join($table, $conditions, $params = array())
{ {
return $this->joinInternal('join', $table, $conditions, $params); return $this->joinInternal('JOIN', $table, $conditions, $params);
}
/**
* Returns the join part in the query.
* @return mixed the join part in the query. This can be an array representing
* multiple join fragments, or a string representing a single jojin fragment.
* Each join fragment will contain the proper join operator (e.g. LEFT JOIN).
* @since 1.1.6
*/
public function getJoin()
{
return isset($this->_query['join']) ? $this->_query['join'] : '';
}
/**
* Sets the join part in the query.
* @param mixed $value the join part in the query. This can be either a string or
* an array representing multiple join parts in the query. Each part must contain
* the proper join operator (e.g. 'LEFT JOIN tbl_profile ON tbl_user.id=tbl_profile.id')
* @since 1.1.6
*/
public function setJoin($value)
{
$this->_query['join'] = $value;
} }
/** /**
...@@ -832,7 +616,7 @@ class Command extends \yii\base\Component ...@@ -832,7 +616,7 @@ class Command extends \yii\base\Component
*/ */
public function leftJoin($table, $conditions, $params = array()) public function leftJoin($table, $conditions, $params = array())
{ {
return $this->joinInternal('left join', $table, $conditions, $params); return $this->joinInternal('LEFT JOIN', $table, $conditions, $params);
} }
/** /**
...@@ -849,7 +633,7 @@ class Command extends \yii\base\Component ...@@ -849,7 +633,7 @@ class Command extends \yii\base\Component
*/ */
public function rightJoin($table, $conditions, $params = array()) public function rightJoin($table, $conditions, $params = array())
{ {
return $this->joinInternal('right join', $table, $conditions, $params); return $this->joinInternal('RIGHT JOIN', $table, $conditions, $params);
} }
/** /**
...@@ -864,7 +648,7 @@ class Command extends \yii\base\Component ...@@ -864,7 +648,7 @@ class Command extends \yii\base\Component
*/ */
public function crossJoin($table) public function crossJoin($table)
{ {
return $this->joinInternal('cross join', $table); return $this->joinInternal('CROSS JOIN', $table);
} }
/** /**
...@@ -879,7 +663,7 @@ class Command extends \yii\base\Component ...@@ -879,7 +663,7 @@ class Command extends \yii\base\Component
*/ */
public function naturalJoin($table) public function naturalJoin($table)
{ {
return $this->joinInternal('natural join', $table); return $this->joinInternal('NATURAL JOIN', $table);
} }
/** /**
...@@ -891,48 +675,13 @@ class Command extends \yii\base\Component ...@@ -891,48 +675,13 @@ class Command extends \yii\base\Component
* @return Command the command object itself * @return Command the command object itself
* @since 1.1.6 * @since 1.1.6
*/ */
public function group($columns) public function groupBy($columns)
{
if (is_string($columns) && strpos($columns, '(') !== false)
$this->_query['group'] = $columns;
else
{
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)) $this->query->groupBy = $columns;
$columns[$i] = (string)$column;
elseif (strpos($column, '(') === false)
$columns[$i] = $this->_connection->quoteColumnName($column);
}
$this->_query['group'] = implode(', ', $columns);
}
return $this; return $this;
} }
/** /**
* Returns the GROUP BY part in the query.
* @return string the GROUP BY part (without 'GROUP BY' ) in the query.
* @since 1.1.6
*/
public function getGroup()
{
return isset($this->_query['group']) ? $this->_query['group'] : '';
}
/**
* Sets the GROUP BY part in the query.
* @param mixed $value the GROUP BY part. Please refer to {@link group()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setGroup($value)
{
$this->group($value);
}
/**
* Sets the HAVING part of the query. * Sets the HAVING part of the query.
* @param mixed $conditions the conditions to be put after HAVING. * @param mixed $conditions the conditions to be put after HAVING.
* Please refer to {@link where} on how to specify conditions. * Please refer to {@link where} on how to specify conditions.
...@@ -942,34 +691,12 @@ class Command extends \yii\base\Component ...@@ -942,34 +691,12 @@ class Command extends \yii\base\Component
*/ */
public function having($conditions, $params = array()) public function having($conditions, $params = array())
{ {
$this->_query['having'] = $this->processConditions($conditions); $this->query->having = $conditions;
foreach ($params as $name => $value) $this->query->addParams($params);
$this->params[$name] = $value;
return $this; return $this;
} }
/** /**
* Returns the HAVING part in the query.
* @return string the HAVING part (without 'HAVING' ) in the query.
* @since 1.1.6
*/
public function getHaving()
{
return isset($this->_query['having']) ? $this->_query['having'] : '';
}
/**
* Sets the HAVING part in the query.
* @param mixed $value the HAVING part. Please refer to {@link having()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setHaving($value)
{
$this->having($value);
}
/**
* Sets the ORDER BY part of the query. * Sets the ORDER BY part of the query.
* @param mixed $columns the columns (and the directions) to be ordered by. * @param mixed $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')). * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array (e.g. array('id ASC', 'name DESC')).
...@@ -978,89 +705,26 @@ class Command extends \yii\base\Component ...@@ -978,89 +705,26 @@ class Command extends \yii\base\Component
* @return Command the command object itself * @return Command the command object itself
* @since 1.1.6 * @since 1.1.6
*/ */
public function order($columns) public function orderBy($columns)
{
if (is_string($columns) && strpos($columns, '(') !== false)
$this->_query['order'] = $columns;
else
{
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)
{ {
if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) $this->query->orderBy = $columns;
$columns[$i] = $this->_connection->quoteColumnName($matches[1]) . ' ' . strtoupper($matches[2]);
else
$columns[$i] = $this->_connection->quoteColumnName($column);
}
}
$this->_query['order'] = implode(', ', $columns);
}
return $this; return $this;
} }
/** /**
* Returns the ORDER BY part in the query.
* @return string the ORDER BY part (without 'ORDER BY' ) in the query.
* @since 1.1.6
*/
public function getOrder()
{
return isset($this->_query['order']) ? $this->_query['order'] : '';
}
/**
* Sets the ORDER BY part in the query.
* @param mixed $value the ORDER BY part. Please refer to {@link order()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setOrder($value)
{
$this->order($value);
}
/**
* Sets the LIMIT part of the query. * Sets the LIMIT part of the query.
* @param integer $limit the limit * @param integer $limit the limit
* @param integer $offset the offset * @param integer $offset the offset
* @return Command the command object itself * @return Command the command object itself
* @since 1.1.6 * @since 1.1.6
*/ */
public function limit($limit, $offset = null) public function limit($limit)
{ {
$this->_query['limit'] = (int)$limit; $this->query->limit = $limit;
if ($offset !== null)
$this->offset($offset);
return $this; return $this;
} }
/** /**
* Returns the LIMIT part in the query.
* @return string the LIMIT part (without 'LIMIT' ) in the query.
* @since 1.1.6
*/
public function getLimit()
{
return isset($this->_query['limit']) ? $this->_query['limit'] : -1;
}
/**
* Sets the LIMIT part in the query.
* @param integer $value the LIMIT part. Please refer to {@link limit()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setLimit($value)
{
$this->limit($value);
}
/**
* Sets the OFFSET part of the query. * Sets the OFFSET part of the query.
* @param integer $offset the offset * @param integer $offset the offset
* @return Command the command object itself * @return Command the command object itself
...@@ -1068,32 +732,11 @@ class Command extends \yii\base\Component ...@@ -1068,32 +732,11 @@ class Command extends \yii\base\Component
*/ */
public function offset($offset) public function offset($offset)
{ {
$this->_query['offset'] = (int)$offset; $this->query->offset = $offset;
return $this; return $this;
} }
/** /**
* Returns the OFFSET part in the query.
* @return string the OFFSET part (without 'OFFSET' ) in the query.
* @since 1.1.6
*/
public function getOffset()
{
return isset($this->_query['offset']) ? $this->_query['offset'] : -1;
}
/**
* Sets the OFFSET part in the query.
* @param integer $value the OFFSET part. Please refer to {@link offset()} for details
* on how to specify this parameter.
* @since 1.1.6
*/
public function setOffset($value)
{
$this->offset($value);
}
/**
* Appends a SQL statement using UNION operator. * Appends a SQL statement using UNION operator.
* @param string $sql the SQL statement to be appended using UNION * @param string $sql the SQL statement to be appended using UNION
* @return Command the command object itself * @return Command the command object itself
...@@ -1101,34 +744,8 @@ class Command extends \yii\base\Component ...@@ -1101,34 +744,8 @@ class Command extends \yii\base\Component
*/ */
public function union($sql) public function union($sql)
{ {
if (isset($this->_query['union']) && is_string($this->_query['union'])) $this->query->union[] = $sql;
$this->_query['union'] = array($this->_query['union']); return $this->query;
$this->_query['union'][] = $sql;
return $this;
}
/**
* Returns the UNION part in the query.
* @return mixed the UNION part (without 'UNION' ) in the query.
* This can be either a string or an array representing multiple union parts.
* @since 1.1.6
*/
public function getUnion()
{
return isset($this->_query['union']) ? $this->_query['union'] : '';
}
/**
* Sets the UNION part in the query.
* @param mixed $value the UNION part. This can be either a string or an array
* representing multiple SQL statements to be unioned together.
* @since 1.1.6
*/
public function setUnion($value)
{
$this->_query['union'] = $value;
} }
/** /**
...@@ -1141,28 +758,8 @@ class Command extends \yii\base\Component ...@@ -1141,28 +758,8 @@ class Command extends \yii\base\Component
*/ */
public function insert($table, $columns) public function insert($table, $columns)
{ {
$params = array(); $sql = $this->connection->getQueryBuilder()->insert($table, $columns, $params);
$names = array(); return $this->setSql($sql)->execute($params);
$placeholders = array();
foreach ($columns as $name => $value)
{
$names[] = $this->_connection->quoteColumnName($name);
if ($value instanceof CDbExpression)
{
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v)
$params[$n] = $v;
}
else
{
$placeholders[] = ':' . $name;
$params[':' . $name] = $value;
}
}
$sql = 'INSERT INTO ' . $this->_connection->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')';
return $this->setText($sql)->execute($params);
} }
/** /**
...@@ -1178,25 +775,8 @@ class Command extends \yii\base\Component ...@@ -1178,25 +775,8 @@ class Command extends \yii\base\Component
*/ */
public function update($table, $columns, $conditions = '', $params = array()) public function update($table, $columns, $conditions = '', $params = array())
{ {
$lines = array(); $sql = $this->connection->getQueryBuilder()->update($table, $columns, $conditions, $params);
foreach ($columns as $name => $value) return $this->setSql($sql)->execute($params);
{
if ($value instanceof CDbExpression)
{
$lines[] = $this->_connection->quoteColumnName($name) . '=' . $value->expression;
foreach ($value->params as $n => $v)
$params[$n] = $v;
}
else
{
$lines[] = $this->_connection->quoteColumnName($name) . '=:' . $name;
$params[':' . $name] = $value;
}
}
$sql = 'UPDATE ' . $this->_connection->quoteTableName($table) . ' SET ' . implode(', ', $lines);
if (($where = $this->processConditions($conditions)) != '')
$sql .= ' WHERE ' . $where;
return $this->setText($sql)->execute($params);
} }
/** /**
...@@ -1210,10 +790,8 @@ class Command extends \yii\base\Component ...@@ -1210,10 +790,8 @@ class Command extends \yii\base\Component
*/ */
public function delete($table, $conditions = '', $params = array()) public function delete($table, $conditions = '', $params = array())
{ {
$sql = 'DELETE FROM ' . $this->_connection->quoteTableName($table); $sql = $this->connection->getQueryBuilder()->delete($table, $conditions);
if (($where = $this->processConditions($conditions)) != '') return $this->setSql($sql)->execute($params);
$sql .= ' WHERE ' . $where;
return $this->setText($sql)->execute($params);
} }
/** /**
...@@ -1235,7 +813,8 @@ class Command extends \yii\base\Component ...@@ -1235,7 +813,8 @@ class Command extends \yii\base\Component
*/ */
public function createTable($table, $columns, $options = null) public function createTable($table, $columns, $options = null)
{ {
return $this->setText($this->getConnection()->getSchema()->createTable($table, $columns, $options))->execute(); $sql = $this->connection->getQueryBuilder()->createTable($table, $columns, $options);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1247,7 +826,8 @@ class Command extends \yii\base\Component ...@@ -1247,7 +826,8 @@ class Command extends \yii\base\Component
*/ */
public function renameTable($table, $newName) public function renameTable($table, $newName)
{ {
return $this->setText($this->getConnection()->getSchema()->renameTable($table, $newName))->execute(); $sql = $this->connection->getQueryBuilder()->renameTable($table, $newName);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1258,7 +838,8 @@ class Command extends \yii\base\Component ...@@ -1258,7 +838,8 @@ class Command extends \yii\base\Component
*/ */
public function dropTable($table) public function dropTable($table)
{ {
return $this->setText($this->getConnection()->getSchema()->dropTable($table))->execute(); $sql = $this->connection->getQueryBuilder()->dropTable($table);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1269,11 +850,8 @@ class Command extends \yii\base\Component ...@@ -1269,11 +850,8 @@ class Command extends \yii\base\Component
*/ */
public function truncateTable($table) public function truncateTable($table)
{ {
$schema = $this->getConnection()->getSchema(); $sql = $this->connection->getQueryBuilder()->truncateTable($table);
$n = $this->setText($schema->truncateTable($table))->execute(); return $this->setSql($sql)->execute();
if (strncasecmp($this->getConnection()->getDriverName(), 'sqlite', 6) === 0)
$schema->resetSequence($schema->getTable($table));
return $n;
} }
/** /**
...@@ -1288,7 +866,8 @@ class Command extends \yii\base\Component ...@@ -1288,7 +866,8 @@ class Command extends \yii\base\Component
*/ */
public function addColumn($table, $column, $type) public function addColumn($table, $column, $type)
{ {
return $this->setText($this->getConnection()->getSchema()->addColumn($table, $column, $type))->execute(); $sql = $this->connection->getQueryBuilder()->addColumn($table, $column, $type);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1300,7 +879,8 @@ class Command extends \yii\base\Component ...@@ -1300,7 +879,8 @@ class Command extends \yii\base\Component
*/ */
public function dropColumn($table, $column) public function dropColumn($table, $column)
{ {
return $this->setText($this->getConnection()->getSchema()->dropColumn($table, $column))->execute(); $sql = $this->connection->getQueryBuilder()->dropColumn($table, $column);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1313,7 +893,8 @@ class Command extends \yii\base\Component ...@@ -1313,7 +893,8 @@ class Command extends \yii\base\Component
*/ */
public function renameColumn($table, $name, $newName) public function renameColumn($table, $name, $newName)
{ {
return $this->setText($this->getConnection()->getSchema()->renameColumn($table, $name, $newName))->execute(); $sql = $this->connection->getQueryBuilder()->renameColumn($table, $name, $newName);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1328,7 +909,8 @@ class Command extends \yii\base\Component ...@@ -1328,7 +909,8 @@ class Command extends \yii\base\Component
*/ */
public function alterColumn($table, $column, $type) public function alterColumn($table, $column, $type)
{ {
return $this->setText($this->getConnection()->getSchema()->alterColumn($table, $column, $type))->execute(); $sql = $this->connection->getQueryBuilder()->alterColumn($table, $column, $type);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1346,7 +928,8 @@ class Command extends \yii\base\Component ...@@ -1346,7 +928,8 @@ class Command extends \yii\base\Component
*/ */
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null) public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
{ {
return $this->setText($this->getConnection()->getSchema()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update))->execute(); $sql = $this->connection->getQueryBuilder()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1358,7 +941,8 @@ class Command extends \yii\base\Component ...@@ -1358,7 +941,8 @@ class Command extends \yii\base\Component
*/ */
public function dropForeignKey($name, $table) public function dropForeignKey($name, $table)
{ {
return $this->setText($this->getConnection()->getSchema()->dropForeignKey($name, $table))->execute(); $sql = $this->connection->getQueryBuilder()->dropForeignKey($name, $table);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1373,7 +957,8 @@ class Command extends \yii\base\Component ...@@ -1373,7 +957,8 @@ class Command extends \yii\base\Component
*/ */
public function createIndex($name, $table, $column, $unique = false) public function createIndex($name, $table, $column, $unique = false)
{ {
return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $column, $unique))->execute(); $sql = $this->connection->getQueryBuilder()->createIndex($name, $table, $column, $unique);
return $this->setSql($sql)->execute();
} }
/** /**
...@@ -1385,78 +970,8 @@ class Command extends \yii\base\Component ...@@ -1385,78 +970,8 @@ class Command extends \yii\base\Component
*/ */
public function dropIndex($name, $table) public function dropIndex($name, $table)
{ {
return $this->setText($this->getConnection()->getSchema()->dropIndex($name, $table))->execute(); $sql = $this->connection->getQueryBuilder()->dropIndex($name, $table);
} return $this->setSql($sql)->execute();
/**
* Generates the condition string that will be put in the WHERE part
* @param mixed $conditions the conditions that will be put in the WHERE part.
* @return string the condition string to put in the WHERE part
*/
private function processConditions($conditions)
{
if (!is_array($conditions))
return $conditions;
elseif ($conditions === array())
return '';
$n = count($conditions);
$operator = strtoupper($conditions[0]);
if ($operator === 'OR' || $operator === 'AND')
{
$parts = array();
for ($i = 1;$i < $n;++$i)
{
$condition = $this->processConditions($conditions[$i]);
if ($condition !== '')
$parts[] = '(' . $condition . ')';
}
return $parts === array() ? '' : implode(' ' . $operator . ' ', $parts);
}
if (!isset($conditions[1], $conditions[2]))
return '';
$column = $conditions[1];
if (strpos($column, '(') === false)
$column = $this->_connection->quoteColumnName($column);
$values = $conditions[2];
if (!is_array($values))
$values = array($values);
if ($operator === 'IN' || $operator === 'NOT IN')
{
if ($values === array())
return $operator === 'IN' ? '0=1' : '';
foreach ($values as $i => $value)
{
if (is_string($value))
$values[$i] = $this->_connection->quoteValue($value);
else
$values[$i] = (string)$value;
}
return $column . ' ' . $operator . ' (' . implode(', ', $values) . ')';
}
if ($operator === 'LIKE' || $operator === 'NOT LIKE' || $operator === 'OR LIKE' || $operator === 'OR NOT LIKE')
{
if ($values === array())
return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0=1' : '';
if ($operator === 'LIKE' || $operator === 'NOT LIKE')
$andor = ' AND ';
else
{
$andor = ' OR ';
$operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE';
}
$expressions = array();
foreach ($values as $value)
$expressions[] = $column . ' ' . $operator . ' ' . $this->_connection->quoteValue($value);
return implode($andor, $expressions);
}
throw new CDbException(Yii::t('yii', 'Unknown operator "{operator}".', array('{operator}' => $operator)));
} }
/** /**
...@@ -1474,25 +989,8 @@ class Command extends \yii\base\Component ...@@ -1474,25 +989,8 @@ class Command extends \yii\base\Component
*/ */
private function joinInternal($type, $table, $conditions = '', $params = array()) private function joinInternal($type, $table, $conditions = '', $params = array())
{ {
if (strpos($table, '(') === false) $this->query->join[] = array($type, $table, $conditions);
{ $this->query->addParams($params);
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) // with alias
$table = $this->_connection->quoteTableName($matches[1]) . ' ' . $this->_connection->quoteTableName($matches[2]);
else
$table = $this->_connection->quoteTableName($table);
}
$conditions = $this->processConditions($conditions);
if ($conditions != '')
$conditions = ' ON ' . $conditions;
if (isset($this->_query['join']) && is_string($this->_query['join']))
$this->_query['join'] = array($this->_query['join']);
$this->_query['join'][] = strtoupper($type) . ' ' . $table . $conditions;
foreach ($params as $name => $value)
$this->params[$name] = $value;
return $this; return $this;
} }
} }
...@@ -460,6 +460,11 @@ class Connection extends \yii\base\ApplicationComponent ...@@ -460,6 +460,11 @@ class Connection extends \yii\base\ApplicationComponent
} }
} }
public function getQueryBuilder()
{
return $this->getSchema()->getQueryBuilder();
}
/** /**
* Returns the ID of the last inserted row or sequence value. * Returns the ID of the last inserted row or sequence value.
* @param string $sequenceName name of the sequence object (required by some DBMS) * @param string $sequenceName name of the sequence object (required by some DBMS)
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\db\dao;
/** /**
* DataReader represents a forward-only stream of rows from a query result set. * DataReader represents a forward-only stream of rows from a query result set.
* *
......
...@@ -16,7 +16,7 @@ namespace yii\db\dao; ...@@ -16,7 +16,7 @@ namespace yii\db\dao;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Query extends CComponent class Query extends \yii\base\Component
{ {
/** /**
* @var mixed the columns being selected. This refers to the SELECT clause in an SQL * @var mixed the columns being selected. This refers to the SELECT clause in an SQL
...@@ -72,7 +72,7 @@ class Query extends CComponent ...@@ -72,7 +72,7 @@ class Query extends CComponent
* @var array list of query parameter values indexed by parameter placeholders. * @var array list of query parameter values indexed by parameter placeholders.
* For example, <code>array(':name'=>'Dan', ':age'=>31)</code>. * For example, <code>array(':name'=>'Dan', ':age'=>31)</code>.
*/ */
public $params; public $params = array();
public $union; public $union;
...@@ -82,6 +82,18 @@ class Query extends CComponent ...@@ -82,6 +82,18 @@ class Query extends CComponent
return $connection->getQueryBuilder()->build($this); return $connection->getQueryBuilder()->build($this);
} }
public function addParams($params)
{
foreach ($params as $name => $value) {
if (is_integer($name)) {
$this->params[] = $value;
}
else {
$this->params[$name] = $value;
}
}
}
/** /**
* Appends a condition to the existing {@link condition}. * Appends a condition to the existing {@link condition}.
* The new condition and the existing condition will be concatenated via the specified operator * The new condition and the existing condition will be concatenated via the specified operator
...@@ -361,157 +373,6 @@ class Query extends CComponent ...@@ -361,157 +373,6 @@ class Query extends CComponent
} }
/** /**
* Merges with another criteria.
* In general, the merging makes the resulting criteria more restrictive.
* For example, if both criterias have conditions, they will be 'AND' together.
* Also, the criteria passed as the parameter takes precedence in case
* two options cannot be merged (e.g. LIMIT, OFFSET).
* @param Query $criteria the criteria to be merged with.
* @param boolean $useAnd whether to use 'AND' to merge condition and having options.
* If false, 'OR' will be used instead. Defaults to 'AND'. This parameter has been
* available since version 1.0.6.
* @since 1.0.5
*/
public function mergeWith($criteria, $useAnd = true)
{
$and = $useAnd ? 'AND' : 'OR';
if (is_array($criteria))
$criteria = new self($criteria);
if ($this->select !== $criteria->select)
{
if ($this->select === '*')
$this->select = $criteria->select;
elseif ($criteria->select !== '*')
{
$select1 = is_string($this->select) ? preg_split('/\s*,\s*/', trim($this->select), -1, PREG_SPLIT_NO_EMPTY) : $this->select;
$select2 = is_string($criteria->select) ? preg_split('/\s*,\s*/', trim($criteria->select), -1, PREG_SPLIT_NO_EMPTY) : $criteria->select;
$this->select = array_merge($select1, array_diff($select2, $select1));
}
}
if ($this->condition !== $criteria->condition)
{
if ($this->condition === '')
$this->condition = $criteria->condition;
elseif ($criteria->condition !== '')
$this->condition = "( {$this->condition}) $and ( {$criteria->condition})";
}
if ($this->params !== $criteria->params)
$this->params = array_merge($this->params, $criteria->params);
if ($criteria->limit > 0)
$this->limit = $criteria->limit;
if ($criteria->offset >= 0)
$this->offset = $criteria->offset;
if ($criteria->alias !== null)
$this->alias = $criteria->alias;
if ($this->order !== $criteria->order)
{
if ($this->order === '')
$this->order = $criteria->order;
elseif ($criteria->order !== '')
$this->order = $criteria->order . ', ' . $this->order;
}
if ($this->group !== $criteria->group)
{
if ($this->group === '')
$this->group = $criteria->group;
elseif ($criteria->group !== '')
$this->group .= ', ' . $criteria->group;
}
if ($this->join !== $criteria->join)
{
if ($this->join === '')
$this->join = $criteria->join;
elseif ($criteria->join !== '')
$this->join .= ' ' . $criteria->join;
}
if ($this->having !== $criteria->having)
{
if ($this->having === '')
$this->having = $criteria->having;
elseif ($criteria->having !== '')
$this->having = "( {$this->having}) $and ( {$criteria->having})";
}
if ($criteria->distinct > 0)
$this->distinct = $criteria->distinct;
if ($criteria->together !== null)
$this->together = $criteria->together;
if ($criteria->index !== null)
$this->index = $criteria->index;
if (empty($this->scopes))
$this->scopes = $criteria->scopes;
elseif (!empty($criteria->scopes))
{
$scopes1 = (array)$this->scopes;
$scopes2 = (array)$criteria->scopes;
foreach ($scopes1 as $k => $v)
{
if (is_integer($k))
$scopes[] = $v;
elseif (isset($scopes2[$k]))
$scopes[] = array($k => $v);
else
$scopes[$k] = $v;
}
foreach ($scopes2 as $k => $v)
{
if (is_integer($k))
$scopes[] = $v;
elseif (isset($scopes1[$k]))
$scopes[] = array($k => $v);
else
$scopes[$k] = $v;
}
$this->scopes = $scopes;
}
if (empty($this->with))
$this->with = $criteria->with;
elseif (!empty($criteria->with))
{
$this->with = (array)$this->with;
foreach ((array)$criteria->with as $k => $v)
{
if (is_integer($k))
$this->with[] = $v;
elseif (isset($this->with[$k]))
{
$excludes = array();
foreach (array('joinType', 'on') as $opt)
{
if (isset($this->with[$k][$opt]))
$excludes[$opt] = $this->with[$k][$opt];
if (isset($v[$opt]))
$excludes[$opt] = ($opt === 'on' && isset($excludes[$opt]) && $v[$opt] !== $excludes[$opt]) ?
"($excludes[$opt]) AND $v[$opt]" : $v[$opt];
unset($this->with[$k][$opt]);
unset($v[$opt]);
}
$this->with[$k] = new self($this->with[$k]);
$this->with[$k]->mergeWith($v, $useAnd);
$this->with[$k] = $this->with[$k]->toArray();
if (count($excludes) !== 0)
$this->with[$k] = CMap::mergeArray($this->with[$k], $excludes);
}
else
$this->with[$k] = $v;
}
}
}
/**
* @return array the array representation of the criteria * @return array the array representation of the criteria
* @since 1.0.6 * @since 1.0.6
*/ */
......
...@@ -42,7 +42,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -42,7 +42,7 @@ class QueryBuilder extends \yii\base\Component
public function __construct($schema) public function __construct($schema)
{ {
$this->connection = $schema->getDbConnection(); $this->connection = $schema->connection;
$this->schema = $schema; $this->schema = $schema;
} }
...@@ -59,8 +59,98 @@ class QueryBuilder extends \yii\base\Component ...@@ -59,8 +59,98 @@ class QueryBuilder extends \yii\base\Component
$this->buildOrderBy($query), $this->buildOrderBy($query),
$this->buildLimit($query), $this->buildLimit($query),
); );
$sql = implode("\n", array_filter($clauses));
if ($this->connection->tablePrefix !== null && strpos($sql, '{') !== false) {
$sql = preg_replace('/{{(.*?)}}/', $this->connection->tablePrefix . '\1', $sql);
}
return $sql;
}
/**
* Creates and executes an INSERT SQL statement.
* The method will properly escape the column names, and bind the values to be inserted.
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column data (name=>value) to be inserted into the table.
* @return integer number of rows affected by the execution.
* @since 1.1.6
*/
public function insert($table, $columns, &$params = array())
{
$names = array();
$placeholders = array();
$count = 0;
foreach ($columns as $name => $value) {
$names[] = $this->schema->quoteColumnName($name);
if ($value instanceof Expression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
}
}
else {
$placeholders[] = ':p' . $count;
$params[':p' . $count] = $value;
$count++;
}
}
return 'INSERT INTO ' . $this->schema->quoteTableName($table)
. ' (' . implode(', ', $names) . ') VALUES ('
. implode(', ', $placeholders) . ')';
}
/**
* Creates and executes an UPDATE SQL statement.
* The method will properly escape the column names and bind the values to be updated.
* @param string $table the table to be updated.
* @param array $columns the column data (name=>value) to be updated.
* @param mixed $conditions the conditions that will be put in the WHERE part. Please
* refer to {@link where} on how to specify conditions.
* @param array $params the parameters to be bound to the query.
* @return integer number of rows affected by the execution.
* @since 1.1.6
*/
public function update($table, $columns, $conditions = '', &$params = array())
{
$lines = array();
$count = 0;
foreach ($columns as $name => $value) {
if ($value instanceof Expression) {
$lines[] = $this->schema->quoteSimpleColumnName($name) . '=' . $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
}
}
else {
$lines[] = $this->schema->quoteSimpleColumnName($name) . '=:p' . $count;
$params[':p' . $count] = $value;
$count++;
}
}
$sql = 'UPDATE ' . $this->schema->quoteTableName($table) . ' SET ' . implode(', ', $lines);
if (($where = $this->buildCondition($conditions)) != '') {
$sql .= ' WHERE ' . $where;
}
return $sql;
}
return implode("\n", array_filter($clauses)); /**
* Creates and executes a DELETE SQL statement.
* @param string $table the table where the data will be deleted from.
* @param mixed $conditions the conditions that will be put in the WHERE part. Please
* refer to {@link where} on how to specify conditions.
* @param array $params the parameters to be bound to the query.
* @return integer number of rows affected by the execution.
* @since 1.1.6
*/
public function delete($table, $conditions = '')
{
$sql = 'DELETE FROM ' . $this->schema->quoteTableName($table);
if (($where = $this->buildCondition($conditions)) != '') {
$sql .= ' WHERE ' . $where;
}
return $sql;
} }
/** /**
...@@ -101,7 +191,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -101,7 +191,7 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function renameTable($table, $newName) public function renameTable($table, $newName)
{ {
return 'RENAME TABLE ' . $this->quoteTableName($table) . ' TO ' . $this->quoteTableName($newName); return 'RENAME TABLE ' . $this->schema->quoteTableName($table) . ' TO ' . $this->schema->quoteTableName($newName);
} }
/** /**
...@@ -111,7 +201,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -111,7 +201,7 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function dropTable($table) public function dropTable($table)
{ {
return "DROP TABLE " . $this->quoteTableName($table); return "DROP TABLE " . $this->schema->quoteTableName($table);
} }
/** /**
...@@ -121,7 +211,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -121,7 +211,7 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function truncateTable($table) public function truncateTable($table)
{ {
return "TRUNCATE TABLE " . $this->quoteTableName($table); return "TRUNCATE TABLE " . $this->schema->quoteTableName($table);
} }
/** /**
...@@ -136,8 +226,8 @@ class QueryBuilder extends \yii\base\Component ...@@ -136,8 +226,8 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function addColumn($table, $column, $type) public function addColumn($table, $column, $type)
{ {
return 'ALTER TABLE ' . $this->quoteTableName($table) return 'ALTER TABLE ' . $this->schema->quoteTableName($table)
. ' ADD ' . $this->quoteColumnName($column) . ' ' . ' ADD ' . $this->schema->quoteColumnName($column) . ' '
. $this->getColumnType($type); . $this->getColumnType($type);
} }
...@@ -150,8 +240,8 @@ class QueryBuilder extends \yii\base\Component ...@@ -150,8 +240,8 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function dropColumn($table, $column) public function dropColumn($table, $column)
{ {
return "ALTER TABLE " . $this->quoteTableName($table) return "ALTER TABLE " . $this->schema->quoteTableName($table)
. " DROP COLUMN " . $this->quoteSimpleColumnName($column); . " DROP COLUMN " . $this->schema->quoteSimpleColumnName($column);
} }
/** /**
...@@ -164,9 +254,9 @@ class QueryBuilder extends \yii\base\Component ...@@ -164,9 +254,9 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function renameColumn($table, $name, $newName) public function renameColumn($table, $name, $newName)
{ {
return "ALTER TABLE " . $this->quoteTableName($table) return "ALTER TABLE " . $this->schema->quoteTableName($table)
. " RENAME COLUMN " . $this->quoteSimpleColumnName($name) . " RENAME COLUMN " . $this->schema->quoteSimpleColumnName($name)
. " TO " . $this->quoteSimpleColumnName($newName); . " TO " . $this->schema->quoteSimpleColumnName($newName);
} }
/** /**
...@@ -180,9 +270,9 @@ class QueryBuilder extends \yii\base\Component ...@@ -180,9 +270,9 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function alterColumn($table, $column, $type) public function alterColumn($table, $column, $type)
{ {
return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE ' return 'ALTER TABLE ' . $this->schema->quoteTableName($table) . ' CHANGE '
. $this->quoteSimpleColumnName($column) . ' ' . $this->schema->quoteSimpleColumnName($column) . ' '
. $this->quoteSimpleColumnName($column) . ' ' . $this->schema->quoteSimpleColumnName($column) . ' '
. $this->getColumnType($type); . $this->getColumnType($type);
} }
...@@ -202,14 +292,14 @@ class QueryBuilder extends \yii\base\Component ...@@ -202,14 +292,14 @@ class QueryBuilder extends \yii\base\Component
{ {
$columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY); $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
foreach ($columns as $i => $col) foreach ($columns as $i => $col)
$columns[$i] = $this->quoteColumnName($col); $columns[$i] = $this->schema->quoteColumnName($col);
$refColumns = preg_split('/\s*,\s*/', $refColumns, -1, PREG_SPLIT_NO_EMPTY); $refColumns = preg_split('/\s*,\s*/', $refColumns, -1, PREG_SPLIT_NO_EMPTY);
foreach ($refColumns as $i => $col) foreach ($refColumns as $i => $col)
$refColumns[$i] = $this->quoteColumnName($col); $refColumns[$i] = $this->schema->quoteColumnName($col);
$sql = 'ALTER TABLE ' . $this->quoteTableName($table) $sql = 'ALTER TABLE ' . $this->schema->quoteTableName($table)
. ' ADD CONSTRAINT ' . $this->quoteColumnName($name) . ' ADD CONSTRAINT ' . $this->schema->quoteColumnName($name)
. ' FOREIGN KEY (' . implode(', ', $columns) . ')' . ' FOREIGN KEY (' . implode(', ', $columns) . ')'
. ' REFERENCES ' . $this->quoteTableName($refTable) . ' REFERENCES ' . $this->schema->quoteTableName($refTable)
. ' (' . implode(', ', $refColumns) . ')'; . ' (' . implode(', ', $refColumns) . ')';
if ($delete !== null) if ($delete !== null)
$sql .= ' ON DELETE ' . $delete; $sql .= ' ON DELETE ' . $delete;
...@@ -226,8 +316,8 @@ class QueryBuilder extends \yii\base\Component ...@@ -226,8 +316,8 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function dropForeignKey($name, $table) public function dropForeignKey($name, $table)
{ {
return 'ALTER TABLE ' . $this->quoteTableName($table) return 'ALTER TABLE ' . $this->schema->quoteTableName($table)
. ' DROP CONSTRAINT ' . $this->quoteColumnName($name); . ' DROP CONSTRAINT ' . $this->schema->quoteColumnName($name);
} }
/** /**
...@@ -248,11 +338,11 @@ class QueryBuilder extends \yii\base\Component ...@@ -248,11 +338,11 @@ class QueryBuilder extends \yii\base\Component
if (strpos($col, '(') !== false) if (strpos($col, '(') !== false)
$cols[] = $col; $cols[] = $col;
else else
$cols[] = $this->quoteColumnName($col); $cols[] = $this->schema->quoteColumnName($col);
} }
return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ') return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')
. $this->quoteTableName($name) . ' ON ' . $this->schema->quoteTableName($name) . ' ON '
. $this->quoteTableName($table) . ' (' . implode(', ', $cols) . ')'; . $this->schema->quoteTableName($table) . ' (' . implode(', ', $cols) . ')';
} }
/** /**
...@@ -263,7 +353,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -263,7 +353,7 @@ class QueryBuilder extends \yii\base\Component
*/ */
public function dropIndex($name, $table) public function dropIndex($name, $table)
{ {
return 'DROP INDEX ' . $this->quoteTableName($name) . ' ON ' . $this->quoteTableName($table); return 'DROP INDEX ' . $this->schema->quoteTableName($name) . ' ON ' . $this->schema->quoteTableName($table);
} }
/** /**
...@@ -330,6 +420,9 @@ class QueryBuilder extends \yii\base\Component ...@@ -330,6 +420,9 @@ class QueryBuilder extends \yii\base\Component
protected function buildSelect($query) protected function buildSelect($query)
{ {
$select = $query->distinct ? 'SELECT DISTINCT' : 'SELECT'; $select = $query->distinct ? 'SELECT DISTINCT' : 'SELECT';
if ($query->selectOption != '') {
$select .= ' ' . $query->selectOption;
}
$columns = $query->select; $columns = $query->select;
if (empty($columns)) { if (empty($columns)) {
...@@ -408,7 +501,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -408,7 +501,7 @@ class QueryBuilder extends \yii\base\Component
} }
$joins[$i] = strtoupper($join[0]) . ' ' . $table; $joins[$i] = strtoupper($join[0]) . ' ' . $table;
if (isset($join[2])) { // join condition if (isset($join[2])) { // join condition
$condition = $this->processCondition($join[2]); $condition = $this->buildCondition($join[2]);
$joins[$i] .= ' ON ' . $condition; $joins[$i] .= ' ON ' . $condition;
} }
} }
...@@ -423,7 +516,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -423,7 +516,7 @@ class QueryBuilder extends \yii\base\Component
protected function buildWhere($query) protected function buildWhere($query)
{ {
$where = $this->processConditions($query->where); $where = $this->buildCondition($query->where);
return empty($where) ? '' : 'WHERE ' . $where; return empty($where) ? '' : 'WHERE ' . $where;
} }
...@@ -453,7 +546,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -453,7 +546,7 @@ class QueryBuilder extends \yii\base\Component
protected function buildHaving($query) protected function buildHaving($query)
{ {
$having = $this->processConditions($query->having); $having = $this->buildCondition($query->having);
return empty($having) ? '' : 'HAVING ' . $having; return empty($having) ? '' : 'HAVING ' . $having;
} }
...@@ -515,7 +608,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -515,7 +608,7 @@ class QueryBuilder extends \yii\base\Component
return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)";
} }
protected function processCondition($conditions) protected function buildCondition($conditions)
{ {
if (!is_array($conditions)) { if (!is_array($conditions)) {
return $conditions; return $conditions;
...@@ -529,7 +622,7 @@ class QueryBuilder extends \yii\base\Component ...@@ -529,7 +622,7 @@ class QueryBuilder extends \yii\base\Component
if ($operator === 'OR' || $operator === 'AND') { if ($operator === 'OR' || $operator === 'AND') {
$parts = array(); $parts = array();
for ($i = 1; $i < $n; ++$i) { for ($i = 1; $i < $n; ++$i) {
$condition = $this->processCondition($conditions[$i]); $condition = $this->buildCondition($conditions[$i]);
if ($condition !== '') { if ($condition !== '') {
$parts[] = '(' . $condition . ')'; $parts[] = '(' . $condition . ')';
} }
......
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