Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
112725: sql: disallow refcursor comparisons during type-checking r=DrewKimball a=DrewKimball

This patch adds back the comparison functions for the `REFCURSOR` data type, since there are various points within the codebase where we rely on their existence. Users are still not allowed to use the comparison functions, but now this is checked during type-checking of the AST, rather than at execution-time. This should avoid internal errors from places in planning and execution that expect comparison overloads to exist, but also maintains parity with postgres.

Fixes #112365
Fixes #112642
Fixes #112362
Fixes #112368

Release note: None

112891: sql: fix for crash with null elements on jsonb_array_to_string_array r=maryliag a=maryliag

Fixes #112829

The builtin `jsonb_array_to_string_array` was crashing, when it had `null` elements. This commit handles this case, removing them from the final array.

Release note: None

112893: kv: tolerate lock acquisition replay with writes in same batch r=arulajmani a=nvanbenschoten

Informs #112221.
Informs #112174.
Informs #112173.
Informs #111984.
Informs #111893.
Informs #111564.
Informs #111530.

This commit fixes the handling of replayed batches that contain a replicated lock acquisition and a later write to the same key. In such cases, the write at the higher sequence number should not be detected as an error during the replay. Instead, it should simply be ignored.

Release note: None

Co-authored-by: Drew Kimball <[email protected]>
Co-authored-by: maryliag <[email protected]>
Co-authored-by: Nathan VanBenschoten <[email protected]>
  • Loading branch information
4 people committed Oct 23, 2023
4 parents f94626d + 6c0823f + e9e9b33 + e2cfb79 commit a3d5317
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/generated/sql/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@
</span></td><td>Immutable</td></tr>
<tr><td><a name="cardinality"></a><code>cardinality(input: anyelement[]) &rarr; <a href="int.html">int</a></code></td><td><span class="funcdesc"><p>Calculates the number of elements contained in <code>input</code></p>
</span></td><td>Immutable</td></tr>
<tr><td><a name="jsonb_array_to_string_array"></a><code>jsonb_array_to_string_array(input: jsonb) &rarr; <a href="string.html">string</a>[]</code></td><td><span class="funcdesc"><p>Convert a JSONB array into a string array.</p>
<tr><td><a name="jsonb_array_to_string_array"></a><code>jsonb_array_to_string_array(input: jsonb) &rarr; <a href="string.html">string</a>[]</code></td><td><span class="funcdesc"><p>Convert a JSONB array into a string array, removing ‘null’ elements.</p>
</span></td><td>Immutable</td></tr>
<tr><td><a name="string_to_array"></a><code>string_to_array(str: <a href="string.html">string</a>, delimiter: <a href="string.html">string</a>) &rarr; <a href="string.html">string</a>[]</code></td><td><span class="funcdesc"><p>Split a string into components on a delimiter.</p>
</span></td><td>Immutable</td></tr>
Expand Down
6 changes: 5 additions & 1 deletion docs/generated/sql/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
<tr><td>oid <code><</code> <a href="int.html">int</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td>oid <code><</code> oid</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>pg_lsn <code><</code> pg_lsn</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code><</code> refcursor</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string</a> <code><</code> <a href="string.html">string</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string[]</a> <code><</code> <a href="string.html">string[]</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="time.html">time</a> <code><</code> <a href="time.html">time</a></td><td><a href="bool.html">bool</a></td></tr>
Expand Down Expand Up @@ -257,6 +258,7 @@
<tr><td>oid <code><=</code> <a href="int.html">int</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td>oid <code><=</code> oid</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>pg_lsn <code><=</code> pg_lsn</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code><=</code> refcursor</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string</a> <code><=</code> <a href="string.html">string</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string[]</a> <code><=</code> <a href="string.html">string[]</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="time.html">time</a> <code><=</code> <a href="time.html">time</a></td><td><a href="bool.html">bool</a></td></tr>
Expand Down Expand Up @@ -320,6 +322,7 @@
<tr><td>oid <code>=</code> <a href="int.html">int</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td>oid <code>=</code> oid</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>pg_lsn <code>=</code> pg_lsn</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code>=</code> refcursor</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string</a> <code>=</code> <a href="string.html">string</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string[]</a> <code>=</code> <a href="string.html">string[]</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="time.html">time</a> <code>=</code> <a href="time.html">time</a></td><td><a href="bool.html">bool</a></td></tr>
Expand Down Expand Up @@ -400,6 +403,7 @@
<tr><td>jsonb <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>oid <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>pg_lsn <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string</a> <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="time.html">time</a> <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="timestamp.html">timestamp</a> <code>IN</code> tuple</td><td><a href="bool.html">bool</a></td></tr>
Expand Down Expand Up @@ -447,7 +451,7 @@
<tr><td>oid <code>IS NOT DISTINCT FROM</code> <a href="int.html">int</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td>oid <code>IS NOT DISTINCT FROM</code> oid</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>pg_lsn <code>IS NOT DISTINCT FROM</code> pg_lsn</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code>IS NOT DISTINCT FROM</code> unknown</td><td><a href="bool.html">bool</a></td></tr>
<tr><td>refcursor <code>IS NOT DISTINCT FROM</code> refcursor</td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string</a> <code>IS NOT DISTINCT FROM</code> <a href="string.html">string</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="string.html">string[]</a> <code>IS NOT DISTINCT FROM</code> <a href="string.html">string[]</a></td><td><a href="bool.html">bool</a></td></tr>
<tr><td><a href="time.html">time</a> <code>IS NOT DISTINCT FROM</code> <a href="time.html">time</a></td><td><a href="bool.html">bool</a></td></tr>
Expand Down
20 changes: 20 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/array
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,26 @@ SELECT jsonb_array_to_string_array(j -> 'a') FROM str_arr ORDER BY j ASC
query error input argument must be JSON array type
SELECT jsonb_array_to_string_array(j -> 'b') FROM str_arr ORDER BY j ASC

