Repository navigation
Conversation
…1207 PR 2b) GroupProperties gains column_lineage (one ColumnLineage per output column, derived once from the input groups' lineage). Summary coverage names a selection's column by lineage; a predicate on a Computed column is not lifted; the rollup group-key check compares lineage. Schema fields and the IR Project lose their table qualifier; the front ends bind qualified names with their own scope while lowering. The requalification patches (with_aggregate_qualifiers, node.rs) are deleted.
…PR 2b) Section 4.2.2: a selection's column is its lineage, the source column (source, column); renames keep it, a computed column (a rate value) is not lifted. Schema fields and ColumnIdentity listings lose the table qualifier.
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Schema fields carried a table qualifier (
Field::table), and every operator rule had to keep it right: aProjectreplaced it with its own qualifier, an aggregate copied it onto its keys, and two patches put it back where rules lost it (with_aggregate_qualifiersin Pass 1, the requalification inOperatorNode::with_new_children/validate_structure). Summary coverage named a selection's column by(table, name), so a rename made the same source column look like another column, andMemo::add_candidate's exact schema compare rejected a readout renamed to an aggregate's column because its qualifier differed.The concepts doc (
memo-groups-properties-requirements.md, section 3, "Column lineage") replaces the qualifier by a group property: each output column's lineage, the source column its values are copied from, orComputed. A front end binds a qualified name such asf.srcwith its own scope while it lowers, and the IR never needs the qualifier again.What
GroupProperties::column_lineage: Vec<ColumnLineage>, one entry per output column in schema order, derived once inderive_group_propertiesfrom the input groups' lineage by the doc's per-operator table.ColumnIdentity { source, column }is a source column. A conjunct on aComputedcolumn is not lifted and stays inunlifted_operators; neither is one on a column whose lineage another column of theSummaryAgg's input shares. The rollup's group-key check compares lineage.table; the IRProjectlosesqualifier.Field::with_tableandSchema::column_id_qualifiedare deleted;resolve_column_refresolves aColumnRef::Qualifiedby its unique name.frontend-common's resolver, used by SQL, PromQL and MetricsQL) bind qualified names with their own scope: each resolved operator carries the qualifier of each output column next to its node. The SQL front end sets the newUnresolvedOp::Scan::qualifierto the table or alias.with_aggregate_qualifiers(Pass 1) and the requalification innode.rsandtumbling_window_chain.rs.Memo::add_candidatestays strict (exact schema compare).asap-primitive-schema.md§4.2.2 (worked example G1–G10 and "Which column a condition is on") uses lineage:latency.job ∈ {api},t.region ∈ {us}. §2's table and the listings ofField,SchemaandColumnIdentityfollow.SourceColumn/Computed).Deleted
Field::table,Field::with_tabletypes/src/ir/schema/mod.rsGroupProperties::column_lineage; front-end scopeSchema::column_id_qualifiedScope::resolve(front end, private)NonASAPOp::Project::qualifiertypes/src/ir/operator/non_asap.rsUnresolvedOp::Project::qualifieronlyOperatorNode::with_new_childrenandvalidate_structuretypes/src/ir/operator/node.rsnamed_as_chaintypes/src/ir/operator/tumbling_window_chain.rswith_aggregate_qualifiersstage1-pass1-per-computation-candidates/src/per_computation_candidates.rsColumnIdentity::{table, name}types/src/ir/properties/summary_coverage.rsColumnIdentity::{source, column}build_input_schemabuild_input(the input group, for its schema and lineage)qualified_field_serde_roundtrip, the qualified case ofpartitions_over_matches_keys_by_column, the qualifier asserts ofrebuilding_preserves_only_explicit_naming_overridesandsql_full_query_retains_project_and_binds_inner_aggregatePending approval (not in the doc or #1207)
UnresolvedOp::Scan::qualifier: Option<String>(pub field)frontend-common/src/unresolved.rsScanqualifies its columns by, now that the schema has no qualifier.Nonefor PromQL / MetricsQL.ColumnIdentityfield namessource,column; test-supportColumnIdentity::new(source, column)(wasnew(table, name))types/src/ir/properties/summary_coverage.rsColumnIdentityis aSourceColumn": its fields areSourceColumn's.asap_types::ir::properties::column_lineagetypes/src/ir/properties/column_lineage.rsColumnLineageand its derivation live, next tosummary_coverage.pub(crate) Operator::<I>::output_column_lineage(&self, output, lineage_of),pub(crate) MemoGroupAsInput::column_lineage(); privateScope,Bound(front end),column_lineage(coverage)ResolveError::Ambiguousmessage "matches several columns" (was "several unqualified columns")types/src/ir/scalar/column_resolution.rsHow
output_column_lineagematches the operator: aScangivesSourceColumn { source: table or metric, column: field name };Filter,Sort,Limit,Dedup,TimeRange,TimeShiftand the other row-selecting PromQL operators pass their input's lineage; aProjectcolumn item keeps its input column's lineage, an expression item isComputed; anAggregateand aSummaryAggfollowaggregate_output_schema's layout (abykey keeps its lineage, awithoutreduction the kept labels, a per-series reduction every column but the value; measures and the state areComputed); a readout (SummaryEstimate,FinalizeExactAccumulator) keeps its state's plain columns in place; aJoinconcatenates;SetOp,Concatand aSummaryMergekeep a column's lineage only where their inputs agree; aBinaryOpkeeps the left side's labels (and the right side's foror/group_rightonly where both agree). Anything else, or a rule whose result has not one entry per output column, givesComputedfor every column.of_summary_aggreads the lineage of theSummaryAgg's input (a group's stored lineage, or a temporary build's, derived from its input group's).residuallifts a conjunct only when its column's lineage is aSourceColumnno other column shares, and keys the selection by it.regroupedmaps each rollup key to the build input's column of equal lineage.resolvereturns a privateBound { node, qualifiers }. Qualified references resolve in aScope { schema, qualifiers }: a column with that qualifier and name first, else the unique name. Qualifiers follow the old field rules: aScanits own, a pass-through operator its input's, aProjectits alias or none, an aggregate'sbykeys their input's, aJoinleft then right, aSetOpthe left side's; PromQL operators have none.Major algorithm changes
Summary coverage: which conjuncts are lifted.
(table, name).SELECT source … FROM (SELECT f.src AS source FROM flows f)named the column(none, source), and one grouped byf.srcnamed it(f, src): the same column looked like two. A conjunct on a computed column (a rate value) was lifted although no source column names it.residual). 2. Lift one when it is a value set or interval on one column whose(table, name)is unique in theSummaryAgg's input. 3. Key the selection by(table, name).SourceColumnunique among the input's columns. 3. Key the selection by that source column.regions). AComputedcolumn has no source to compare by, so it stays in the definition, which merges only equal definitions. Tests:types/tests/column_lineage.rsandmemo_summary_coverage.rs.O(columns)per group (a lineage vector is copied from each input group).residualcounts namesakes as before,O(columns)per conjunct.rate(m[5m]) > 0no longer liftsvalue ∈ (0, ∞):valueis computed byrate. A top-k readout's and a population evaluation's columns areComputed(conservative).Key code interfaces
crates/types/src/ir/properties/column_lineage.rscrates/types/src/stages/memo.rscrates/types/src/ir/properties/summary_coverage.rscrates/types/src/ir/schema/mod.rscrates/types/src/ir/operator/non_asap.rscrates/frontend-common/src/unresolved.rsUsage
A SQL query, lowered and put in the memo; then Pass 1 (PR 3) adds an HLL readout as a candidate of the aggregate's group. Pass 1 is
todo!()until PR 3, soan_hll_readout_renamed_to_the_aggregate_column_is_its_candidate(types/tests/column_lineage.rs, passing at this head) builds the groups Stage 0 and Pass 1 would add (helpers:scan,filter,equals,hllin that file):Before this PR
Field::tableright; coverage named columns by(table, name), so a rename broke column identity, and a readout renamed to an aggregate's column failedadd_candidate's exact schema compare (PR 3 had a stopgap that compared columns ignoring the qualifier, removed again for this PR).After this PR
column_lineage; coverage, its merge check and its rollup key check read it. Renamed columns keep their identity; the tworegions of a join stay apart; a computed column is never named in a selection.add_candidate.Wire format
The exported
SchemaJSON no longer has a"table"key on fields, andColumnIdentityserializes as{source, column}(only in a materialized view's coverage, which istodo!()until PR 6). Older plan files still deserialize (a field's unknown keys are ignored). No consumer reads a qualifier (the executor matches columns by name), so nothing is derived at the export boundary; PR 6 owns the wire version.Verification
types/tests/column_lineage.rs), one per doc example:an_hll_readout_renamed_to_the_aggregate_column_is_its_candidate(example 1): lineage of G1–G5 andadd_candidateaccepting the renamed readout.a_rename_keeps_lineage_and_the_rollup_key_check_compares_it(example 2): the renamingProjectkeepsflows.src; a selection on the renamedareaisflows.region; the rollup key check passes.join_columns_of_one_name_have_distinct_lineage(example 3):sales.regionvsusers.regionin lineage and in selections.scan_predicates_lift_and_their_states_merge(example 4, inmemo_summary_coverage.rs, now also assertinglatency.job):latency.job ∈ {api}and{web}merge.a_predicate_on_a_computed_column_stays_unlifted.a_qualified_group_key_binds_its_join_side(frontend-sql):GROUP BY h.serviceovermetrics m JOIN hosts hbinds the right side'sservice.cargo fmt --all --check,cargo clippy --workspace --all-features --tests --examples -- -D warnings: clean.Test status
cargo test -p <crate> --all-features --no-fail-fastper crate at b5daf86 (the code commit; the head adds one assertion to a new test and the doc). The +4 passed are the new tests (types +3 net: four new,qualified_field_serde_roundtripdeleted; frontend-sql +1). Every failure is atodo!()of a later PR, with the same causes and counts as PR 2. PR 2 (#1230, e710906) for comparison: 1136 passed / 229 failed / 9 ignored.asap_plannersubprocess that stops at atodo!(): 2; label a group by its expression (PR 9): 1