Skip to content

Commit

Permalink
Fix #18101: Fix behavior of OUTPUT INSERTED.* for SQL Server query: "…
Browse files Browse the repository at this point in the history
…insert default values"; correct MSSQL unit tests; turn off profiling echo message in migration test
  • Loading branch information
darkdef committed Jun 14, 2020
1 parent 472600e commit d317e41
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 6 deletions.
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Yii Framework 2 Change Log
- Added `yii\db\Connection::usePrimary()` and deprecated `useMaster()`.
- Added `yii\validators\ExistValidator::$forcePrimaryDb` and deprecated `$forceMasterDb` via magic methods.
- Added `yii\validators\UniqueValidator::$forcePrimaryDb` and deprecated `$forceMasterDb` via magic methods.
- Bug #18101: Fix behavior of OUTPUT INSERTED.* for SQL Server query: "insert default values"; correct MSSQL unit tests; turn off profiling echo message in migration test (darkdef)


2.0.35 May 02, 2020
Expand Down
6 changes: 3 additions & 3 deletions framework/db/mssql/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ private function normalizeTableRowData($table, $columns, &$params)
// @see https://github.com/yiisoft/yii2/issues/12599
if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value) || $value === null)) {
$phName = $this->bindParam($value, $params);
// @see https://github.com/yiisoft/yii2/issues/12599
// @see https://github.com/yiisoft/yii2/issues/12599
$columns[$name] = new Expression("CONVERT(VARBINARY(MAX), $phName)", $params);
}
}
Expand All @@ -450,8 +450,8 @@ public function insert($table, $columns, &$params)
list($names, $placeholders, $values, $params) = $this->prepareInsertValues($table, $columns, $params);
return 'INSERT INTO ' . $this->db->quoteTableName($table)
. (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')
. (!empty($placeholders) ?
($version2005orLater ? ' OUTPUT INSERTED.*' : '') . ' VALUES (' . implode(', ', $placeholders) . ')' : $values);
. ($version2005orLater ? ' OUTPUT INSERTED.*' : '')
. (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);
}

/**
Expand Down
96 changes: 93 additions & 3 deletions tests/framework/db/mssql/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace yiiunit\framework\db\mssql;

use yii\db\Expression;
use yii\db\Query;
use yiiunit\data\base\TraversableObject;

Expand Down Expand Up @@ -262,6 +263,95 @@ public function batchInsertProvider()
return $data;
}

public function insertProvider()
{
return [
'regular-values' => [
'customer',
[
'email' => 'test@example.com',
'name' => 'silverfire',
'address' => 'Kyiv {{city}}, Ukraine',
'is_active' => false,
'related_id' => null,
],
[],
$this->replaceQuotes('INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]]) OUTPUT INSERTED.* VALUES (:qp0, :qp1, :qp2, :qp3, :qp4)'),
[
':qp0' => 'test@example.com',
':qp1' => 'silverfire',
':qp2' => 'Kyiv {{city}}, Ukraine',
':qp3' => false,
':qp4' => null,
],
],
'params-and-expressions' => [
'{{%type}}',
[
'{{%type}}.[[related_id]]' => null,
'[[time]]' => new Expression('now()'),
],
[],
'INSERT INTO {{%type}} ({{%type}}.[[related_id]], [[time]]) OUTPUT INSERTED.* VALUES (:qp0, now())',
[
':qp0' => null,
],
],
'carry passed params' => [
'customer',
[
'email' => 'test@example.com',
'name' => 'sergeymakinen',
'address' => '{{city}}',
'is_active' => false,
'related_id' => null,
'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
],
[':phBar' => 'bar'],
$this->replaceQuotes('INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]], [[col]]) OUTPUT INSERTED.* VALUES (:qp1, :qp2, :qp3, :qp4, :qp5, CONCAT(:phFoo, :phBar))'),
[
':phBar' => 'bar',
':qp1' => 'test@example.com',
':qp2' => 'sergeymakinen',
':qp3' => '{{city}}',
':qp4' => false,
':qp5' => null,
':phFoo' => 'foo',
],
],
'carry passed params (query)' => [
'customer',
(new Query())
->select([
'email',
'name',
'address',
'is_active',
'related_id',
])
->from('customer')
->where([
'email' => 'test@example.com',
'name' => 'sergeymakinen',
'address' => '{{city}}',
'is_active' => false,
'related_id' => null,
'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
]),
[':phBar' => 'bar'],
$this->replaceQuotes('INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]]) OUTPUT INSERTED.* SELECT [[email]], [[name]], [[address]], [[is_active]], [[related_id]] FROM [[customer]] WHERE ([[email]]=:qp1) AND ([[name]]=:qp2) AND ([[address]]=:qp3) AND ([[is_active]]=:qp4) AND ([[related_id]] IS NULL) AND ([[col]]=CONCAT(:phFoo, :phBar))'),
[
':phBar' => 'bar',
':qp1' => 'test@example.com',
':qp2' => 'sergeymakinen',
':qp3' => '{{city}}',
':qp4' => false,
':phFoo' => 'foo',
],
],
];
}

public function testResetSequence()
{
$qb = $this->getQueryBuilder();
Expand Down Expand Up @@ -297,13 +387,13 @@ public function upsertProvider()
3 => 'MERGE [T_upsert] WITH (HOLDLOCK) USING (SELECT [email], 2 AS [status] FROM [customer] WHERE [name]=:qp0 ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [EXCLUDED] ([email], [status]) ON ([T_upsert].[email]=[EXCLUDED].[email]) WHEN NOT MATCHED THEN INSERT ([email], [status]) VALUES ([EXCLUDED].[email], [EXCLUDED].[status]);',
],
'values and expressions' => [
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) VALUES (:qp0, now())',
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) OUTPUT INSERTED.* VALUES (:qp0, now())',
],
'values and expressions with update part' => [
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) VALUES (:qp0, now())',
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) OUTPUT INSERTED.* VALUES (:qp0, now())',
],
'values and expressions without update part' => [
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) VALUES (:qp0, now())',
3 => 'INSERT INTO {{%T_upsert}} ({{%T_upsert}}.[[email]], [[ts]]) OUTPUT INSERTED.* VALUES (:qp0, now())',
],
'query, values and expressions with update part' => [
3 => 'MERGE {{%T_upsert}} WITH (HOLDLOCK) USING (SELECT :phEmail AS [email], now() AS [[time]]) AS [EXCLUDED] ([email], [[time]]) ON ({{%T_upsert}}.[email]=[EXCLUDED].[email]) WHEN MATCHED THEN UPDATE SET [ts]=:qp1, [[orders]]=T_upsert.orders + 1 WHEN NOT MATCHED THEN INSERT ([email], [[time]]) VALUES ([EXCLUDED].[email], [EXCLUDED].[[time]]);',
Expand Down
1 change: 1 addition & 0 deletions tests/framework/web/session/AbstractDbSessionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public function testWriteCustomFieldWithUserId()

// add mapped custom column
$migration = new Migration;
$migration->compact = true;
$migration->addColumn($session->sessionTable, 'user_id', $migration->integer());

$session->writeCallback = function ($session) {
Expand Down

0 comments on commit d317e41

Please sign in to comment.