QUAL Pushdown causes ERROR on syntactically and semantically correct SQL Query

From: Hans Buschmann <buschmann(at)nidsa(dot)net>
To: "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>
Subject: QUAL Pushdown causes ERROR on syntactically and semantically correct SQL Query
Date: 2023-06-05 14:40:35
Message-ID: 97e79a26f4544c2ea1039041dc7732ab@nidsa.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

I have reworked the case of BUG #17842 to include the data and the questions for further investigation.

qualstest_data contais the data export with --insert (to test it on other DB systems)

qualstest_query contains the failing query and a short introduction to the data.

The problem is NOT to correct the query to a working case, but to show a fundamental problem with qual pushdown.

On pg16b1 (same on 15.3) the explain of the second query produces:

qualstest=#
qualstest=#
qualstest=# explain -- select * from ( -- select count(*) from ( -- select length(sel) from (
qualstest-# select * from (
qualstest(# select
qualstest(# onum
qualstest(# ,vname
qualstest(# ,vlen
qualstest(# ,nlen
qualstest(# ,olen
qualstest(# ,NULLIF(vlen-olen,0) as delta_len
qualstest(# from (
qualstest(# select *
qualstest(# ,('0'||split_part(split_part(nline,'(',2),')',1))::smallint as nlen
qualstest(# ,('0'||split_part(split_part(oline,'(',2),')',1))::smallint as olen
qualstest(# from newcol
qualstest(# join oldcol on onum=nnum
qualstest(# join (
qualstest(# select
qualstest(# vnum
qualstest(# ,split_part(vline,' ',1) as vname
qualstest(# ,('0'||split_part(split_part(vline,'(',2),')',1))::smallint as vlen
qualstest(# from varcol
qualstest(# ) qv on nline like '%'||vname||'%'
qualstest(# where nline not like '%KEY%'
qualstest(# ) qj
qualstest(# --limit 30
qualstest(# where vlen!=olen
qualstest(# ) qcomp
qualstest-# where
qualstest-# nlen > 0
qualstest-# ;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=90.37..10257.60 rows=2188 width=44)
Hash Cond: (newcol.nnum = oldcol.onum)
Join Filter: ((('0'::text || split_part(split_part(varcol.vline, '('::text, 2), ')'::text, 1)))::smallint <> (('0'::text || split_part(split_part(oldcol.oline, '('::text, 2), ')'::text, 1)))::smallint)
-> Nested Loop (cost=0.00..10008.26 rows=2199 width=73)
Join Filter: (newcol.nline ~~ (('%'::text || split_part(varcol.vline, ' '::text, 1)) || '%'::text))
-> Seq Scan on newcol (cost=0.00..98.23 rows=738 width=36)
Filter: ((nline !~~ '%KEY%'::text) AND ((('0'::text || split_part(split_part(nline, '('::text, 2), ')'::text, 1)))::smallint > 0))
-> Materialize (cost=0.00..14.94 rows=596 width=37)
-> Seq Scan on varcol (cost=0.00..11.96 rows=596 width=37)
-> Hash (cost=60.72..60.72 rows=2372 width=44)
-> Seq Scan on oldcol (cost=0.00..60.72 rows=2372 width=44)
(11 Zeilen)

qualstest=#
qualstest=# select version ();
version
---------------------------------------------------------------
PostgreSQL 16beta1, compiled by Visual C++ build 1934, 64-bit
(1 Zeile)

on execution:
FEHLER: ungültige Eingabesyntax für Typ smallint: »08,2«

ANALYSIS:

The join conditions matches all rows from oldcol and newcol, which then are filtered by inner join with only the varchar columns from varcol. Therefore the lines

,('0'||split_part(split_part(nline,'(',2),')',1))::smallint as nlen
,('0'||split_part(split_part(oline,'(',2),')',1))::smallint as olen

should be applied only to filtered results known to have smallint values between the parentesis in varchar definitions.

This is done correctly in the first (full) query without the final where clause.

When the where nlen > 0 comes into play, the plan is changed and the filter qual is applied to all lines.
There are other lines where the cast is not possible and the query fails with above error.

The fundamental problem is that quals should not be pushed down for tuples not in the result set, when the operation classes of these quals could error out.

Some operator classes have no runtime errors (like cast from smallint to int), but when such an error is possible, they should not be applied to tuples not part of the joined result set!

I stumbled over the error by adding this harmless where clause (where nlen > 0) to a just working query and got the error.

Other where-clauses (where nnum < 100) cause the same error.

Operator classes which could error out should not be applied for filtering columns from relations, which are not the outermost relation in joins and could be eliminated by another join.

These queries are syntactically and semantically correct but the postgre implementations causes them to error out.
This is very surprising for the SQL User!

The problem seems to exist also in certain backbranches.

Hans Buschmann

Attachment Content-Type Size
qualstest.zip application/x-zip-compressed 34.8 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Heikki Linnakangas 2023-06-05 14:51:57 Let's make PostgreSQL multi-threaded
Previous Message Evan Jones 2023-06-05 13:47:30 [PATCH] pg_regress.c: Fix "make check" on Mac OS X: Pass DYLD_LIBRARY_PATH