Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
113714: ccl/backupccl: add BACKUP/RESTORE tests for procedures r=mgartner a=mgartner

The tests added are very similar to the UDF tests in the
`user-defined-functions` test file.

Epic: CRDB-25388

Release note: None


Co-authored-by: Marcus Gartner <marcus@cockroachlabs.com>
  • Loading branch information
craig[bot] and mgartner committed Nov 8, 2023
2 parents 331cb13 + ffbe869 commit 6d37da2
Showing 1 changed file with 393 additions and 0 deletions.
393 changes: 393 additions & 0 deletions pkg/ccl/backupccl/testdata/backup-restore/procedures
Original file line number Diff line number Diff line change
@@ -0,0 +1,393 @@
# Test backing up and restoring a database with procedures.
new-cluster name=s
----

exec-sql
CREATE DATABASE db1;
USE db1;
CREATE SCHEMA sc1;
CREATE TABLE sc1.tbl1(a INT PRIMARY KEY);
CREATE TYPE sc1.enum1 AS ENUM('Good');
CREATE SEQUENCE sc1.sq1;
CREATE PROCEDURE sc1.p1(a sc1.enum1) LANGUAGE SQL AS $$
SELECT a FROM sc1.tbl1;
SELECT nextval('sc1.sq1');
$$;
CREATE SCHEMA sc2;
CREATE TABLE sc2.tbl2(a INT PRIMARY KEY);
CREATE PROCEDURE sc2.p2(i INT) LANGUAGE SQL AS $$
INSERT INTO sc2.tbl2 VALUES (i)
$$;
----

exec-sql
CALL sc2.p2(123)
----

query-sql
SELECT * FROM sc2.tbl2
----
123

exec-sql
BACKUP DATABASE db1 INTO 'nodelocal://1/test/'
----

query-sql
WITH descs AS (
SHOW BACKUP LATEST IN 'nodelocal://1/test/'
)
SELECT database_name, parent_schema_name, object_name, object_type, is_full_cluster FROM descs
----
<nil> <nil> db1 database false
db1 <nil> public schema false
db1 <nil> sc1 schema false
db1 sc1 tbl1 table false
db1 sc1 enum1 type false
db1 sc1 _enum1 type false
db1 sc1 sq1 table false
db1 sc1 p1 function false
db1 <nil> sc2 schema false
db1 sc2 tbl2 table false
db1 sc2 p2 function false

query-sql
SELECT create_statement FROM [SHOW CREATE PROCEDURE sc1.p1]
----
CREATE PROCEDURE sc1.p1(IN a db1.sc1.enum1)
LANGUAGE SQL
AS $$
SELECT a FROM db1.sc1.tbl1;
SELECT nextval('sc1.sq1'::REGCLASS);
$$

exec-sql
CALL sc1.p1('Good'::sc1.enum1)
----

query-sql
SELECT currval('sc1.sq1')
----
1

exec-sql
DROP DATABASE db1
----

exec-sql
RESTORE DATABASE db1 FROM LATEST IN 'nodelocal://1/test/' WITH new_db_name = db1_new
----

exec-sql
USE db1_new
----

# Make sure procedure ids in signature and body are rewritten.
# 1. argument type id is rewritten so that type name is deserialized correctly.
# 2. db name in qualified name is rewritten.
# 3. sequence id is rewritten so that sequence name is deserialized correctly.
query-sql
SELECT create_statement FROM [SHOW CREATE PROCEDURE sc1.p1]
----
CREATE PROCEDURE sc1.p1(IN a db1_new.sc1.enum1)
LANGUAGE SQL
AS $$
SELECT a FROM db1_new.sc1.tbl1;
SELECT nextval('sc1.sq1'::REGCLASS);
$$

# Make sure procedure signature is rewritten in schema descriptor so that
# procedure can be resolved and executed.
exec-sql
CALL sc1.p1('Good'::sc1.enum1)
----

query-sql
SELECT currval('sc1.sq1')
----
1

