diff --git a/pkg/sql/opt/memo/cost.go b/pkg/sql/opt/memo/cost.go index 3ee6ce0a140d..309613b1c2de 100644 --- a/pkg/sql/opt/memo/cost.go +++ b/pkg/sql/opt/memo/cost.go @@ -36,7 +36,7 @@ type Cost struct { // member will have a lower cost. var MaxCost = Cost{ C: math.Inf(+1), - Penalties: HugeCostPenalty | FullScanPenalty | UnboundedCardinalityPenalty, + Penalties: HugeCostPenalty | FullScanPenalty | PlanGramMismatchPenalty | UnboundedCardinalityPenalty, } // Less returns true if this cost is lower than the given cost. @@ -105,6 +105,11 @@ const ( // plan is possible. FullScanPenalty + // PlanGramMismatchPenalty is true if the plan does not match the required + // PlanGram pattern, indicating that this plan should only be used if no + // PlanGram-matching plan is possible. + PlanGramMismatchPenalty + // UnboundedCardinalityPenalty is true if the operator or any of its // descendants have no guaranteed upperbound on the number of rows that they // can produce. See props.AnyCardinality. @@ -121,8 +126,9 @@ const ( // Where: // // is the floating point cost value. -// contains "H", "F", or "U" for HugeCostPenalty, FullScanPenalty, -// and UnboundedCardinalityPenalty, respectively. +// contains "H", "F", "P", or "U" for HugeCostPenalty, +// FullScanPenalty, PlanGramMismatchPenalty, and +// UnboundedCardinalityPenalty, respectively. // contains the number of full scans and unbounded reads. // // For example, the summary "1.23:HF:5f6u" indicates a cost of 1.23 with the @@ -137,6 +143,9 @@ func (c Cost) Summary() string { if c.Penalties&FullScanPenalty != 0 { sb.WriteByte('F') } + if c.Penalties&PlanGramMismatchPenalty != 0 { + sb.WriteByte('P') + } if c.Penalties&UnboundedCardinalityPenalty != 0 { sb.WriteByte('U') } diff --git a/pkg/sql/opt/memo/cost_test.go b/pkg/sql/opt/memo/cost_test.go index da0cd078d747..9c503c454678 100644 --- a/pkg/sql/opt/memo/cost_test.go +++ b/pkg/sql/opt/memo/cost_test.go @@ -36,8 +36,12 @@ func TestCostLess(t *testing.T) { {MaxCost, MaxCost, false}, {MaxCost, Cost{C: 1.0, Penalties: FullScanPenalty}, false}, {Cost{C: 1.0, Penalties: HugeCostPenalty}, MaxCost, true}, + {Cost{C: 2.0}, Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, true}, + {Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, Cost{C: 2.0}, false}, {Cost{C: 2.0}, Cost{C: 1.0, Penalties: UnboundedCardinalityPenalty}, true}, {Cost{C: 1.0, Penalties: UnboundedCardinalityPenalty}, Cost{C: 2.0}, false}, + {Cost{C: 1.0, Penalties: UnboundedCardinalityPenalty}, Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, true}, + {Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, Cost{C: 1.0, Penalties: FullScanPenalty}, true}, // Auxiliary information should not affect the comparison. {Cost{C: 1.0, aux: testAux{0, 0}}, Cost{C: 1.0, aux: testAux{1, 1}}, false}, {Cost{C: 1.0, aux: testAux{1, 1}}, Cost{C: 1.0, aux: testAux{0, 0}}, false}, @@ -59,6 +63,8 @@ func TestCostAdd(t *testing.T) { {Cost{C: 1.5}, Cost{C: 2.5}, Cost{C: 4.0}}, {Cost{C: 1.0, Penalties: FullScanPenalty}, Cost{C: 2.0}, Cost{C: 3.0, Penalties: FullScanPenalty}}, {Cost{C: 1.0}, Cost{C: 2.0, Penalties: HugeCostPenalty}, Cost{C: 3.0, Penalties: HugeCostPenalty}}, + {Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, Cost{C: 2.0, Penalties: FullScanPenalty}, Cost{C: 3.0, Penalties: FullScanPenalty | PlanGramMismatchPenalty}}, + {Cost{C: 1.0, Penalties: PlanGramMismatchPenalty}, Cost{C: 2.0, Penalties: PlanGramMismatchPenalty}, Cost{C: 3.0, Penalties: PlanGramMismatchPenalty}}, {Cost{C: 1.0, Penalties: UnboundedCardinalityPenalty}, Cost{C: 2.0, Penalties: HugeCostPenalty}, Cost{C: 3.0, Penalties: HugeCostPenalty | UnboundedCardinalityPenalty}}, {Cost{C: 1.0, aux: testAux{1, 4}}, Cost{C: 1.0, aux: testAux{2, 5}}, Cost{C: 2.0, aux: testAux{3, 9}}}, {Cost{C: 1.0, aux: testAux{65530, 65530}}, Cost{C: 1.0, aux: testAux{100, 100}}, Cost{C: 2.0, aux: testAux{65535, 65535}}}, @@ -81,8 +87,9 @@ func TestCostSummary(t *testing.T) { {Cost{C: 1.23456}, "1.23456::0f0u"}, {Cost{C: 1.23, Penalties: HugeCostPenalty}, "1.23:H:0f0u"}, {Cost{C: 1.23, Penalties: FullScanPenalty}, "1.23:F:0f0u"}, + {Cost{C: 1.23, Penalties: PlanGramMismatchPenalty}, "1.23:P:0f0u"}, {Cost{C: 1.23, Penalties: UnboundedCardinalityPenalty}, "1.23:U:0f0u"}, - {Cost{C: 1.23, Penalties: HugeCostPenalty | FullScanPenalty | UnboundedCardinalityPenalty}, "1.23:HFU:0f0u"}, + {Cost{C: 1.23, Penalties: HugeCostPenalty | FullScanPenalty | PlanGramMismatchPenalty | UnboundedCardinalityPenalty}, "1.23:HFPU:0f0u"}, {Cost{C: 1.23, Penalties: HugeCostPenalty | FullScanPenalty | UnboundedCardinalityPenalty}, "1.23:HFU:0f0u"}, {Cost{C: 1.23, aux: testAux{5, 0}}, "1.23::5f0u"}, {Cost{C: 1.23, aux: testAux{0, 6}}, "1.23::0f6u"}, diff --git a/pkg/sql/opt/memo/expr_format.go b/pkg/sql/opt/memo/expr_format.go index 90ca7bc87fb6..1c952122f316 100644 --- a/pkg/sql/opt/memo/expr_format.go +++ b/pkg/sql/opt/memo/expr_format.go @@ -933,6 +933,9 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { if cost.Penalties&HugeCostPenalty != 0 { b.WriteString(" huge-cost-penalty") } + if cost.Penalties&PlanGramMismatchPenalty != 0 { + b.WriteString(" plangram-mismatch") + } if cost.Penalties&UnboundedCardinalityPenalty != 0 { b.WriteString(" unbounded-cardinality") }