Fixed excess escaping in Command::batchInsert() (#13236)

This commit is contained in:
Dmitry Naumenko
2017-10-23 22:43:39 +03:00
committed by GitHub
parent e65451991d
commit 7e7faeebd1
7 changed files with 128 additions and 6 deletions

View File

@ -19,6 +19,7 @@ Yii Framework 2 Change Log
- Enh #14087: Added `yii\web\View::registerCsrfMetaTags()` method that registers CSRF tags dynamically ensuring that caching doesn't interfere (RobinKamps)
- Bug #6526: Fixed `yii\db\Command::batchInsert()` casting of double values correctly independent of the locale (cebe, leammas)
- Bug #7890: Allow `migrate/mark` to mark history at the point of the base migration (cebe)
- Bug #11242: Fixed excess escaping in `yii\db\Command::batchInsert()` (silverfire)
- Bug #11825: User can login by cookie only once when `autoRenewCookie` is set to false (shirase, silverfire)
- Bug #12860: Fixed possible race conditions in `yii\mutex\FileMutex` (kidol)
- Bug #13720: Improve `yii\helpers\FormatConverter::convertDatePhpToIcu()` to handle escaped chars correctly (rob006)

View File

@ -137,19 +137,43 @@ class Command extends Component
}
/**
* Specifies the SQL statement to be executed.
* The previous SQL execution (if any) will be cancelled, and [[params]] will be cleared as well.
* Specifies the SQL statement to be executed. The SQL statement will be quoted using [[Connection::quoteSql()]].
* The previous SQL (if any) will be discarded, and [[params]] will be cleared as well. See [[reset()]]
* for details.
*
* @param string $sql the SQL statement to be set.
* @return $this this command instance
* @see reset()
* @see cancel()
*/
public function setSql($sql)
{
if ($sql !== $this->_sql) {
$this->cancel();
$this->reset();
$this->_sql = $this->db->quoteSql($sql);
$this->_pendingParams = [];
$this->params = [];
$this->_refreshTableName = null;
}
return $this;
}
/**
* Specifies the SQL statement to be executed. The SQL statement will not be modified in any way.
* The previous SQL (if any) will be discarded, and [[params]] will be cleared as well. See [[reset()]]
* for details.
*
* @param string $sql the SQL statement to be set.
* @return $this this command instance
* @since 2.0.13
* @see reset()
* @see cancel()
*/
public function setRawSql($sql)
{
if ($sql !== $this->_sql) {
$this->cancel();
$this->reset();
$this->_sql = $sql;
}
return $this;
@ -461,9 +485,16 @@ class Command extends Component
*/
public function batchInsert($table, $columns, $rows)
{
$table = $this->db->quoteSql($table);
$columns = array_map(function ($column) {
return $this->db->quoteSql($column);
}, $columns);
$sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows);
return $this->setSql($sql);
$this->setRawSql($sql);
return $this;
}
/**
@ -1081,4 +1112,17 @@ class Command extends Component
$this->db->getSchema()->refreshTableSchema($this->_refreshTableName);
}
}
/**
* Resets [[sql]] and [[params]] properties.
*
* @since 2.0.13
*/
protected function reset()
{
$this->_sql = null;
$this->_pendingParams = [];
$this->params = [];
$this->_refreshTableName = null;
}
}

View File

