From 3e45097126b5290e6b097bccdab2057d836f910c Mon Sep 17 00:00:00 2001 From: Fourteenmeister Date: Thu, 8 May 2014 13:04:36 +0900 Subject: [PATCH 1/4] MS SQL Server fix for older versions than 2012 (alternative solution) This alternative solution LIMIT, OFFSET problem SQL Server version older than 2012. --- framework/db/mssql/QueryBuilder.php | 89 +++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 8a8fece389..5bdabb66b3 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -139,4 +139,93 @@ class QueryBuilder extends \yii\db\QueryBuilder return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL"; } + + public function buildOrderBy($columns) + { + if (empty($columns)) { + return 'ORDER BY (SELECT NULL)'; // hack so limit will work if no order by is specified + } else { + return parent::buildOrderBy($columns); + } + } + + public function build($query, $params = []) + { + $query->prepareBuild($this); + + $params = empty($params) ? $query->params : array_merge($params, $query->params); + + $clauses = [ + $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption), + $this->buildFrom($query->from, $params), + $this->buildJoin($query->join, $params), + $this->buildWhere($query->where, $params), + $this->buildGroupBy($query->groupBy), + $this->buildHaving($query->having, $params), + $this->buildOrderBy($query->orderBy), + $this->olderMssql() ? '' : $this->buildLimit($query->limit, $query->offset), + ]; + + $sql = implode($this->separator, array_filter($clauses)); + if ($this->olderMssql()) + $sql = $this->applyLimit($sql, $query); + $union = $this->buildUnion($query->union, $params); + if ($union !== '') { + $sql = "($sql){$this->separator}$union"; + } + + + return [$sql, $params]; + } + + public function applyLimit($sql, $query) + { + $limit = $query->limit !== null ? (int)$query->limit : -1; + $offset = $query->offset !== null ? (int)$query->offset : -1; + if ($limit > 0 || $offset >= 0) + $sql = $this->rewriteLimitOffsetSql($sql, $limit, $offset, $query); + return $sql; + } + + protected function rewriteLimitOffsetSql($sql, $limit, $offset, $query) + { + $originalOrdering = $this->buildOrderBy($query->orderBy); + if ($query->select) { + $select = implode(', ', $query->select); + } + else { + $select = $query->select = '*'; + } + if ($select === '*') { + $columns = $this->getAllColumnNames($query->from[0]); + if ($columns && is_array($columns)) + $select = implode(', ', $columns); + else + $select = $columns; + } + $sql = str_replace($originalOrdering, '', $sql); + $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i', "\\1SELECT\\2 rowNum = ROW_NUMBER() over ({$originalOrdering}),", $sql); + $sql = "SELECT TOP {$limit} {$select} FROM ($sql) sub WHERE rowNum > {$offset}"; + return $sql; + } + + protected function getAllColumnNames($table = null) + { + if (!$table) { + return null; + } + $columns = (new \yii\db\Query()) + ->select('name') + ->from('sys.columns') + ->where("object_id = OBJECT_ID('dbo.{$table}')") + ->column(); + return $columns; + } + + protected function olderMssql() + { + $this->db->open(); + $version = preg_split("/\./", $this->db->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION)); + return $version[0] < 11; + } } From b36b9bfdade19f261de35a79067a3d0dc03c3498 Mon Sep 17 00:00:00 2001 From: Fourteenmeister Date: Tue, 13 May 2014 10:41:29 +0900 Subject: [PATCH 2/4] fix error fix error when if fields whose names are reserved word --- framework/db/mssql/QueryBuilder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 5bdabb66b3..c6a1287c43 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -219,6 +219,7 @@ class QueryBuilder extends \yii\db\QueryBuilder ->from('sys.columns') ->where("object_id = OBJECT_ID('dbo.{$table}')") ->column(); + array_walk($columns, create_function('&$str', '$str = "[$str]";')); return $columns; } From f46c706033fb7da27342bc8f2f118220fdc31c57 Mon Sep 17 00:00:00 2001 From: Fourteenmeister Date: Wed, 14 May 2014 10:20:57 +0900 Subject: [PATCH 3/4] Update QueryBuilder.php fix error occurs when you use multiple databases. --- framework/db/mssql/QueryBuilder.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index c6a1287c43..006e46ff69 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -214,11 +214,7 @@ class QueryBuilder extends \yii\db\QueryBuilder if (!$table) { return null; } - $columns = (new \yii\db\Query()) - ->select('name') - ->from('sys.columns') - ->where("object_id = OBJECT_ID('dbo.{$table}')") - ->column(); + $columns = $this->db->createCommand("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='{$table}'")->queryColumn(); array_walk($columns, create_function('&$str', '$str = "[$str]";')); return $columns; } From f98c414cd11a98935d0aa10fcd4c81e5c75a57d5 Mon Sep 17 00:00:00 2001 From: Fourteenmeister Date: Thu, 15 May 2014 12:03:50 +0900 Subject: [PATCH 4/4] Update QueryBuilder.php solution used **heukirne** for the names of columns. --- framework/db/mssql/QueryBuilder.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/framework/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php index 006e46ff69..96e812dc39 100644 --- a/framework/db/mssql/QueryBuilder.php +++ b/framework/db/mssql/QueryBuilder.php @@ -197,7 +197,7 @@ class QueryBuilder extends \yii\db\QueryBuilder $select = $query->select = '*'; } if ($select === '*') { - $columns = $this->getAllColumnNames($query->from[0]); + $columns = $this->getAllColumnNames($query->modelClass); if ($columns && is_array($columns)) $select = implode(', ', $columns); else @@ -209,13 +209,14 @@ class QueryBuilder extends \yii\db\QueryBuilder return $sql; } - protected function getAllColumnNames($table = null) + protected function getAllColumnNames($modelClass = null) { - if (!$table) { + if (!$modelClass) { return null; } - $columns = $this->db->createCommand("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='{$table}'")->queryColumn(); - array_walk($columns, create_function('&$str', '$str = "[$str]";')); + $model = new $modelClass; + $schema = $model->getTableSchema(); + $columns = array_keys($schema->columns); return $columns; }