Commit b01e6de9 by Alexander Makarov

Fixes #3399: Added support for MS SQL Server older than 2012

parent f5e39f88
...@@ -63,6 +63,7 @@ Yii Framework 2 Change Log ...@@ -63,6 +63,7 @@ Yii Framework 2 Change Log
- Enh #3298: Supported configuring `View::theme` using a class name (netyum, qiangxue) - Enh #3298: Supported configuring `View::theme` using a class name (netyum, qiangxue)
- Enh #3328: `BaseMailer` generates better text body from html body (armab) - Enh #3328: `BaseMailer` generates better text body from html body (armab)
- Enh #3380: Allow `value` in `defaultValueValidator` to be a closure (Alex-Code) - Enh #3380: Allow `value` in `defaultValueValidator` to be a closure (Alex-Code)
- Enh #3399: Added support for MS SQL Server older than 2012 (fourteenmeister, samdark)
- Enh #3472: Added configurable option to encode spaces in dropDownLists and listBoxes (kartik-v) - Enh #3472: Added configurable option to encode spaces in dropDownLists and listBoxes (kartik-v)
- Enh #3518: `yii\helpers\Html::encode()` now replaces invalid code sequences with "�" (DaSourcerer) - Enh #3518: `yii\helpers\Html::encode()` now replaces invalid code sequences with "�" (DaSourcerer)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe) - Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
......
...@@ -140,15 +140,22 @@ class QueryBuilder extends \yii\db\QueryBuilder ...@@ -140,15 +140,22 @@ class QueryBuilder extends \yii\db\QueryBuilder
return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL"; return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL";
} }
/**
* @inheritdoc
*/
public function buildOrderBy($columns) public function buildOrderBy($columns)
{ {
if (empty($columns)) { if (empty($columns)) {
return 'ORDER BY (SELECT NULL)'; // hack so limit will work if no order by is specified // hack so LIMIT will work if no ORDER BY is specified
return 'ORDER BY (SELECT NULL)';
} else { } else {
return parent::buildOrderBy($columns); return parent::buildOrderBy($columns);
} }
} }
/**
* @inheritdoc
*/
public function build($query, $params = []) public function build($query, $params = [])
{ {
$query->prepareBuild($this); $query->prepareBuild($this);
...@@ -163,31 +170,48 @@ class QueryBuilder extends \yii\db\QueryBuilder ...@@ -163,31 +170,48 @@ class QueryBuilder extends \yii\db\QueryBuilder
$this->buildGroupBy($query->groupBy), $this->buildGroupBy($query->groupBy),
$this->buildHaving($query->having, $params), $this->buildHaving($query->having, $params),
$this->buildOrderBy($query->orderBy), $this->buildOrderBy($query->orderBy),
$this->olderMssql() ? '' : $this->buildLimit($query->limit, $query->offset), $this->isOldMssql() ? '' : $this->buildLimit($query->limit, $query->offset),
]; ];
$sql = implode($this->separator, array_filter($clauses)); $sql = implode($this->separator, array_filter($clauses));
if ($this->olderMssql()) if ($this->isOldMssql()) {
$sql = $this->applyLimit($sql, $query); $sql = $this->applyLimitAndOffset($sql, $query);
}
$union = $this->buildUnion($query->union, $params); $union = $this->buildUnion($query->union, $params);
if ($union !== '') { if ($union !== '') {
$sql = "($sql){$this->separator}$union"; $sql = "($sql){$this->separator}$union";
} }
return [$sql, $params]; return [$sql, $params];
} }
public function applyLimit($sql, $query) /**
* Applies limit and offset to SQL query
*
* @param string $sql SQL query
* @param \yii\db\ActiveQuery $query the [[Query]] object from which the SQL statement generated
* @return string resulting SQL
*/
private function applyLimitAndOffset($sql, $query)
{ {
$limit = $query->limit !== null ? (int)$query->limit : -1; $limit = $query->limit !== null ? (int)$query->limit : -1;
$offset = $query->offset !== null ? (int)$query->offset : -1; $offset = $query->offset !== null ? (int)$query->offset : -1;
if ($limit > 0 || $offset >= 0) if ($limit > 0 || $offset >= 0) {
$sql = $this->rewriteLimitOffsetSql($sql, $limit, $offset, $query); $sql = $this->rewriteLimitOffsetSql($sql, $limit, $offset, $query);
}
return $sql; return $sql;
} }
protected function rewriteLimitOffsetSql($sql, $limit, $offset, $query) /**
* Rewrites limit and offset in SQL query
*
* @param string $sql SQL query
* @param integer $limit
* @param integer $offset
* @param \yii\db\ActiveQuery $query the [[Query]] object from which the SQL statement generated
* @return string resulting SQL query
*/
private function rewriteLimitOffsetSql($sql, $limit, $offset, $query)
{ {
$originalOrdering = $this->buildOrderBy($query->orderBy); $originalOrdering = $this->buildOrderBy($query->orderBy);
if ($query->select) { if ($query->select) {
...@@ -198,29 +222,42 @@ class QueryBuilder extends \yii\db\QueryBuilder ...@@ -198,29 +222,42 @@ class QueryBuilder extends \yii\db\QueryBuilder
} }
if ($select === '*') { if ($select === '*') {
$columns = $this->getAllColumnNames($query->modelClass); $columns = $this->getAllColumnNames($query->modelClass);
if ($columns && is_array($columns)) if ($columns && is_array($columns)) {
$select = implode(', ', $columns); $select = implode(', ', $columns);
else } else {
$select = $columns; $select = $columns;
} }
}
$sql = str_replace($originalOrdering, '', $sql); $sql = str_replace($originalOrdering, '', $sql);
$sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i', "\\1SELECT\\2 rowNum = ROW_NUMBER() over ({$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}"; $sql = "SELECT TOP {$limit} {$select} FROM ($sql) sub WHERE rowNum > {$offset}";
return $sql; return $sql;
} }
/**
* Returns an array of column names given model name
*
* @param string $modelClass name of the model class
* @return array|null array of column names
*/
protected function getAllColumnNames($modelClass = null) protected function getAllColumnNames($modelClass = null)
{ {
if (!$modelClass) { if (!$modelClass) {
return null; return null;
} }
/** @var \yii\db\ActiveRecord $model */
$model = new $modelClass; $model = new $modelClass;
$schema = $model->getTableSchema(); $schema = $model->getTableSchema();
$columns = array_keys($schema->columns); $columns = array_keys($schema->columns);
return $columns; return $columns;
} }
protected function olderMssql() /**
* @return boolean if MSSQL used is old
* @throws \yii\base\InvalidConfigException
* @throws \yii\db\Exception
*/
protected function isOldMssql()
{ {
$this->db->open(); $this->db->open();
$version = preg_split("/\./", $this->db->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION)); $version = preg_split("/\./", $this->db->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment