Skip to content

Commit

Permalink
cast: remove invalid casts for REFCURSOR
Browse files Browse the repository at this point in the history
The initial implementation for the `REFCURSOR` data type included
assignment casts from and explicit casts to every other type. In postgres,
`REFCURSOR` has only the following valid casts:
1. Explicit casts from each string type.
2. Assignment casts to each string type.

This patch aligns the casts for `REFCURSOR` with those of postgres, and
adds according tests.

Informs cockroachdb#111560

Release note: None
  • Loading branch information
DrewKimball committed Oct 6, 2023
1 parent 43e9e39 commit e5c5577
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 313 deletions.
210 changes: 197 additions & 13 deletions pkg/sql/logictest/testdata/logic_test/refcursor
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ statement ok
ALTER TABLE xy ADD COLUMN curs REFCURSOR;

statement ok
INSERT INTO xy VALUES (1, 2, 'foo');
INSERT INTO xy VALUES (1, 2, 'foo'::REFCURSOR);

query IIT
SELECT * FROM xy;
Expand All @@ -38,14 +38,14 @@ SELECT * FROM xy;
# Alter a column type to REFCURSOR.
statement ok
DROP TABLE IF EXISTS xy;
CREATE TABLE xy (x INT, y INT);
CREATE TABLE xy (x INT, y TEXT);
SET enable_experimental_alter_column_type_general=true;

statement ok
ALTER TABLE xy ALTER COLUMN y TYPE REFCURSOR;

statement ok
INSERT INTO xy VALUES (1, 'bar');
INSERT INTO xy VALUES (1, 'bar'::REFCURSOR);

query IT
SELECT * FROM xy;
Expand All @@ -60,10 +60,10 @@ INSERT INTO xy VALUES (3, 4);

# Create a partial index that uses the REFCURSOR type.
statement ok
CREATE INDEX part ON xy (x) WHERE y::REFCURSOR < '3';
CREATE INDEX part ON xy (x) WHERE y::TEXT::REFCURSOR < '3';

query II
SELECT * FROM xy@part WHERE y::REFCURSOR < '3';
SELECT * FROM xy@part WHERE y::TEXT::REFCURSOR < '3';
----
1 2

Expand All @@ -74,7 +74,7 @@ INSERT INTO xy VALUES (1, 2);

# Add a check constraint that uses the REFCURSOR type.
statement ok
ALTER TABLE xy ADD CONSTRAINT bar CHECK (y::REFCURSOR < 'baz');
ALTER TABLE xy ADD CONSTRAINT bar CHECK (y::TEXT::REFCURSOR < 'baz');

query II
SELECT * FROM xy;
Expand All @@ -88,7 +88,7 @@ statement ok
CREATE TYPE typ AS (x INT, y REFCURSOR);

query T
SELECT (100, 'bar')::typ;
SELECT (100, 'bar'::REFCURSOR)::typ;
----
(100,bar)

Expand All @@ -97,7 +97,7 @@ subtest function
# Function that returns REFCURSOR.
statement ok
CREATE FUNCTION f() RETURNS REFCURSOR AS $$
SELECT 'foo';
SELECT 'foo'::REFCURSOR;
$$ LANGUAGE SQL;

query T
Expand All @@ -111,7 +111,7 @@ DROP FUNCTION f;
statement ok
CREATE FUNCTION f() RETURNS REFCURSOR AS $$
BEGIN
RETURN 'foo';
RETURN 'foo'::REFCURSOR;
END
$$ LANGUAGE PLpgSQL;

Expand All @@ -130,7 +130,7 @@ CREATE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
$$ LANGUAGE SQL;

query I
SELECT f('foo');
SELECT f('foo'::REFCURSOR);
----
0

Expand All @@ -145,7 +145,7 @@ CREATE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
$$ LANGUAGE PLpgSQL;

query I
SELECT f('foo');
SELECT f('foo'::REFCURSOR);
----
0

Expand Down Expand Up @@ -186,7 +186,7 @@ DROP FUNCTION f;
# Function that returns a composite type with REFCURSOR component.
statement ok
CREATE FUNCTION f() RETURNS typ AS $$
SELECT (1, 'foo');
SELECT (1, 'foo'::REFCURSOR);
$$ LANGUAGE SQL;

query T
Expand All @@ -200,7 +200,7 @@ DROP FUNCTION f;
statement ok
CREATE FUNCTION f() RETURNS typ AS $$
BEGIN
RETURN (1, 'foo');
RETURN (1, 'foo'::REFCURSOR);
END
$$ LANGUAGE PLpgSQL;

