SQLite Forum

inner join bug with compound queries
Login
Thanks for the report and for simplifying the test case.  Here is an even
simpler test case:

> ~~~
WITH
  t1(x) AS (SELECT 111),
  t2(y) AS (SELECT 222),
  t3(z) AS (SELECT * FROM t2 WHERE false UNION ALL SELECT * FROM t2)
SELECT * FROM t1, t3;
~~~

I think I understand the malfunction.  I should have a fix later today.
If you are just curious about what is going wrong, you can keep reading.

Processing begins by expanding the various subqueries defined by
the WITH clause.  We have:

> ~~~
SELECT *
  FROM (SELECT 111) AS t1,
       (SELECT * FROM (SELECT 222) WHERE false UNION ALL SELECT * FROM (SELECT 222)) AS t2;
~~~

The query optimizer then brings the UNION ALL operator to the top-level
as follows:

> ~~~
SELECT *
  FROM (SELECT 111) AS t1, (SELECT * FROM (SELECT 222)) AS t2 WHERE false
UNION ALL
SELECT *
  FROM (SELECT 111) AS t1, (SELECT * FROM (SELECT 222)) AS t2;
~~~

Next the [push down optimization][1] is applied to the
left-hand side of the UNION ALL operator to give us:

[1]: https://sqlite.org/optoverview.html#the_push_down_optimization

> ~~~
SELECT *
  FROM (SELECT 111 WHERE false) AS t1, (SELECT * FROM (SELECT 222) WHERE false) AS t2 WHERE false
UNION ALL
SELECT *
  FROM (SELECT 111) AS t1, (SELECT * FROM (SELECT 222)) AS t2;
~~~

So far, everything is copacetic.  The optimized query is still equivalent
to the original.  But here comes the problem:  While SQLite is
generated the byte-code for the left-hand side of the UNION ALL, it sees the
"T1" subquery and it notices that T1 will be reused when evaluating the
right-hand side of UNION ALL so it saves the result of the
first T1 and tries to reuse it for the second T1.  SQLite fails to notice
that the first T1 was modified by the push-down optimization and is hence
no longer equivalent to the second T1.  Thus the query that ends up being
evaluated is:

>
<pre>
SELECT *
  FROM (SELECT 111 WHERE false) AS t1, (SELECT * FROM (SELECT 222) WHERE false) AS t2 WHERE false
UNION ALL
SELECT *
  FROM (SELECT 111 <font color="red">WHERE false</font>) AS t1, (SELECT * FROM (SELECT 222)) AS t2;
</pre>

The extra "WHERE false" (shown in red)
that gets inserted into the second T1 is the
cause of the incorrect result.