Skip to content

Commit

Permalink
opt: fold unary minus operations
Browse files Browse the repository at this point in the history
This commit introduces a new rule, FoldUnaryMinus, which transforms
constant expressions of the form (UnaryMinus x) to -x.

There were some cases around this which would result in us not correctly
recognizing expressions like -1:::FLOAT as constant, causing us to
perform a full table scan instead of a point lookup.

Release note: None
  • Loading branch information
Justin Jaffray committed Jun 22, 2018
1 parent 271b2fa commit d96b434
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 35 deletions.
48 changes: 48 additions & 0 deletions pkg/sql/opt/exec/execbuilder/testdata/select
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,54 @@ scan · · (f float) ·
statement ok
DROP TABLE flt

# ------------------------------------------------------------------------------
# Verify we create the correct spans for negative numbers with extra
# operations.
# ------------------------------------------------------------------------------

statement ok
CREATE TABLE num (
i int null,
unique index (i),
f float null,
unique index (f),
d decimal null,
unique index (d),
n interval null,
unique index (n)
)

query TTTTT
EXPLAIN (TYPES) SELECT i FROM num WHERE i = -1:::INT
----
scan · · (i int) ·
· table num@num_i_key · ·
· spans /-1-/0 · ·

query TTTTT
EXPLAIN (TYPES) SELECT f FROM num WHERE f = -1:::FLOAT
----
scan · · (f float) ·
· table num@num_f_key · ·
· spans /-1-/-1/PrefixEnd · ·

query TTTTT
EXPLAIN (TYPES) SELECT d FROM num WHERE d = -1:::DECIMAL
----
scan · · (d decimal) ·
· table num@num_d_key · ·
· spans /-1-/-1/PrefixEnd · ·

query TTTTT
EXPLAIN (TYPES) SELECT n FROM num WHERE n = -'1h':::INTERVAL
----
scan · · (n interval) ·
· table num@num_n_key · ·
· spans /-1h-/1d-25h · ·

statement ok
DROP TABLE num

# ------------------------------------------------------------------------------
# ANY, ALL tests.
# ------------------------------------------------------------------------------
Expand Down
10 changes: 10 additions & 0 deletions pkg/sql/opt/idxconstraint/testdata/single-column
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ index-constraints vars=(int) index=(@1 desc)
----
[/5 - /5]

index-constraints vars=(int) index=(@1)
@1 = -1
----
[/-1 - /-1]

index-constraints vars=(decimal) index=(@1)
@1 = -2.0
----
[/-2.0 - /-2.0]

index-constraints vars=(int) index=(@1 desc)
@1 IS DISTINCT FROM 5
----
Expand Down
29 changes: 29 additions & 0 deletions pkg/sql/opt/norm/custom_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,3 +761,32 @@ func (c *CustomFuncs) IsOne(input memo.GroupID) bool {
}
return false
}

// CanFoldUnaryMinus checks if a constant numeric value can be negated.
func (c *CustomFuncs) CanFoldUnaryMinus(input memo.GroupID) bool {
d := c.f.mem.LookupPrivate(c.f.mem.NormExpr(input).AsConst().Value()).(tree.Datum)
if t, ok := d.(*tree.DInt); ok {
return *t != math.MinInt64
}
return true
}

// NegateNumeric applies a unary minus to a numeric value.
func (c *CustomFuncs) NegateNumeric(input memo.GroupID) memo.GroupID {
d := c.f.mem.LookupPrivate(c.f.mem.NormExpr(input).AsConst().Value()).(tree.Datum)
var id memo.PrivateID
switch t := d.(type) {
case *tree.DDecimal:
id = c.f.InternDatum(t.Negate())
case *tree.DFloat:
id = c.f.InternDatum(t.Negate())
case *tree.DInt:
id = c.f.InternDatum(t.Negate())
case *tree.DInterval:
id = c.f.InternDatum(t.Negate())
default:
panic("unrecognized numeric type")
}

return c.f.ConstructConst(id)
}
8 changes: 8 additions & 0 deletions pkg/sql/opt/norm/rules/numeric.opt
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ $left
# EliminateUnaryMinus discards a doubled UnaryMinus operator.
[EliminateUnaryMinus, Normalize]
(UnaryMinus (UnaryMinus $input:*)) => $input