# Make sure procedure still inserts into the correct table.
exec-sql
CALL sc2.p2(456)
----

query-sql
SELECT * FROM sc2.tbl2
----
123
456

# Make sure dependency IDs are rewritten.
# Note that technically this only tests forward-reference IDs in depended-on
# objects are rewritten. But since we have cross-references validation, so this
# also means back-references in the function descriptor are good.
exec-sql
DROP SEQUENCE sc1.sq1
----
pq: cannot drop sequence sq1 because other objects depend on it

exec-sql
DROP TABLE sc1.tbl1
----
pq: cannot drop table tbl1 because other objects depend on it

# TODO(mgartner): The error message should say "procedure".
exec-sql
ALTER TABLE sc1.tbl1 RENAME TO tbl1_new
----
pq: cannot rename relation "sc1.tbl1" because function "p1" depends on it
HINT: consider dropping "p1" first.

# TODO(mgartner): The error message should say "procedure".
exec-sql
ALTER TABLE sc1.tbl1 SET SCHEMA sc2;
----
pq: cannot set schema on relation "tbl1" because function "p1" depends on it
HINT: consider dropping "p1" first.

exec-sql
DROP TYPE sc1.enum1
----
pq: cannot drop type "enum1" because other objects ([db1_new.sc1.p1]) still depend on it

# Test backing up and restoring a full cluster with procedures.
new-cluster name=s1
----

exec-sql cluster=s1
CREATE DATABASE db1;
USE db1;
CREATE SCHEMA sc1;
CREATE TABLE sc1.tbl1(a INT PRIMARY KEY);
CREATE TYPE sc1.enum1 AS ENUM('Good');
CREATE SEQUENCE sc1.sq1;
CREATE PROCEDURE sc1.p1(a sc1.enum1) LANGUAGE SQL AS $$
SELECT a FROM sc1.tbl1;
SELECT nextval('sc1.sq1');
$$;
CREATE SCHEMA sc2;
CREATE TABLE sc2.tbl2(a INT PRIMARY KEY);
CREATE PROCEDURE sc2.p2(i INT) LANGUAGE SQL AS $$
INSERT INTO sc2.tbl2 VALUES (i)
$$;
----

exec-sql
CALL sc2.p2(123)
----

query-sql
SELECT * FROM sc2.tbl2
----
123

exec-sql
BACKUP INTO 'nodelocal://1/test/'
----

query-sql
WITH descs AS (
SHOW BACKUP LATEST IN 'nodelocal://1/test/'
)
SELECT
database_name, parent_schema_name, object_name, object_type, is_full_cluster
FROM
descs
WHERE
database_name = 'db1'

----
db1 <nil> public schema true
db1 <nil> sc1 schema true
db1 sc1 tbl1 table true
db1 sc1 enum1 type true
db1 sc1 _enum1 type true
db1 sc1 sq1 table true
db1 sc1 p1 function true
db1 <nil> sc2 schema true
db1 sc2 tbl2 table true
db1 sc2 p2 function true

query-sql
SELECT create_statement FROM [SHOW CREATE PROCEDURE sc1.p1]
----
CREATE PROCEDURE sc1.p1(IN a db1.sc1.enum1)
LANGUAGE SQL
AS $$
SELECT a FROM db1.sc1.tbl1;
SELECT nextval('sc1.sq1'::REGCLASS);
$$

query-sql
CALL sc1.p1('Good'::sc1.enum1)
----

query-sql
SELECT currval('sc1.sq1')
----
1

# Start a new cluster with the same IO dir.
new-cluster name=s2 share-io-dir=s1
----

# Restore into the new cluster.
exec-sql cluster=s2
RESTORE FROM LATEST IN 'nodelocal://1/test/'
----

exec-sql
USE db1
----