Expand All @@ -221,4 +221,188 @@ foo
statement error pgcode 42601 syntax error
SELECT 'foo'::REFCURSOR(2);

# Testing casts.
statement ok
CREATE TABLE implicit_types (
a TEXT, b CHAR, c VARCHAR, d NAME, e INT, f FLOAT, g DECIMAL, h BOOL,
i INTERVAL, j DATE, k TIMESTAMP, l REFCURSOR
);

# Only the string types can be explicitly cast to REFCURSOR.
subtest explicit_cast_to

query T
SELECT NULL::REFCURSOR;
----
NULL

query T
SELECT 'foo'::TEXT::REFCURSOR;
----
foo

query T
SELECT 'a'::CHAR::REFCURSOR;
----
a

query T
SELECT 'foo'::VARCHAR::REFCURSOR;
----
foo

query T
SELECT 'foo'::NAME::REFCURSOR;
----
foo

statement error pgcode 42846 pq: invalid cast: int -> refcursor
SELECT 1::INT::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: float -> refcursor
SELECT 1.0::FLOAT::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: decimal -> refcursor
SELECT 1.0::DECIMAL::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: bool -> refcursor
SELECT False::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: interval -> refcursor
SELECT '34h2s'::INTERVAL::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: date -> refcursor
SELECT '2015-08-30'::DATE::REFCURSOR;

statement error pgcode 42846 pq: invalid cast: timestamp -> refcursor
SELECT '2015-08-30 03:34:45.34567'::TIMESTAMP::REFCURSOR;

# Only the string types can be explicitly cast from REFCURSOR.
subtest explicit_cast_from

query T
SELECT 'foo'::REFCURSOR::TEXT;
----
foo

query T
SELECT 'a'::REFCURSOR::CHAR;
----
a

query T
SELECT 'foo'::REFCURSOR::VARCHAR;
----
foo

query T
SELECT 'foo'::REFCURSOR::NAME;
----
foo

statement error pgcode 42846 pq: invalid cast: refcursor -> int
SELECT '1'::REFCURSOR::INT;

statement error pgcode 42846 pq: invalid cast: refcursor -> float
SELECT '1.0'::REFCURSOR::FLOAT;

statement error pgcode 42846 pq: invalid cast: refcursor -> decimal
SELECT '1.0'::REFCURSOR::DECIMAL;

statement error pgcode 42846 pq: invalid cast: refcursor -> bool
SELECT 'False'::REFCURSOR::BOOL;

statement error pgcode 42846 pq: invalid cast: refcursor -> interval
SELECT '34h2s'::REFCURSOR::INTERVAL;

statement error pgcode 42846 pq: invalid cast: refcursor -> date
SELECT '2015-08-30'::REFCURSOR::DATE;

statement error pgcode 42846 pq: invalid cast: refcursor -> timestamp
SELECT '2015-08-30 03:34:45.34567'::REFCURSOR::TIMESTAMP;

# There are no implicit casts to REFCURSOR.
subtest implicit_cast_to

statement ok
INSERT INTO implicit_types(l) VALUES (NULL);

statement error pgcode 42804 pq: value type string doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('foo'::TEXT);

statement error pgcode 42804 pq: value type char doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('a'::CHAR);

statement error pgcode 42804 pq: value type varchar doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('foo'::VARCHAR);

statement error pgcode 42804 pq: value type name doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('foo'::NAME);

statement error pgcode 42804 pq: value type int doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES (1::INT);

statement error pgcode 42804 pq: value type float doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES (1.0::FLOAT);

statement error pgcode 42804 pq: value type decimal doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES (1.0::DECIMAL);

statement error pgcode 42804 pq: value type bool doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES (False::BOOL);

statement error pgcode 42804 pq: value type interval doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('34h2s'::INTERVAL);

statement error pgcode 42804 pq: value type date doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('2015-08-30'::DATE);

statement error pgcode 42804 pq: value type timestamp doesn't match type refcursor of column \"l\"
INSERT INTO implicit_types(l) VALUES ('2015-08-30 03:34:45.34567'::TIMESTAMP);

# Only the strings types can be implicitly casted from refcursor.
subtest implicit_cast_from

statement ok
INSERT INTO implicit_types(a) VALUES ('foo'::REFCURSOR);

statement ok
INSERT INTO implicit_types(b) VALUES ('a'::REFCURSOR);