# FoldUnaryMinus negates a constant value within a UnaryMinus.
[FoldUnaryMinus, Normalize]
(UnaryMinus
$input:(Const) & (CanFoldUnaryMinus $input)
)
=>
(NegateNumeric $input)
48 changes: 24 additions & 24 deletions pkg/sql/opt/norm/testdata/rules/combo
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ PushFilterIntoJoinLeft
+ └── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
+ └── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
================================================================================
PruneJoinLeftCols
PruneJoinRightCols
Cost: 2163.33
================================================================================
project
Expand Down Expand Up @@ -176,7 +176,7 @@ PruneJoinLeftCols
└── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
└── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
================================================================================
PruneSelectCols
PruneLimitCols
Cost: 2143.33
================================================================================
project
Expand Down Expand Up @@ -210,7 +210,7 @@ PruneSelectCols
└── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
└── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
================================================================================
EliminateProject
EliminateProjectProject
Cost: 2143.33
================================================================================
project
Expand Down Expand Up @@ -247,7 +247,7 @@ EliminateProject
└── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
└── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
================================================================================
PruneJoinRightCols
PruneAggCols
Cost: 2133.33
================================================================================
project
Expand Down Expand Up @@ -277,7 +277,7 @@ PruneJoinRightCols
└── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
└── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
--------------------------------------------------------------------------------
GenerateIndexScans (higher cost)
ConstrainScan (higher cost)
--------------------------------------------------------------------------------
project
├── columns: s:4(string)
Expand Down Expand Up @@ -308,13 +308,13 @@ GenerateIndexScans (higher cost)
└── filters [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
└── a.k = xy.x [type=bool, outer=(1,6), constraints=(/1: (/NULL - ]; /6: (/NULL - ])]
--------------------------------------------------------------------------------
ConstrainScan (no changes)
PushFilterIntoLookupJoinNoRemainder (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ConstrainLookupJoinIndexScan (no changes)
NumRuleNames (no changes)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
ConstrainScan (no changes)
--------------------------------------------------------------------------------
================================================================================
Final best expression
Expand Down Expand Up @@ -385,7 +385,7 @@ SimplifyFilters
+ ├── a.s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight)]
+ └── a.f > 100.0 [type=bool, outer=(3), constraints=(/3: [/100.00000000000001 - ]; tight)]
================================================================================
PruneSelectCols
PruneLimitCols
Cost: 1090.00
================================================================================
project
Expand All @@ -408,7 +408,7 @@ PruneSelectCols
├── a.s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight)]
└── a.f > 100.0 [type=bool, outer=(3), constraints=(/3: [/100.00000000000001 - ]; tight)]
================================================================================
GenerateIndexScans
ConstrainScan
Cost: 1080.00
================================================================================
project
Expand All @@ -428,10 +428,10 @@ GenerateIndexScans
├── a.s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight)]
└── a.f > 100.0 [type=bool, outer=(3), constraints=(/3: [/100.00000000000001 - ]; tight)]
--------------------------------------------------------------------------------
ConstrainScan (no changes)
PushFilterIntoLookupJoinNoRemainder (no changes)
--------------------------------------------------------------------------------
================================================================================
ConstrainScan
PushFilterIntoLookupJoinNoRemainder
Cost: 1.54
================================================================================
project
Expand Down Expand Up @@ -648,7 +648,7 @@ EliminateEmptyAnd
- └── filters [type=bool]
+ └── true [type=bool]
================================================================================
EliminateSelect
EnsureSelectFiltersAnd
Cost: 2160.00
================================================================================
-select
Expand Down Expand Up @@ -680,7 +680,7 @@ EliminateSelect
+ └── filters [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]
+ └── xy.y = a.i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]
--------------------------------------------------------------------------------
GenerateIndexScans (higher cost)
ConstrainScan (higher cost)
--------------------------------------------------------------------------------
semi-join
├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
Expand All @@ -704,7 +704,7 @@ GenerateIndexScans (higher cost)
└── filters [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]
└── xy.y = a.i [type=bool, outer=(2,7), constraints=(/2: (/NULL - ]; /7: (/NULL - ])]
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
ConstrainScan (no changes)
--------------------------------------------------------------------------------
================================================================================
Final best expression
Expand Down Expand Up @@ -750,7 +750,7 @@ Initial expression
│ └── fd: (3)-->(4-7), (5,6)~~>(3,4,7)
└── variable: xy.x [type=int, outer=(1)]
================================================================================
PruneScanCols
PruneSelectCols
Cost: 2100.00
================================================================================
project
Expand All @@ -772,7 +772,7 @@ PruneScanCols
+ │ └── key: (3)
└── variable: xy.x [type=int, outer=(1)]
================================================================================
EliminateProject
EliminateProjectProject
Cost: 2100.00
================================================================================
project
Expand All @@ -793,7 +793,7 @@ EliminateProject
+ │ └── key: (3)
└── variable: xy.x [type=int, outer=(1)]
================================================================================
PruneScanCols
PruneSelectCols
Cost: 2090.00
================================================================================
project
Expand Down Expand Up @@ -867,7 +867,7 @@ HoistProjectSubquery
+ └── projections [outer=(11)]
+ └── variable: case [type=bool, outer=(11)]
================================================================================
EnsureSelectFilters
MergeSelects
Cost: 2110.01
================================================================================
project
Expand Down Expand Up @@ -1268,7 +1268,7 @@ EliminateGroupByProject
└── projections [outer=(11)]
└── variable: case [type=bool, outer=(11)]
================================================================================
EliminateSelect
EnsureSelectFiltersAnd
Cost: 2120.00
================================================================================
project
Expand Down Expand Up @@ -1340,7 +1340,7 @@ EliminateSelect
└── projections [outer=(11)]
└── variable: case [type=bool, outer=(11)]
================================================================================
EliminateSelect
EnsureSelectFiltersAnd
Cost: 2110.00
================================================================================
project
Expand Down Expand Up @@ -1413,7 +1413,7 @@ EliminateSelect
└── projections [outer=(11)]
└── variable: case [type=bool, outer=(11)]
================================================================================
PruneProjectCols
PruneScanCols
Cost: 2110.00
================================================================================
project
Expand Down Expand Up @@ -1519,10 +1519,10 @@ InlineProjectInProject
+ └── projections [outer=(1,10)]
+ └── CASE WHEN bool_or AND (xy.x IS NOT NULL) THEN true WHEN bool_or IS NULL THEN false END [type=bool, outer=(1,10)]
--------------------------------------------------------------------------------
GenerateIndexScans (no changes)
ConstrainScan (no changes)
--------------------------------------------------------------------------------
================================================================================
GenerateIndexScans
ConstrainScan
Cost: 2100.00
================================================================================
project
Expand Down
Loading

0 comments on commit d96b434

Please sign in to comment.