Keep order for set operators such as UNION / UNION ALL#116
Keep order for set operators such as UNION / UNION ALL#116mbabker merged 24 commits intojoomla-framework:2.0-devfrom
Conversation
|
Seems fair to me. Just add a note to https://github.com/joomla-framework/database/blob/2.0-dev/docs/v1-to-v2-update.md and I'd say this should be fine, we just need to sort out deprecation notices then in the CMS repo and this repo's master (1.x) branch. |
|
I'm not sure about variable name '$merge' but I have not found a better name. |
|
If necessary, correct my documentation. |
|
look nice to me too, IIRC |
|
Nice, I think about it "too much" and now I have another solution, where we can put ORDER BY and LIMIT before UNION statement. [UPDATED] |
|
👍 |
docs/v1-to-v2-update.md
Outdated
| Method `union()` by default has `$distinct = true`. | ||
| If `$distinct` is `false` then generates `UNION ALL` sql statement. | ||
|
|
||
| Class variables `$union` and `$unionAll` has been merged into one variable `$merge`. |
|
To get Option 1:$tbl = $db->quoteName('#__foo');
$query = $db->getQuery(true);
$query2 = $db->getQuery(true);
$query3 = $db->getQuery(true);
$query->querySet($query2->select('name')->from($tbl)->order('id DESC')->setLimit(1))
->unionAll($query3->select('name')->from($tbl)->order('id')->setLimit(1))
->order('name')
->setLimit(1)Option 2:$tbl = $db->quoteName('#__foo');
$query = $db->getQuery(true);
$query2 = $db->getQuery(true);
$query->select('name')->from($tbl)->order('id DESC')->setLimit(1)
->toQuerySet()
->unionAll($query2->select('name')->from($tbl)->order('id')->setLimit(1))
->order('name')
->setLimit(1);Common result:(
SELECT name FROM `#__foo` ORDER BY id DESC LIMIT 1
)
UNION (
SELECT name FROM `#__foo` ORDER BY id LIMIT 1
)
ORDER BY name LIMIT 1 |
|
Seems fine to me.
Well luckily the testing infrastructure here is a lot better off (and you can somewhat copy the current Travis config to get a working SQL Server instance to test against). No tests are failing for now so let's roll with it and we can work on updating the test suite to better cover cases like this. |
|
Tested successfully. |
|
I will spend a little time to fix sqlsrv stuff, I need to back to my previous PR #104 |
|
My tests: require 'vendor/autoload.php';
$db = Joomla\Database\DatabaseDriver::getInstance(['driver' => 'sqlsrv', 'database' => '', 'user' => '', 'password' => '', 'prefix' => 'j38_']);
$q = $db->getQuery(1)->select('id')->from('j38_modules')->where('id <= 5')->order('id DESC')->setLimit(2, 1)->toQuerySet()->unionAll($db->getQuery(1)->select('id')->from('j38_modules')->where('id = 3 OR id = 4')->order('0+id DESC')->setLimit(1, 1))->order('id DESC');
echo $q;
SELECT * FROM (
SELECT * FROM (
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber_4c1bcee9724ef8f6f37135d9596d299a
FROM (
SELECT TOP 3 id
FROM j38_modules
WHERE id <= 5
ORDER BY id DESC) AS A
) AS A
WHERE RowNumber_4c1bcee9724ef8f6f37135d9596d299a > 1) AS merge_0
UNION ALL SELECT * FROM (
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber_b0af95c67b1589f1db7914ea2e26f658
FROM (
SELECT TOP 2 id
FROM j38_modules
WHERE id = 3 OR id = 4
ORDER BY 0+id DESC) AS A
) AS A
WHERE RowNumber_b0af95c67b1589f1db7914ea2e26f658 > 1) AS merge_1
) AS merges
ORDER BY id DESC
echo $q->setLimit(5, 2);
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber_e89d72665d4b94533c8b495e1eebf7ee
FROM (
SELECT TOP 7 * FROM (
SELECT * FROM (
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber_4c1bcee9724ef8f6f37135d9596d299a
FROM (
SELECT TOP 3 id
FROM j38_modules
WHERE id <= 5
ORDER BY id DESC) AS A
) AS A
WHERE RowNumber_4c1bcee9724ef8f6f37135d9596d299a > 1) AS merge_0
UNION ALL SELECT * FROM (
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber_b0af95c67b1589f1db7914ea2e26f658
FROM (
SELECT TOP 2 id
FROM j38_modules
WHERE id = 3 OR id = 4
ORDER BY 0+id DESC) AS A
) AS A
WHERE RowNumber_b0af95c67b1589f1db7914ea2e26f658 > 1) AS merge_1
) AS merges
ORDER BY id DESC) AS A
) AS A
WHERE RowNumber_e89d72665d4b94533c8b495e1eebf7ee > 2It looks a little ugly but we can only remove |
|
Since the minimum version of SQL Server is now 2012, it would be good to wait for the new |
…all, limit and offset
…ll, limit and offset
…all, limit and offset
…all, limit and offset
|
Now unit tests execute real query tests for UNIONs on |
|
Unit tests passed. |
src/Pdo/PdoDriver.php
Outdated
| catch (\PDOException $exception) | ||
| { | ||
| throw new PrepareStatementFailureException($exception->getMessage(), $exception->getCode(), $exception); | ||
| throw new PrepareStatementFailureException($exception->getMessage() . $query, (int) $exception->getCode(), $exception); |
There was a problem hiding this comment.
If we really need the query as part of the Exception, it should be a separate property similar to the query failure exception so the query isn't shown plain text as part of the message.
Also, we shouldn't cast the exception code to integer here. It is correct that the DB drivers don't strictly use numeric codes and Exception codes aren't type enforced.
There was a problem hiding this comment.
This should also be consistently done for all drivers (i.e. the non-PDO ones), not just PDO.
There was a problem hiding this comment.
Ups, it was only for debug, I will remove it
Yes, but try to do a bug in code: diff --git a/src/Sqlite/SqliteQuery.php b/src/Sqlite/SqliteQuery.php
index 940d030..b196fcc 100644
--- a/src/Sqlite/SqliteQuery.php
+++ b/src/Sqlite/SqliteQuery.php
@@ -73,7 +73,7 @@ class SqliteQuery extends PdoQuery
case 'querySet':
$query = $this->querySet;
- if ($query->order || $query->limit || $query->offset)
+ if (0)//$query->order || $query->limit || $query->offset)Then run phpunit by Then I got:
Maybe override constructor for |
|
If it really comes down to that yes the constructor should be overloaded. Now I'm curious what is overloading core PHP's |
php7.1 -a
Interactive mode enabled
php > new RuntimeException('', '');
PHP Warning: Uncaught Error: Wrong parameters for RuntimeException([string $message [, long $code [, Throwable $previous = NULL]]]) in php shell code:1
Stack trace:
#0 php shell code(1): Exception->__construct('', '')
#1 {main}
thrown in php shell code on line 1 |
|
:eyeroll: |
|
It seems to be a known issue http://php.net/manual/en/class.pdoexception.php#95812 Should I add the constructor for |
|
Might as well. |
|
Done |
Pull Request for Issue #115
Summary of Changes
EXCEPTandINTERSECTunionDistinct()- removed, useunion()instead.$unionand$unionAllreplaced by variable$merge(array) to preserve the order.union()andunionAll()methods.DatabaseQuery::querySet($query)andDatabaseQuery::toQuerySet(), see Keep order for set operators such as UNION / UNION ALL #116 (comment)Testing Instructions
database/srcfolder.php -aor create a short script like below and execute it. Replace dots....Documentation Changes Required
Joomla\Database\DatabaseQuerygeneral changesChanges in methods
union(),unionAll()andunionDistinct()unionDistinct()has been removed. Useunion()instead.$querystops accepting the array. OnlyDatabaseQueryobject or string.$distincthas been removed fromunionAll.$gluehas been removed from both.Method
union()by default has$distinct = true.If
$distinctisfalsethen generatesUNION ALLsql statement.Class variables
$unionand$unionAllhas been merged into one variable$merge.The new variable represents an ordered array of individual elements.
Stop supporting
$type = 'union'in method__toString().Mix of union types
Source https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries004.htm