Merge branch 'master' into unique-validator-fix

This commit is contained in:
Alexander Makarov
2017-05-12 00:45:55 +03:00
committed by GitHub
118 changed files with 2331 additions and 482 deletions

View File

@ -909,12 +909,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* ```php
* public function beforeSave($insert)
* {
* if (parent::beforeSave($insert)) {
* // ...custom code here...
* return true;
* } else {
* if (!parent::beforeSave($insert)) {
* return false;
* }
*
* // ...custom code here...
* return true;
* }
* ```
*
@ -964,12 +964,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* ```php
* public function beforeDelete()
* {
* if (parent::beforeDelete()) {
* // ...custom code here...
* return true;
* } else {
* if (!parent::beforeDelete()) {
* return false;
* }
*
* // ...custom code here...
return true;
* }
* ```
*

View File

@ -537,7 +537,7 @@ class Command extends Component
{
$sql = $this->db->getQueryBuilder()->createTable($table, $columns, $options);
return $this->setSql($sql);
return $this->setSql($sql)->requireTableSchemaRefresh($table);
}
/**
@ -680,7 +680,7 @@ class Command extends Component
{
$sql = $this->db->getQueryBuilder()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);
return $this->setSql($sql);
return $this->setSql($sql)->requireTableSchemaRefresh($table);
}
/**
@ -693,7 +693,7 @@ class Command extends Component
{
$sql = $this->db->getQueryBuilder()->dropForeignKey($name, $table);
return $this->setSql($sql);
return $this->setSql($sql)->requireTableSchemaRefresh($table);
}
/**
@ -771,7 +771,7 @@ class Command extends Component
{
$sql = $this->db->getQueryBuilder()->addCommentOnColumn($table, $column, $comment);
return $this->setSql($sql);
return $this->setSql($sql)->requireTableSchemaRefresh($table);
}
/**
@ -801,7 +801,7 @@ class Command extends Component
{
$sql = $this->db->getQueryBuilder()->dropCommentFromColumn($table, $column);
return $this->setSql($sql);
return $this->setSql($sql)->requireTableSchemaRefresh($table);
}
/**
@ -828,10 +828,7 @@ class Command extends Component
public function execute()
{
$sql = $this->getSql();
$rawSql = $this->getRawSql();
Yii::info($rawSql, __METHOD__);
list($profile, $rawSql) = $this->logQuery(__METHOD__);
if ($sql == '') {
return 0;
@ -839,21 +836,40 @@ class Command extends Component
$this->prepare(false);
$token = $rawSql;
try {
Yii::beginProfile($token, __METHOD__);
$profile and Yii::beginProfile($rawSql, __METHOD__);
$this->pdoStatement->execute();
$n = $this->pdoStatement->rowCount();
Yii::endProfile($token, __METHOD__);
$profile and Yii::endProfile($rawSql, __METHOD__);
$this->refreshTableSchema();
return $n;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
throw $this->db->getSchema()->convertException($e, $rawSql);
$profile and Yii::endProfile($rawSql, __METHOD__);
throw $this->db->getSchema()->convertException($e, $rawSql ?: $this->getRawSql());
}
}
/**
* Logs the current database query if query logging is enabled and returns
* the profiling token if profiling is enabled.
* @param string $category the log category.
* @return array array of two elements, the first is boolean of whether profiling is enabled or not.
* The second is the rawSql if it has been created.
*/
private function logQuery($category)
{
if ($this->db->enableLogging) {
$rawSql = $this->getRawSql();
Yii::info($rawSql, $category);
}
if (!$this->db->enableProfiling) {
return [false, isset($rawSql) ? $rawSql : null];
} else {
return [true, isset($rawSql) ? $rawSql : $this->getRawSql()];
}
}
@ -868,9 +884,7 @@ class Command extends Component
*/
protected function queryInternal($method, $fetchMode = null)
{
$rawSql = $this->getRawSql();
Yii::info($rawSql, 'yii\db\Command::query');
list($profile, $rawSql) = $this->logQuery('yii\db\Command::query');
if ($method !== '') {
$info = $this->db->getQueryCacheInfo($this->queryCacheDuration, $this->queryCacheDependency);
@ -883,7 +897,7 @@ class Command extends Component
$fetchMode,
$this->db->dsn,
$this->db->username,
$rawSql,
$rawSql ?: $rawSql = $this->getRawSql(),
];
$result = $cache->get($cacheKey);
if (is_array($result) && isset($result[0])) {
@ -895,9 +909,8 @@ class Command extends Component
$this->prepare(true);
$token = $rawSql;
try {
Yii::beginProfile($token, 'yii\db\Command::query');
$profile and Yii::beginProfile($rawSql, 'yii\db\Command::query');
$this->pdoStatement->execute();
@ -911,10 +924,10 @@ class Command extends Component
$this->pdoStatement->closeCursor();
}
Yii::endProfile($token, 'yii\db\Command::query');
$profile and Yii::endProfile($rawSql, 'yii\db\Command::query');
} catch (\Exception $e) {
Yii::endProfile($token, 'yii\db\Command::query');
throw $this->db->getSchema()->convertException($e, $rawSql);
$profile and Yii::endProfile($rawSql, 'yii\db\Command::query');
throw $this->db->getSchema()->convertException($e, $rawSql ?: $this->getRawSql());
}
if (isset($cache, $cacheKey, $info)) {

View File

@ -373,7 +373,22 @@ class Connection extends Component
* @see masters
*/
public $shuffleMasters = true;
/**
* @var bool whether to enable logging of database queries. Defaults to true.
* You may want to disable this option in a production environment to gain performance
* if you do not need the information being logged.
* @since 2.0.12
* @see enableProfiling
*/
public $enableLogging = true;
/**
* @var bool whether to enable profiling of database queries. Defaults to true.
* You may want to disable this option in a production environment to gain performance
* if you do not need the information being logged.
* @since 2.0.12
* @see enableLogging
*/
public $enableProfiling = true;
/**
* @var Transaction the currently active transaction
*/
@ -1065,4 +1080,18 @@ class Connection extends Component
$this->close();
return array_keys((array) $this);
}
/**
* Reset the connection after cloning.
*/
public function __clone()
{
parent::__clone();
$this->_master = false;
$this->_slave = false;
$this->pdo = null;
$this->_schema = null;
$this->_transaction = null;
}
}

