-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
97038: sql: use udf in check constraint r=chengxiong-ruan a=chengxiong-ruan Informs #87699 **sql: use UDFs in check constraints** This commit turns on UDF usages in CHECK constraints. It does a few things: 1. Add a concept of `SchemaExprContext`, which used to be a pure string. This commit makes it a type so that we can switch on different context of a expression. This allow fine grain control over in which circumstance that a UDF is allowed. 2. Serialize Function Expression to use OID reference to UDF. This allows objects reference UDFs by ID so that functions can be renamed. 3. Refine cross reference validation in function descriptor and table descriptor for more safety. 4. Fix legacy schema changer, so that cross references between UDF and tables are properly tracked when CHECK constraints are added and dropped. 5. Fix declarative schema changer, so that UDFs are recognized in check constraint elements, and ops are added to add and remove UDF back references. Release note (sql change): previously UDFs are not allowed in tables and any other object. This patch enables UDF usage in CHECK constraints of tables in both legacy schema changer and delcarative schema changer. Dependency circles are not allowed, namely if a UDF depends on a table, then the table can't use that UDF. **sql: version gate udf usage check** This commit adds version gate for udf usage check, so that cross reference won't be broken because there won't be cases like a constraint is added in new version, but dropped in an old version. Release note (sql change): This patch adds version gate so the UDF usage in CHECK constraints are not allowed until cluster is fully upgraded to 23.1. **backupccl: fix backup and restore to work with objects referencing UDFs** Release note (enterprise change): Previously UDFs can't be referenced from other objects, so backup and restore oly needs to read and write UDF descriptors without worrying about missing UDFs when doing `RESTORE TABLE`. Now that we turned UDF's usage in table CHECK constraints. We need to be able to handle all function ID rewrites in CHECK constraint expressions during `RESTORE`. A new `RSTORE` command option `skip_missing_udfs` is added, so that when the option is given, UDF dependencies will be skipped if UDF descriptors are missing. The main use case, as of this patch is that currently when doing `RESTORE TABLE`, we don't restore referenced UDFs together, so UDFs are consider missing, which blocks the table restore unless the `skip_missing_udfs` option is specified, so that those check constraints are dropped to remove the dependency to unblock `RESTORE TABLE`. Co-authored-by: Chengxiong Ruan <[email protected]>
- Loading branch information
Showing
108 changed files
with
3,119 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
228 changes: 228 additions & 0 deletions
228
pkg/ccl/backupccl/testdata/backup-restore/user-defined-functions-in-checks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
# Test backing up and restoring a database with user defined functions. | ||
new-cluster name=s | ||
---- | ||
|
||
exec-sql | ||
CREATE DATABASE db1; | ||
USE db1; | ||
CREATE SCHEMA sc1; | ||
CREATE FUNCTION sc1.f1(a INT) RETURNS INT LANGUAGE SQL AS $$ | ||
SELECT a + 1; | ||
$$; | ||
CREATE TABLE sc1.t1(a INT PRIMARY KEY, b INT CHECK(sc1.f1(b) > 1)); | ||
---- | ||
|
||
exec-sql | ||
BACKUP DATABASE db1 INTO 'nodelocal://0/test/' | ||
---- | ||
|
||
query-sql | ||
WITH descs AS ( | ||
SHOW BACKUP LATEST IN 'nodelocal://0/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 f1 function false | ||
db1 sc1 t1 table false | ||
|
||
exec-sql | ||
RESTORE DATABASE db1 FROM LATEST IN 'nodelocal://0/test/' WITH new_db_name = db1_new | ||
---- | ||
|
||
exec-sql | ||
USE db1_new | ||
---- | ||
|
||
# Make sure function ids in CHECK constraint are rewritten. | ||
query-sql | ||
SELECT create_statement FROM [SHOW CREATE TABLE sc1.t1] | ||
---- | ||
CREATE TABLE sc1.t1 ( | ||
a INT8 NOT NULL, | ||
b INT8 NULL, | ||
CONSTRAINT t1_pkey PRIMARY KEY (a ASC), | ||
CONSTRAINT check_b CHECK (sc1.f1(b) > 1:::INT8) | ||
) | ||
|
||
# Make sure that the CHECK constraint still applies correctly | ||
query-sql | ||
INSERT INTO sc1.t1 VALUES (1, 0) | ||
---- | ||
pq: failed to satisfy CHECK constraint (sc1.f1(b) > 1:::INT8) | ||
|
||
# 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 UDF descriptor are good. | ||
exec-sql | ||
DROP FUNCTION sc1.f1 | ||
---- | ||
pq: cannot drop function "f1" because other objects ([db1_new.sc1.t1]) still depend on it | ||
|
||
# Test backing up and restoring a full cluster with user defined function. | ||
new-cluster name=s1 | ||
---- | ||
|
||
exec-sql cluster=s1 | ||
CREATE DATABASE db1; | ||
USE db1; | ||
CREATE SCHEMA sc1; | ||
CREATE FUNCTION sc1.f1(a INT) RETURNS INT LANGUAGE SQL AS $$ | ||
SELECT a + 1; | ||
$$; | ||
CREATE TABLE sc1.t1(a INT PRIMARY KEY, b INT CHECK(sc1.f1(b) > 1)); | ||
---- | ||
|
||
exec-sql | ||
BACKUP INTO 'nodelocal://0/test/' | ||
---- | ||
|
||
query-sql | ||
WITH descs AS ( | ||
SHOW BACKUP LATEST IN 'nodelocal://0/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 f1 function true | ||
db1 sc1 t1 table true | ||
|
||
# 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://0/test/' | ||
---- | ||
|
||
exec-sql | ||
USE db1 | ||
---- | ||
|
||
# Make sure function ids in CHECK constraint are rewritten. | ||
query-sql | ||
SELECT create_statement FROM [SHOW CREATE TABLE sc1.f1] | ||
---- | ||
pq: relation "sc1.f1" does not exist | ||
|
||
# Make sure that CHECK constraint still applies correctly | ||
query-sql | ||
INSERT INTO sc1.t1 VALUES (1, 0) | ||
---- | ||
pq: failed to satisfy CHECK constraint (sc1.f1(b) > 1:::INT8) | ||
|
||
# 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 UDF descriptor are good. | ||
exec-sql | ||
DROP FUNCTION sc1.f1 | ||
---- | ||
pq: cannot drop function "f1" because other objects ([db1.sc1.t1]) still depend on it | ||
|
||
# Make sure that backup and restore individual tables referencing UDFs able to | ||
# drop check constraints. | ||
new-cluster name=s3 | ||
---- | ||
|
||
exec-sql cluster=s3 | ||
CREATE DATABASE db1; | ||
CREATE DATABASE db2; | ||
CREATE DATABASE db3; | ||
USE db1; | ||
CREATE SCHEMA sc1; | ||
CREATE FUNCTION sc1.f1(a INT) RETURNS INT LANGUAGE SQL AS $$ | ||
SELECT a + 1; | ||
$$; | ||
CREATE TABLE sc1.t1(a INT PRIMARY KEY, b INT CHECK(sc1.f1(b) > 1)); | ||
---- | ||
|
||
exec-sql | ||
BACKUP DATABASE db1 INTO 'nodelocal://0/test/' | ||
---- | ||
|
||
query-sql | ||
WITH descs AS ( | ||
SHOW BACKUP LATEST IN 'nodelocal://0/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 f1 function false | ||
db1 sc1 t1 table false | ||
|
||
exec-sql | ||
RESTORE TABLE sc1.t1 FROM LATEST IN 'nodelocal://0/test/' WITH into_db = 'db2'; | ||
---- | ||
pq: cannot restore table "t1" without referenced function 114 (or "skip_missing_udfs" option) | ||
|
||
exec-sql | ||
RESTORE TABLE sc1.t1 FROM LATEST IN 'nodelocal://0/test/' WITH into_db = 'db2', skip_missing_udfs; | ||
---- | ||
|
||
exec-sql | ||
USE db2 | ||
---- | ||
|
||
# Make sure CHECK constraint is dropped. | ||
query-sql | ||
SELECT create_statement FROM [SHOW CREATE TABLE sc1.t1] | ||
---- | ||
CREATE TABLE sc1.t1 ( | ||
a INT8 NOT NULL, | ||
b INT8 NULL, | ||
CONSTRAINT t1_pkey PRIMARY KEY (a ASC) | ||
) | ||
|
||
exec-sql | ||
USE db1 | ||
---- | ||
|
||
exec-sql | ||
BACKUP TABLE sc1.t1 INTO 'nodelocal://0/test/' | ||
---- | ||
|
||
query-sql | ||
WITH descs AS ( | ||
SHOW BACKUP LATEST IN 'nodelocal://0/test/' | ||
) | ||
SELECT database_name, parent_schema_name, object_name, object_type, is_full_cluster FROM descs | ||
---- | ||
<nil> <nil> db1 database false | ||
db1 <nil> sc1 schema false | ||
db1 sc1 t1 table false | ||
|
||
exec-sql | ||
RESTORE TABLE sc1.t1 FROM LATEST IN 'nodelocal://0/test/' WITH into_db = 'db3'; | ||
---- | ||
pq: cannot restore table "t1" without referenced function 114 (or "skip_missing_udfs" option) | ||
|
||
exec-sql | ||
RESTORE TABLE sc1.t1 FROM LATEST IN 'nodelocal://0/test/' WITH into_db = 'db3', skip_missing_udfs; | ||
---- | ||
|
||
exec-sql | ||
USE db3 | ||
---- | ||
|
||
# Make sure CHECK constraint is dropped. | ||
query-sql | ||
SELECT create_statement FROM [SHOW CREATE TABLE sc1.t1] | ||
---- | ||
CREATE TABLE sc1.t1 ( | ||
a INT8 NOT NULL, | ||
b INT8 NULL, | ||
CONSTRAINT t1_pkey PRIMARY KEY (a ASC) | ||
) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.