diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ec66929b76..ce580c49be 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.49 under development ------------------------ +- Bug #9899: Fix caching a MSSQL query with BLOB data type (terabytesoftw) - Bug #16208: Fix `yii\log\FileTarget` to not export empty messages (terabytesoftw) - Bug #19857: Fix AttributeTypecastBehavior::resetOldAttributes() causes "class has no attribute named" InvalidArgumentException (uaoleg) - Bug #18859: Fix `yii\web\Controller::bindInjectedParams()` to not throw error when argument of `ReflectionUnionType` type is passed (bizley) diff --git a/framework/caching/DbCache.php b/framework/caching/DbCache.php index 5bf3b02ded..d707ba41a2 100644 --- a/framework/caching/DbCache.php +++ b/framework/caching/DbCache.php @@ -317,7 +317,7 @@ class DbCache extends Cache */ protected function getDataFieldName() { - return $this->isVarbinaryDataField() ? 'convert(nvarchar(max),[data]) data' : 'data'; + return $this->isVarbinaryDataField() ? 'CONVERT(VARCHAR(MAX), [[data]]) data' : 'data'; } /** diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index cc3595519c..1225c466cc 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -460,10 +460,9 @@ class QueryBuilder extends \yii\db\QueryBuilder $columnSchemas = $tableSchema->columns; foreach ($columns as $name => $value) { // @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); + if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value))) { // @see https://github.com/yiisoft/yii2/issues/12599 - $columns[$name] = new Expression("CONVERT(VARBINARY(MAX), $phName)", $params); + $columns[$name] = new Expression('CONVERT(VARBINARY(MAX), ' . ('0x' . bin2hex($value)) . ')'); } } } diff --git a/tests/framework/db/mssql/CommandTest.php b/tests/framework/db/mssql/CommandTest.php index b09f87bb6b..25a58a3a9e 100644 --- a/tests/framework/db/mssql/CommandTest.php +++ b/tests/framework/db/mssql/CommandTest.php @@ -134,22 +134,19 @@ class CommandTest extends \yiiunit\framework\db\CommandTest { $db = $this->getConnection(); - $testData = json_encode(['test' => 'string', 'test2' => 'integer']); + $qb = $db->getQueryBuilder(); + $testData = json_encode(['test' => 'string', 'test2' => 'integer'], JSON_THROW_ON_ERROR); + $params = []; - $qb = $db->getQueryBuilder(); - $sql = $qb->upsert('T_upsert_varbinary', ['id' => 1, 'blob_col' => $testData] , ['blob_col' => $testData], $params); - + $sql = $qb->upsert('T_upsert_varbinary', ['id' => 1, 'blob_col' => $testData], ['blob_col' => $testData], $params); $result = $db->createCommand($sql, $params)->execute(); - $this->assertEquals(1, $result); - - $query = (new Query()) - ->select(['convert(nvarchar(max),blob_col) as blob_col']) - ->from('T_upsert_varbinary') - ->where(['id' => 1]); + $this->assertSame(1, $result); + $query = (new Query())->select(['blob_col'])->from('T_upsert_varbinary')->where(['id' => 1]); $resultData = $query->createCommand($db)->queryOne(); - $this->assertEquals($testData, $resultData['blob_col']); + + $this->assertSame($testData, $resultData['blob_col']); } } diff --git a/tests/framework/db/mssql/QueryCacheTest.php b/tests/framework/db/mssql/QueryCacheTest.php new file mode 100644 index 0000000000..b35c11fde1 --- /dev/null +++ b/tests/framework/db/mssql/QueryCacheTest.php @@ -0,0 +1,55 @@ +getConnection(); + $db->enableQueryCache = true; + $db->queryCache = new FileCache(['cachePath' => '@yiiunit/runtime/cache']); + + $db->createCommand()->delete('type')->execute(); + $db->createCommand()->insert('type', [ + 'int_col' => $key = 1, + 'char_col' => '', + 'char_col2' => '6a3ce1a0bffe8eeb6fa986caf443e24c', + 'float_col' => 0.0, + 'blob_col' => 'a:1:{s:13:"template";s:1:"1";}', + 'bool_col' => true, + ])->execute(); + + $function = function($db) use ($key){ + return (new Query()) + ->select(['blob_col']) + ->from('type') + ->where(['int_col' => $key]) + ->createCommand($db) + ->queryScalar(); + }; + + // First run return + $result = $db->cache($function); + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + + // After the request has been cached return + $result = $db->cache($function); + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + } +} diff --git a/tests/framework/db/mssql/type/VarbinaryTest.php b/tests/framework/db/mssql/type/VarbinaryTest.php new file mode 100644 index 0000000000..b72a2b4c46 --- /dev/null +++ b/tests/framework/db/mssql/type/VarbinaryTest.php @@ -0,0 +1,44 @@ +getConnection(); + + $db->createCommand()->delete('type')->execute(); + $db->createCommand()->insert('type', [ + 'int_col' => $key = 1, + 'char_col' => '', + 'char_col2' => '6a3ce1a0bffe8eeb6fa986caf443e24c', + 'float_col' => 0.0, + 'blob_col' => 'a:1:{s:13:"template";s:1:"1";}', + 'bool_col' => true, + ])->execute(); + + $result = (new Query()) + ->select(['blob_col']) + ->from('type') + ->where(['int_col' => $key]) + ->createCommand($db) + ->queryScalar(); + + $this->assertSame('a:1:{s:13:"template";s:1:"1";}', $result); + } +}