diff --git a/pkg/sql/create_function.go b/pkg/sql/create_function.go index f5de663ce0b9..53e0eca05fdf 100644 --- a/pkg/sql/create_function.go +++ b/pkg/sql/create_function.go @@ -443,7 +443,7 @@ func setFuncOptions( return err } typeReplacedFuncBody, err := serializeUserDefinedTypes( - params.ctx, params.p.SemaCtx(), seqReplacedFuncBody, true, /* multiStmt */ + params.ctx, params.p.SemaCtx(), seqReplacedFuncBody, true /* multiStmt */, "UDFs", ) if err != nil { return err diff --git a/pkg/sql/create_view.go b/pkg/sql/create_view.go index ebddc530d523..888939e475b5 100644 --- a/pkg/sql/create_view.go +++ b/pkg/sql/create_view.go @@ -411,7 +411,8 @@ func makeViewTableDesc( desc.ViewQuery = sequenceReplacedQuery } - typeReplacedQuery, err := serializeUserDefinedTypes(ctx, semaCtx, desc.ViewQuery, false /* multiStmt */) + typeReplacedQuery, err := serializeUserDefinedTypes(ctx, semaCtx, desc.ViewQuery, + false /* multiStmt */, "view queries") if err != nil { return tabledesc.Mutable{}, err } @@ -490,7 +491,7 @@ func replaceSeqNamesWithIDs( // and serialize any user defined types, so that renaming the type // does not corrupt the view. func serializeUserDefinedTypes( - ctx context.Context, semaCtx *tree.SemaContext, queries string, multiStmt bool, + ctx context.Context, semaCtx *tree.SemaContext, queries string, multiStmt bool, parentType string, ) (string, error) { replaceFunc := func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) { var innerExpr tree.Expr @@ -505,20 +506,6 @@ func serializeUserDefinedTypes( default: return true, expr, nil } - // We cannot type-check subqueries without using optbuilder, and there - // is no need to because we only need to rewrite string values that are - // directly cast to enums. For example, we must rewrite the 'foo' in: - // - // SELECT 'foo'::myenum - // - // We don't need to rewrite the 'foo' in the query below, which can be - // corrupted by renaming the 'foo' value in the myenum type. - // - // SELECT (SELECT 'foo')::myenum - // - if _, ok := innerExpr.(*tree.Subquery); ok { - return true, expr, nil - } // semaCtx may be nil if this is a virtual view being created at // init time. var typeResolver tree.TypeReferenceResolver @@ -533,6 +520,14 @@ func serializeUserDefinedTypes( if !typ.UserDefined() { return true, expr, nil } + { + // We cannot type-check subqueries without using optbuilder, so we + // currently do not support casting expressions with subqueries to + // UDTs. + context := "casts to enums within " + parentType + defer semaCtx.Properties.Restore(semaCtx.Properties) + semaCtx.Properties.Require(context, tree.RejectSubqueries) + } texpr, err := innerExpr.TypeCheck(ctx, semaCtx, typ) if err != nil { return false, expr, err @@ -603,7 +598,8 @@ func (p *planner) replaceViewDesc( toReplace.ViewQuery = updatedQuery } - typeReplacedQuery, err := serializeUserDefinedTypes(ctx, p.SemaCtx(), toReplace.ViewQuery, false /* multiStmt */) + typeReplacedQuery, err := serializeUserDefinedTypes(ctx, p.SemaCtx(), toReplace.ViewQuery, + false /* multiStmt */, "view queries") if err != nil { return nil, err } diff --git a/pkg/sql/logictest/testdata/logic_test/delete b/pkg/sql/logictest/testdata/logic_test/delete index 66f63b3a6e69..5fb8fa05bb60 100644 --- a/pkg/sql/logictest/testdata/logic_test/delete +++ b/pkg/sql/logictest/testdata/logic_test/delete @@ -576,3 +576,15 @@ statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORD DELETE FROM t107634 ORDER BY sum(a) LIMIT 1; subtest end + +# Regression test for #108166. Do not allow aggregate functions in ORDER BY when +# the function is wrapped by a conditional expression. +subtest regression_108166 + +statement ok +CREATE TABLE t108166 (a INT) + +statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORDER BY in DELETE +DELETE FROM t108166 ORDER BY COALESCE(sum(a), 1) LIMIT 1; + +subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/srfs b/pkg/sql/logictest/testdata/logic_test/srfs index 824c03836bde..19244444b8d4 100644 --- a/pkg/sql/logictest/testdata/logic_test/srfs +++ b/pkg/sql/logictest/testdata/logic_test/srfs @@ -1322,16 +1322,16 @@ subtest generator-syntax # Regression test for #97119 and #94890 - return syntax error when CASE or # COALESCE is used with a set-generating function as argument. -statement error pq: set-returning functions are not allowed in CASE +statement error pq: set-returning functions are not allowed in conditional expressions SELECT CASE generate_series(1, 3) WHEN 3 THEN 0 ELSE 1 END; -statement error pq: set-returning functions are not allowed in CASE +statement error pq: set-returning functions are not allowed in conditional expressions SELECT CASE WHEN true THEN generate_series(1, 3) ELSE 1 END; -statement error pq: set-returning functions are not allowed in CASE +statement error pq: set-returning functions are not allowed in conditional expressions SELECT CASE WHEN false THEN 1 ELSE generate_series(1, 3) END; -statement error pq: set-returning functions are not allowed in COALESCE +statement error pq: set-returning functions are not allowed in conditional expressions SELECT COALESCE(generate_series(1, 10)); # A subquery with a generator function is allowed within CASE and COALESCE. @@ -1376,12 +1376,12 @@ SELECT COALESCE(sum(x) OVER ()) FROM xy; 15 # IF does not allow generator functions. -statement error pq: set-returning functions are not allowed in IF +statement error pq: set-returning functions are not allowed in conditional expressions SELECT IF(x > y, generate_series(1, 3), 0) FROM xy; # IFNULL does not allow generator functions. Note that the error mentions # COALESCE because IFNULL is parsed directly as a COALESCE expression. -statement error pq: set-returning functions are not allowed in COALESCE +statement error pq: set-returning functions are not allowed in conditional expressions SELECT IFNULL(1, generate_series(1, 2)); # NULLIF allows generator functions. diff --git a/pkg/sql/logictest/testdata/logic_test/udf_regressions b/pkg/sql/logictest/testdata/logic_test/udf_regressions index 25bfcb0adc67..15a94ee19967 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf_regressions +++ b/pkg/sql/logictest/testdata/logic_test/udf_regressions @@ -554,29 +554,25 @@ $$ subtest end -# Regression test for #105259. Do not type-check subqueries in UDFs outside -# optbuilder. Doing so can cause internal errors. +# Regression tests for #105259 and #107654. Do not type-check subqueries in UDFs +# outside optbuilder. Doing so can cause internal errors. subtest regression_105259 statement ok CREATE TYPE e105259 AS ENUM ('foo'); -statement ok +statement error pgcode 0A000 subqueries are not allowed in casts to enums within UDFs CREATE FUNCTION f() RETURNS VOID LANGUAGE SQL AS $$ SELECT (SELECT 'foo')::e105259; SELECT NULL; $$ -query T -SELECT f() ----- -NULL - -statement ok -ALTER TYPE e105259 RENAME VALUE 'foo' TO 'bar' - -# Renaming the enum value corrupts the UDF. This is expected behavior. -statement error pgcode 22P02 invalid input value for enum e105259: "foo" -SELECT f() +statement error pgcode 0A000 subqueries are not allowed in casts to enums within UDFs +CREATE FUNCTION f() RETURNS VOID LANGUAGE SQL AS $$ + SELECT ( + CASE WHEN true THEN (SELECT 'foo') ELSE NULL END + )::e105259; + SELECT NULL; +$$ subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/udf_unsupported b/pkg/sql/logictest/testdata/logic_test/udf_unsupported index 5a74f495bd96..ca200d987c74 100644 --- a/pkg/sql/logictest/testdata/logic_test/udf_unsupported +++ b/pkg/sql/logictest/testdata/logic_test/udf_unsupported @@ -183,4 +183,3 @@ statement error pgcode 0A000 unimplemented: cannot create UDFs under a temporary CREATE FUNCTION $temp_schema_102964.f_102964 () RETURNS INT AS 'SELECT 1' LANGUAGE sql; subtest end - diff --git a/pkg/sql/logictest/testdata/logic_test/update b/pkg/sql/logictest/testdata/logic_test/update index 6feaa006b02c..f9757512505b 100644 --- a/pkg/sql/logictest/testdata/logic_test/update +++ b/pkg/sql/logictest/testdata/logic_test/update @@ -672,3 +672,15 @@ statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORD UPDATE t107634 SET a = 1 ORDER BY sum(a) LIMIT 1; subtest end + +# Regression test for #108166. Do not allow aggregate functions in ORDER BY when +# the function is wrapped by a conditional expression. +subtest regression_108166 + +statement ok +CREATE TABLE t108166 (a INT) + +statement error pgcode 42803 sum\(\): aggregate functions are not allowed in ORDER BY in UPDATE +UPDATE t108166 SET a = 1 ORDER BY COALESCE(sum(a), 1) LIMIT 1; + +subtest end diff --git a/pkg/sql/logictest/testdata/logic_test/views b/pkg/sql/logictest/testdata/logic_test/views index 9fcdca2bfacb..cbbfb5fc054c 100644 --- a/pkg/sql/logictest/testdata/logic_test/views +++ b/pkg/sql/logictest/testdata/logic_test/views @@ -1900,28 +1900,22 @@ SELECT * FROM v104927 subtest end -# Regression test for #105259. Do not type-check subqueries in views outside -# optbuilder. Doing so can cause internal errors. -subtest regression_105259 +# Regression tests for #105259 and #107654. Do not type-check subqueries in +# views outside optbuilder. Doing so can cause internal errors. +subtest regression_105259_107654 statement ok CREATE TYPE e105259 AS ENUM ('foo'); -statement ok -CREATE VIEW v105259 AS +statement error pgcode 0A000 subqueries are not allowed in casts to enums within view queries +CREATE VIEW v AS SELECT (SELECT 'foo')::e105259 -query T -SELECT * FROM v105259 ----- -foo - -statement ok -ALTER TYPE e105259 RENAME VALUE 'foo' TO 'bar' - -# Renaming the enum value corrupts the view. This is expected behavior. -statement error pgcode 22P02 invalid input value for enum e105259: "foo" -SELECT * FROM v105259 +statement error pgcode 0A000 subqueries are not allowed in casts to enums within view queries +CREATE VIEW v AS +SELECT ( + CASE WHEN true THEN (SELECT 'foo') ELSE NULL END +)::e105259 subtest end diff --git a/pkg/sql/opt/optbuilder/scope.go b/pkg/sql/opt/optbuilder/scope.go index ad44fab80ab3..d1ff62b2ae71 100644 --- a/pkg/sql/opt/optbuilder/scope.go +++ b/pkg/sql/opt/optbuilder/scope.go @@ -1358,15 +1358,10 @@ func (s *scope) replaceWindowFn(f *tree.FuncExpr, def *tree.ResolvedFunctionDefi // We will be performing type checking on expressions from PARTITION BY and // ORDER BY clauses below, and we need the semantic context to know that we // are in a window function. InWindowFunc is updated when type checking - // FuncExpr above, but it is reset upon returning from that, so we need to do - // this update manually. - defer func(ctx *tree.SemaContext, prevWindow bool) { - ctx.Properties.Derived.InWindowFunc = prevWindow - }( - s.builder.semaCtx, - s.builder.semaCtx.Properties.Derived.InWindowFunc, - ) - s.builder.semaCtx.Properties.Derived.InWindowFunc = true + // FuncExpr above, but it is reset upon returning from that, so we need to + // do this update manually. + defer s.builder.semaCtx.Properties.Ancestors.PopTo(s.builder.semaCtx.Properties.Ancestors) + s.builder.semaCtx.Properties.Ancestors.Push(tree.WindowFuncAncestor) oldPartitions := f.WindowDef.Partitions f.WindowDef.Partitions = make(tree.Exprs, len(oldPartitions)) diff --git a/pkg/sql/opt/optbuilder/srfs.go b/pkg/sql/opt/optbuilder/srfs.go index 471191265e85..76965329580a 100644 --- a/pkg/sql/opt/optbuilder/srfs.go +++ b/pkg/sql/opt/optbuilder/srfs.go @@ -50,6 +50,9 @@ func (s *srf) TypeCheck( // invalid usage here. return nil, tree.NewInvalidFunctionUsageError(tree.GeneratorClass, ctx.TypeCheckContext()) } + if ctx.Properties.Ancestors.Has(tree.ConditionalAncestor) { + return nil, tree.NewInvalidFunctionUsageError(tree.GeneratorClass, "conditional expressions") + } if ctx.Properties.Derived.SeenGenerator { // This error happens if this srf struct is nested inside a raw srf that // has not yet been replaced. This is possible since scope.replaceSRF first diff --git a/pkg/sql/opt/optbuilder/testdata/aggregate b/pkg/sql/opt/optbuilder/testdata/aggregate index f3ea910ebd01..7f2e2cc0a09b 100644 --- a/pkg/sql/opt/optbuilder/testdata/aggregate +++ b/pkg/sql/opt/optbuilder/testdata/aggregate @@ -2616,7 +2616,7 @@ sort # Grouping columns cannot be reused inside an aggregate input expression # because the aggregate input expressions and grouping expressions are -# built as part of the same projection. +# built as part of the same projection. build SELECT max((k+v)/(k-v)) AS r, (k+v)*(k-v) AS s FROM kv GROUP BY k+v, k-v ---- diff --git a/pkg/sql/randgen/expr.go b/pkg/sql/randgen/expr.go index 86403cc592cd..412710e55653 100644 --- a/pkg/sql/randgen/expr.go +++ b/pkg/sql/randgen/expr.go @@ -161,8 +161,8 @@ func randExpr( } } if len(cols) > 1 { - // If any of the columns are nullable, set the computed column to be - // nullable. + // If any of the columns are nullable, the resulting expression + // could be null. for _, x := range cols { if x.Nullable.Nullability != tree.NotNull { nullability = x.Nullable.Nullability diff --git a/pkg/sql/schemachanger/scbuild/builder_state.go b/pkg/sql/schemachanger/scbuild/builder_state.go index 18d9a5273a70..6ba485d46ca0 100644 --- a/pkg/sql/schemachanger/scbuild/builder_state.go +++ b/pkg/sql/schemachanger/scbuild/builder_state.go @@ -1591,20 +1591,6 @@ func (b *builderState) serializeUserDefinedTypes(queryStr string) string { default: return true, expr, nil } - // We cannot type-check subqueries without using optbuilder, and there - // is no need to because we only need to rewrite string values that are - // directly cast to enums. For example, we must rewrite the 'foo' in: - // - // SELECT 'foo'::myenum - // - // We don't need to rewrite the 'foo' in the query below, which can be - // corrupted by renaming the 'foo' value in the myenum type. - // - // SELECT (SELECT 'foo')::myenum - // - if _, ok := innerExpr.(*tree.Subquery); ok { - return true, expr, nil - } var typ *types.T typ, err = tree.ResolveType(b.ctx, typRef, b.semaCtx.TypeResolver) if err != nil { @@ -1613,6 +1599,13 @@ func (b *builderState) serializeUserDefinedTypes(queryStr string) string { if !typ.UserDefined() { return true, expr, nil } + { + // We cannot type-check subqueries without using optbuilder, so we + // currently do not support casting expressions with subqueries to + // UDTs. + defer b.semaCtx.Properties.Restore(b.semaCtx.Properties) + b.semaCtx.Properties.Require("casts to enums within UDFs", tree.RejectSubqueries) + } texpr, err := innerExpr.TypeCheck(b.ctx, b.semaCtx, typ) if err != nil { return false, expr, err diff --git a/pkg/sql/sem/tree/type_check.go b/pkg/sql/sem/tree/type_check.go index 0dc19c51df78..1eedea54d05c 100644 --- a/pkg/sql/sem/tree/type_check.go +++ b/pkg/sql/sem/tree/type_check.go @@ -82,17 +82,21 @@ type SemaContext struct { // Restore() method, see below. type SemaProperties struct { // required constraints type checking to only accept certain kinds - // of expressions. See SetConstraint + // of expressions. See Require. required semaRequirements // Derived is populated during semantic analysis with properties // from the expression being analyzed. The caller is responsible // for re-initializing this when needed. Derived ScalarProperties + + // Ancestors is mutated during semantic analysis to provide contextual + // information for each descendent during traversal of sub-expressions. + Ancestors ScalarAncestors } type semaRequirements struct { - // context is the name of the semantic anlysis context, for use in + // context is the name of the semantic analysis context, for use in // error messages. context string @@ -102,11 +106,14 @@ type semaRequirements struct { rejectFlags SemaRejectFlags } -// Require resets the derived properties and sets required constraints. +// Require resets the derived properties and the scalar ancestors, and sets +// required constraints. It must only be called before starting semantic +// analysis and during traversal by semantic analysis itself. func (s *SemaProperties) Require(context string, rejectFlags SemaRejectFlags) { s.required.context = context s.required.rejectFlags = rejectFlags s.Derived.Clear() + s.Ancestors.clear() } // IsSet checks if the given rejectFlag is set as a required property. @@ -180,16 +187,6 @@ type ScalarProperties struct { // SeenGenerator is set to true if the expression originally // contained a SRF. SeenGenerator bool - - // inFuncExpr is temporarily set to true while type checking the - // parameters of a function. Used to process RejectNestedGenerators - // properly. - inFuncExpr bool - - // InWindowFunc is temporarily set to true while type checking the - // parameters of a window function in order to reject nested window - // functions. - InWindowFunc bool } // Clear resets the scalar properties to defaults. @@ -197,6 +194,50 @@ func (sp *ScalarProperties) Clear() { *sp = ScalarProperties{} } +// ScalarAncestors provides context for the current scalar expression during +// semantic analysis. Ancestors are temporarily modified by expressions so that +// their descendent expressions can be analyzed with respect to their ancestors. +type ScalarAncestors byte + +const ( + // FuncExprAncestor is temporarily added to ScalarAncestors while type + // checking the parameters of a function. Used to process + // RejectNestedGenerators properly. + FuncExprAncestor ScalarAncestors = 1 << iota + + // WindowFuncAncestor is temporarily added to ScalarAncestors while type + // checking the parameters of a window function in order to reject nested + // window functions. + WindowFuncAncestor + + // ConditionalAncestor is temporarily added to ScalarAncestors while type + // checking condition expressions CASE, COALESCE, and IF. Used to reject + // set-returning functions within conditional expressions. + ConditionalAncestor +) + +// Push adds the given ancestor to s. +func (s *ScalarAncestors) Push(other ScalarAncestors) { + *s = *s | other +} + +// Has returns true if s has the given ancestor. +func (s ScalarAncestors) Has(other ScalarAncestors) bool { + return s&other != 0 +} + +// PopTo returns s to the given set of ancestors. Use with: +// +// defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) +func (s *ScalarAncestors) PopTo(orig ScalarAncestors) { + *s = orig +} + +// clear resets s to the default set of ancestors. +func (s *ScalarAncestors) clear() { + *s = 0 +} + // MakeSemaContext initializes a simple SemaContext suitable // for "lightweight" type checking such as the one performed for default // expressions. @@ -388,11 +429,8 @@ func (expr *CaseExpr) TypeCheck( ctx context.Context, semaCtx *SemaContext, desired *types.T, ) (TypedExpr, error) { if semaCtx != nil { - // We need to save and restore the previous value of the field in - // semaCtx in case we are recursively called within a subquery - // context. - defer semaCtx.Properties.Restore(semaCtx.Properties) - semaCtx.Properties.Require("CASE", RejectGenerators) + defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) + semaCtx.Properties.Ancestors.Push(ConditionalAncestor) } var err error tmpExprs := make([]Expr, 0, len(expr.Whens)+1) @@ -606,7 +644,7 @@ func (expr *CastExpr) TypeCheck( castFrom := typedSubExpr.ResolvedType() allowStable := true context := "" - if semaCtx != nil && semaCtx.Properties.required.rejectFlags&RejectStableOperators != 0 { + if semaCtx != nil && semaCtx.Properties.IsSet(RejectStableOperators) { allowStable = false context = semaCtx.Properties.required.context } @@ -841,11 +879,8 @@ func (expr *CoalesceExpr) TypeCheck( ctx context.Context, semaCtx *SemaContext, desired *types.T, ) (TypedExpr, error) { if semaCtx != nil { - // We need to save and restore the previous value of the field in - // semaCtx in case we are recursively called within a subquery - // context. - defer semaCtx.Properties.Restore(semaCtx.Properties) - semaCtx.Properties.Require("COALESCE", RejectGenerators) + defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) + semaCtx.Properties.Ancestors.Push(ConditionalAncestor) } typedSubExprs, retType, err := typeCheckSameTypedExprs(ctx, semaCtx, desired, expr.Exprs...) if err != nil { @@ -962,12 +997,12 @@ func (sc *SemaContext) checkFunctionUsage(expr *FuncExpr, def *ResolvedFunctionD return err } if expr.IsWindowFunctionApplication() { - if sc.Properties.required.rejectFlags&RejectWindowApplications != 0 { + if sc.Properties.IsSet(RejectWindowApplications) { return NewInvalidFunctionUsageError(WindowClass, sc.Properties.required.context) } - if sc.Properties.Derived.InWindowFunc && - sc.Properties.required.rejectFlags&RejectNestedWindowFunctions != 0 { + if sc.Properties.Ancestors.Has(WindowFuncAncestor) && + sc.Properties.IsSet(RejectNestedWindowFunctions) { return pgerror.Newf(pgcode.Windowing, "window function calls cannot be nested") } sc.Properties.Derived.SeenWindowApplication = true @@ -975,24 +1010,27 @@ func (sc *SemaContext) checkFunctionUsage(expr *FuncExpr, def *ResolvedFunctionD // If it is an aggregate function *not used OVER a window*, then // we have an aggregation. if fnCls == AggregateClass { - if sc.Properties.Derived.inFuncExpr && - sc.Properties.required.rejectFlags&RejectNestedAggregates != 0 { + if sc.Properties.Ancestors.Has(FuncExprAncestor) && + sc.Properties.IsSet(RejectNestedAggregates) { return NewAggInAggError() } - if sc.Properties.required.rejectFlags&RejectAggregates != 0 { + if sc.Properties.IsSet(RejectAggregates) { return NewInvalidFunctionUsageError(AggregateClass, sc.Properties.required.context) } sc.Properties.Derived.SeenAggregate = true } } if fnCls == GeneratorClass { - if sc.Properties.Derived.inFuncExpr && - sc.Properties.required.rejectFlags&RejectNestedGenerators != 0 { + if sc.Properties.Ancestors.Has(FuncExprAncestor) && + sc.Properties.IsSet(RejectNestedGenerators) { return NewInvalidNestedSRFError(sc.Properties.required.context) } - if sc.Properties.required.rejectFlags&RejectGenerators != 0 { + if sc.Properties.IsSet(RejectGenerators) { return NewInvalidFunctionUsageError(GeneratorClass, sc.Properties.required.context) } + if sc.Properties.Ancestors.Has(ConditionalAncestor) { + return NewInvalidFunctionUsageError(GeneratorClass, "conditional expressions") + } sc.Properties.Derived.SeenGenerator = true } return nil @@ -1023,7 +1061,7 @@ func (sc *SemaContext) checkVolatility(v volatility.V) error { } switch v { case volatility.Volatile: - if sc.Properties.required.rejectFlags&RejectVolatileFunctions != 0 { + if sc.Properties.IsSet(RejectVolatileFunctions) { // The code FeatureNotSupported is a bit misleading here, // because we probably can't support the feature at all. However // this error code matches PostgreSQL's in the same conditions. @@ -1031,7 +1069,7 @@ func (sc *SemaContext) checkVolatility(v volatility.V) error { "volatile functions are not allowed in %s", sc.Properties.required.context) } case volatility.Stable: - if sc.Properties.required.rejectFlags&RejectStableOperators != 0 { + if sc.Properties.IsSet(RejectStableOperators) { return NewContextDependentOpsNotAllowedError(sc.Properties.required.context) } } @@ -1080,23 +1118,15 @@ func (expr *FuncExpr) TypeCheck( } if semaCtx != nil { - // We'll need to remember we are in a function application to - // generate suitable errors in checkFunctionUsage(). We cannot - // set ctx.inFuncExpr earlier (in particular not before the call - // to checkFunctionUsage() above) because the top-level FuncExpr - // must be acceptable even if it is a SRF and - // RejectNestedGenerators is set. - defer func(semaCtx *SemaContext, prevFunc bool, prevWindow bool) { - semaCtx.Properties.Derived.inFuncExpr = prevFunc - semaCtx.Properties.Derived.InWindowFunc = prevWindow - }( - semaCtx, - semaCtx.Properties.Derived.inFuncExpr, - semaCtx.Properties.Derived.InWindowFunc, - ) - semaCtx.Properties.Derived.inFuncExpr = true + // We'll need to remember we are in a function application to generate + // suitable errors in checkFunctionUsage(). We cannot enter + // FuncExprAncestor earlier (in particular not before the call to + // checkFunctionUsage() above) because the top-level FuncExpr must be + // acceptable even if it is a SRF and RejectNestedGenerators is set. + defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) + semaCtx.Properties.Ancestors.Push(FuncExprAncestor) if expr.WindowDef != nil { - semaCtx.Properties.Derived.InWindowFunc = true + semaCtx.Properties.Ancestors.Push(WindowFuncAncestor) } } @@ -1327,11 +1357,8 @@ func (expr *IfExpr) TypeCheck( ctx context.Context, semaCtx *SemaContext, desired *types.T, ) (TypedExpr, error) { if semaCtx != nil { - // We need to save and restore the previous value of the field in - // semaCtx in case we are recursively called within a subquery - // context. - defer semaCtx.Properties.Restore(semaCtx.Properties) - semaCtx.Properties.Require("IF", RejectGenerators) + defer semaCtx.Properties.Ancestors.PopTo(semaCtx.Properties.Ancestors) + semaCtx.Properties.Ancestors.Push(ConditionalAncestor) } typedCond, err := typeCheckAndRequireBoolean(ctx, semaCtx, expr.Cond, "IF condition") if err != nil { @@ -1529,7 +1556,7 @@ func (expr *RangeCond) TypeCheck( // TypeCheck implements the Expr interface. func (expr *Subquery) TypeCheck(_ context.Context, sc *SemaContext, _ *types.T) (TypedExpr, error) { - if sc != nil && sc.Properties.required.rejectFlags&RejectSubqueries != 0 { + if sc != nil && sc.Properties.IsSet(RejectSubqueries) { return nil, pgerror.Newf(pgcode.FeatureNotSupported, "subqueries are not allowed in %s", sc.Properties.required.context) }