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 2, 2023
1 parent db31fb4 commit 4f5726a
Show file tree
Hide file tree
Showing 24 changed files with 766 additions and 146 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
146 changes: 146 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,146 @@
# 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.
# ----------------------------------------------------------------------

# 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;

# 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');

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

# 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;

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

upgrade 0

user root nodeidx=0

# 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;

# 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');

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

# 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;
181 changes: 181 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/refcursor
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
statement ok
CREATE TABLE xy (x INT, y INT);

# Cast to REFCURSOR.
query T
SELECT 'foo'::REFCURSOR;
----
foo

# Cast to REFCURSOR using the vectorized engine.
query T
SELECT 'foo'::REFCURSOR FROM generate_series(1, 100) LIMIT 1;
----
foo

# Table that references REFCURSOR.
statement ok
CREATE TABLE t (x REFCURSOR);

# Add a REFCURSOR column.
statement ok
ALTER TABLE xy ADD COLUMN curs REFCURSOR;

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

query IIT
SELECT * FROM xy;
----
1 2 foo

# Alter a column type to REFCURSOR.
statement ok
DROP TABLE IF EXISTS xy;
CREATE TABLE xy (x INT, y INT);
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');

query IT
SELECT * FROM xy;
----
1 bar

statement ok
DROP TABLE IF EXISTS xy;
CREATE TABLE xy (x INT, y INT);
INSERT INTO xy VALUES (1, 2);
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';

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

statement ok
DROP TABLE IF EXISTS xy;
CREATE TABLE xy (x INT, y INT);
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');

query II
SELECT * FROM xy;
----
1 2

# UDT that references REFCURSOR.
statement ok
CREATE TYPE typ AS (x INT, y REFCURSOR);

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

# Function that returns REFCURSOR.
statement ok
CREATE FUNCTION f() RETURNS REFCURSOR AS $$
SELECT 'foo';
$$ LANGUAGE SQL;

query T
SELECT f();
----
foo

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f() RETURNS REFCURSOR AS $$
BEGIN
RETURN 'foo';
END
$$ LANGUAGE PLpgSQL;

query T
SELECT f();
----
foo

statement ok
DROP FUNCTION f;

# Function that takes REFCURSOR argument.
statement ok
CREATE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
SELECT 0;
$$ LANGUAGE SQL;

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

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f(curs REFCURSOR) RETURNS INT AS $$
BEGIN
RETURN 0;
END
$$ LANGUAGE PLpgSQL;

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

statement ok
DROP FUNCTION f;

# Function that references REFCURSOR internally.
statement ok
CREATE FUNCTION f() RETURNS INT AS $$
SELECT 'foo'::REFCURSOR;
SELECT 0;
$$ LANGUAGE SQL;

query I
SELECT f();
----
0

statement ok
DROP FUNCTION f;

statement ok
CREATE FUNCTION f() RETURNS INT AS $$
BEGIN
SELECT 'foo'::REFCURSOR;
RETURN 0;
END
$$ LANGUAGE PLpgSQL;

query I
SELECT f();
----
0

# TODO(drewk): REFCURSOR should not support collation.
query T
SELECT 'foo'::REFCURSOR COLLATE en;
----
foo

# REFCURSOR does not have a width.
statement error pgcode 42601 syntax error
SELECT 'foo'::REFCURSOR(2);
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ go_test(
"//pkg/sql/logictest:testdata", # keep
],
exec_properties = {"Pool": "large"},
shard_count = 12,
shard_count = 13,
tags = [
"cpu:2",
],
Expand Down

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

Loading

0 comments on commit 4f5726a

Please sign in to comment.