query T
SELECT jsonb_array_to_string_array('[1, 2, 3]':::JSONB)
----
{1,2,3}

query T
SELECT jsonb_array_to_string_array('[true, false, false, true]':::JSONB)
----
{true,false,false,true}

query T
SELECT jsonb_array_to_string_array('[null]':::JSONB)
----
{}

query T
SELECT jsonb_array_to_string_array('["foo", null]':::JSONB)
----
{foo}

# Regression test for #23429.

statement ok
Expand Down
66 changes: 38 additions & 28 deletions pkg/sql/logictest/testdata/logic_test/refcursor
Original file line number Diff line number Diff line change
Expand Up @@ -394,64 +394,79 @@ NULL NULL NULL foo NULL NULL NULL NULL NULL NULL NULL NULL
# REFCURSOR doesn't support any comparisons (with an exception mentioned below).
subtest comparisons

# TODO(drewk): The error code should be 42883.
statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR = 'foo'::REFCURSOR;

# TODO(drewk): The error code should be 42883.
statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR = 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR = NULL;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR < 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR < 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR < NULL;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR > 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR > 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR > NULL;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR <= 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR <= 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR <= NULL;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR >= 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR >= 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR >= NULL;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM 'foo'::TEXT;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM 'foo'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM 'foo'::TEXT;

statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR BETWEEN 'foo'::REFCURSOR AND 'bar'::REFCURSOR;

statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR BETWEEN 'foo'::TEXT AND 'bar'::TEXT;

statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR BETWEEN NULL AND NULL;

statement error pgcode 42883 pq: unsupported comparison operator
SELECT NULLIF('foo'::REFCURSOR, 'bar'::REFCURSOR);

statement error pgcode 42883 pq: unsupported comparison operator
SELECT NULLIF('foo'::REFCURSOR, NULL);

# Regression test for #112010 - REFCURSOR should allow IS NULL and
# IS NOT NULL comparisons.
subtest is_null
Expand Down Expand Up @@ -514,17 +529,17 @@ true
true

# Comparison with column is not allowed.
statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM l FROM implicit_types;

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM l FROM implicit_types;

# Comparison with typed NULL is not allowed.
statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM (NULL::REFCURSOR);

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM (NULL::REFCURSOR);

# Comparison with CASE expression is not allowed.
Expand All @@ -534,23 +549,18 @@ SELECT 'foo'::REFCURSOR IS DISTINCT FROM (CASE WHEN true THEN NULL ELSE 1 END);
statement error pgcode 22023 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM (CASE WHEN true THEN NULL ELSE 1 END);

# The CASE operator is folded into an untyped NULL, so it's allowed.
# TODO(drewk): Postgres doesn't allow this case.
query B
# The CASE operator is folded into an untyped NULL.
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM (CASE WHEN true THEN NULL ELSE NULL END);
----
true

query B
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM (CASE WHEN true THEN NULL ELSE NULL END);
----
false

