Fixes #8293: yii\db\Query can be passed to insert method in yii\db\QueryBuilder

This commit is contained in:
voroks
2017-01-28 23:45:18 +03:00
committed by Alexander Makarov
parent 240bca515a
commit 25f08afc96
7 changed files with 237 additions and 50 deletions

View File

@@ -142,7 +142,9 @@ class QueryBuilder extends \yii\base\Object
* The method will properly escape the table and column names.
*
* @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.
* @param array|\yii\db\Query $columns the column data (name => value) to be inserted into the table or instance
* of [[yii\db\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.
* Passing of [[yii\db\Query|Query]] is available since version 2.0.11.
* @param array $params the binding parameters that will be generated by this method.
* They should be bound to the DB command later.
* @return string the INSERT SQL
@@ -157,23 +159,58 @@ class QueryBuilder extends \yii\base\Object
}
$names = [];
$placeholders = [];
foreach ($columns as $name => $value) {
$names[] = $schema->quoteColumnName($name);
if ($value instanceof Expression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
$values = ' DEFAULT VALUES';
if ($columns instanceof \yii\db\Query) {
list($names, $values) = $this->prepareInsertSelectSubQuery($columns, $schema);
} else {
foreach ($columns as $name => $value) {
$names[] = $schema->quoteColumnName($name);
if ($value instanceof Expression) {
$placeholders[] = $value->expression;
foreach ($value->params as $n => $v) {
$params[$n] = $v;
}
} else {
$phName = self::PARAM_PREFIX . count($params);
$placeholders[] = $phName;
$params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;
}
} else {
$phName = self::PARAM_PREFIX . count($params);
$placeholders[] = $phName;
$params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;
}
}
return 'INSERT INTO ' . $schema->quoteTableName($table)
. (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')
. (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : ' DEFAULT VALUES');
. (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);
}
/**
* Prepare select-subquery and field names for INSERT INTO ... SELECT SQL statement.
*
* @param \yii\db\Query $columns Object, which represents select query
* @param \yii\db\Schema $schema Schema object to qoute column name
* @return array
* @since 2.0.11
*/
protected function prepareInsertSelectSubQuery($columns, $schema)
{
if (!is_array($columns->select) || empty($columns->select) || in_array('*', $columns->select)) {
throw new InvalidParamException('Expected select query object with enumerated (named) parameters');
}
list ($values, ) = $this->build($columns);
$names = [];
$values = ' ' . $values;
foreach ($columns->select as $title => $field) {
if (is_string($title)) {
$names[] = $title;
} else if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $field, $matches)) {
$names[] = $schema->quoteColumnName($matches[2]);
} else {
$names[] = $schema->quoteColumnName($field);
}
}
return [$names, $values];
}
/**