Skip to content

Commit

Permalink
sql: add support for REFCURSOR data type
Browse files Browse the repository at this point in the history
This patch adds support for the `REFCURSOR` type. It is a special type
used to hold the name of a cursor for PLpgSQL, although it can be used
in other contexts (like function return types or column types). `REFCURSOR`
is a string under the hood, and behaves like the `TEXT` data type in most
cases.

`REFCURSOR` has no casts in the `pg_cast` table; instead, all of its
casts are the "IO" string casts. That means that there is an assignment
cast from every type to `REFCURSOR`, and an explicit cast from `REFCURSOR`
to every other type (except other string types, which are again assignment).
See `ContextOriginAutomaticIOConversion` in `cast.go` for more info.

This patch also adds `REFCURSOR` to the checks from the previous commit,
so that statements with `REFCURSOR` are disallowed unless all nodes
are running v23.2.

Fixes #111560

Release note (sql change): Added support for the `REFCURSOR` data type.
`REFCURSOR` is a special string type that is used to handle cursors.
PLpgSQL cursor declarations are required to use a variable of type
`REFCURSOR`, and the name of a cursor can be passed to and from a
PLpgSQL function or procedure.
  • Loading branch information
DrewKimball committed Oct 4, 2023
1 parent efc6267 commit 3198d3b
Show file tree
Hide file tree
Showing 25 changed files with 881 additions and 148 deletions.
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions pkg/sql/catalog/colinfo/col_type_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ func ValidateColumnDefType(ctx context.Context, version clusterversion.Handle, t
return pgerror.Newf(pgcode.Syntax, `invalid locale %s`, t.Locale())
}
}
if t.Oid() == oid.T_refcursor {
if !version.IsActive(ctx, clusterversion.V23_2) {
return pgerror.Newf(
pgcode.FeatureNotSupported,
"refcursor not supported until version 23.2",
)
}
}

