[BP to 7.1.1] - WITH clause distribution over union queries deviates from sql standard
Description
Components
Fix versions
Environment
Link to Log File, atop/blg, CBCollectInfo, Core dump
Release Notes Description
is a backport of
relates
Activity

CB robot October 7, 2022 at 8:56 AM
Build couchbase-server-7.2.0-5000 contains query commit 175b287 with commit message:
Fix scope of CTE to cover setop queries

Bingjie Miao June 13, 2022 at 8:57 PMEdited
Yes this is expected, we allow a "full select" under set operations (UNION, etc), this is above and beyond what SQL standard
allows, to be compatible with Postgres, as well as previous Couchbase server versions. A "full select" means a SELECT statement with optional WITH clause in front of it, as well as optional ORDER BY clause. Note that if a "full select" is used under set options (e.g. UNION) the "full select" must be enclosed in parentheses.
Yes an error is expected, since here we don't have any set operations, only a single SELECT, in which case we don't allow
nested WITH clause specification. Similar error is returned from Postgres and Oracle.
This is the same as #4 above.

Pierre Regazzoni June 13, 2022 at 6:37 PM
Ran on 7.1.1-3141 and see some diff. can you confirm behavior

CB robot June 1, 2022 at 9:30 PM
Build couchbase-server-7.1.1-3110 contains query commit 175b287 with commit message:
Fix scope of CTE to cover setop queries
Details
Details
Assignee

Reporter

Is this a Regression?
Triage
Priority
Instabug
PagerDuty
PagerDuty Incident
PagerDuty

Sentry
Linked Issues
Sentry
Zendesk Support
Linked Tickets
Zendesk Support

SQL and N1QL currently behave differently when dealing with with clauses before a series of unions. It seems to me that SQL accepts only 1 with clause per level, union queries are considered on the same level, and if these union queries contain with clauses the clause will overwrite previous with clauses. In N1QL, the with clause only attaches to the first select statement after the with clause and this only happens if the select is not in a subquery, i.e same level. Here are some examples to make this more clear:
n1ql:
1. WITH cte AS (SELECT 1 as a) SELECT cte.a from cte union all SELECT cte.a from cte --> error
2. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all (SELECT cte.a from cte) --> error
3. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all SELECT cte.a from cte --> error
4. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1) union all SELECT cte1.a from cte1 --> error
5. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1) --> error
6. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1) union all (SELECT cte1.a from cte1) --> error
7. WITH cte AS (SELECT 1 as a) SELECT cte.a from cte union all WITH cte AS (SELECT 1 as a) SELECT cte.a from cte --> works
8. (WITH cte AS (SELECT 1 as a) SELECT cte.a from cte) union all (WITH cte AS (SELECT 1 as a) SELECT cte.a from cte); --> works
9. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all (SELECT cte.a from cte) --> error
10. WITH cte AS (SELECT 1 as a) (with cte2 as (select 2 as a) SELECT cte.a from cte) union all (SELECT cte.a from cte) --> error
11. WITH cte1 AS (SELECT 1 as a) SELECT cte1.a from cte1 union all with cte2 as (SELECT 2 as a) SELECT cte2.a from cte2 --> works
12. WITH cte1 AS (SELECT 1 as a) SELECT cte1.a from cte1 union all (with cte2 as (SELECT 2 as a) SELECT cte2.a from cte2) --> works
sql:
1. WITH cte AS (SELECT 1 as a) SELECT cte.a from cte union all SELECT cte.a from cte; --> return two 1s
2. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all (SELECT cte.a from cte); --> returns two 1s
3. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all SELECT cte.a from cte; --> returns two 1s
4. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1) union all SELECT cte1.a from cte1; --> error
5. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1); --> return one 1
6. WITH cte1 AS (SELECT 1 as a) (with cte2 as (SELECT 2 as b) SELECT cte1.a from cte1) union all (SELECT cte1.a from cte1); --> error
7. WITH cte AS (SELECT 1 as a) SELECT cte.a from cte union all WITH cte AS (SELECT 1 as a) SELECT cte.a from cte --> error
8. (WITH cte AS (SELECT 1 as a) SELECT cte.a from cte) union all (WITH cte AS (SELECT 1 as a) SELECT cte.a from cte); --> works
9. WITH cte AS (SELECT 1 as a) (SELECT cte.a from cte) union all (SELECT cte.a from cte); --> works
10. WITH cte AS (SELECT 1 as a) (with cte2 as (select 2 as a) SELECT cte.a from cte) union all (SELECT cte.a from cte); --> error
11. WITH cte1 AS (SELECT 1 as a) SELECT cte1.a from cte1 union all with cte2 as (SELECT 2 as a) SELECT cte2.a from cte2 --> error
12. WITH cte1 AS (SELECT 1 as a) SELECT cte1.a from cte1 union all (with cte2 as (SELECT 2 as a) SELECT cte2.a from cte2) --> works