Skip to content

Commit

Permalink
opt: add rule to fold limits
Browse files Browse the repository at this point in the history
Previously, there was no rule to fold a Limit on top of a Limit
when the outer limit value is smaller than the inner limit value.

This patch adds a rule to fold two Limits together when the
outer Limit has a smaller limit value than the inner Limit and
the inner ordering implies the outer ordering.

Release note (sql change): The optimizer can now fold two Limit
operators together.
  • Loading branch information
DrewKimball committed Jun 8, 2020
1 parent 685194e commit 759e144
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 26 deletions.
15 changes: 12 additions & 3 deletions pkg/sql/opt/norm/general_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,15 @@ func (c *CustomFuncs) OrdinalityOrdering(private *memo.OrdinalityPrivate) physic
}

// IsSameOrdering evaluates whether the two orderings are equal.
func (c *CustomFuncs) IsSameOrdering(
first physical.OrderingChoice, other physical.OrderingChoice,
) bool {
func (c *CustomFuncs) IsSameOrdering(first, other physical.OrderingChoice) bool {
return first.Equals(&other)
}

// OrderingImplies returns true if the first OrderingChoice implies the second.
func (c *CustomFuncs) OrderingImplies(first, second physical.OrderingChoice) bool {
return first.Implies(&second)
}

// -----------------------------------------------------------------------
//
// Filter functions
Expand Down Expand Up @@ -950,3 +953,9 @@ func (c *CustomFuncs) CanAddConstInts(first tree.Datum, second tree.Datum) bool
func (c *CustomFuncs) IntConst(d *tree.DInt) opt.ScalarExpr {
return c.f.ConstructConst(d, types.Int)
}

// IsGreaterThan returns true if the first datum compares as greater than the
// second.
func (c *CustomFuncs) IsGreaterThan(first, second tree.Datum) bool {
return first.Compare(c.f.evalCtx, second) == 1
}
19 changes: 19 additions & 0 deletions pkg/sql/opt/norm/rules/limit.opt
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,22 @@ $input
$limitExpr
$ordering
)

# FoldLimits replaces a Limit on top of a Limit with a single Limit operator
# when the outer limit value is smaller than or equal to the inner limit value
# and the inner ordering implies the outer ordering. Note: the case when the
# outer limit value is larger than the inner is handled by EliminateLimit.
[FoldLimits, Normalize]
(Limit
(Limit
$innerInput:*
$innerLimitExpr:(Const $innerLimit:*)
$innerOrdering:*
)
$outerLimitExpr:(Const $outerLimit:*) &
^(IsGreaterThan $outerLimit $innerLimit)
$outerOrdering:* &
(OrderingImplies $innerOrdering $outerOrdering)
)
=>
(Limit $innerInput $outerLimitExpr $innerOrdering)
189 changes: 166 additions & 23 deletions pkg/sql/opt/norm/testdata/rules/limit
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,25 @@ limit
│ └── limit hint: 100.00
└── 100

# Don't eliminate the outer limit if it's less than the inner.
norm
# Don't eliminate the outer limit if it's less than the inner (the limit is
# instead removed by FoldLimits).
norm expect-not=EliminateLimit
SELECT * FROM (SELECT * FROM a LIMIT 100) LIMIT 99
----
limit
├── columns: k:1!null i:2 f:3 s:4 j:5
├── cardinality: [0 - 99]
├── key: (1)
├── fd: (1)-->(2-5)
├── limit
├── scan a
│ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ ├── cardinality: [0 - 100]
│ ├── key: (1)
│ ├── fd: (1)-->(2-5)
│ ├── limit hint: 99.00
│ ├── scan a
│ │ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ │ ├── key: (1)
│ │ ├── fd: (1)-->(2-5)
│ │ └── limit hint: 100.00
│ └── 100
│ └── limit hint: 99.00
└── 99

# High limits (> max uint32), can't eliminate in this case.
norm
norm expect-not=EliminateLimit
SELECT * FROM (SELECT * FROM a LIMIT 5000000000) LIMIT 5100000000
----
limit
Expand All @@ -95,8 +89,9 @@ limit
│ └── 5000000000
└── 5100000000

# Don't eliminate in case of negative limit.
norm
# Don't eliminate in case of negative limit (the limit is instead removed by
# FoldLimits).
norm expect-not=EliminateLimit
SELECT * FROM (SELECT * FROM a LIMIT 0) LIMIT -1
----
limit
Expand Down Expand Up @@ -1109,18 +1104,11 @@ limit
│ │ ├── cardinality: [0 - 10]
│ │ ├── key: (1)
│ │ ├── fd: (1)-->(2)
│ │ ├── limit
│ │ ├── scan ab
│ │ │ ├── columns: a:1!null b:2
│ │ │ ├── cardinality: [0 - 20]
│ │ │ ├── key: (1)
│ │ │ ├── fd: (1)-->(2)
│ │ │ ├── limit hint: 10.00
│ │ │ ├── scan ab
│ │ │ │ ├── columns: a:1!null b:2
│ │ │ │ ├── key: (1)
│ │ │ │ ├── fd: (1)-->(2)
│ │ │ │ └── limit hint: 20.00
│ │ │ └── 20
│ │ │ └── limit hint: 10.00
│ │ └── 10
│ ├── scan uv
│ │ ├── columns: u:3!null v:4
Expand Down Expand Up @@ -1245,3 +1233,158 @@ limit
│ └── filters
│ └── a:1 = u:3 [outer=(1,3), constraints=(/1: (/NULL - ]; /3: (/NULL - ]), fd=(1)==(3), (3)==(1)]
└── 10