# The CASE operator is folded into a typed NULL, so it's not allowed.
statement error pgcode 22023 pq: unsupported comparison operator
# The CASE operator is folded into a typed NULL.
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS DISTINCT FROM (CASE WHEN true THEN NULL ELSE NULL::REFCURSOR END);

statement error pgcode 22023 pq: unsupported comparison operator
statement error pgcode 42883 pq: unsupported comparison operator
SELECT 'foo'::REFCURSOR IS NOT DISTINCT FROM (CASE WHEN true THEN NULL ELSE NULL::REFCURSOR END);

# Regression test for #112099 - REFCURSOR is not valid as an index column.
Expand Down
10 changes: 6 additions & 4 deletions pkg/sql/sem/builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -3566,14 +3566,16 @@ value if you rely on the HLC for accuracy.`,
if err != nil {
return nil, err
}
err = strArray.Append(tree.NewDString(*str))
if err != nil {
return nil, err
if str != nil {
err = strArray.Append(tree.NewDString(*str))
if err != nil {
return nil, err
}
}
}
return strArray, nil
},
Info: "Convert a JSONB array into a string array.",
Info: "Convert a JSONB array into a string array, removing 'null' elements.",
Volatility: volatility.Immutable,
CalledOnNullInput: true,
}),
Expand Down
9 changes: 5 additions & 4 deletions pkg/sql/sem/tree/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,7 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeEqFn(types.Jsonb, types.Jsonb, volatility.Immutable),
makeEqFn(types.Oid, types.Oid, volatility.Leakproof),
makeEqFn(types.PGLSN, types.PGLSN, volatility.Leakproof),
makeEqFn(types.RefCursor, types.RefCursor, volatility.Leakproof),
makeEqFn(types.String, types.String, volatility.Leakproof),
makeEqFn(types.Time, types.Time, volatility.Leakproof),
makeEqFn(types.TimeTZ, types.TimeTZ, volatility.Leakproof),
Expand Down Expand Up @@ -1601,6 +1602,7 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeLtFn(types.Interval, types.Interval, volatility.Leakproof),
makeLtFn(types.Oid, types.Oid, volatility.Leakproof),
makeLtFn(types.PGLSN, types.PGLSN, volatility.Leakproof),
makeLtFn(types.RefCursor, types.RefCursor, volatility.Leakproof),
makeLtFn(types.String, types.String, volatility.Leakproof),
makeLtFn(types.Time, types.Time, volatility.Leakproof),
makeLtFn(types.TimeTZ, types.TimeTZ, volatility.Leakproof),
Expand Down Expand Up @@ -1659,6 +1661,7 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeLeFn(types.Interval, types.Interval, volatility.Leakproof),
makeLeFn(types.Oid, types.Oid, volatility.Leakproof),
makeLeFn(types.PGLSN, types.PGLSN, volatility.Leakproof),
makeLeFn(types.RefCursor, types.RefCursor, volatility.Leakproof),
makeLeFn(types.String, types.String, volatility.Leakproof),
makeLeFn(types.Time, types.Time, volatility.Leakproof),
makeLeFn(types.TimeTZ, types.TimeTZ, volatility.Leakproof),
Expand Down Expand Up @@ -1738,6 +1741,7 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeIsFn(types.Jsonb, types.Jsonb, volatility.Immutable),
makeIsFn(types.Oid, types.Oid, volatility.Leakproof),
makeIsFn(types.PGLSN, types.PGLSN, volatility.Leakproof),
makeIsFn(types.RefCursor, types.RefCursor, volatility.Leakproof),
makeIsFn(types.String, types.String, volatility.Leakproof),
makeIsFn(types.Time, types.Time, volatility.Leakproof),
makeIsFn(types.TimeTZ, types.TimeTZ, volatility.Leakproof),
Expand Down Expand Up @@ -1775,10 +1779,6 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeIsFn(types.Void, types.Unknown, volatility.Leakproof),
makeIsFn(types.Unknown, types.Void, volatility.Leakproof),

// REFCURSOR cannot be compared with itself, but as a special case, it can
// be compared with NULL using IS NOT DISTINCT FROM.
makeIsFn(types.RefCursor, types.Unknown, volatility.Leakproof),

// Tuple comparison.
{
LeftType: types.AnyTuple,
Expand Down Expand Up @@ -1809,6 +1809,7 @@ var CmpOps = cmpOpFixups(map[treecmp.ComparisonOperatorSymbol]*CmpOpOverloads{
makeEvalTupleIn(types.Jsonb, volatility.Leakproof),
makeEvalTupleIn(types.Oid, volatility.Leakproof),
makeEvalTupleIn(types.PGLSN, volatility.Leakproof),
makeEvalTupleIn(types.RefCursor, volatility.Leakproof),
makeEvalTupleIn(types.String, volatility.Leakproof),
makeEvalTupleIn(types.Time, volatility.Leakproof),
makeEvalTupleIn(types.TimeTZ, volatility.Leakproof),
Expand Down
63 changes: 63 additions & 0 deletions pkg/sql/sem/tree/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,11 @@ func (expr *ComparisonExpr) TypeCheck(
expr.Left,
expr.Right,
)
if err == nil {
err = checkRefCursorComparison(
expr.SubOperator.Symbol, leftTyped.ResolvedType(), rightTyped.ResolvedType(),
)
}
} else {
leftTyped, rightTyped, cmpOp, alwaysNull, err = typeCheckComparisonOp(
ctx,
Expand All @@ -947,6 +952,11 @@ func (expr *ComparisonExpr) TypeCheck(
expr.Left,
expr.Right,
)
if err == nil {
err = checkRefCursorComparison(
expr.Operator.Symbol, leftTyped.ResolvedType(), rightTyped.ResolvedType(),
)
}
}
if err != nil {
return nil, err
Expand Down Expand Up @@ -1535,6 +1545,10 @@ func (expr *NullIfExpr) TypeCheck(
leftType, op, rightType)
return nil, decorateTypeCheckError(err, "incompatible NULLIF expressions")
}
err = checkRefCursorComparison(treecmp.EQ, leftType, rightType)
if err != nil {
return nil, err
}

expr.Expr1, expr.Expr2 = typedSubExprs[0], typedSubExprs[1]
expr.typ = retType
Expand Down Expand Up @@ -1616,10 +1630,20 @@ func (expr *RangeCond) TypeCheck(
ctx context.Context, semaCtx *SemaContext, desired *types.T,
) (TypedExpr, error) {
leftFromTyped, fromTyped, _, _, err := typeCheckComparisonOp(ctx, semaCtx, treecmp.MakeComparisonOperator(treecmp.GT), expr.Left, expr.From)
if err == nil {
err = checkRefCursorComparison(
treecmp.GT, leftFromTyped.ResolvedType(), fromTyped.ResolvedType(),
)
}
if err != nil {
return nil, err
}
leftToTyped, toTyped, _, _, err := typeCheckComparisonOp(ctx, semaCtx, treecmp.MakeComparisonOperator(treecmp.LT), expr.Left, expr.To)
if err == nil {
err = checkRefCursorComparison(
treecmp.LT, leftToTyped.ResolvedType(), toTyped.ResolvedType(),
)
}
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -3478,3 +3502,42 @@ func CheckUnsupportedType(ctx context.Context, semaCtx *SemaContext, typ *types.
}
return semaCtx.UnsupportedTypeChecker.CheckType(ctx, typ)
}

// checkRefCursorComparison checks whether the given types are or contain the
// REFCURSOR data type, which is invalid for comparison. We don't simply remove
// the relevant comparison overloads because we rely on their existence in
// various locations throughout the codebase.
func checkRefCursorComparison(op treecmp.ComparisonOperatorSymbol, left, right *types.T) error {
if (op == treecmp.IsNotDistinctFrom || op == treecmp.IsDistinctFrom) &&
(left.Family() == types.RefCursorFamily && right == types.Unknown) {
// Special case: "REFCURSOR IS [NOT] DISTINCT FROM NULL" is allowed.
return nil
}
if left.Family() == types.RefCursorFamily || right.Family() == types.RefCursorFamily {
return pgerror.Newf(pgcode.UndefinedFunction,
"unsupported comparison operator: %s %s %s", left, op, right,
)
}
var checkRecursive func(*types.T) bool
checkRecursive = func(typ *types.T) bool {
switch typ.Family() {
case types.RefCursorFamily:
return true
case types.TupleFamily:
for _, t := range typ.TupleContents() {
if checkRecursive(t) {
return true
}
}
case types.ArrayFamily:
return checkRecursive(typ.ArrayContents())
}
return false
}
if checkRecursive(left) || checkRecursive(right) {
return pgerror.Newf(pgcode.UndefinedFunction,
"could not identify a comparison function for type refcursor",
)
}
return nil
}
Loading

0 comments on commit a3d5317

Please sign in to comment.