Column metadata probing appends a LIMIT instead of wrapping — on set operations the probe pays for the full result set #44389
gpiccione-ship-it
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Corpo (da incollare in Ideas)
Context
Follow-up to #44241 / #44242 (merged), which fixed the double execution of the column metadata probe
and deliberately left this part out as needing its own discussion. This is that discussion.
The observation
get_columns_descriptionbuilds the probe statement withDatabase.apply_limit_to_sql, which appliesthe limit using the engine spec's
limit_method. For engines withFORCE_LIMIT— the default —the limit is appended at the tail of the user's statement.
For a probe, the only thing needed from the statement is
cursor.description. Zero rows are wanted.But an appended
LIMIT 0is not free on set operations: on aUNION, the union has to bematerialised before the outer limit can discard it, so
LIMIT 0costs whatLIMIT 1costs, which iswhat the whole query costs.
Measured
Tenant database MariaDB 10.6.22, dataset whose SQL is a
UNIONover ~7.19M rows:… UNION … LIMIT 0(appended — what the code builds today)SELECT * FROM (…) t LIMIT 0(wrapped)SELECT * FROM (…) AS probe WHERE 1=0(wrapped, no rows to discard)The column metadata returned is identical in the three cases. Observed on Superset 6.1.0; the
construct is unchanged on
masterafter #44242.Why this may be cheaper to fix than it looks
The wrapping shape is not new machinery:
LimitMethod.WRAP_SQLalready buildsSELECT * FROM (<statement>) LIMIT n, and Db2, SAP HANA and Kusto already use it. What the probe doestoday is simply inherit the engine's user-query limit method, which is chosen for a different
purpose.
The engine spec README notes that
WRAP_SQL"might be inefficient, since the database optimizer mightnot be able to push the limit to the inner query". That objection is about user queries, where the
inner rows are the point. For a metadata probe there are no rows to push — which is exactly why the
probe is the one place where wrapping is the cheaper shape rather than the more expensive one.
What we are asking
Not a specific patch — a direction, since this changes the statement every engine receives:
engine's
limit_method?WRAP_SQLas it stands, or a probe-specific predicate that discardsrows in the inner query (
WHERE 1=0on most dialects), overridable onBaseEngineSpecwhere asubquery in
FROMneeds different handling?We work around it locally today with
SQL_QUERY_MUTATORon the hook added by #29885, so we have nourgency — but a workaround that rewrites SQL by pattern-matching a trailing
LIMIT 0is not somethingwe would recommend to anyone else, which is why we would rather see the shape decided upstream.
All reactions