# Make sure procedure ids in signature and body are rewritten.
# 1. argument type id is rewritten so that type name is deserialized correctly.
# 2. db name in qualified name is rewritten.
# 3. sequence id is rewritten so that sequence name is deserialized correctly.
query-sql
SELECT create_statement FROM [SHOW CREATE PROCEDURE sc1.p1]
----
CREATE PROCEDURE sc1.p1(IN a db1.sc1.enum1)
LANGUAGE SQL
AS $$
SELECT a FROM db1.sc1.tbl1;
SELECT nextval('sc1.sq1'::REGCLASS);
$$

# Make sure procedure signature is rewritten in schema descriptor so that
# procedure can be resolved and executed.
exec-sql
CALL sc2.p2(456)
----

query-sql
SELECT * FROM sc2.tbl2
----
123
456

# Make sure dependency IDs are rewritten.
# Note that technically this only tests forward-reference IDs in depended-on
# objects are rewritten. But since we have cross-references validation, so this
# also means back-references in the function descriptor are good.
exec-sql
DROP SEQUENCE sc1.sq1
----
pq: cannot drop sequence sq1 because other objects depend on it

exec-sql
DROP TABLE sc1.tbl1
----
pq: cannot drop table tbl1 because other objects depend on it

exec-sql
ALTER TABLE sc1.tbl1 RENAME TO tbl1_new
----
pq: cannot rename relation "sc1.tbl1" because function "p1" depends on it
HINT: consider dropping "p1" first.

exec-sql
ALTER TABLE sc1.tbl1 SET SCHEMA sc2;
----
pq: cannot set schema on relation "tbl1" because function "p1" depends on it
HINT: consider dropping "p1" first.

exec-sql
DROP TYPE sc1.enum1
----
pq: cannot drop type "enum1" because other objects ([db1.sc1.p1]) still depend on it

# Make sure that backup and restore individual tables from schema with procedure
# does not crash.
new-cluster name=s3
----

exec-sql cluster=s3
CREATE DATABASE db1;
CREATE SCHEMA sc1;
CREATE TABLE sc1.t(a INT PRIMARY KEY);
CREATE PROCEDURE sc1.p() LANGUAGE SQL AS $$ SELECT 1 $$;
----

# Make sure the original schema has procedure signatures
query-sql
WITH db_id AS (
SELECT id FROM system.namespace WHERE name = 'defaultdb'
),
schema_id AS (
SELECT ns.id
FROM system.namespace AS ns
JOIN db_id ON ns."parentID" = db_id.id
WHERE ns.name = 'sc1'
)
SELECT id FROM schema_id;
----
109

query-sql
WITH to_json AS (
SELECT
id,
crdb_internal.pb_to_json(
'cockroach.sql.sqlbase.Descriptor',
descriptor,
false
) AS d
FROM
system.descriptor
WHERE id = 109
)
SELECT d->'schema'->>'functions'::string FROM to_json;
----
{"p": {"signatures": [{"id": 111, "isProcedure": true, "returnType": {"family": "VoidFamily", "oid": 2278}}]}}

exec-sql
BACKUP TABLE sc1.t INTO 'nodelocal://1/test/'
----

exec-sql
RESTORE TABLE sc1.t FROM LATEST IN 'nodelocal://1/test/' WITH into_db = 'db1';
----

exec-sql
USE db1;
----

query-sql
WITH db_id AS (
SELECT id FROM system.namespace WHERE name = 'db1'
),
schema_id AS (
SELECT ns.id
FROM system.namespace AS ns
JOIN db_id ON ns."parentID" = db_id.id
WHERE ns.name = 'sc1'
)
SELECT id FROM schema_id;
----
112

query-sql
WITH to_json AS (
SELECT
id,
crdb_internal.pb_to_json(
'cockroach.sql.sqlbase.Descriptor',
descriptor,
false
) AS d
FROM
system.descriptor
WHERE id = 112
)
SELECT d->'schema'->>'functions'::string FROM to_json;
----
<nil>

# Make sure proper error message is returned when trying to resolve the
# procedure from the restore target db.
query-sql
CALL p()
----
pq: procedure p does not exist

0 comments on commit 6d37da2

Please sign in to comment.