diff --git a/pkg/sql/opt/exec/execbuilder/scalar.go b/pkg/sql/opt/exec/execbuilder/scalar.go index e7a04d612575..4bbc14d6e4f4 100644 --- a/pkg/sql/opt/exec/execbuilder/scalar.go +++ b/pkg/sql/opt/exec/execbuilder/scalar.go @@ -27,6 +27,8 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sem/tree/treecmp" "github.com/cockroachdb/cockroach/pkg/sql/sem/volatility" "github.com/cockroachdb/cockroach/pkg/sql/types" + "github.com/cockroachdb/cockroach/pkg/util/errorutil" + "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/errors" "github.com/cockroachdb/redact" ) @@ -946,7 +948,29 @@ func (b *Builder) buildRoutinePlanGenerator( var o xform.Optimizer planGen := func( ctx context.Context, ref tree.RoutineExecFactory, args tree.Datums, fn tree.RoutinePlanGeneratedFunc, - ) error { + ) (err error) { + defer func() { + if r := recover(); r != nil { + // This code allows us to propagate internal errors without + // having to add error checks everywhere throughout the code. + // This is only possible because the code does not update shared + // state and does not manipulate locks. + // + // This is the same panic-catching logic that exists in + // o.Optimize() below. It's required here because it's possible + // for factory functions to panic below, like + // CopyAndReplaceDefault. + if ok, e := errorutil.ShouldCatch(r); ok { + err = e + log.VEventf(ctx, 1, "%v", err) + } else { + // Other panic objects can't be considered "safe" and thus + // are propagated as crashes that terminate the session. + panic(r) + } + } + }() + for i := range stmts { stmt := stmts[i] o.Init(ctx, b.evalCtx, b.catalog)