case types.DecimalFamily:
switch {
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/catalog/colinfo/column_type_properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func TestCanHaveCompositeKeyEncoding(t *testing.T) {
{types.Jsonb, true},
{types.Name, false},
{types.Oid, false},
{types.RefCursor, false},
{types.String, false},
{types.StringArray, false},
{types.Time, false},
Expand Down
32 changes: 31 additions & 1 deletion pkg/sql/logictest/testdata/logic_test/grant_table
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ test crdb_internal kv_flow_token_deductions publi
test crdb_internal kv_inherited_role_members public SELECT false
test crdb_internal kv_node_liveness public SELECT false
test crdb_internal kv_node_status public SELECT false
test crdb_internal kv_repairable_catalog_corruptions public SELECT false
test crdb_internal kv_protected_ts_records public SELECT false
test crdb_internal kv_repairable_catalog_corruptions public SELECT false
test crdb_internal kv_store_status public SELECT false
test crdb_internal kv_system_privileges public SELECT false
test crdb_internal leases public SELECT false
Expand Down Expand Up @@ -491,6 +491,12 @@ test pg_catalog record root
test pg_catalog record[] admin ALL false
test pg_catalog record[] public USAGE false
test pg_catalog record[] root ALL false
test pg_catalog refcursor admin ALL false
test pg_catalog refcursor public USAGE false
test pg_catalog refcursor root ALL false
test pg_catalog refcursor[] admin ALL false
test pg_catalog refcursor[] public USAGE false
test pg_catalog refcursor[] root ALL false
test pg_catalog regclass admin ALL false
test pg_catalog regclass public USAGE false
test pg_catalog regclass root ALL false
Expand Down Expand Up @@ -708,6 +714,10 @@ test pg_catalog record admin ALL false
test pg_catalog record root ALL false
test pg_catalog record[] admin ALL false
test pg_catalog record[] root ALL false
test pg_catalog refcursor admin ALL false
test pg_catalog refcursor root ALL false
test pg_catalog refcursor[] admin ALL false
test pg_catalog refcursor[] root ALL false
test pg_catalog regclass admin ALL false
test pg_catalog regclass root ALL false
test pg_catalog regclass[] admin ALL false
Expand Down Expand Up @@ -1284,6 +1294,10 @@ a pg_catalog record admin ALL
a pg_catalog record root ALL false
a pg_catalog record[] admin ALL false
a pg_catalog record[] root ALL false
a pg_catalog refcursor admin ALL false
a pg_catalog refcursor root ALL false
a pg_catalog refcursor[] admin ALL false
a pg_catalog refcursor[] root ALL false
a pg_catalog regclass admin ALL false
a pg_catalog regclass root ALL false
a pg_catalog regclass[] admin ALL false
Expand Down Expand Up @@ -1456,6 +1470,10 @@ defaultdb pg_catalog record admin ALL
defaultdb pg_catalog record root ALL false
defaultdb pg_catalog record[] admin ALL false
defaultdb pg_catalog record[] root ALL false
defaultdb pg_catalog refcursor admin ALL false
defaultdb pg_catalog refcursor root ALL false
defaultdb pg_catalog refcursor[] admin ALL false
defaultdb pg_catalog refcursor[] root ALL false
defaultdb pg_catalog regclass admin ALL false
defaultdb pg_catalog regclass root ALL false
defaultdb pg_catalog regclass[] admin ALL false
Expand Down Expand Up @@ -1628,6 +1646,10 @@ postgres pg_catalog record admin ALL
postgres pg_catalog record root ALL false
postgres pg_catalog record[] admin ALL false
postgres pg_catalog record[] root ALL false
postgres pg_catalog refcursor admin ALL false
postgres pg_catalog refcursor root ALL false
postgres pg_catalog refcursor[] admin ALL false
postgres pg_catalog refcursor[] root ALL false
postgres pg_catalog regclass admin ALL false
postgres pg_catalog regclass root ALL false
postgres pg_catalog regclass[] admin ALL false
Expand Down Expand Up @@ -1800,6 +1822,10 @@ system pg_catalog record admin ALL
system pg_catalog record root ALL false
system pg_catalog record[] admin ALL false
system pg_catalog record[] root ALL false
system pg_catalog refcursor admin ALL false
system pg_catalog refcursor root ALL false
system pg_catalog refcursor[] admin ALL false
system pg_catalog refcursor[] root ALL false
system pg_catalog regclass admin ALL false
system pg_catalog regclass root ALL false
system pg_catalog regclass[] admin ALL false
Expand Down Expand Up @@ -2320,6 +2346,10 @@ test pg_catalog record admin ALL
test pg_catalog record root ALL false
test pg_catalog record[] admin ALL false
test pg_catalog record[] root ALL false
test pg_catalog refcursor admin ALL false
test pg_catalog refcursor root ALL false
test pg_catalog refcursor[] admin ALL false
test pg_catalog refcursor[] root ALL false
test pg_catalog regclass admin ALL false
test pg_catalog regclass root ALL false
test pg_catalog regclass[] admin ALL false
Expand Down
185 changes: 185 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/mixed_version_refcursor
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# LogicTest: cockroach-go-testserver-upgrade-to-master

statement ok
CREATE TABLE xy (x INT, y INT);

# ----------------------------------------------------------------------
# Test REFCURSOR references with all nodes running old binaries.
# ----------------------------------------------------------------------

subtest all_old_cast

# Cast to REFCURSOR.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
SELECT 'foo'::REFCURSOR;

# Cast to REFCURSOR using the vectorized engine.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
SELECT 'foo'::REFCURSOR FROM generate_series(1, 100) LIMIT 1;

subtest all_old_table

# Table that references REFCURSOR.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE TABLE t (x REFCURSOR);

# Add a REFCURSOR column.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
ALTER TABLE xy ADD COLUMN curs REFCURSOR;

# Alter a column type to REFCURSOR.
statement ok
SET enable_experimental_alter_column_type_general=true;

statement error pgcode 42704 pq: type \"refcursor\" does not exist
ALTER TABLE xy ALTER COLUMN y TYPE REFCURSOR;

# Create a partial index that uses the REFCURSOR type.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE INDEX part ON xy (x) WHERE y::REFCURSOR < 'foo';

# Add a check constraint that uses the REFCURSOR type.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
ALTER TABLE xy ADD CONSTRAINT bar CHECK (y::REFCURSOR < 'baz');

subtest all_old_type

# UDT that references REFCURSOR.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE TYPE typ AS (x INT, y REFCURSOR);

subtest all_old_function

# Function that returns REFCURSOR.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE OR REPLACE FUNCTION f() RETURNS REFCURSOR AS $$
SELECT 'foo';
$$ LANGUAGE SQL;

# Function that takes REFCURSOR argument.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE OR REPLACE FUNCTION f(curs REFCURSOR) RETURNS STRING AS $$
SELECT curs;
$$ LANGUAGE SQL;

# Function that references REFCURSOR internally.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
SELECT 'foo'::REFCURSOR;
SELECT 0;
$$ LANGUAGE SQL;

# Function that returns a composite type with REFCURSOR component.
statement error pgcode 42704 pq: type \"refcursor\" does not exist
CREATE FUNCTION f() RETURNS RECORD AS $$
SELECT (1, 'foo'::REFCURSOR, true);
$$ LANGUAGE SQL;

subtest end

# ----------------------------------------------------------------------
# Verify that REFCURSOR is not allowed after upgrading the gateway.
# ----------------------------------------------------------------------

upgrade 0

user root nodeidx=0

subtest upgrade_one_cast

# Cast to REFCURSOR.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
SELECT 'foo'::REFCURSOR;

# Cast to REFCURSOR using the vectorized engine.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
SELECT 'foo'::REFCURSOR FROM generate_series(1, 100) LIMIT 1;

subtest upgrade_one_table

# Table that references REFCURSOR.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE TABLE t (x REFCURSOR);

# Add a REFCURSOR column.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
ALTER TABLE xy ADD COLUMN curs REFCURSOR;

# Alter a column type to REFCURSOR.
statement ok
SET enable_experimental_alter_column_type_general=true;

statement error pgcode 0A000 pq: refcursor not supported until version 23.2
ALTER TABLE xy ALTER COLUMN y TYPE REFCURSOR;

# Create a partial index that uses the REFCURSOR type.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE INDEX part ON xy (x) WHERE y::REFCURSOR < 'foo';

# Add a check constraint that uses the REFCURSOR type.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
ALTER TABLE xy ADD CONSTRAINT bar CHECK (y::REFCURSOR < 'baz');

subtest upgrade_one_type

# UDT that references REFCURSOR.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE TYPE typ AS (x INT, y REFCURSOR);

subtest upgrade_one_function

# Function that returns REFCURSOR.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f() RETURNS REFCURSOR AS $$
SELECT 'foo';
$$ LANGUAGE SQL;

statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f() RETURNS REFCURSOR AS $$
BEGIN
RETURN 'foo';
END
$$ LANGUAGE PLpgSQL;

# Function that takes REFCURSOR argument.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
SELECT 0;
$$ LANGUAGE SQL;

statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
BEGIN
RETURN 0;
END
$$ LANGUAGE PLpgSQL;

# Function that references REFCURSOR internally.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
SELECT 'foo'::REFCURSOR;
SELECT 0;
$$ LANGUAGE SQL;

statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE OR REPLACE FUNCTION f() RETURNS INT AS $$
BEGIN
SELECT 'foo'::REFCURSOR;
RETURN 0;
END
$$ LANGUAGE PLpgSQL;

# Function that returns a composite type with REFCURSOR component.
statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE FUNCTION f() RETURNS RECORD AS $$
SELECT (1, 'foo'::REFCURSOR, true);
$$ LANGUAGE SQL;

statement error pgcode 0A000 pq: refcursor not supported until version 23.2
CREATE FUNCTION f() RETURNS RECORD AS $$
BEGIN
RETURN (1, 'foo'::REFCURSOR, true);
END
$$ LANGUAGE PLpgSQL;

subtest end
10 changes: 10 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/pg_catalog
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,8 @@ oid typname typnamespace typowner typlen typbyval typty
1562 varbit 4294967110 NULL -1 false b
1563 _varbit 4294967110 NULL -1 false b
1700 numeric 4294967110 NULL -1 false b
1790 refcursor 4294967110 NULL -1 false b
2201 _refcursor 4294967110 NULL -1 false b
2202 regprocedure 4294967110 NULL 4 true b
2205 regclass 4294967110 NULL 4 true b
2206 regtype 4294967110 NULL 4 true b
Expand Down Expand Up @@ -1969,6 +1971,8 @@ oid typname typcategory typispreferred typisdefined typdel
1562 varbit V false true , 0 0 1563
1563 _varbit A false true , 0 1562 0
1700 numeric N false true , 0 0 1231
1790 refcursor S false true , 0 0 2201
2201 _refcursor A false true , 0 1790 0
2202 regprocedure N false true , 0 0 2207
2205 regclass N false true , 0 0 2210
2206 regtype N false true , 0 0 2211
Expand Down Expand Up @@ -2082,6 +2086,8 @@ oid typname typinput typoutput typreceive
1562 varbit varbit_in varbit_out varbit_recv varbit_send 0 0 0
1563 _varbit array_in array_out array_recv array_send 0 0 0
1700 numeric numeric_in numeric_out numeric_recv numeric_send 0 0 0
1790 refcursor refcursorin refcursorout refcursorrecv refcursorsend 0 0 0
2201 _refcursor array_in array_out array_recv array_send 0 0 0
2202 regprocedure regprocedurein regprocedureout regprocedurerecv regproceduresend 0 0 0
2205 regclass regclassin regclassout regclassrecv regclasssend 0 0 0
2206 regtype regtypein regtypeout regtyperecv regtypesend 0 0 0
Expand Down Expand Up @@ -2195,6 +2201,8 @@ oid typname typalign typstorage typnotnull typbasetype ty
1562 varbit NULL NULL false 0 -1
1563 _varbit NULL NULL false 0 -1
1700 numeric NULL NULL false 0 -1
1790 refcursor NULL NULL false 0 -1
2201 _refcursor NULL NULL false 0 -1
2202 regprocedure NULL NULL false 0 -1
2205 regclass NULL NULL false 0 -1
2206 regtype NULL NULL false 0 -1
Expand Down Expand Up @@ -2308,6 +2316,8 @@ oid typname typndims typcollation typdefaultbin typdefault
1562 varbit 0 0 NULL NULL NULL
1563 _varbit 0 0 NULL NULL NULL
1700 numeric 0 0 NULL NULL NULL
1790 refcursor 0 3403232968 NULL NULL NULL
2201 _refcursor 0 3403232968 NULL NULL NULL
2202 regprocedure 0 0 NULL NULL NULL
2205 regclass 0 0 NULL NULL NULL
2206 regtype 0 0 NULL NULL NULL
Expand Down
Loading

0 comments on commit 3198d3b

Please sign in to comment.