@ -368,6 +368,47 @@ SQL;
setlocale(LC_NUMERIC, $locale);
}
public function batchInsertSqlProvider()
{
return [
'issue11242' => [
'type',
['int_col', 'float_col', 'char_col'],
[['', '', 'Kyiv {{city}}, Ukraine']],
'expected' => "INSERT INTO `type` (`int_col`, `float_col`, `char_col`) VALUES (NULL, NULL, 'Kyiv {{city}}, Ukraine')"
// See https://github.com/yiisoft/yii2/issues/11242
// Make sure curly bracelets (`{{..}}`) in values will not be escaped
],
'wrongBehavior' => [
'{{%type}}',
['{{%type}}.[[int_col]]', '[[float_col]]', 'char_col'],
[['', '', 'Kyiv {{city}}, Ukraine']],
'expected' => "INSERT INTO `type` (`type`.`int_col`, `float_col`, `char_col`) VALUES ('', '', 'Kyiv {{city}}, Ukraine')"
/* Test covers potentially wrong behavior and marks it as expected!
* In case table name or table column is passed with curly or square bracelets,
* QueryBuilder can not determine the table schema and typecast values properly.
* TODO: make it work. Impossible without BC breaking for public methods.
*/
],
];
}
/**
* Make sure that `{{something}}` in values will not be encoded
* https://github.com/yiisoft/yii2/issues/11242
*
* @dataProvider batchInsertSqlProvider
*/
public function testBatchInsertSQL($table, $columns, $values, $expected)
{
$command = $this->getConnection()->createCommand();
$command->batchInsert($table, $columns, $values);
$this->assertEquals($expected, $command->getSql());
}
public function testInsert()
{
$db = $this->getConnection();

View File

@ -84,6 +84,15 @@ class CommandTest extends \yiiunit\framework\db\CommandTest
$this->assertEquals('SELECT "id", "t"."name" FROM "customer" t', $command->sql);
}
public function batchInsertSqlProvider()
{
$data = parent::batchInsertSqlProvider();
$data['issue11242']['expected'] = 'INSERT INTO "type" ("int_col", "float_col", "char_col") VALUES (NULL, NULL, \'Kyiv {{city}}, Ukraine\')';
$data['wrongBehavior']['expected'] = 'INSERT INTO "type" ("int_col", "float_col", "char_col") VALUES (\'\', \'\', \'Kyiv {{city}}, Ukraine\')';
return $data;
}
public function testAddDropCheck()
{
$this->markTestSkipped('CUBRID does not support adding/dropping check constraints.');

View File

@ -116,4 +116,13 @@ class CommandTest extends \yiiunit\framework\db\CommandTest
$db->createCommand()->dropDefaultValue($name, $tableName)->execute();
$this->assertEmpty($schema->getTableDefaultValues($tableName, true));
}
public function batchInsertSqlProvider()
{
$data = parent::batchInsertSqlProvider();
$data['issue11242']['expected'] = 'INSERT INTO [type] ([int_col], [float_col], [char_col]) VALUES (NULL, NULL, \'Kyiv {{city}}, Ukraine\')';
$data['wrongBehavior']['expected'] = 'INSERT INTO [type] ([int_col], [float_col], [char_col]) VALUES (\'\', \'\', \'Kyiv {{city}}, Ukraine\')';
return $data;
}
}

View File

@ -33,4 +33,13 @@ class CommandTest extends \yiiunit\framework\db\CommandTest
$command->execute();
$this->assertEquals(3, $db->getSchema()->getLastInsertID('profile_SEQ'));
}
public function batchInsertSqlProvider()
{
$data = parent::batchInsertSqlProvider();
$data['issue11242']['expected'] = 'INSERT INTO "type" ("int_col", "float_col", "char_col") VALUES (NULL, NULL, \'Kyiv {{city}}, Ukraine\')';
$data['wrongBehavior']['expected'] = 'INSERT INTO "type" ("type"."int_col", "float_col", "char_col") VALUES (\'\', \'\', \'Kyiv {{city}}, Ukraine\')';
return $data;
}
}

View File

@ -99,4 +99,13 @@ class CommandTest extends \yiiunit\framework\db\CommandTest
], ['char_col' => 'serialize']);
$this->assertEquals(1, $command->execute());
}
public function batchInsertSqlProvider()
{
$data = parent::batchInsertSqlProvider();
$data['issue11242']['expected'] = 'INSERT INTO "type" ("int_col", "float_col", "char_col") VALUES (NULL, NULL, \'Kyiv {{city}}, Ukraine\')';
$data['wrongBehavior']['expected'] = 'INSERT INTO "type" ("type"."int_col", "float_col", "char_col") VALUES (\'\', \'\', \'Kyiv {{city}}, Ukraine\')';
return $data;
}
}