# ----------
# FoldLimits
# ----------

# Basic case with no orderings.
norm expect=FoldLimits
SELECT * FROM (SELECT * FROM ab LIMIT 10) LIMIT 5
----
limit
├── columns: a:1!null b:2
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2)
├── scan ab
│ ├── columns: a:1!null b:2
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ └── limit hint: 5.00
└── 5

# Case where the inner limit has an ordering and the outer limit is unordered.
norm expect=FoldLimits
SELECT * FROM (SELECT * FROM ab ORDER BY a LIMIT 10) LIMIT 5
----
limit
├── columns: a:1!null b:2
├── internal-ordering: +1
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2)
├── scan ab
│ ├── columns: a:1!null b:2
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ ├── ordering: +1
│ └── limit hint: 5.00
└── 5

# Case where the inner limit ordering implies the outer ordering.
norm expect=FoldLimits
SELECT * FROM (SELECT * FROM a ORDER BY i, f LIMIT 10) ORDER BY i LIMIT 5
----
limit
├── columns: k:1!null i:2 f:3 s:4 j:5
├── internal-ordering: +2,+3
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2-5)
├── ordering: +2
├── sort
│ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ ├── key: (1)
│ ├── fd: (1)-->(2-5)
│ ├── ordering: +2,+3
│ ├── limit hint: 5.00
│ └── scan a
│ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ ├── key: (1)
│ └── fd: (1)-->(2-5)
└── 5

# No-op case where the outer limit is larger than the inner limit. (The limit is
# instead removed by EliminateLimit).
norm expect-not=FoldLimits
SELECT * FROM (SELECT * FROM ab LIMIT 5) LIMIT 10
----
limit
├── columns: a:1!null b:2
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2)
├── scan ab
│ ├── columns: a:1!null b:2
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ └── limit hint: 5.00
└── 5

# No-op case where the inner limit ordering does not imply the outer limit
# ordering.
norm expect-not=FoldLimits
SELECT * FROM (SELECT * FROM ab ORDER BY b LIMIT 10) ORDER BY a LIMIT 5
----
limit
├── columns: a:1!null b:2
├── internal-ordering: +1
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2)
├── ordering: +1
├── sort
│ ├── columns: a:1!null b:2
│ ├── cardinality: [0 - 10]
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ ├── ordering: +1
│ ├── limit hint: 5.00
│ └── limit
│ ├── columns: a:1!null b:2
│ ├── internal-ordering: +2
│ ├── cardinality: [0 - 10]
│ ├── key: (1)
│ ├── fd: (1)-->(2)
│ ├── sort
│ │ ├── columns: a:1!null b:2
│ │ ├── key: (1)
│ │ ├── fd: (1)-->(2)
│ │ ├── ordering: +2
│ │ ├── limit hint: 10.00
│ │ └── scan ab
│ │ ├── columns: a:1!null b:2
│ │ ├── key: (1)
│ │ └── fd: (1)-->(2)
│ └── 10
└── 5

# No-op case where the outer ordering implies the inner, but the inner doesn't
# imply the outer.
norm expect-not=FoldLimits
SELECT * FROM (SELECT * FROM a ORDER BY i LIMIT 10) ORDER BY i, f LIMIT 5
----
limit
├── columns: k:1!null i:2 f:3 s:4 j:5
├── internal-ordering: +2,+3
├── cardinality: [0 - 5]
├── key: (1)
├── fd: (1)-->(2-5)
├── ordering: +2,+3
├── sort (segmented)
│ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ ├── cardinality: [0 - 10]
│ ├── key: (1)
│ ├── fd: (1)-->(2-5)
│ ├── ordering: +2,+3
│ ├── limit hint: 5.00
│ └── limit
│ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ ├── internal-ordering: +2
│ ├── cardinality: [0 - 10]
│ ├── key: (1)
│ ├── fd: (1)-->(2-5)
│ ├── ordering: +2
│ ├── sort
│ │ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ │ ├── key: (1)
│ │ ├── fd: (1)-->(2-5)
│ │ ├── ordering: +2
│ │ ├── limit hint: 10.00
│ │ └── scan a
│ │ ├── columns: k:1!null i:2 f:3 s:4 j:5
│ │ ├── key: (1)
│ │ └── fd: (1)-->(2-5)
│ └── 10
└── 5

0 comments on commit 759e144

Please sign in to comment.