diff --git a/pkg/sql/opt/norm/project_funcs.go b/pkg/sql/opt/norm/project_funcs.go index 3a0e64337ed7..6c6bcc8685de 100644 --- a/pkg/sql/opt/norm/project_funcs.go +++ b/pkg/sql/opt/norm/project_funcs.go @@ -1008,3 +1008,23 @@ func (c *CustomFuncs) HasVolatileProjection(projections memo.ProjectionsExpr) bo } return false } + +// SimplifyCoalesceInProjections simplifies Coalesce expressions in projections +// using the given not-null columns. Projections whose element is unchanged are +// reused as-is to avoid unnecessary memo invalidation. +func (c *CustomFuncs) SimplifyCoalesceInProjections( + projections memo.ProjectionsExpr, notNullCols opt.ColSet, +) memo.ProjectionsExpr { + newProjections := make(memo.ProjectionsExpr, len(projections)) + for i := range projections { + p := &projections[i] + simplified := c.SimplifyCoalesceInScalar(p.Element, notNullCols) + if simplified == p.Element { + // No change; reuse the original ProjectionsItem. + newProjections[i] = *p + } else { + newProjections[i] = c.f.ConstructProjectionsItem(simplified, p.Col) + } + } + return newProjections +} diff --git a/pkg/sql/opt/norm/rules/project.opt b/pkg/sql/opt/norm/rules/project.opt index 012f32f11c9b..c013d55ee8cb 100644 --- a/pkg/sql/opt/norm/rules/project.opt +++ b/pkg/sql/opt/norm/rules/project.opt @@ -331,6 +331,27 @@ $input $passthrough ) +# SimplifyCoalesceProject simplifies Coalesce projections by eliminating leading +# operands that are guaranteed to be non-null according to the input's +# relational properties. +[SimplifyCoalesceProject, Normalize, LowPriority] +(Project + $input:* + $projections:[ + ... + (ProjectionsItem $scalar:*) & + (CanSimplifyCoalesceInScalar $scalar (NotNullCols $input)) + ... + ] + $passthrough:* +) +=> +(Project + $input + (SimplifyCoalesceInProjections $projections (NotNullCols $input)) + $passthrough +) + # FoldIsNullProject folds "x IS NULL" projections to false if "x" is not null in # the Project's input. It matches if there is at least one projection that can # be folded, and it replaces all projections that can be folded. diff --git a/pkg/sql/opt/norm/rules/select.opt b/pkg/sql/opt/norm/rules/select.opt index 544a8cbcf8b1..b13bdc93917c 100644 --- a/pkg/sql/opt/norm/rules/select.opt +++ b/pkg/sql/opt/norm/rules/select.opt @@ -31,6 +31,23 @@ => (Select $input (SimplifyFilters $filters)) +# SimplifyCoalesceSelect simplifies Coalesce expressions in Select filters by +# eliminating leading operands that are guaranteed to be non-null according to +# the input's relational properties and null-rejecting columns from sibling +# filter conditions. +[SimplifyCoalesceSelect, Normalize, LowPriority] +(Select + $input:* + $filters:[ + ... + (FiltersItem $cond:*) & + (CanSimplifyCoalesceInFilters $filters $cond (NotNullCols $input)) + ... + ] +) +=> +(Select $input (SimplifyCoalesceInFilters $filters (NotNullCols $input))) + # ConsolidateSelectFilters consolidates filters that constrain a single # variable. For example, filters x >= 5 and x <= 10 would be combined into a # single Range operation. diff --git a/pkg/sql/opt/norm/scalar_funcs.go b/pkg/sql/opt/norm/scalar_funcs.go index 10d9fd33a9bd..65c03f95dea4 100644 --- a/pkg/sql/opt/norm/scalar_funcs.go +++ b/pkg/sql/opt/norm/scalar_funcs.go @@ -74,25 +74,109 @@ func (c *CustomFuncs) ConstructSortedUniqueList( // SimplifyCoalesce discards any leading null operands, and then if the next // operand is a constant, replaces with that constant. func (c *CustomFuncs) SimplifyCoalesce(args memo.ScalarListExpr) opt.ScalarExpr { - for i := 0; i < len(args)-1; i++ { - item := args[i] + return c.simplifyCoalesce(args, opt.ColSet{}) +} + +func (c *CustomFuncs) simplifyCoalesce( + args memo.ScalarListExpr, notNullCols opt.ColSet, +) opt.ScalarExpr { + // Iterate over all args (including the last) so that ExprIsNeverNull can + // fire for any position: once a provably-non-null arg is found, COALESCE + // will always return that arg, so we can replace the whole expression. + for i := 0; i < len(args); i++ { + if memo.ExprIsNeverNull(args[i], notNullCols) { + return args[i] + } - // If item is not a constant value, then its value may turn out to be - // null, so no more folding. Return operands from then on. - if !c.IsConstValueOrGroupOfConstValues(item) { + if !c.IsConstValueOrGroupOfConstValues(args[i]) { + if i >= len(args)-1 { + return args[i] + } return c.f.ConstructCoalesce(args[i:]) } - if item.Op() != opt.NullOp { - return item + if args[i].Op() != opt.NullOp { + return args[i] } } - // All operands up to the last were null (or the last is the only operand), - // so return the last operand without the wrapping COALESCE function. return args[len(args)-1] } +// CanSimplifyCoalesce returns true if simplifyCoalesce would change the given +// Coalesce expression. It mirrors the logic of simplifyCoalesce directly to +// avoid the cost of constructing a new expression just to compare arg counts. +func (c *CustomFuncs) CanSimplifyCoalesce(args memo.ScalarListExpr, notNullCols opt.ColSet) bool { + for i, arg := range args { + if memo.ExprIsNeverNull(arg, notNullCols) { + return true + } + if !c.IsConstValueOrGroupOfConstValues(arg) { + // Non-constant arg at position i blocks further simplification. The + // expression changes only if leading nulls (i > 0) were already + // stripped. + return i > 0 + } + if arg.Op() != opt.NullOp { + // Non-null constant: simplifyCoalesce returns it directly. + return true + } + // NullOp constant: will be stripped; continue to next arg. + } + // The loop exhausted all args, meaning every arg was a null constant. + // simplifyCoalesce returns args[last] directly (unwrapping the COALESCE), + // which is a simplification whenever there is more than one arg. The + // single-arg case is already handled by EliminateCoalesce before this + // function is called, so we can safely return true here. + return true +} + +// SimplifyCoalesceInScalar recursively simplifies Coalesce expressions in the +// given scalar expression tree using the provided not-null columns. It handles +// nested Coalesce expressions by recursing into the simplified result. +func (c *CustomFuncs) SimplifyCoalesceInScalar( + e opt.ScalarExpr, notNullCols opt.ColSet, +) opt.ScalarExpr { + var replace ReplaceFunc + replace = func(e opt.Expr) opt.Expr { + if co, ok := e.(*memo.CoalesceExpr); ok { + simplified := c.simplifyCoalesce(co.Args, notNullCols) + // Recurse into the simplified result so that nested Coalesce + // expressions (e.g. COALESCE(a, COALESCE(b, c))) are also handled + // when the outer simplification does not fire. + return c.f.Replace(simplified, replace) + } + return c.f.Replace(e, replace) + } + return replace(e).(opt.ScalarExpr) +} + +// CanSimplifyCoalesceInScalar returns true if the scalar expression tree +// contains any Coalesce expression that can be simplified using notNullCols. It +// recurses into Coalesce args so that nested Coalesce expressions are not +// missed when the outer Coalesce is not itself simplifiable. +func (c *CustomFuncs) CanSimplifyCoalesceInScalar(e opt.ScalarExpr, notNullCols opt.ColSet) bool { + if co, ok := e.(*memo.CoalesceExpr); ok { + if c.CanSimplifyCoalesce(co.Args, notNullCols) { + return true + } + for _, arg := range co.Args { + if c.CanSimplifyCoalesceInScalar(arg, notNullCols) { + return true + } + } + return false + } + for i, n := 0, e.ChildCount(); i < n; i++ { + if sc, ok := e.Child(i).(opt.ScalarExpr); ok { + if c.CanSimplifyCoalesceInScalar(sc, notNullCols) { + return true + } + } + } + return false +} + // IsConstValueEqual returns whether const1 and const2 are equal. func (c *CustomFuncs) IsConstValueEqual(const1, const2 opt.ScalarExpr) bool { op1 := const1.Op() diff --git a/pkg/sql/opt/norm/select_funcs.go b/pkg/sql/opt/norm/select_funcs.go index c9b5ab8c7d90..b0a6255b6679 100644 --- a/pkg/sql/opt/norm/select_funcs.go +++ b/pkg/sql/opt/norm/select_funcs.go @@ -419,3 +419,71 @@ func (c *CustomFuncs) addConjuncts( func (c *CustomFuncs) ForDuplicateRemoval(private *memo.OrdinalityPrivate) (ok bool) { return private.ForDuplicateRemoval } + +// computeFilterNotNullCols returns per-filter NOT NULL column sets and their +// total union for the given filters. It extracts null-rejecting columns from +// each filter item's constraints. Used by both CanSimplifyCoalesceInFilters and +// SimplifyCoalesceInFilters to avoid duplicating constraint extraction. +func computeFilterNotNullCols( + c *CustomFuncs, filters memo.FiltersExpr, +) (perFilter []opt.ColSet, total opt.ColSet) { + perFilter = make([]opt.ColSet, len(filters)) + for i := range filters { + constraints := filters[i].ScalarProps().Constraints + if constraints != nil { + constraints.ExtractNotNullCols(c.f.ctx, c.f.evalCtx, &perFilter[i]) + } + } + for _, fc := range perFilter { + total = total.Union(fc) + } + return +} + +// CanSimplifyCoalesceInFilters returns true if any filter condition contains a +// Coalesce expression that can be simplified using the input's NOT NULL columns +// plus null-rejecting columns from other filter conditions in the same set. +// The _ opt.ScalarExpr parameter is unused; it is required by the optgen rule +// which binds $cond but this function works with the full $filters set. +func (c *CustomFuncs) CanSimplifyCoalesceInFilters( + filters memo.FiltersExpr, _ opt.ScalarExpr, notNullCols opt.ColSet, +) bool { + perFilter, total := computeFilterNotNullCols(c, filters) + for i := range filters { + diff := total.Difference(perFilter[i]) + enriched := notNullCols + if !diff.Empty() { + enriched = notNullCols.Union(diff) + } + if c.CanSimplifyCoalesceInScalar(filters[i].Condition, enriched) { + return true + } + } + return false +} + +// SimplifyCoalesceInFilters simplifies Coalesce expressions in filter +// conditions using the given not-null columns, including null-rejecting columns +// derived from other filter conditions. Filters whose condition is unchanged +// are reused as-is to avoid unnecessary memo invalidation. +func (c *CustomFuncs) SimplifyCoalesceInFilters( + filters memo.FiltersExpr, notNullCols opt.ColSet, +) memo.FiltersExpr { + perFilter, total := computeFilterNotNullCols(c, filters) + newFilters := make(memo.FiltersExpr, len(filters)) + for i := range filters { + f := &filters[i] + diff := total.Difference(perFilter[i]) + enriched := notNullCols + if !diff.Empty() { + enriched = notNullCols.Union(diff) + } + simplified := c.SimplifyCoalesceInScalar(f.Condition, enriched) + if simplified == f.Condition { + newFilters[i] = *f + } else { + newFilters[i] = c.f.ConstructFiltersItem(simplified) + } + } + return newFilters +} diff --git a/pkg/sql/opt/norm/testdata/rules/decorrelate b/pkg/sql/opt/norm/testdata/rules/decorrelate index 821c33265641..abb2b5198258 100644 --- a/pkg/sql/opt/norm/testdata/rules/decorrelate +++ b/pkg/sql/opt/norm/testdata/rules/decorrelate @@ -3006,7 +3006,7 @@ group-by (hash) └── y:2 # Right input of SemiJoin is Project. -norm expect=TryDecorrelateSemiJoin +norm expect=TryDecorrelateSemiJoin disable=SimplifyCoalesceProject SELECT k FROM a WHERE EXISTS ( diff --git a/pkg/sql/opt/norm/testdata/rules/scalar b/pkg/sql/opt/norm/testdata/rules/scalar index a6bea921cd82..aaadb9115432 100644 --- a/pkg/sql/opt/norm/testdata/rules/scalar +++ b/pkg/sql/opt/norm/testdata/rules/scalar @@ -2,6 +2,10 @@ exec-ddl CREATE TABLE a (k INT PRIMARY KEY, i INT, f FLOAT, s STRING, arr int[]) ---- +exec-ddl +CREATE TABLE ij (i INT NOT NULL, j INT) +---- + exec-ddl CREATE TABLE xy (x INT PRIMARY KEY, y INT) ---- @@ -172,7 +176,9 @@ project └── projections └── COALESCE(s:4, s:4 || 'foo') [as=coalesce:8, outer=(4), immutable] -# Trailing null can't be removed. +# Trailing null can't be removed when leading arg is a non-constant (nullable +# variable): the original SimplifyCoalesce rule only fires when the leading arg +# is a constant, so no folding happens here. norm SELECT COALESCE(i, NULL, NULL) FROM a ---- @@ -183,6 +189,44 @@ project └── projections └── COALESCE(i:2, CAST(NULL AS INT8), CAST(NULL AS INT8)) [as=coalesce:8, outer=(2)] +# All-null COALESCE collapses to a single null via SimplifyCoalesce (the scalar +# rule fires when the leading arg is constant). CanSimplifyCoalesce correctly +# returns true when the loop exhausts all-null args, matching simplifyCoalesce's +# behaviour of returning args[last] (unwrapping the COALESCE). +norm expect=SimplifyCoalesce +SELECT COALESCE(NULL::INT, NULL::INT) FROM a +---- +project + ├── columns: coalesce:8 + ├── fd: ()-->(8) + ├── scan a + └── projections + └── CAST(NULL AS INT8) [as=coalesce:8] + +# -------------------------------------------------- +# SimplifyCoalesceProject +# -------------------------------------------------- + +norm expect=SimplifyCoalesceProject +SELECT COALESCE(i, j) FROM ij +---- +project + ├── columns: coalesce:6!null + ├── scan ij + │ └── columns: i:1!null + └── projections + └── i:1 [as=coalesce:6, outer=(1)] + +norm expect=SimplifyCoalesceProject +SELECT COALESCE(i, NULL, NULL) FROM ij +---- +project + ├── columns: coalesce:6!null + ├── scan ij + │ └── columns: i:1!null + └── projections + └── i:1 [as=coalesce:6, outer=(1)] + norm expect=SimplifyCoalesce SELECT COALESCE((1, 2, 3), (2, 3, 4)) FROM a ---- @@ -193,6 +237,69 @@ project └── projections └── (1, 2, 3) [as=coalesce:8] +# Negative test: should not fire when no arg is provably non-null. +norm expect-not=SimplifyCoalesceProject +SELECT COALESCE(i, f) FROM a +---- +project + ├── columns: coalesce:8 + ├── immutable + ├── scan a + │ └── columns: i:2 f:3 + └── projections + └── COALESCE(i:2::FLOAT8, f:3) [as=coalesce:8, outer=(2,3), immutable] + +# Middle argument is provably non-null: leading NULLs are stripped, then the +# NOT NULL arg is returned directly. +norm expect=SimplifyCoalesceProject +SELECT COALESCE(NULL::INT, i, j) FROM ij +---- +project + ├── columns: coalesce:6!null + ├── scan ij + │ └── columns: i:1!null + └── projections + └── i:1 [as=coalesce:6, outer=(1)] + +# Nested COALESCE: outer is not directly simplifiable, but the inner +# COALESCE(i, j) can be simplified to i (since i is NOT NULL). The fix +# ensures the inner Coalesce is not missed. +norm expect=SimplifyCoalesceProject +SELECT COALESCE(j, COALESCE(i, j)) FROM ij +---- +project + ├── columns: coalesce:6 + ├── scan ij + │ └── columns: i:1!null j:2 + └── projections + └── COALESCE(j:2, i:1) [as=coalesce:6, outer=(1,2)] + +# COALESCE inside an IF expression: the inner nested Coalesce is simplified +# via SimplifyCoalesceProject even though the outer Coalesce is not itself +# directly simplifiable. +norm expect=SimplifyCoalesceProject +SELECT IF(j > 0, COALESCE(j, COALESCE(i, j)), j) FROM ij +---- +project + ├── columns: if:6 + ├── scan ij + │ └── columns: i:1!null j:2 + └── projections + └── CASE j:2 > 0 WHEN true THEN COALESCE(j:2, i:1) ELSE j:2 END [as=if:6, outer=(1,2)] + +# SimplifyCoalesceProject does not fire when the first arg is a non-constant +# nullable variable: CanSimplifyCoalesce exits early with false because i is +# not a constant, and no leading nulls were stripped (i > 0 = false). +norm expect-not=SimplifyCoalesceProject +SELECT COALESCE(i, NULL, NULL) FROM a +---- +project + ├── columns: coalesce:8 + ├── scan a + │ └── columns: i:2 + └── projections + └── COALESCE(i:2, CAST(NULL AS INT8), CAST(NULL AS INT8)) [as=coalesce:8, outer=(2)] + # -------------------------------------------------- # EliminateCast diff --git a/pkg/sql/opt/norm/testdata/rules/select b/pkg/sql/opt/norm/testdata/rules/select index 47821e278b2d..72c5d3b950ba 100644 --- a/pkg/sql/opt/norm/testdata/rules/select +++ b/pkg/sql/opt/norm/testdata/rules/select @@ -1945,6 +1945,104 @@ select └── a:2 IS NOT DISTINCT FROM d:5 [outer=(2,5)] +# -------------------------------------------------- +# SimplifyCoalesceSelect +# -------------------------------------------------- + +norm expect=SimplifyCoalesceSelect +SELECT * FROM d WHERE COALESCE(a, b) = 1 +---- +select + ├── columns: k:1!null a:2!null b:3 c:4 d:5 + ├── key: (1) + ├── fd: ()-->(2), (1)-->(3-5) + ├── scan d + │ ├── columns: k:1!null a:2!null b:3 c:4 d:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── a:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + +# Negative test: should not fire when no argument is provably non-null. +norm expect-not=SimplifyCoalesceSelect +SELECT * FROM a WHERE COALESCE(i, 0) > 0 +---- +select + ├── columns: k:1!null i:2 f:3 s:4 j:5 + ├── key: (1) + ├── fd: (1)-->(2-5) + ├── scan a + │ ├── columns: k:1!null i:2 f:3 s:4 j:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── COALESCE(i:2, 0) > 0 [outer=(2)] + +# Leading NULLs stripped, then NOT NULL arg is reached. +norm expect=SimplifyCoalesceSelect +SELECT * FROM d WHERE COALESCE(NULL::INT, a, b) = 1 +---- +select + ├── columns: k:1!null a:2!null b:3 c:4 d:5 + ├── key: (1) + ├── fd: ()-->(2), (1)-->(3-5) + ├── scan d + │ ├── columns: k:1!null a:2!null b:3 c:4 d:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── a:2 = 1 [outer=(2), constraints=(/2: [/1 - /1]; tight), fd=()-->(2)] + +# Nested COALESCE: outer is not directly simplifiable, but the inner +# COALESCE(a, c) can be simplified to a (since a is NOT NULL). +norm expect=SimplifyCoalesceSelect +SELECT * FROM d WHERE COALESCE(b, COALESCE(a, c)) = 1 +---- +select + ├── columns: k:1!null a:2!null b:3 c:4 d:5 + ├── key: (1) + ├── fd: (1)-->(2-5) + ├── scan d + │ ├── columns: k:1!null a:2!null b:3 c:4 d:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── COALESCE(b:3, a:2) = 1 [outer=(2,3)] + +# Filter-derived NOT NULL: the condition b > 0 null-rejects b, so +# COALESCE(b, c) simplifies to b even though b is nullable in the schema. +# The redundant b > 0 filter is elided by existing normalization (b = 1 implies b > 0). +norm expect=SimplifyCoalesceSelect +SELECT * FROM d WHERE b > 0 AND COALESCE(b, c) = 1 +---- +select + ├── columns: k:1!null a:2!null b:3!null c:4 d:5 + ├── key: (1) + ├── fd: ()-->(3), (1)-->(2,4,5) + ├── scan d + │ ├── columns: k:1!null a:2!null b:3 c:4 d:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── b:3 = 1 [outer=(3), constraints=(/3: [/1 - /1]; tight), fd=()-->(3)] + +# Nested COALESCE with filter-derived NOT NULL: c > 0 null-rejects c, so the +# outer COALESCE(c, COALESCE(b, c)) simplifies to c. The redundant c > 0 filter +# is elided by existing normalization (c = 1 implies c > 0). +norm expect=SimplifyCoalesceSelect +SELECT * FROM d WHERE c > 0 AND COALESCE(c, COALESCE(b, c)) = 1 +---- +select + ├── columns: k:1!null a:2!null b:3 c:4!null d:5 + ├── key: (1) + ├── fd: ()-->(4), (1)-->(2,3,5) + ├── scan d + │ ├── columns: k:1!null a:2!null b:3 c:4 d:5 + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + └── filters + └── c:4 = 1 [outer=(4), constraints=(/4: [/1 - /1]; tight), fd=()-->(4)] + # -------------------------------------------------- # EliminateCaseTrailingFalsyBranch # -------------------------------------------------- diff --git a/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50 b/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50 index 66874dbb4ebe..fb3271653bb5 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50 +++ b/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50 @@ -1792,39 +1792,39 @@ limit │ │ │ ├── key: (4) │ │ │ ├── fd: ()-->(107), (4)-->(101,103,104) │ │ │ ├── select - │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (4) │ │ │ │ ├── fd: (4)-->(101-104), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ ├── window partition=() ordering=+(102|106) - │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (4) │ │ │ │ │ ├── fd: (4)-->(101-104), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ ├── window partition=() ordering=+(101|105) - │ │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ ├── fd: (4)-->(101-103), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 ws_item_sk:4!null return_ratio:101 currency_ratio:102 + │ │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null ws_item_sk:4!null return_ratio:101 currency_ratio:102!null │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ ├── fd: (4)-->(101,102), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ ├── columns: return_ratio:101 currency_ratio:102 ws_item_sk:4!null + │ │ │ │ │ │ │ │ ├── columns: return_ratio:101 currency_ratio:102!null ws_item_sk:4!null │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ │ ├── fd: (4)-->(101,102) │ │ │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ │ │ ├── columns: ws_item_sk:4!null sum:94 sum:96 sum:98 sum:100 + │ │ │ │ │ │ │ │ │ ├── columns: ws_item_sk:4!null sum:94 sum:96!null sum:98!null sum:100!null │ │ │ │ │ │ │ │ │ ├── grouping columns: ws_item_sk:4!null │ │ │ │ │ │ │ │ │ ├── internal-ordering: +4 │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ │ │ ├── fd: (4)-->(94,96,98,100) │ │ │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ │ │ ├── columns: column93:93 column95:95 column97:97 column99:99 ws_item_sk:4!null + │ │ │ │ │ │ │ │ │ │ ├── columns: column93:93 column95:95!null column97:97!null column99:99!null ws_item_sk:4!null │ │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ │ ├── ordering: +4 │ │ │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -1865,9 +1865,9 @@ limit │ │ │ │ │ │ │ │ │ │ │ └── d_moy:71 = 11 [outer=(71), constraints=(/71: [/11 - /11]; tight), fd=()-->(71)] │ │ │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ │ │ ├── COALESCE(wr_return_quantity:51, 0) [as=column93:93, outer=(51)] - │ │ │ │ │ │ │ │ │ │ ├── COALESCE(ws_quantity:19, 0) [as=column95:95, outer=(19)] - │ │ │ │ │ │ │ │ │ │ ├── COALESCE(wr_return_amt:52::DECIMAL, 0) [as=column97:97, outer=(52), immutable] - │ │ │ │ │ │ │ │ │ │ └── COALESCE(ws_net_paid:30::DECIMAL, 0) [as=column99:99, outer=(30), immutable] + │ │ │ │ │ │ │ │ │ │ ├── ws_quantity:19 [as=column95:95, outer=(19)] + │ │ │ │ │ │ │ │ │ │ ├── wr_return_amt:52::DECIMAL [as=column97:97, outer=(52), immutable] + │ │ │ │ │ │ │ │ │ │ └── ws_net_paid:30::DECIMAL [as=column99:99, outer=(30), immutable] │ │ │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ │ │ ├── sum [as=sum:94, outer=(93)] │ │ │ │ │ │ │ │ │ │ └── column93:93 @@ -1904,39 +1904,39 @@ limit │ │ ├── key: (123) │ │ ├── fd: ()-->(217), (123)-->(211,213,214) │ │ ├── select - │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ ├── immutable │ │ │ ├── key: (123) │ │ │ ├── fd: (123)-->(211-214), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ ├── window partition=() ordering=+(212|216) - │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (123) │ │ │ │ ├── fd: (123)-->(211-214), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ ├── window partition=() ordering=+(211|215) - │ │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (123) │ │ │ │ │ ├── fd: (123)-->(211-213), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ │ ├── project - │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 cs_item_sk:123!null return_ratio:211 currency_ratio:212 + │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null cs_item_sk:123!null return_ratio:211 currency_ratio:212!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ ├── fd: (123)-->(211,212), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ ├── columns: return_ratio:211 currency_ratio:212 cs_item_sk:123!null + │ │ │ │ │ │ │ ├── columns: return_ratio:211 currency_ratio:212!null cs_item_sk:123!null │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ │ ├── fd: (123)-->(211,212) │ │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ │ ├── columns: cs_item_sk:123!null sum:204 sum:206 sum:208 sum:210 + │ │ │ │ │ │ │ │ ├── columns: cs_item_sk:123!null sum:204 sum:206!null sum:208!null sum:210!null │ │ │ │ │ │ │ │ ├── grouping columns: cs_item_sk:123!null │ │ │ │ │ │ │ │ ├── internal-ordering: +123 │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ │ │ ├── fd: (123)-->(204,206,208,210) │ │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ │ ├── columns: column203:203 column205:205 column207:207 column209:209 cs_item_sk:123!null + │ │ │ │ │ │ │ │ │ ├── columns: column203:203 column205:205!null column207:207!null column209:209!null cs_item_sk:123!null │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ ├── ordering: +123 │ │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -1977,9 +1977,9 @@ limit │ │ │ │ │ │ │ │ │ │ └── d_moy:181 = 11 [outer=(181), constraints=(/181: [/11 - /11]; tight), fd=()-->(181)] │ │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ │ ├── COALESCE(cr_return_quantity:161, 0) [as=column203:203, outer=(161)] - │ │ │ │ │ │ │ │ │ ├── COALESCE(cs_quantity:126, 0) [as=column205:205, outer=(126)] - │ │ │ │ │ │ │ │ │ ├── COALESCE(cr_return_amount:162::DECIMAL, 0) [as=column207:207, outer=(162), immutable] - │ │ │ │ │ │ │ │ │ └── COALESCE(cs_net_paid:137::DECIMAL, 0) [as=column209:209, outer=(137), immutable] + │ │ │ │ │ │ │ │ │ ├── cs_quantity:126 [as=column205:205, outer=(126)] + │ │ │ │ │ │ │ │ │ ├── cr_return_amount:162::DECIMAL [as=column207:207, outer=(162), immutable] + │ │ │ │ │ │ │ │ │ └── cs_net_paid:137::DECIMAL [as=column209:209, outer=(137), immutable] │ │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ │ ├── sum [as=sum:204, outer=(203)] │ │ │ │ │ │ │ │ │ └── column203:203 @@ -2016,39 +2016,39 @@ limit │ ├── key: (225) │ ├── fd: ()-->(314), (225)-->(308,310,311) │ ├── select - │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ ├── immutable │ │ ├── key: (225) │ │ ├── fd: (225)-->(308-311), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ ├── window partition=() ordering=+(309|313) - │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ │ ├── immutable │ │ │ ├── key: (225) │ │ │ ├── fd: (225)-->(308-311), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ ├── window partition=() ordering=+(308|312) - │ │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (225) │ │ │ │ ├── fd: (225)-->(308-310), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ │ ├── project - │ │ │ │ │ ├── columns: rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 ss_item_sk:225!null return_ratio:308 currency_ratio:309 + │ │ │ │ │ ├── columns: rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null ss_item_sk:225!null return_ratio:308 currency_ratio:309!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (225) │ │ │ │ │ ├── fd: (225)-->(308,309), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ │ │ ├── project - │ │ │ │ │ │ ├── columns: return_ratio:308 currency_ratio:309 ss_item_sk:225!null + │ │ │ │ │ │ ├── columns: return_ratio:308 currency_ratio:309!null ss_item_sk:225!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (225) │ │ │ │ │ │ ├── fd: (225)-->(308,309) │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ ├── columns: ss_item_sk:225!null sum:301 sum:303 sum:305 sum:307 + │ │ │ │ │ │ │ ├── columns: ss_item_sk:225!null sum:301 sum:303!null sum:305!null sum:307!null │ │ │ │ │ │ │ ├── grouping columns: ss_item_sk:225!null │ │ │ │ │ │ │ ├── internal-ordering: +225 │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (225) │ │ │ │ │ │ │ ├── fd: (225)-->(301,303,305,307) │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ ├── columns: column300:300 column302:302 column304:304 column306:306 ss_item_sk:225!null + │ │ │ │ │ │ │ │ ├── columns: column300:300 column302:302!null column304:304!null column306:306!null ss_item_sk:225!null │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── ordering: +225 │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -2089,9 +2089,9 @@ limit │ │ │ │ │ │ │ │ │ └── d_moy:278 = 11 [outer=(278), constraints=(/278: [/11 - /11]; tight), fd=()-->(278)] │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ ├── COALESCE(sr_return_quantity:258, 0) [as=column300:300, outer=(258)] - │ │ │ │ │ │ │ │ ├── COALESCE(ss_quantity:233, 0) [as=column302:302, outer=(233)] - │ │ │ │ │ │ │ │ ├── COALESCE(sr_return_amt:259::DECIMAL, 0) [as=column304:304, outer=(259), immutable] - │ │ │ │ │ │ │ │ └── COALESCE(ss_net_paid:243::DECIMAL, 0) [as=column306:306, outer=(243), immutable] + │ │ │ │ │ │ │ │ ├── ss_quantity:233 [as=column302:302, outer=(233)] + │ │ │ │ │ │ │ │ ├── sr_return_amt:259::DECIMAL [as=column304:304, outer=(259), immutable] + │ │ │ │ │ │ │ │ └── ss_net_paid:243::DECIMAL [as=column306:306, outer=(243), immutable] │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ ├── sum [as=sum:301, outer=(300)] │ │ │ │ │ │ │ │ └── column300:300 diff --git a/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50-no-stats b/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50-no-stats index 85a5da539fa9..6c312c8396a2 100644 --- a/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50-no-stats +++ b/pkg/sql/opt/xform/testdata/external/tpcds/q41-q50-no-stats @@ -1697,39 +1697,39 @@ limit │ │ │ ├── key: (4) │ │ │ ├── fd: ()-->(107), (4)-->(101,103,104) │ │ │ ├── select - │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (4) │ │ │ │ ├── fd: (4)-->(101-104), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ ├── window partition=() ordering=+(102|106) - │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank:104 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (4) │ │ │ │ │ ├── fd: (4)-->(101-104), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ ├── window partition=() ordering=+(101|105) - │ │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102 rank:103 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 + │ │ │ │ │ │ ├── columns: ws_item_sk:4!null return_ratio:101 currency_ratio:102!null rank:103 rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ ├── fd: (4)-->(101-103), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:105 rank_2_orderby_1_1:106 ws_item_sk:4!null return_ratio:101 currency_ratio:102 + │ │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:105 rank_2_orderby_1_1:106!null ws_item_sk:4!null return_ratio:101 currency_ratio:102!null │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ ├── fd: (4)-->(101,102), (101)==(105), (105)==(101), (102)==(106), (106)==(102) │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ ├── columns: return_ratio:101 currency_ratio:102 ws_item_sk:4!null + │ │ │ │ │ │ │ │ ├── columns: return_ratio:101 currency_ratio:102!null ws_item_sk:4!null │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ │ ├── fd: (4)-->(101,102) │ │ │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ │ │ ├── columns: ws_item_sk:4!null sum:94 sum:96 sum:98 sum:100 + │ │ │ │ │ │ │ │ │ ├── columns: ws_item_sk:4!null sum:94 sum:96!null sum:98!null sum:100!null │ │ │ │ │ │ │ │ │ ├── grouping columns: ws_item_sk:4!null │ │ │ │ │ │ │ │ │ ├── internal-ordering: +4 │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ ├── key: (4) │ │ │ │ │ │ │ │ │ ├── fd: (4)-->(94,96,98,100) │ │ │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ │ │ ├── columns: column93:93 column95:95 column97:97 column99:99 ws_item_sk:4!null + │ │ │ │ │ │ │ │ │ │ ├── columns: column93:93 column95:95!null column97:97!null column99:99!null ws_item_sk:4!null │ │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ │ ├── ordering: +4 │ │ │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -1770,9 +1770,9 @@ limit │ │ │ │ │ │ │ │ │ │ │ └── d_moy:71 = 11 [outer=(71), constraints=(/71: [/11 - /11]; tight), fd=()-->(71)] │ │ │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ │ │ ├── COALESCE(wr_return_quantity:51, 0) [as=column93:93, outer=(51)] - │ │ │ │ │ │ │ │ │ │ ├── COALESCE(ws_quantity:19, 0) [as=column95:95, outer=(19)] - │ │ │ │ │ │ │ │ │ │ ├── COALESCE(wr_return_amt:52::DECIMAL, 0) [as=column97:97, outer=(52), immutable] - │ │ │ │ │ │ │ │ │ │ └── COALESCE(ws_net_paid:30::DECIMAL, 0) [as=column99:99, outer=(30), immutable] + │ │ │ │ │ │ │ │ │ │ ├── ws_quantity:19 [as=column95:95, outer=(19)] + │ │ │ │ │ │ │ │ │ │ ├── wr_return_amt:52::DECIMAL [as=column97:97, outer=(52), immutable] + │ │ │ │ │ │ │ │ │ │ └── ws_net_paid:30::DECIMAL [as=column99:99, outer=(30), immutable] │ │ │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ │ │ ├── sum [as=sum:94, outer=(93)] │ │ │ │ │ │ │ │ │ │ └── column93:93 @@ -1809,39 +1809,39 @@ limit │ │ ├── key: (123) │ │ ├── fd: ()-->(217), (123)-->(211,213,214) │ │ ├── select - │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ ├── immutable │ │ │ ├── key: (123) │ │ │ ├── fd: (123)-->(211-214), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ ├── window partition=() ordering=+(212|216) - │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank:214 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (123) │ │ │ │ ├── fd: (123)-->(211-214), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ ├── window partition=() ordering=+(211|215) - │ │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212 rank:213 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 + │ │ │ │ │ ├── columns: cs_item_sk:123!null return_ratio:211 currency_ratio:212!null rank:213 rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (123) │ │ │ │ │ ├── fd: (123)-->(211-213), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ │ ├── project - │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:215 rank_2_orderby_1_1:216 cs_item_sk:123!null return_ratio:211 currency_ratio:212 + │ │ │ │ │ │ ├── columns: rank_1_orderby_1_1:215 rank_2_orderby_1_1:216!null cs_item_sk:123!null return_ratio:211 currency_ratio:212!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ ├── fd: (123)-->(211,212), (211)==(215), (215)==(211), (212)==(216), (216)==(212) │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ ├── columns: return_ratio:211 currency_ratio:212 cs_item_sk:123!null + │ │ │ │ │ │ │ ├── columns: return_ratio:211 currency_ratio:212!null cs_item_sk:123!null │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ │ ├── fd: (123)-->(211,212) │ │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ │ ├── columns: cs_item_sk:123!null sum:204 sum:206 sum:208 sum:210 + │ │ │ │ │ │ │ │ ├── columns: cs_item_sk:123!null sum:204 sum:206!null sum:208!null sum:210!null │ │ │ │ │ │ │ │ ├── grouping columns: cs_item_sk:123!null │ │ │ │ │ │ │ │ ├── internal-ordering: +123 │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── key: (123) │ │ │ │ │ │ │ │ ├── fd: (123)-->(204,206,208,210) │ │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ │ ├── columns: column203:203 column205:205 column207:207 column209:209 cs_item_sk:123!null + │ │ │ │ │ │ │ │ │ ├── columns: column203:203 column205:205!null column207:207!null column209:209!null cs_item_sk:123!null │ │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ │ ├── ordering: +123 │ │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -1882,9 +1882,9 @@ limit │ │ │ │ │ │ │ │ │ │ └── d_moy:181 = 11 [outer=(181), constraints=(/181: [/11 - /11]; tight), fd=()-->(181)] │ │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ │ ├── COALESCE(cr_return_quantity:161, 0) [as=column203:203, outer=(161)] - │ │ │ │ │ │ │ │ │ ├── COALESCE(cs_quantity:126, 0) [as=column205:205, outer=(126)] - │ │ │ │ │ │ │ │ │ ├── COALESCE(cr_return_amount:162::DECIMAL, 0) [as=column207:207, outer=(162), immutable] - │ │ │ │ │ │ │ │ │ └── COALESCE(cs_net_paid:137::DECIMAL, 0) [as=column209:209, outer=(137), immutable] + │ │ │ │ │ │ │ │ │ ├── cs_quantity:126 [as=column205:205, outer=(126)] + │ │ │ │ │ │ │ │ │ ├── cr_return_amount:162::DECIMAL [as=column207:207, outer=(162), immutable] + │ │ │ │ │ │ │ │ │ └── cs_net_paid:137::DECIMAL [as=column209:209, outer=(137), immutable] │ │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ │ ├── sum [as=sum:204, outer=(203)] │ │ │ │ │ │ │ │ │ └── column203:203 @@ -1921,39 +1921,39 @@ limit │ ├── key: (225) │ ├── fd: ()-->(314), (225)-->(308,310,311) │ ├── select - │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ ├── immutable │ │ ├── key: (225) │ │ ├── fd: (225)-->(308-311), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ ├── window partition=() ordering=+(309|313) - │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank:311 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ │ ├── immutable │ │ │ ├── key: (225) │ │ │ ├── fd: (225)-->(308-311), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ ├── window partition=() ordering=+(308|312) - │ │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309 rank:310 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 + │ │ │ │ ├── columns: ss_item_sk:225!null return_ratio:308 currency_ratio:309!null rank:310 rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null │ │ │ │ ├── immutable │ │ │ │ ├── key: (225) │ │ │ │ ├── fd: (225)-->(308-310), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ │ ├── project - │ │ │ │ │ ├── columns: rank_1_orderby_1_1:312 rank_2_orderby_1_1:313 ss_item_sk:225!null return_ratio:308 currency_ratio:309 + │ │ │ │ │ ├── columns: rank_1_orderby_1_1:312 rank_2_orderby_1_1:313!null ss_item_sk:225!null return_ratio:308 currency_ratio:309!null │ │ │ │ │ ├── immutable │ │ │ │ │ ├── key: (225) │ │ │ │ │ ├── fd: (225)-->(308,309), (308)==(312), (312)==(308), (309)==(313), (313)==(309) │ │ │ │ │ ├── project - │ │ │ │ │ │ ├── columns: return_ratio:308 currency_ratio:309 ss_item_sk:225!null + │ │ │ │ │ │ ├── columns: return_ratio:308 currency_ratio:309!null ss_item_sk:225!null │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ ├── key: (225) │ │ │ │ │ │ ├── fd: (225)-->(308,309) │ │ │ │ │ │ ├── group-by (streaming) - │ │ │ │ │ │ │ ├── columns: ss_item_sk:225!null sum:301 sum:303 sum:305 sum:307 + │ │ │ │ │ │ │ ├── columns: ss_item_sk:225!null sum:301 sum:303!null sum:305!null sum:307!null │ │ │ │ │ │ │ ├── grouping columns: ss_item_sk:225!null │ │ │ │ │ │ │ ├── internal-ordering: +225 │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ ├── key: (225) │ │ │ │ │ │ │ ├── fd: (225)-->(301,303,305,307) │ │ │ │ │ │ │ ├── project - │ │ │ │ │ │ │ │ ├── columns: column300:300 column302:302 column304:304 column306:306 ss_item_sk:225!null + │ │ │ │ │ │ │ │ ├── columns: column300:300 column302:302!null column304:304!null column306:306!null ss_item_sk:225!null │ │ │ │ │ │ │ │ ├── immutable │ │ │ │ │ │ │ │ ├── ordering: +225 │ │ │ │ │ │ │ │ ├── inner-join (lookup date_dim) @@ -2006,9 +2006,9 @@ limit │ │ │ │ │ │ │ │ │ └── d_moy:278 = 11 [outer=(278), constraints=(/278: [/11 - /11]; tight), fd=()-->(278)] │ │ │ │ │ │ │ │ └── projections │ │ │ │ │ │ │ │ ├── COALESCE(sr_return_quantity:258, 0) [as=column300:300, outer=(258)] - │ │ │ │ │ │ │ │ ├── COALESCE(ss_quantity:233, 0) [as=column302:302, outer=(233)] - │ │ │ │ │ │ │ │ ├── COALESCE(sr_return_amt:259::DECIMAL, 0) [as=column304:304, outer=(259), immutable] - │ │ │ │ │ │ │ │ └── COALESCE(ss_net_paid:243::DECIMAL, 0) [as=column306:306, outer=(243), immutable] + │ │ │ │ │ │ │ │ ├── ss_quantity:233 [as=column302:302, outer=(233)] + │ │ │ │ │ │ │ │ ├── sr_return_amt:259::DECIMAL [as=column304:304, outer=(259), immutable] + │ │ │ │ │ │ │ │ └── ss_net_paid:243::DECIMAL [as=column306:306, outer=(243), immutable] │ │ │ │ │ │ │ └── aggregations │ │ │ │ │ │ │ ├── sum [as=sum:301, outer=(300)] │ │ │ │ │ │ │ │ └── column300:300