statement ok
INSERT INTO implicit_types(c) VALUES ('foo'::REFCURSOR);

statement ok
INSERT INTO implicit_types(d) VALUES ('foo'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type int of column \"e\"
INSERT INTO implicit_types(e) VALUES ('1'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type float of column \"f\"
INSERT INTO implicit_types(f) VALUES ('1.0'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type decimal of column \"g\"
INSERT INTO implicit_types(g) VALUES ('1.0'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type bool of column \"h\"
INSERT INTO implicit_types(h) VALUES ('False'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type interval of column \"i\"
INSERT INTO implicit_types(i) VALUES ('34h2s'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type date of column \"j\"
INSERT INTO implicit_types(j) VALUES ('2015-08-30'::REFCURSOR);

statement error pgcode 42804 pq: value type refcursor doesn't match type timestamp of column \"k\"
INSERT INTO implicit_types(k) VALUES ('2015-08-30 03:34:45.34567'::REFCURSOR);

query TTTTIRRBTTTT rowsort
SELECT * FROM implicit_types;
----
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
foo NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
NULL a NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
NULL NULL foo NULL NULL NULL NULL NULL NULL NULL NULL NULL
NULL NULL NULL foo NULL NULL NULL NULL NULL NULL NULL NULL

subtest end
32 changes: 7 additions & 25 deletions pkg/sql/sem/builtins/fixed_oids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2470,31 +2470,13 @@ var builtinOidsArray = []string{
2499: `refcursorout(refcursor: refcursor) -> bytes`,
2500: `refcursorsend(refcursor: refcursor) -> bytes`,
2501: `refcursorrecv(input: anyelement) -> refcursor`,
2502: `refcursor(time: time) -> refcursor`,
2503: `refcursor(tsvector: tsvector) -> refcursor`,
2504: `refcursor(date: date) -> refcursor`,
2505: `refcursor(float: float) -> refcursor`,
2506: `refcursor(decimal: decimal) -> refcursor`,
2507: `refcursor(timestamp: timestamp) -> refcursor`,
2508: `refcursor(void: void) -> refcursor`,
2509: `refcursor(bool: bool) -> refcursor`,
2510: `refcursor(bytes: bytes) -> refcursor`,
2511: `refcursor(jsonb: jsonb) -> refcursor`,
2512: `refcursor(timetz: timetz) -> refcursor`,
2513: `refcursor(tsquery: tsquery) -> refcursor`,
2514: `refcursor(bit: bit) -> refcursor`,
2515: `refcursor(interval: interval) -> refcursor`,
2516: `refcursor(string: string) -> refcursor`,
2517: `refcursor(pg_lsn: pg_lsn) -> refcursor`,
2518: `refcursor(int: int) -> refcursor`,
2519: `refcursor(uuid: uuid) -> refcursor`,
2520: `refcursor(inet: inet) -> refcursor`,
2521: `refcursor(timestamptz: timestamptz) -> refcursor`,
2522: `refcursor(box2d: box2d) -> refcursor`,
2523: `refcursor(geometry: geometry) -> refcursor`,
2524: `refcursor(oid: oid) -> refcursor`,
2525: `refcursor(tuple: tuple) -> refcursor`,
2526: `refcursor(geography: geography) -> refcursor`,
2502: `refcursor(refcursor: refcursor) -> refcursor`,
2503: `refcursor(string: string) -> refcursor`,
2504: `bpchar(refcursor: refcursor) -> char`,
2505: `char(refcursor: refcursor) -> "char"`,
2506: `name(refcursor: refcursor) -> name`,
2507: `text(refcursor: refcursor) -> string`,
2508: `varchar(refcursor: refcursor) -> varchar`,
}

var builtinOidsBySignature map[string]oid.Oid
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/sem/builtins/pgformat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ go_test(
"//pkg/util/log",
"//pkg/util/randutil",
"//pkg/util/timeutil",
"@com_github_lib_pq//oid",
"@com_github_stretchr_testify//require",
],
)
6 changes: 6 additions & 0 deletions pkg/sql/sem/builtins/pgformat/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/lib/pq/oid"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -59,6 +60,11 @@ func TestFormat(t *testing.T) {
return true
}
}
if typ.Oid() == oid.T_refcursor {
// TODO(drewk): this case is temporarily skipped until we split REFCURSOR
// into its own family.
return true
}
return !randgen.IsLegalColumnType(typ)
}
for _, typ := range types.OidToType {
Expand Down
Loading

0 comments on commit e5c5577

Please sign in to comment.