Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
212c5ee3
Commit
212c5ee3
authored
Sep 03, 2014
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes #4254: `SqlDataProvider` does not work with Oracle and SQL Server
parent
85af21c7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
141 additions
and
160 deletions
+141
-160
db-dao.md
docs/guide/db-dao.md
+1
-2
QueryBuilder.php
extensions/sphinx/QueryBuilder.php
+22
-0
CHANGELOG.md
framework/CHANGELOG.md
+1
-0
SqlDataProvider.php
framework/data/SqlDataProvider.php
+21
-12
QueryBuilder.php
framework/db/QueryBuilder.php
+22
-2
QueryBuilder.php
framework/db/mssql/QueryBuilder.php
+64
-104
QueryBuilder.php
framework/db/oci/QueryBuilder.php
+10
-40
No files found.
docs/guide/db-dao.md
View file @
212c5ee3
...
...
@@ -12,8 +12,7 @@ uniform API and solves some inconsistencies between different DBMS. By default Y
-
[
PostgreSQL
](
http://www.postgresql.org/
)
-
[
CUBRID
](
http://www.cubrid.org/
)
: version 9.1.0 or higher.
-
[
Oracle
](
http://www.oracle.com/us/products/database/overview/index.html
)
-
[
MSSQL
](
https://www.microsoft.com/en-us/sqlserver/default.aspx
)
: version 2012 or above is required if you
want to use LIMIT/OFFSET.
-
[
MSSQL
](
https://www.microsoft.com/en-us/sqlserver/default.aspx
)
: version 2005 or higher.
Configuration
...
...
extensions/sphinx/QueryBuilder.php
View file @
212c5ee3
...
...
@@ -525,6 +525,27 @@ class QueryBuilder extends Object
}
/**
* Builds the ORDER BY and LIMIT/OFFSET clauses and appends them to the given SQL.
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
* @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
* @param integer $limit the limit number. See [[Query::limit]] for more details.
* @param integer $offset the offset number. See [[Query::offset]] for more details.
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
*/
public
function
buildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
if
(
$orderBy
!==
''
)
{
$sql
.=
$this
->
separator
.
$orderBy
;
}
$limit
=
$this
->
buildLimit
(
$limit
,
$offset
);
if
(
$limit
!==
''
)
{
$sql
.=
$this
->
separator
.
$limit
;
}
return
$sql
;
}
/**
* @param array $columns
* @return string the ORDER BY clause built from [[query]].
*/
...
...
@@ -999,6 +1020,7 @@ class QueryBuilder extends Object
* @param array $operands contains two column names.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws InvalidParamException if count($operands) is not 2
*/
public
function
buildSimpleCondition
(
$operator
,
$operands
,
&
$params
)
{
...
...
framework/CHANGELOG.md
View file @
212c5ee3
...
...
@@ -68,6 +68,7 @@ Yii Framework 2 Change Log
-
Bug #4127:
`CaptchaValidator`
clientside error message wasn't formed properly (samdark)
-
Bug #4162: Fixed bug where schema name was not used in ’SHOW CREATE TABLE’ query in
`yii\db\mysql\Schema`
(stevekr)
-
Bug #4241:
`yii\widgets\Pjax`
was incorrectly setting container id (mitalcoi)
-
Bug #4254:
`SqlDataProvider`
does not work with Oracle and SQL Server (qiangxue, miramir)
-
Bug #4276: Added check for UPLOAD_ERR_NO_FILE in
`yii\web\UploadedFile`
and return null if no file was uploaded (OmgDef)
-
Bug #4342: mssql (dblib) driver does not support getting attributes (tof06)
-
Bug #4371: Active form client validation wasn't working in case of two models having same named fields (abrahamy)
...
...
framework/data/SqlDataProvider.php
View file @
212c5ee3
...
...
@@ -10,6 +10,7 @@ namespace yii\data;
use
Yii
;
use
yii\base\InvalidConfigException
;
use
yii\db\Connection
;
use
yii\db\Expression
;
use
yii\di\Instance
;
/**
...
...
@@ -102,25 +103,33 @@ class SqlDataProvider extends BaseDataProvider
*/
protected
function
prepareModels
()
{
$sql
=
$this
->
sql
;
$qb
=
$this
->
db
->
getQueryBuilder
();
if
((
$sort
=
$this
->
getSort
())
!==
false
)
{
$orderBy
=
$qb
->
buildOrderBy
(
$sort
->
getOrders
());
if
(
!
empty
(
$orderBy
))
{
$orderBy
=
substr
(
$orderBy
,
9
);
if
(
preg_match
(
'/\s+order\s+by\s+[\w\s,\.]+$/i'
,
$sql
))
{
$sql
.=
', '
.
$orderBy
;
}
else
{
$sql
.=
' ORDER BY '
.
$orderBy
;
$sort
=
$this
->
getSort
();
$pagination
=
$this
->
getPagination
();
if
(
$pagination
===
false
&&
$sort
===
false
)
{
return
$this
->
db
->
createCommand
(
$this
->
sql
,
$this
->
params
)
->
queryAll
();
}
$sql
=
$this
->
sql
;
$orders
=
[];
$limit
=
$offset
=
null
;
if
(
$sort
!==
false
)
{
$orders
=
$sort
->
getOrders
();
$pattern
=
'/\s+order\s+by\s+([\w\s,\.]+)$/i'
;
if
(
preg_match
(
$pattern
,
$sql
,
$matches
))
{
array_unshift
(
$orders
,
new
Expression
(
$matches
[
1
]));
$sql
=
preg_replace
(
$pattern
,
''
,
$sql
);
}
}
if
(
(
$pagination
=
$this
->
getPagination
())
!==
false
)
{
if
(
$pagination
!==
false
)
{
$pagination
->
totalCount
=
$this
->
getTotalCount
();
$sql
.=
' '
.
$qb
->
buildLimit
(
$pagination
->
getLimit
(),
$pagination
->
getOffset
());
$limit
=
$pagination
->
getLimit
();
$offset
=
$pagination
->
getOffset
();
}
$sql
=
$this
->
db
->
getQueryBuilder
()
->
buildOrderByAndLimit
(
$sql
,
$orders
,
$limit
,
$offset
);
return
$this
->
db
->
createCommand
(
$sql
,
$this
->
params
)
->
queryAll
();
}
...
...
framework/db/QueryBuilder.php
View file @
212c5ee3
...
...
@@ -96,11 +96,10 @@ class QueryBuilder extends \yii\base\Object
$this
->
buildWhere
(
$query
->
where
,
$params
),
$this
->
buildGroupBy
(
$query
->
groupBy
),
$this
->
buildHaving
(
$query
->
having
,
$params
),
$this
->
buildOrderBy
(
$query
->
orderBy
),
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
),
];
$sql
=
implode
(
$this
->
separator
,
array_filter
(
$clauses
));
$sql
=
$this
->
buildOrderByAndLimit
(
$sql
,
$query
->
orderBy
,
$query
->
limit
,
$query
->
offset
);
$union
=
$this
->
buildUnion
(
$query
->
union
,
$params
);
if
(
$union
!==
''
)
{
...
...
@@ -755,6 +754,27 @@ class QueryBuilder extends \yii\base\Object
}
/**
* Builds the ORDER BY and LIMIT/OFFSET clauses and appends them to the given SQL.
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
* @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
* @param integer $limit the limit number. See [[Query::limit]] for more details.
* @param integer $offset the offset number. See [[Query::offset]] for more details.
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
*/
public
function
buildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
if
(
$orderBy
!==
''
)
{
$sql
.=
$this
->
separator
.
$orderBy
;
}
$limit
=
$this
->
buildLimit
(
$limit
,
$offset
);
if
(
$limit
!==
''
)
{
$sql
.=
$this
->
separator
.
$limit
;
}
return
$sql
;
}
/**
* @param array $columns
* @return string the ORDER BY clause built from [[Query::$orderBy]].
*/
...
...
framework/db/mssql/QueryBuilder.php
View file @
212c5ee3
...
...
@@ -41,25 +41,77 @@ class QueryBuilder extends \yii\db\QueryBuilder
/**
* @param integer $limit
* @param integer $offset
* @return string the LIMIT and OFFSET clauses built from [[\yii\db\Query::$limit]].
* @inheritdoc
*/
public
function
build
Limit
(
$limit
,
$offset
=
0
)
public
function
build
OrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$hasOffset
=
$this
->
hasOffset
(
$offset
);
$hasLimit
=
$this
->
hasLimit
(
$limit
);
if
(
$hasOffset
||
$hasLimit
)
{
if
(
!
$this
->
hasOffset
(
$offset
)
&&
!
$this
->
hasLimit
(
$limit
))
{
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
return
$orderBy
===
''
?
$sql
:
$sql
.
$this
->
separator
.
$orderBy
;
}
if
(
$this
->
isOldMssql
())
{
return
$this
->
oldbuildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
);
}
else
{
return
$this
->
newBuildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
);
}
}
/**
* Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
* @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
* @param integer $limit the limit number. See [[Query::limit]] for more details.
* @param integer $offset the offset number. See [[Query::offset]] for more details.
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
*/
protected
function
newBuildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
if
(
$orderBy
===
''
)
{
// ORDER BY clause is required when FETCH and OFFSET are in the SQL
$orderBy
=
'ORDER BY (SELECT NULL)'
;
}
$sql
.=
$this
->
separator
.
$orderBy
;
// http://technet.microsoft.com/en-us/library/gg699618.aspx
$sql
=
'OFFSET '
.
(
$hasOffset
?
$offset
:
'0'
)
.
' ROWS'
;
if
(
$hasLimit
)
{
$sql
.=
" FETCH NEXT
$limit
ROWS ONLY"
;
$offset
=
$this
->
hasOffset
(
$offset
)
?
$offset
:
'0'
;
$sql
.=
$this
->
separator
.
"OFFSET
$offset
ROWS"
;
if
(
$this
->
hasLimit
(
$limit
))
{
$sql
.=
$this
->
separator
.
"FETCH NEXT
$limit
ROWS ONLY"
;
}
return
$sql
;
}
/**
* Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2005 to 2008.
* @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
* @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
* @param integer $limit the limit number. See [[Query::limit]] for more details.
* @param integer $offset the offset number. See [[Query::offset]] for more details.
* @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
*/
protected
function
oldBuildOrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
if
(
$orderBy
===
''
)
{
// ROW_NUMBER() requires an ORDER BY clause
$orderBy
=
'ORDER BY (SELECT NULL)'
;
}
$sql
=
preg_replace
(
'/^([\s(])*SELECT(\s+DISTINCT)?(?!\s*TOP\s*\()/i'
,
"
\\
1SELECT
\\
2 rowNum = ROW_NUMBER() over (
$orderBy
),"
,
$sql
);
if
(
$this
->
hasLimit
(
$limit
))
{
$sql
=
"SELECT TOP
$limit
* FROM (
$sql
) sub"
;
}
else
{
return
''
;
$sql
=
"SELECT * FROM (
$sql
) sub"
;
}
if
(
$this
->
hasOffset
(
$offset
))
{
$sql
.=
$this
->
separator
.
"WHERE rowNum >
$offset
"
;
}
return
$sql
;
}
/**
...
...
@@ -127,98 +179,6 @@ class QueryBuilder extends \yii\db\QueryBuilder
}
/**
* @inheritdoc
*/
public
function
build
(
$query
,
$params
=
[])
{
$query
->
prepareBuild
(
$this
);
$params
=
empty
(
$params
)
?
$query
->
params
:
array_merge
(
$params
,
$query
->
params
);
$orderBy
=
$this
->
buildOrderBy
(
$query
->
orderBy
);
if
(
$orderBy
===
''
&&
(
$this
->
hasOffset
(
$query
->
offset
)
||
$this
->
hasLimit
(
$query
->
limit
))
&&
!
$this
->
isOldMssql
())
{
// ORDER BY clause is required when FETCH and OFFSET are in the SQL
$orderBy
=
'ORDER BY (SELECT NULL)'
;
}
$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
),
$orderBy
,
$this
->
isOldMssql
()
?
''
:
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
),
];
$sql
=
implode
(
$this
->
separator
,
array_filter
(
$clauses
));
if
(
$this
->
isOldMssql
())
{
$sql
=
$this
->
applyLimitAndOffset
(
$sql
,
$query
);
}
$union
=
$this
->
buildUnion
(
$query
->
union
,
$params
);
if
(
$union
!==
''
)
{
$sql
=
"(
$sql
)
{
$this
->
separator
}
$union
"
;
}
return
[
$sql
,
$params
];
}
/**
* Applies limit and offset to SQL query
*
* @param string $sql SQL query
* @param \yii\db\Query $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
;
$offset
=
$query
->
offset
!==
null
?
(
int
)
$query
->
offset
:
-
1
;
if
(
$limit
>
0
||
$offset
>=
0
)
{
$sql
=
$this
->
rewriteLimitOffsetSql
(
$sql
,
$limit
,
$offset
,
$query
);
}
return
$sql
;
}
/**
* Rewrites limit and offset in SQL query
*
* @param string $sql SQL query
* @param integer $limit
* @param integer $offset
* @param \yii\db\Query $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
);
if
(
$query
->
select
)
{
$select
=
implode
(
', '
,
$query
->
select
);
}
else
{
$select
=
$query
->
select
=
'*'
;
}
if
(
$select
===
'*'
)
{
$columns
=
$this
->
getAllColumnNames
(
$query
->
modelClass
);
if
(
$columns
&&
is_array
(
$columns
))
{
$select
=
implode
(
', '
,
$columns
);
}
else
{
$select
=
$columns
;
}
}
$sql
=
str_replace
(
$originalOrdering
,
''
,
$sql
);
if
(
$originalOrdering
===
''
)
{
// hack so LIMIT will work because ROW_NUMBER requires an ORDER BY clause
$originalOrdering
=
'ORDER BY (SELECT NULL)'
;
}
$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
;
}
/**
* Returns an array of column names given model name
*
* @param string $modelClass name of the model class
...
...
@@ -242,7 +202,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
private
$_oldMssql
;
/**
* @return boolean whether
MSSQL used is old
.
* @return boolean whether
the version of the MSSQL being used is older than 2012
.
* @throws \yii\base\InvalidConfigException
* @throws \yii\db\Exception
*/
...
...
framework/db/oci/QueryBuilder.php
View file @
212c5ee3
...
...
@@ -40,66 +40,36 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema
::
TYPE_MONEY
=>
'NUMBER(19,4)'
,
];
private
$_sql
;
/**
* @inheritdoc
*/
public
function
build
(
$query
,
$params
=
[]
)
public
function
build
OrderByAndLimit
(
$sql
,
$orderBy
,
$limit
,
$offset
)
{
$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
->
_sql
=
implode
(
$this
->
separator
,
array_filter
(
$clauses
));
$this
->
_sql
=
$this
->
buildLimit
(
$query
->
limit
,
$query
->
offset
);
$union
=
$this
->
buildUnion
(
$query
->
union
,
$params
);
if
(
$union
!==
''
)
{
$this
->
_sql
=
"
{
$this
->
_sql
}{
$this
->
separator
}
$union
"
;
}
return
[
$this
->
_sql
,
$params
];
$orderBy
=
$this
->
buildOrderBy
(
$orderBy
);
if
(
$orderBy
!==
''
)
{
$sql
.=
$this
->
separator
.
$orderBy
;
}
/**
* @inheritdoc
*/
public
function
buildLimit
(
$limit
,
$offset
)
{
$filters
=
[];
if
(
$this
->
hasOffset
(
$offset
)
>
0
)
{
if
(
$this
->
hasOffset
(
$offset
))
{
$filters
[]
=
'rowNumId > '
.
$offset
;
}
if
(
$this
->
hasLimit
(
$limit
))
{
$filters
[]
=
'rownum <= '
.
$limit
;
}
if
(
empty
(
$filters
))
{
return
$sql
;
}
if
(
!
empty
(
$filters
))
{
$filter
=
implode
(
' and '
,
$filters
);
$filter
=
implode
(
' AND '
,
$filters
);
return
<<<EOD
WITH USER_SQL AS (
{$this->_sql}
),
WITH USER_SQL AS (
$sql
),
PAGINATION AS (SELECT USER_SQL.*, rownum as rowNumId FROM USER_SQL)
SELECT *
FROM PAGINATION
WHERE $filter
EOD;
}
else
{
return
$this
->
_sql
;
}
}
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment