Repository navigation
Build a match candidate from an index defined on an unnested record type - #4641
Conversation
c781f27 to
f3be10b
Compare
9a0ac5f to
6db6c25
Compare
f3be10b to
10fe834
Compare
6db6c25 to
758a824
Compare
647841e to
a130c4c
Compare
An index defined on an `UnnestedRecordType` had no match candidate, so the Cascades planner could never use it. Build one from the graph #4626 expands the type into, and teach the relational layer to declare such a type from SQL: a synthetic table whose constituents come from unnesting a repeated field, the metadata to serialize it, and the DDL to define an index over it. The query side is a plain explode of the same repeated field, and it matches the candidate's explode with ordinality through #4619, whose mapping reaches the element because the candidate flows it as a column of a record constructor. `unnested-record-type-indexes.yamsql` covers the whole path end to end, from declaring the type in a schema template to a covering scan of the index. Squashed from the pre-split branch and re-rooted onto #4619, so that #4626 -> #4651 -> #4619 -> #4641 is linear.
758a824 to
9e55929
Compare
a130c4c to
e35f8bf
Compare
An index defined on an `UnnestedRecordType` had no match candidate, so the Cascades planner could never use it. Build one from the graph #4626 expands the type into, and teach the relational layer to declare such a type from SQL: a synthetic table whose constituents come from unnesting a repeated field, the metadata to serialize it, and the DDL to define an index over it. The query side is a plain explode of the same repeated field, and it matches the candidate's explode with ordinality through #4619, whose mapping reaches the element because the candidate flows it as a column of a record constructor. `unnested-record-type-indexes.yamsql` covers the whole path end to end, from declaring the type in a schema template to a covering scan of the index. Squashed from the pre-split branch and re-rooted onto #4619, so that #4626 -> #4651 -> #4619 -> #4641 is linear.
7a12faa to
8f381f5
Compare
e35f8bf to
5e8fdae
Compare
d4f7b19 to
7edbf34
Compare
7edbf34 to
a1ea13f
Compare
5e8fdae to
7c96f4f
Compare
| # no grouping | ||
| # TODO Issue #4720: every column read is in the index, so this should be a covering scan. | ||
| - query: SELECT COUNT(*) AS "n" | ||
| FROM "restaurant", (SELECT "reviewer", "rating" FROM "restaurant"."reviews") AS SQ | ||
| - explain: "ISCAN(mv_split <,>) | MAP (_.parent AS _0, _.unnesting_0 AS _1) | AGG (count_star(*) AS _0) GROUP BY () | ON EMPTY NULL | MAP (coalesce_long(_._0._0, 0l) AS n)" | ||
| - result: [{n: 5}] |
2e57b8a to
60b348d
Compare
eec4cb0 to
4928482
Compare
1dd2445 to
d1fa5eb
Compare
4928482 to
2f6ce9c
Compare
d1fa5eb to
018c42f
Compare
2f6ce9c to
da3fca9
Compare
94e50c8 to
1685a5f
Compare
da3fca9 to
b483614
Compare
1685a5f to
e5b7282
Compare
e5b7282 to
a3f777d
Compare
b483614 to
23fc442
Compare
a3f777d to
b0a515c
Compare
23fc442 to
37d978f
Compare
Six queries covering an inequality on the leading key column, both sides of the same column bounding one range, and an equality prefix of one and two columns followed by a range on the column after it. Each is served by a covering scan of the unnesting index with no residual filter, which is what the bounds being pushed into the scan range looks like.
… function explode Explains now read `TF explode(..)` and navigate `q1._0`, and a query that cannot be planned reports UNABLE_TO_PLAN, as it does on main.
…cannot be planned after the explode carries its ordinal
37d978f to
d3d906f
Compare
📊 Metrics Diff Analysis ReportSummary
ℹ️ About this analysisThis automated analysis compares query planner metrics between the base branch and this PR. It categorizes changes into:
The last category in particular may indicate planner regressions that should be investigated. New QueriesCount of new queries by file:
Plan and Metrics ChangedThese queries experienced both plan and metrics changes. This generally indicates that there was some planner change Total: 11 queries Statistical Summary (Plan and Metrics Changed)
There were no queries with significant regressions detected. Minor Changes (Plan and Metrics Changed)In addition, there were 11 queries with minor changes. Only Metrics ChangedThese queries experienced only metrics changes without any plan changes. If these metrics have substantially changed, Total: 4 queries Statistical Summary (Only Metrics Changed)
There were no queries with significant regressions detected. Minor Changes (Only Metrics Changed)In addition, there were 4 queries with minor changes. |
Makes an index defined on an
UnnestedRecordTypeusable by the Cascades planner. Builds on #4840, which offers such indexes to the planner, and on #4816, whose compensation change lets a match that stands in for a join compensate only its result.Changes
ValueIndexExpansionVisitorbuilds the candidate overSyntheticRecordType.expandinstead of a scan of stored types, and re-expresses the key over the record that expansion assembles, so that e.g.base.unnesting_0.reviewerbecomesexplode._0.reviewer, the value a query over the same unnesting flows.ListKeyExpression: the primary key of a synthetic type is now expanded inKeyExpressionExpansionVisitorandScalarTranslationVisitor(each position is a record of its child's columns), and covering scans read the columns nested inside it, such as__positions.FetchIndexRecords.SYNTHETIC_CONSTITUENTS; mixing stored and synthetic types in one candidate is rejected.AbstractDataAccessRule: matches are grouped by the set of for-each quantifiers they compensate rather than requiring exactly one, so a match covering the whole join can become a data access.Placeholder.translateLeafPredicatepasses on whether to simplify the translated value.Tests
unnested-record-type-indexes.yamsql: covering plans and rows over indexes on unnested types, including names that are not protobuf compliant, arrays under nested structs, structs inside array elements, the primary key in the index key, PartiQL and subquery spellings, equality and range predicates, and the choice between an index on the stored table and one on the synthetic type.SyntheticRecordTypeExpansionTest,IndexEntryTranslatorEquivalenceTest, andTransactionBoundQueryTest, which now expects the unnested index to be used.Known issues
mv_deep_element(GROUP BY "a"."name"."last") is commented out: it cannot be planned since the explode carries its ordinal. It was also not covering before (Aggregates over an index on an unnested record type never use a covering scan #4720).