Skip to content

Commit

Permalink
optbuilder: build insert fast-path uniq checks only if all checks can…
Browse files Browse the repository at this point in the history
… be built

This commit avoids unnecessary processing of insert fast path uniqueness
checks by modifying the optbuilder to avoid building a
`FastPathUniqueChecksExpr` in the Insert expression if one or more
of the required `FastPathUniqueChecksItem`s could not be built.

Epic: CRDB-26290
Informs: #58047

Release note: None
  • Loading branch information
Mark Sirek committed Oct 6, 2023
1 parent da37d62 commit e33ad40
Show file tree
Hide file tree
Showing 5 changed files with 802 additions and 901 deletions.
5 changes: 5 additions & 0 deletions pkg/sql/opt/exec/execbuilder/mutation.go
Expand Up @@ -142,6 +142,11 @@ func (b *Builder) tryBuildFastPathInsert(ins *memo.InsertExpr) (_ execPlan, ok b
if !b.allowInsertFastPath {
return execPlan{}, false, nil
}
// If there are unique checks required, there must be the same number of fast
// path unique checks.
if len(ins.UniqueChecks) != len(ins.FastPathUniqueChecks) {
return execPlan{}, false, nil
}

insInput := ins.Input
values, ok := insInput.(*memo.ValuesExpr)
Expand Down
46 changes: 28 additions & 18 deletions pkg/sql/opt/optbuilder/mutation_builder_unique.go
Expand Up @@ -44,6 +44,7 @@ func (mb *mutationBuilder) buildUniqueChecksForInsert() {

h := &mb.uniqueCheckHelper

buildFastPathCheck := true
for i, n := 0, mb.tab.UniqueCount(); i < n; i++ {
// If this constraint is already enforced by an index, we don't need to plan
// a check.
Expand All @@ -58,9 +59,16 @@ func (mb *mutationBuilder) buildUniqueChecksForInsert() {
continue
}
if h.init(mb, i) {
uniqueChecksItem, fastPathUniqueChecksItem := h.buildInsertionCheck(true /* buildFastPathCheck */)
uniqueChecksItem, fastPathUniqueChecksItem := h.buildInsertionCheck(buildFastPathCheck)
if fastPathUniqueChecksItem == nil {
// If we can't build one fast path check, don't build any of them into
// the expression tree.
buildFastPathCheck = false
mb.fastPathUniqueChecks = nil
} else {
mb.fastPathUniqueChecks = append(mb.fastPathUniqueChecks, *fastPathUniqueChecksItem)
}
mb.uniqueChecks = append(mb.uniqueChecks, uniqueChecksItem)
mb.fastPathUniqueChecks = append(mb.fastPathUniqueChecks, fastPathUniqueChecksItem)
}
}
telemetry.Inc(sqltelemetry.UniqueChecksUseCounter)
Expand Down Expand Up @@ -409,10 +417,12 @@ func (h *uniqueCheckHelper) buildFiltersForFastPathCheck(
// buildInsertionCheck creates a unique check for rows which are added to a
// table. The input to the insertion check will be produced from the input to
// the mutation operator. If buildFastPathCheck is true, a fast-path unique
// check for the insert is also built.
// check for the insert is also built. A `UniqueChecksItem` can always be built,
// but if it is not possible to build a `FastPathUniqueChecksItem`, the second
// return value is nil.
func (h *uniqueCheckHelper) buildInsertionCheck(
buildFastPathCheck bool,
) (memo.UniqueChecksItem, memo.FastPathUniqueChecksItem) {
) (memo.UniqueChecksItem, *memo.FastPathUniqueChecksItem) {
f := h.mb.b.factory

// Build a self semi-join, with the new values on the left and the
Expand Down Expand Up @@ -531,6 +541,14 @@ func (h *uniqueCheckHelper) buildInsertionCheck(
// violation error.
project := f.ConstructProject(semiJoin, nil /* projections */, keyCols.ToSet())

uniqueChecks := f.ConstructUniqueChecksItem(project, &memo.UniqueChecksItemPrivate{
Table: h.mb.tabID,
CheckOrdinal: h.uniqueOrdinal,
KeyCols: keyCols,
})
if !buildFastPathCheck {
return uniqueChecks, nil
}
// Build a SelectExpr which can be optimized in the explore phase and used
// to build information needed to perform the fast path uniqueness check.
// The goal is for the Select to be rewritten into a constrained scan on
Expand All @@ -549,24 +567,16 @@ func (h *uniqueCheckHelper) buildInsertionCheck(
newFilters := f.CustomFuncs().RemapScanColsInFilter(scanFilters, &scanExpr.ScanPrivate, newScanPrivate)
uniqueFastPathCheck = f.ConstructSelect(newScanExpr, newFilters)
} else {
uniqueFastPathCheck = f.CustomFuncs().ConstructEmptyValues(opt.ColSet{})
// Don't build a fast-path check if we failed to create a new ScanExpr.
return uniqueChecks, nil
}
} else if buildFastPathCheck {
// Things blow up if a RelExpr is nil, so construct a minimal dummy relation
// that will not be used.
uniqueFastPathCheck = f.CustomFuncs().ConstructEmptyValues(opt.ColSet{})
}

uniqueChecks := f.ConstructUniqueChecksItem(project, &memo.UniqueChecksItemPrivate{
Table: h.mb.tabID,
CheckOrdinal: h.uniqueOrdinal,
KeyCols: keyCols,
})
if !buildFastPathCheck {
return uniqueChecks, memo.FastPathUniqueChecksItem{}
// Don't build a fast-path check if we failed to build a ScanExpr with
// filters on all unique check columns.
return uniqueChecks, nil
}
fastPathChecks := f.ConstructFastPathUniqueChecksItem(uniqueFastPathCheck, &memo.FastPathUniqueChecksItemPrivate{ReferencedTableID: h.mb.tabID, CheckOrdinal: h.uniqueOrdinal})
return uniqueChecks, fastPathChecks
return uniqueChecks, &fastPathChecks
}

// buildTableScan builds a Scan of the table. The ordinals of the columns
Expand Down

0 comments on commit e33ad40

Please sign in to comment.