View File

@ -276,9 +276,8 @@ class QueryBuilder extends \yii\db\QueryBuilder
if (!$modelClass) {
return null;
}
/* @var $model \yii\db\ActiveRecord */
$model = new $modelClass;
$schema = $model->getTableSchema();
/* @var $modelClass \yii\db\ActiveRecord */
$schema = $modelClass::getTableSchema();
return array_keys($schema->columns);
}

View File

@ -331,4 +331,59 @@ EOD;
}
return parent::buildLikeCondition($operator, $operands, $params);
}
/**
* @inheritdoc
*/
public function buildInCondition($operator, $operands, &$params)
{
$splitCondition = $this->splitInCondition($operator, $operands, $params);
if ($splitCondition !== null) {
return $splitCondition;
}
return parent::buildInCondition($operator, $operands, $params);
}
/**
* Oracle DBMS does not support more than 1000 parameters in `IN` condition.
* This method splits long `IN` condition into series of smaller ones.
*
* @param string $operator
* @param array $operands
* @param array $params
* @return null|string null when split is not required. Otherwise - built SQL condition.
* @throws Exception
* @since 2.0.12
*/
protected function splitInCondition($operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1])) {
throw new Exception("Operator '$operator' requires two operands.");
}
list($column, $values) = $operands;
if ($values instanceof \Traversable) {
$values = iterator_to_array($values);
}
if (!is_array($values)) {
return null;
}
$maxParameters = 1000;
$count = count($values);
if ($count <= $maxParameters) {
return null;
}
$condition = [($operator === 'IN') ? 'OR' : 'AND'];
for ($i = 0; $i < $count; $i += $maxParameters) {
$condition[] = [$operator, $column, array_slice($values, $i, $maxParameters)];
}
return $this->buildCondition(['AND', $condition], $params);
}
}