diff --git a/docs/generated/sql/bnf/BUILD.bazel b/docs/generated/sql/bnf/BUILD.bazel index c16ed534f90b..94fee4bd0e86 100644 --- a/docs/generated/sql/bnf/BUILD.bazel +++ b/docs/generated/sql/bnf/BUILD.bazel @@ -74,6 +74,7 @@ FILES = [ "create_database_stmt", "create_ddl_stmt", "create_extension_stmt", + "create_func_stmt", "create_index_stmt", "create_inverted_index_stmt", "create_role_stmt", @@ -126,6 +127,9 @@ FILES = [ "insert_stmt", "iso_level", "joined_table", + "legacy_begin_stmt", + "legacy_end_stmt", + "legacy_transaction_stmt", "like_table_option_list", "limit_clause", "move_cursor_stmt", @@ -163,6 +167,8 @@ FILES = [ "resume_stmt", "revoke_stmt", "rollback_transaction", + "routine_body_stmt", + "routine_return_stmt", "row_source_extension_stmt", "savepoint_stmt", "scrub_database_stmt", @@ -226,6 +232,7 @@ FILES = [ "split_table_at", "stmt", "stmt_block", + "stmt_without_legacy_transaction", "table_clause", "table_constraint", "table_ref", diff --git a/docs/generated/sql/bnf/begin_stmt.bnf b/docs/generated/sql/bnf/begin_stmt.bnf index 16c09600aae2..1059df25a0ad 100644 --- a/docs/generated/sql/bnf/begin_stmt.bnf +++ b/docs/generated/sql/bnf/begin_stmt.bnf @@ -1,19 +1 @@ begin_stmt ::= - 'BEGIN' 'TRANSACTION' 'PRIORITY' 'LOW' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'PRIORITY' 'NORMAL' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'PRIORITY' 'HIGH' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'READ' 'ONLY' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'READ' 'WRITE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'DEFERRABLE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' 'NOT' 'DEFERRABLE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'TRANSACTION' - | 'BEGIN' 'PRIORITY' 'LOW' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'PRIORITY' 'NORMAL' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'PRIORITY' 'HIGH' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'READ' 'ONLY' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'READ' 'WRITE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'DEFERRABLE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' 'NOT' 'DEFERRABLE' ( ( ( ',' | ) ( ( 'PRIORITY' ( 'LOW' | 'NORMAL' | 'HIGH' ) ) | ( 'READ' 'ONLY' | 'READ' 'WRITE' ) | ( 'AS' 'OF' 'SYSTEM' 'TIME' a_expr ) | ( 'DEFERRABLE' | 'NOT' 'DEFERRABLE' ) ) ) )* - | 'BEGIN' diff --git a/docs/generated/sql/bnf/commit_transaction.bnf b/docs/generated/sql/bnf/commit_transaction.bnf index 852a7e983367..f4d95a9e6f30 100644 --- a/docs/generated/sql/bnf/commit_transaction.bnf +++ b/docs/generated/sql/bnf/commit_transaction.bnf @@ -1,5 +1,3 @@ commit_stmt ::= 'COMMIT' 'TRANSACTION' | 'COMMIT' - | 'END' 'TRANSACTION' - | 'END' diff --git a/docs/generated/sql/bnf/create_ddl_stmt.bnf b/docs/generated/sql/bnf/create_ddl_stmt.bnf index c14860b51768..308db39242eb 100644 --- a/docs/generated/sql/bnf/create_ddl_stmt.bnf +++ b/docs/generated/sql/bnf/create_ddl_stmt.bnf @@ -7,3 +7,4 @@ create_ddl_stmt ::= | create_type_stmt | create_view_stmt | create_sequence_stmt + | create_func_stmt diff --git a/docs/generated/sql/bnf/create_func_stmt.bnf b/docs/generated/sql/bnf/create_func_stmt.bnf new file mode 100644 index 000000000000..6261dca3715a --- /dev/null +++ b/docs/generated/sql/bnf/create_func_stmt.bnf @@ -0,0 +1,2 @@ +create_func_stmt ::= + 'CREATE' opt_or_replace 'FUNCTION' func_create_name '(' opt_func_arg_with_default_list ')' 'RETURNS' opt_return_set func_return_type opt_create_func_opt_list opt_routine_body diff --git a/docs/generated/sql/bnf/legacy_begin_stmt.bnf b/docs/generated/sql/bnf/legacy_begin_stmt.bnf new file mode 100644 index 000000000000..3974be960389 --- /dev/null +++ b/docs/generated/sql/bnf/legacy_begin_stmt.bnf @@ -0,0 +1,2 @@ +legacy_begin_stmt ::= + 'BEGIN' opt_transaction begin_transaction diff --git a/docs/generated/sql/bnf/legacy_end_stmt.bnf b/docs/generated/sql/bnf/legacy_end_stmt.bnf new file mode 100644 index 000000000000..51e539de45ac --- /dev/null +++ b/docs/generated/sql/bnf/legacy_end_stmt.bnf @@ -0,0 +1,2 @@ +legacy_end_stmt ::= + 'END' opt_transaction diff --git a/docs/generated/sql/bnf/legacy_transaction_stmt.bnf b/docs/generated/sql/bnf/legacy_transaction_stmt.bnf new file mode 100644 index 000000000000..86e69e3eaa9c --- /dev/null +++ b/docs/generated/sql/bnf/legacy_transaction_stmt.bnf @@ -0,0 +1,3 @@ +legacy_transaction_stmt ::= + legacy_begin_stmt + | legacy_end_stmt diff --git a/docs/generated/sql/bnf/routine_body_stmt.bnf b/docs/generated/sql/bnf/routine_body_stmt.bnf new file mode 100644 index 000000000000..f6143b545995 --- /dev/null +++ b/docs/generated/sql/bnf/routine_body_stmt.bnf @@ -0,0 +1,3 @@ +routine_body_stmt ::= + stmt_without_legacy_transaction + | routine_return_stmt diff --git a/docs/generated/sql/bnf/routine_return_stmt.bnf b/docs/generated/sql/bnf/routine_return_stmt.bnf new file mode 100644 index 000000000000..ee632ac76569 --- /dev/null +++ b/docs/generated/sql/bnf/routine_return_stmt.bnf @@ -0,0 +1,2 @@ +routine_return_stmt ::= + 'RETURN' a_expr diff --git a/docs/generated/sql/bnf/stmt.bnf b/docs/generated/sql/bnf/stmt.bnf index e8e556311237..7ccaafdc0f5b 100644 --- a/docs/generated/sql/bnf/stmt.bnf +++ b/docs/generated/sql/bnf/stmt.bnf @@ -1,23 +1,4 @@ stmt ::= 'HELPTOKEN' - | preparable_stmt - | analyze_stmt - | copy_from_stmt - | comment_stmt - | execute_stmt - | deallocate_stmt - | discard_stmt - | grant_stmt - | prepare_stmt - | revoke_stmt - | savepoint_stmt - | reassign_owned_by_stmt - | drop_owned_by_stmt - | release_stmt - | refresh_stmt - | nonpreparable_set_stmt - | transaction_stmt - | close_cursor_stmt - | declare_cursor_stmt - | fetch_cursor_stmt - | move_cursor_stmt + | stmt_without_legacy_transaction + | legacy_transaction_stmt diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 46f1299fb6b6..239cddd67599 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -3,7 +3,12 @@ stmt_block ::= stmt ::= 'HELPTOKEN' - | preparable_stmt + | stmt_without_legacy_transaction + | legacy_transaction_stmt + | + +stmt_without_legacy_transaction ::= + preparable_stmt | analyze_stmt | copy_from_stmt | comment_stmt @@ -24,7 +29,10 @@ stmt ::= | declare_cursor_stmt | fetch_cursor_stmt | move_cursor_stmt - | + +legacy_transaction_stmt ::= + legacy_begin_stmt + | legacy_end_stmt preparable_stmt ::= alter_stmt @@ -140,6 +148,12 @@ fetch_cursor_stmt ::= move_cursor_stmt ::= 'MOVE' cursor_movement_specifier +legacy_begin_stmt ::= + 'BEGIN' opt_transaction begin_transaction + +legacy_end_stmt ::= + 'END' opt_transaction + alter_stmt ::= alter_ddl_stmt | alter_role_stmt @@ -397,12 +411,10 @@ set_transaction_stmt ::= | 'SET' 'SESSION' 'TRANSACTION' transaction_mode_list begin_stmt ::= - 'BEGIN' opt_transaction begin_transaction - | 'START' 'TRANSACTION' begin_transaction + 'START' 'TRANSACTION' begin_transaction commit_stmt ::= 'COMMIT' opt_transaction - | 'END' opt_transaction rollback_stmt ::= 'ROLLBACK' opt_transaction @@ -445,6 +457,14 @@ cursor_movement_specifier ::= | 'FIRST' opt_from_or_in cursor_name | 'LAST' opt_from_or_in cursor_name +opt_transaction ::= + 'TRANSACTION' + | + +begin_transaction ::= + transaction_mode_list + | + alter_ddl_stmt ::= alter_table_stmt | alter_index_stmt @@ -528,6 +548,7 @@ create_ddl_stmt ::= | create_type_stmt | create_view_stmt | create_sequence_stmt + | create_func_stmt create_stats_stmt ::= 'CREATE' 'STATISTICS' statistics_name opt_stats_columns 'FROM' create_stats_target opt_create_stats_options @@ -927,6 +948,7 @@ unreserved_keyword ::= | 'ALWAYS' | 'ASENSITIVE' | 'AT' + | 'ATOMIC' | 'ATTRIBUTE' | 'AUTOMATIC' | 'AVAILABILITY' @@ -940,6 +962,7 @@ unreserved_keyword ::= | 'BUNDLE' | 'BY' | 'CACHE' + | 'CALLED' | 'CANCEL' | 'CANCELQUERY' | 'CASCADE' @@ -984,6 +1007,7 @@ unreserved_keyword ::= | 'DELETE' | 'DEFAULTS' | 'DEFERRED' + | 'DEFINER' | 'DELIMITER' | 'DESTINATION' | 'DETACHED' @@ -1010,6 +1034,7 @@ unreserved_keyword ::= | 'EXPLAIN' | 'EXPORT' | 'EXTENSION' + | 'EXTERNAL' | 'FAILURE' | 'FILES' | 'FILTER' @@ -1042,6 +1067,7 @@ unreserved_keyword ::= | 'HOUR' | 'IDENTITY' | 'IMMEDIATE' + | 'IMMUTABLE' | 'IMPORT' | 'INCLUDE' | 'INCLUDING' @@ -1051,10 +1077,12 @@ unreserved_keyword ::= | 'INDEXES' | 'INHERITS' | 'INJECT' + | 'INPUT' | 'INSERT' | 'INTO_DB' | 'INVERTED' | 'ISOLATION' + | 'INVOKER' | 'JOB' | 'JOBS' | 'JSON' @@ -1067,6 +1095,7 @@ unreserved_keyword ::= | 'LATEST' | 'LC_COLLATE' | 'LC_CTYPE' + | 'LEAKPROOF' | 'LEASE' | 'LESS' | 'LEVEL' @@ -1144,6 +1173,7 @@ unreserved_keyword ::= | 'OVER' | 'OWNED' | 'OWNER' + | 'PARALLEL' | 'PARENT' | 'PARTIAL' | 'PARTITION' @@ -1199,6 +1229,8 @@ unreserved_keyword ::= | 'RESTRICTED' | 'RESUME' | 'RETRY' + | 'RETURN' + | 'RETURNS' | 'REVISION_HISTORY' | 'REVOKE' | 'ROLE' @@ -1223,6 +1255,7 @@ unreserved_keyword ::= | 'SCRUB' | 'SEARCH' | 'SECOND' + | 'SECURITY' | 'SERIALIZABLE' | 'SEQUENCE' | 'SEQUENCES' @@ -1244,6 +1277,7 @@ unreserved_keyword ::= | 'SPLIT' | 'SQL' | 'SQLLOGIN' + | 'STABLE' | 'START' | 'STATE' | 'STATEMENTS' @@ -1257,6 +1291,7 @@ unreserved_keyword ::= | 'STRICT' | 'SUBSCRIPTION' | 'SUPER' + | 'SUPPORT' | 'SURVIVE' | 'SURVIVAL' | 'SYNTAX' @@ -1275,6 +1310,7 @@ unreserved_keyword ::= | 'TRANSACTION' | 'TRANSACTIONS' | 'TRANSFER' + | 'TRANSFORM' | 'TRIGGER' | 'TRUNCATE' | 'TRUSTED' @@ -1301,6 +1337,7 @@ unreserved_keyword ::= | 'VIEWACTIVITYREDACTED' | 'VIEWCLUSTERSETTING' | 'VISIBLE' + | 'VOLATILE' | 'VOTERS' | 'WITHIN' | 'WITHOUT' @@ -1332,6 +1369,7 @@ col_name_keyword ::= | 'IF' | 'IFERROR' | 'IFNULL' + | 'INOUT' | 'INT' | 'INTEGER' | 'INTERVAL' @@ -1347,6 +1385,7 @@ col_name_keyword ::= | 'PRECISION' | 'REAL' | 'ROW' + | 'SETOF' | 'SMALLINT' | 'STRING' | 'SUBSTRING' @@ -1397,14 +1436,6 @@ type_list ::= transaction_mode_list ::= ( transaction_mode ) ( ( opt_comma transaction_mode ) )* -opt_transaction ::= - 'TRANSACTION' - | - -begin_transaction ::= - transaction_mode_list - | - opt_abort_mod ::= 'TRANSACTION' | 'WORK' @@ -1607,6 +1638,9 @@ create_sequence_stmt ::= 'CREATE' opt_temp 'SEQUENCE' sequence_name opt_sequence_option_list | 'CREATE' opt_temp 'SEQUENCE' 'IF' 'NOT' 'EXISTS' sequence_name opt_sequence_option_list +create_func_stmt ::= + 'CREATE' opt_or_replace 'FUNCTION' func_create_name '(' opt_func_arg_with_default_list ')' 'RETURNS' opt_return_set func_return_type opt_create_func_opt_list opt_routine_body + statistics_name ::= name @@ -2266,6 +2300,33 @@ opt_sequence_option_list ::= sequence_option_list | +opt_or_replace ::= + 'OR' 'REPLACE' + | + +func_create_name ::= + db_object_name + +opt_func_arg_with_default_list ::= + func_arg_with_default_list + | + +opt_return_set ::= + 'SETOF' + | + +func_return_type ::= + func_arg_type + +opt_create_func_opt_list ::= + create_func_opt_list + | + +opt_routine_body ::= + routine_return_stmt + | 'BEGIN' 'ATOMIC' routine_body_stmt_list 'END' + | + changefeed_target ::= opt_table_prefix table_name opt_changefeed_family @@ -2788,6 +2849,21 @@ create_as_table_defs ::= enum_val_list ::= ( 'SCONST' ) ( ( ',' 'SCONST' ) )* +func_arg_with_default_list ::= + ( func_arg_with_default ) ( ( ',' func_arg_with_default ) )* + +func_arg_type ::= + typename + +create_func_opt_list ::= + ( create_func_opt_item ) ( ( create_func_opt_item ) )* + +routine_return_stmt ::= + 'RETURN' a_expr + +routine_body_stmt_list ::= + ( ) ( ( routine_body_stmt ';' ) )* + opt_table_prefix ::= 'TABLE' | @@ -3157,6 +3233,20 @@ family_def ::= create_as_constraint_def ::= create_as_constraint_elem +func_arg_with_default ::= + func_arg + | func_arg 'DEFAULT' a_expr + | func_arg '=' a_expr + +create_func_opt_item ::= + 'AS' func_as + | 'LANGUAGE' non_reserved_word_or_sconst + | common_func_opt_item + +routine_body_stmt ::= + stmt_without_legacy_transaction + | routine_return_stmt + family_name ::= name @@ -3424,6 +3514,26 @@ opt_family_name ::= create_as_constraint_elem ::= 'PRIMARY' 'KEY' '(' create_as_params ')' opt_with_storage_parameter_list +func_arg ::= + func_arg_class param_name func_arg_type + | param_name func_arg_class func_arg_type + | param_name func_arg_type + | func_arg_class func_arg_type + | func_arg_type + +func_as ::= + 'SCONST' + +common_func_opt_item ::= + 'CALLED' 'ON' 'NULL' 'INPUT' + | 'RETURNS' 'NULL' 'ON' 'NULL' 'INPUT' + | 'STRICT' + | 'IMMUTABLE' + | 'STABLE' + | 'VOLATILE' + | 'LEAKPROOF' + | 'NOT' 'LEAKPROOF' + group_by_item ::= a_expr @@ -3505,6 +3615,12 @@ create_as_col_qualification_elem ::= create_as_params ::= ( create_as_param ) ( ( ',' create_as_param ) )* +func_arg_class ::= + 'IN' + +param_name ::= + type_function_name + col_qualification ::= 'CONSTRAINT' constraint_name col_qualification_elem | col_qualification_elem diff --git a/docs/generated/sql/bnf/stmt_without_legacy_transaction.bnf b/docs/generated/sql/bnf/stmt_without_legacy_transaction.bnf new file mode 100644 index 000000000000..efb2571bd1a6 --- /dev/null +++ b/docs/generated/sql/bnf/stmt_without_legacy_transaction.bnf @@ -0,0 +1,22 @@ +stmt_without_legacy_transaction ::= + preparable_stmt + | analyze_stmt + | copy_from_stmt + | comment_stmt + | execute_stmt + | deallocate_stmt + | discard_stmt + | grant_stmt + | prepare_stmt + | revoke_stmt + | savepoint_stmt + | reassign_owned_by_stmt + | drop_owned_by_stmt + | release_stmt + | refresh_stmt + | nonpreparable_set_stmt + | transaction_stmt + | close_cursor_stmt + | declare_cursor_stmt + | fetch_cursor_stmt + | move_cursor_stmt diff --git a/pkg/gen/docs.bzl b/pkg/gen/docs.bzl index 5281cb40e3d9..dbf599907e79 100644 --- a/pkg/gen/docs.bzl +++ b/pkg/gen/docs.bzl @@ -86,6 +86,7 @@ DOCS_SRCS = [ "//docs/generated/sql/bnf:create_database_stmt.bnf", "//docs/generated/sql/bnf:create_ddl_stmt.bnf", "//docs/generated/sql/bnf:create_extension_stmt.bnf", + "//docs/generated/sql/bnf:create_func_stmt.bnf", "//docs/generated/sql/bnf:create_index_stmt.bnf", "//docs/generated/sql/bnf:create_inverted_index_stmt.bnf", "//docs/generated/sql/bnf:create_role_stmt.bnf", @@ -138,6 +139,9 @@ DOCS_SRCS = [ "//docs/generated/sql/bnf:insert_stmt.bnf", "//docs/generated/sql/bnf:iso_level.bnf", "//docs/generated/sql/bnf:joined_table.bnf", + "//docs/generated/sql/bnf:legacy_begin_stmt.bnf", + "//docs/generated/sql/bnf:legacy_end_stmt.bnf", + "//docs/generated/sql/bnf:legacy_transaction_stmt.bnf", "//docs/generated/sql/bnf:like_table_option_list.bnf", "//docs/generated/sql/bnf:limit_clause.bnf", "//docs/generated/sql/bnf:move_cursor_stmt.bnf", @@ -175,6 +179,8 @@ DOCS_SRCS = [ "//docs/generated/sql/bnf:resume_stmt.bnf", "//docs/generated/sql/bnf:revoke_stmt.bnf", "//docs/generated/sql/bnf:rollback_transaction.bnf", + "//docs/generated/sql/bnf:routine_body_stmt.bnf", + "//docs/generated/sql/bnf:routine_return_stmt.bnf", "//docs/generated/sql/bnf:row_source_extension_stmt.bnf", "//docs/generated/sql/bnf:savepoint_stmt.bnf", "//docs/generated/sql/bnf:scrub_database_stmt.bnf", @@ -238,6 +244,7 @@ DOCS_SRCS = [ "//docs/generated/sql/bnf:split_table_at.bnf", "//docs/generated/sql/bnf:stmt.bnf", "//docs/generated/sql/bnf:stmt_block.bnf", + "//docs/generated/sql/bnf:stmt_without_legacy_transaction.bnf", "//docs/generated/sql/bnf:table_clause.bnf", "//docs/generated/sql/bnf:table_constraint.bnf", "//docs/generated/sql/bnf:table_ref.bnf", diff --git a/pkg/sql/logictest/testdata/logic_test/udf b/pkg/sql/logictest/testdata/logic_test/udf new file mode 100644 index 000000000000..03cb98c91d11 --- /dev/null +++ b/pkg/sql/logictest/testdata/logic_test/udf @@ -0,0 +1,2 @@ +statement error pq: unimplemented: CREATE FUNCTION unimplemented +CREATE OR REPLACE FUNCTION f(a int = 7) RETURNS INT AS 'SELECT 1' LANGUAGE SQL diff --git a/pkg/sql/opt/optbuilder/builder.go b/pkg/sql/opt/optbuilder/builder.go index 1cc7c6883e9e..069485ab7798 100644 --- a/pkg/sql/opt/optbuilder/builder.go +++ b/pkg/sql/opt/optbuilder/builder.go @@ -305,6 +305,10 @@ func (b *Builder) buildStmt( case *tree.CreateView: return b.buildCreateView(stmt, inScope) + case *tree.CreateFunction: + // TODO (Chengxiong): implement this + panic(unimplemented.NewWithIssue(83228, "CREATE FUNCTION unimplemented")) + case *tree.Explain: return b.buildExplain(stmt, inScope) diff --git a/pkg/sql/parser/parse.go b/pkg/sql/parser/parse.go index 77b9f260b18b..5b884f45b2c4 100644 --- a/pkg/sql/parser/parse.go +++ b/pkg/sql/parser/parse.go @@ -152,13 +152,23 @@ func (p *Parser) scanOneStmt() (sql string, tokens []sqlSymType, done bool) { // We make the resulting token positions match the returned string. lval.pos = 0 tokens = append(tokens, lval) + var preValID int32 + curFuncBodyCnt := 0 for { if lval.id == ERROR { return p.scanner.In()[startPos:], tokens, true } + preValID = lval.id posBeforeScan := p.scanner.Pos() p.scanner.Scan(&lval) - if lval.id == 0 || lval.id == ';' { + + if preValID == BEGIN && lval.id == ATOMIC { + curFuncBodyCnt++ + } + if curFuncBodyCnt > 0 && lval.id == END { + curFuncBodyCnt-- + } + if lval.id == 0 || (curFuncBodyCnt == 0 && lval.id == ';') { return p.scanner.In()[startPos:posBeforeScan], tokens, (lval.id == 0) } lval.pos -= startPos diff --git a/pkg/sql/parser/parse_test.go b/pkg/sql/parser/parse_test.go index 05f741332e56..222b5c706b0b 100644 --- a/pkg/sql/parser/parse_test.go +++ b/pkg/sql/parser/parse_test.go @@ -421,8 +421,6 @@ func TestUnimplementedSyntax(t *testing.T) { {`CREATE EXTENSION IF NOT EXISTS a WITH schema = 'public'`, 74777, `create extension if not exists with`, ``}, {`CREATE FOREIGN DATA WRAPPER a`, 0, `create fdw`, ``}, {`CREATE FOREIGN TABLE a`, 0, `create foreign table`, ``}, - {`CREATE FUNCTION a`, 17511, `create`, ``}, - {`CREATE OR REPLACE FUNCTION a`, 17511, `create`, ``}, {`CREATE LANGUAGE a`, 17511, `create language a`, ``}, {`CREATE OPERATOR a`, 65017, ``, ``}, {`CREATE PUBLICATION a`, 0, `create publication`, ``}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 48a44ab5c606..e022510a9f44 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -781,6 +781,27 @@ func (u *sqlSymUnion) cursorStmt() tree.CursorStmt { func (u *sqlSymUnion) asTenantClause() tree.TenantID { return u.val.(tree.TenantID) } +func (u *sqlSymUnion) functionOptions() tree.FunctionOptions { + return u.val.(tree.FunctionOptions) +} +func (u *sqlSymUnion) functionOption() tree.FunctionOption { + return u.val.(tree.FunctionOption) +} +func (u *sqlSymUnion) functionArgs() tree.FuncArgs { + return u.val.(tree.FuncArgs) +} +func (u *sqlSymUnion) functionArg() tree.FuncArg { + return u.val.(tree.FuncArg) +} +func (u *sqlSymUnion) functionArgClass() tree.FuncArgClass { + return u.val.(tree.FuncArgClass) +} +func (u *sqlSymUnion) stmts() tree.Statements { + return u.val.(tree.Statements) +} +func (u *sqlSymUnion) routineBody() *tree.RoutineBody { + return u.val.(*tree.RoutineBody) +} %} // NB: the %token definitions must come before the %type definitions in this @@ -802,23 +823,23 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { // Ordinary key words in alphabetical order. %token ABORT ABSOLUTE ACCESS ACTION ADD ADMIN AFTER AGGREGATE %token ALL ALTER ALWAYS ANALYSE ANALYZE AND AND_AND ANY ANNOTATE_TYPE ARRAY AS ASC -%token ASENSITIVE ASYMMETRIC AT ATTRIBUTE AUTHORIZATION AUTOMATIC AVAILABILITY +%token ASENSITIVE ASYMMETRIC AT ATOMIC ATTRIBUTE AUTHORIZATION AUTOMATIC AVAILABILITY %token BACKUP BACKUPS BACKWARD BEFORE BEGIN BETWEEN BIGINT BIGSERIAL BINARY BIT %token BUCKET_COUNT %token BOOLEAN BOTH BOX2D BUNDLE BY -%token CACHE CANCEL CANCELQUERY CASCADE CASE CAST CBRT CHANGEFEED CHAR +%token CACHE CALLED CANCEL CANCELQUERY CASCADE CASE CAST CBRT CHANGEFEED CHAR %token CHARACTER CHARACTERISTICS CHECK CLOSE %token CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT %token COMMITTED COMPACT COMPLETE COMPLETIONS CONCAT CONCURRENTLY CONFIGURATION CONFIGURATIONS CONFIGURE %token CONFLICT CONNECTION CONSTRAINT CONSTRAINTS CONTAINS CONTROLCHANGEFEED CONTROLJOB -%token CONVERSION CONVERT COPY COVERING CREATE CREATEDB CREATELOGIN CREATEROLE +%token CONVERSION CONVERT COPY COST COVERING CREATE CREATEDB CREATELOGIN CREATEROLE %token CROSS CSV CUBE CURRENT CURRENT_CATALOG CURRENT_DATE CURRENT_SCHEMA %token CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP %token CURRENT_USER CURSOR CYCLE -%token DATA DATABASE DATABASES DATE DAY DEBUG_PAUSE_ON DEC DECIMAL DEFAULT DEFAULTS +%token DATA DATABASE DATABASES DATE DAY DEBUG_PAUSE_ON DEC DECIMAL DEFAULT DEFAULTS DEFINER %token DEALLOCATE DECLARE DEFERRABLE DEFERRED DELETE DELIMITER DESC DESTINATION DETACHED %token DISCARD DISTINCT DO DOMAIN DOUBLE DROP @@ -826,7 +847,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %token EXISTS EXECUTE EXECUTION EXPERIMENTAL %token EXPERIMENTAL_FINGERPRINTS EXPERIMENTAL_REPLICA %token EXPERIMENTAL_AUDIT EXPERIMENTAL_RELOCATE -%token EXPIRATION EXPLAIN EXPORT EXTENSION EXTRACT EXTRACT_DURATION +%token EXPIRATION EXPLAIN EXPORT EXTENSION EXTERNAL EXTRACT EXTRACT_DURATION %token FAILURE FALSE FAMILY FETCH FETCHVAL FETCHTEXT FETCHVAL_PATH FETCHTEXT_PATH %token FILES FILTER @@ -840,19 +861,19 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %token HAVING HASH HEADER HIGH HISTOGRAM HOLD HOUR %token IDENTITY -%token IF IFERROR IFNULL IGNORE_FOREIGN_KEYS ILIKE IMMEDIATE IMPORT IN INCLUDE +%token IF IFERROR IFNULL IGNORE_FOREIGN_KEYS ILIKE IMMEDIATE IMMUTABLE IMPORT IN INCLUDE %token INCLUDING INCREMENT INCREMENTAL INCREMENTAL_LOCATION %token INET INET_CONTAINED_BY_OR_EQUALS %token INET_CONTAINS_OR_EQUALS INDEX INDEXES INHERITS INJECT INITIALLY -%token INNER INSENSITIVE INSERT INT INTEGER -%token INTERSECT INTERVAL INTO INTO_DB INVERTED IS ISERROR ISNULL ISOLATION +%token INNER INOUT INPUT INSENSITIVE INSERT INT INTEGER +%token INTERSECT INTERVAL INTO INTO_DB INVERTED INVOKER IS ISERROR ISNULL ISOLATION %token JOB JOBS JOIN JSON JSONB JSON_SOME_EXISTS JSON_ALL_EXISTS %token KEY KEYS KMS KV %token LANGUAGE LAST LATERAL LATEST LC_CTYPE LC_COLLATE -%token LEADING LEASE LEAST LEFT LESS LEVEL LIKE LIMIT +%token LEADING LEASE LEAST LEAKPROOF LEFT LESS LEVEL LIKE LIMIT %token LINESTRING LINESTRINGM LINESTRINGZ LINESTRINGZM %token LIST LOCAL LOCALITY LOCALTIME LOCALTIMESTAMP LOCKED LOGIN LOOKUP LOW LSHIFT @@ -869,7 +890,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %token OF OFF OFFSET OID OIDS OIDVECTOR OLD_KMS ON ONLY OPT OPTION OPTIONS OR %token ORDER ORDINALITY OTHERS OUT OUTER OVER OVERLAPS OVERLAY OWNED OWNER OPERATOR -%token PARENT PARTIAL PARTITION PARTITIONS PASSWORD PAUSE PAUSED PHYSICAL PLACEMENT PLACING +%token PARALLEL PARENT PARTIAL PARTITION PARTITIONS PASSWORD PAUSE PAUSED PHYSICAL PLACEMENT PLACING %token PLAN PLANS POINT POINTM POINTZ POINTZM POLYGON POLYGONM POLYGONZ POLYGONZM %token POSITION PRECEDING PRECISION PREPARE PRESERVE PRIMARY PRIOR PRIORITY PRIVILEGES %token PROCEDURAL PUBLIC PUBLICATION @@ -879,21 +900,21 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %token RANGE RANGES READ REAL REASON REASSIGN RECURSIVE RECURRING REF REFERENCES REFRESH %token REGCLASS REGION REGIONAL REGIONS REGNAMESPACE REGPROC REGPROCEDURE REGROLE REGTYPE REINDEX %token RELATIVE RELOCATE REMOVE_PATH RENAME REPEATABLE REPLACE REPLICATION -%token RELEASE RESET RESTART RESTORE RESTRICT RESTRICTED RESUME RETURNING RETRY REVISION_HISTORY +%token RELEASE RESET RESTART RESTORE RESTRICT RESTRICTED RESUME RETURNING RETURN RETURNS RETRY REVISION_HISTORY %token REVOKE RIGHT ROLE ROLES ROLLBACK ROLLUP ROUTINES ROW ROWS RSHIFT RULE RUNNING -%token SAVEPOINT SCANS SCATTER SCHEDULE SCHEDULES SCROLL SCHEMA SCHEMAS SCRUB SEARCH SECOND SELECT SEQUENCE SEQUENCES -%token SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETS SETTING SETTINGS +%token SAVEPOINT SCANS SCATTER SCHEDULE SCHEDULES SCROLL SCHEMA SCHEMAS SCRUB SEARCH SECOND SECURITY SELECT SEQUENCE SEQUENCES +%token SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETOF SETS SETTING SETTINGS %token SHARE SHOW SIMILAR SIMPLE SKIP SKIP_LOCALITIES_CHECK SKIP_MISSING_FOREIGN_KEYS %token SKIP_MISSING_SEQUENCES SKIP_MISSING_SEQUENCE_OWNERS SKIP_MISSING_VIEWS SMALLINT SMALLSERIAL SNAPSHOT SOME SPLIT SQL %token SQLLOGIN -%token START STATE STATISTICS STATUS STDIN STREAM STRICT STRING STORAGE STORE STORED STORING SUBSTRING SUPER -%token SURVIVE SURVIVAL SYMMETRIC SYNTAX SYSTEM SQRT SUBSCRIPTION STATEMENTS +%token STABLE START STATE STATISTICS STATUS STDIN STREAM STRICT STRING STORAGE STORE STORED STORING SUBSTRING SUPER +%token SUPPORT SURVIVE SURVIVAL SYMMETRIC SYNTAX SYSTEM SQRT SUBSCRIPTION STATEMENTS %token TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TENANT TENANTS TESTING_RELOCATE TEXT THEN %token TIES TIME TIMETZ TIMESTAMP TIMESTAMPTZ TO THROTTLING TRAILING TRACE -%token TRANSACTION TRANSACTIONS TRANSFER TREAT TRIGGER TRIM TRUE +%token TRANSACTION TRANSACTIONS TRANSFER TRANSFORM TREAT TRIGGER TRIM TRUE %token TRUNCATE TRUSTED TYPE TYPES %token TRACING @@ -901,7 +922,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %token UPDATE UPSERT UNSET UNTIL USE USER USERS USING UUID %token VALID VALIDATE VALUE VALUES VARBIT VARCHAR VARIADIC VIEW VARYING VIEWACTIVITY VIEWACTIVITYREDACTED -%token VIEWCLUSTERSETTING VIRTUAL VISIBLE VOTERS +%token VIEWCLUSTERSETTING VIRTUAL VISIBLE VOLATILE VOTERS %token WHEN WHERE WINDOW WITH WITHIN WITHOUT WORK WRITE @@ -939,7 +960,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { } %type stmt_block -%type stmt +%type stmt stmt_without_legacy_transaction %type alter_stmt @@ -1053,6 +1074,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %type create_table_as_stmt %type create_view_stmt %type create_sequence_stmt +%type create_func_stmt %type create_stats_stmt %type <*tree.CreateStatsOptions> opt_create_stats_options @@ -1163,7 +1185,7 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %type session_var %type <*string> comment_text -%type transaction_stmt +%type transaction_stmt legacy_transaction_stmt legacy_begin_stmt legacy_end_stmt %type truncate_stmt %type update_stmt %type upsert_stmt @@ -1495,6 +1517,19 @@ func (u *sqlSymUnion) asTenantClause() tree.TenantID { %type target_object_type %type opt_as_tenant_clause +// User defined function relevant components. +%type opt_or_replace opt_return_set +%type param_name func_as +%type opt_func_arg_with_default_list func_arg_with_default_list +%type func_arg_with_default func_arg +%type func_return_type func_arg_type +%type opt_create_func_opt_list create_func_opt_list +%type create_func_opt_item common_func_opt_item +%type func_arg_class +%type <*tree.UnresolvedObjectName> func_create_name +%type routine_return_stmt routine_body_stmt +%type routine_body_stmt_list +%type <*tree.RoutineBody> opt_routine_body // Precedence: lowest to highest %nonassoc VALUES // see value_clause @@ -1574,7 +1609,15 @@ stmt_block: stmt: HELPTOKEN { return helpWith(sqllex, "") } -| preparable_stmt // help texts in sub-rule +| stmt_without_legacy_transaction +| legacy_transaction_stmt +| /* EMPTY */ + { + $$.val = tree.Statement(nil) + } + +stmt_without_legacy_transaction: + preparable_stmt // help texts in sub-rule | analyze_stmt // EXTEND WITH HELP: ANALYZE | copy_from_stmt | comment_stmt @@ -1596,10 +1639,6 @@ stmt: | fetch_cursor_stmt // EXTEND WITH HELP: FETCH | move_cursor_stmt // EXTEND WITH HELP: MOVE | reindex_stmt -| /* EMPTY */ - { - $$.val = tree.Statement(nil) - } // %Help: ALTER // %Category: Group @@ -3797,6 +3836,251 @@ create_extension_stmt: } | CREATE EXTENSION error // SHOW HELP: CREATE EXTENSION +create_func_stmt: + CREATE opt_or_replace FUNCTION func_create_name '(' opt_func_arg_with_default_list ')' RETURNS opt_return_set func_return_type + opt_create_func_opt_list opt_routine_body + { + name := $4.unresolvedObjectName().ToFunctionName() + $$.val = &tree.CreateFunction{ + IsProcedure: false, + Replace: $2.bool(), + FuncName: name, + Args: $6.functionArgs(), + ReturnType: tree.FuncReturnType{ + Type: $10.typeReference(), + IsSet: $9.bool(), + }, + Options: $11.functionOptions(), + RoutineBody: $12.routineBody(), + } + } + +opt_or_replace: + OR REPLACE { $$.val = true } +| /* EMPTY */ { $$.val = false } + +opt_return_set: + SETOF { $$.val = true} +| /* EMPTY */ { $$.val = false } + +func_create_name: + db_object_name + +opt_func_arg_with_default_list: + func_arg_with_default_list { $$.val = $1.functionArgs() } +| /* Empty */ { $$.val = tree.FuncArgs{} } + +func_arg_with_default_list: + func_arg_with_default { $$.val = tree.FuncArgs{$1.functionArg()} } +| func_arg_with_default_list ',' func_arg_with_default { $$.val = append($1.functionArgs(), $3.functionArg()) } + +func_arg_with_default: + func_arg +| func_arg DEFAULT a_expr + { + arg := $1.functionArg() + arg.DefaultVal = $3.expr() + $$.val = arg + } +| func_arg '=' a_expr + { + arg := $1.functionArg() + arg.DefaultVal = $3.expr() + $$.val = arg + } + +func_arg: + func_arg_class param_name func_arg_type + { + $$.val = tree.FuncArg{ + Name: tree.Name($2), + Type: $3.typeReference(), + Class: $1.functionArgClass(), + } + } +| param_name func_arg_class func_arg_type + { + $$.val = tree.FuncArg{ + Name: tree.Name($1), + Type: $3.typeReference(), + Class: $2.functionArgClass(), + } + } +| param_name func_arg_type + { + $$.val = tree.FuncArg{ + Name: tree.Name($1), + Type: $2.typeReference(), + Class: tree.FunctionArgIn, + } + } +| func_arg_class func_arg_type + { + $$.val = tree.FuncArg{ + Type: $2.typeReference(), + Class: $1.functionArgClass(), + } + } +| func_arg_type + { + $$.val = tree.FuncArg{ + Type: $1.typeReference(), + Class: tree.FunctionArgIn, + } + } + +func_arg_class: + IN { $$.val = tree.FunctionArgIn } +| OUT { return unimplemented(sqllex, "create function with 'OUT' argument class") } +| INOUT { return unimplemented(sqllex, "create function with 'INOUT' argument class") } +| IN OUT { return unimplemented(sqllex, "create function with 'IN OUT' argument class") } +| VARIADIC { return unimplemented(sqllex, "create function with 'VARIADIC' argument class") } + +func_arg_type: + typename + +func_return_type: + func_arg_type + +opt_create_func_opt_list: + create_func_opt_list { $$.val = $1.functionOptions() } +| /* EMPTY */ { $$.val = tree.FunctionOptions{} } + +create_func_opt_list: + create_func_opt_item { $$.val = tree.FunctionOptions{$1.functionOption()} } +| create_func_opt_list create_func_opt_item { $$.val = append($1.functionOptions(), $2.functionOption())} + +create_func_opt_item: + AS func_as + { + $$.val = tree.FunctionBodyStr($2) + } +| LANGUAGE non_reserved_word_or_sconst + { + lang, err := tree.AsFunctionLanguage($2) + if err != nil { + return setErr(sqllex, err) + } + $$.val = lang + } +| TRANSFORM { return unimplemented(sqllex, "create function...transform") } +| WINDOW { return unimplemented(sqllex, "create function...window") } +| common_func_opt_item + { + $$.val = $1.functionOption() + } + +common_func_opt_item: + CALLED ON NULL INPUT + { + $$.val = tree.FunctionCalledOnNullInput + } +| RETURNS NULL ON NULL INPUT + { + $$.val = tree.FunctionReturnsNullOnNullInput + } +| STRICT + { + $$.val = tree.FunctionStrict + } +| IMMUTABLE + { + $$.val = tree.FunctionImmutable + } +| STABLE + { + $$.val = tree.FunctionStable + } +| VOLATILE + { + $$.val = tree.FunctionVolatile + } +| EXTERNAL SECURITY DEFINER + { + return unimplemented(sqllex, "create function...security") + } +| EXTERNAL SECURITY INVOKER + { + return unimplemented(sqllex, "create function...security") + } +| SECURITY DEFINER + { + return unimplemented(sqllex, "create function...security") + } +| SECURITY INVOKER + { + return unimplemented(sqllex, "create function...security") + } +| LEAKPROOF + { + $$.val = tree.FunctionLeakProof(true) + } +| NOT LEAKPROOF + { + $$.val = tree.FunctionLeakProof(false) + } +| COST numeric_only + { + return unimplemented(sqllex, "create function...cost") + } +| ROWS numeric_only + { + return unimplemented(sqllex, "create function...rows") + } +| SUPPORT name + { + return unimplemented(sqllex, "create function...support") + } +// In theory we should parse the a whole set/reset statement here. But it's fine +// to just return fast on SET/RESET keyword for now since it's not supported +// yet. +| SET { return unimplemented(sqllex, "create function...set") } +| RESET { return unimplemented(sqllex, "create function...reset") } +| PARALLEL { return unimplemented(sqllex, "create function...parallel") } + +func_as: + SCONST + +routine_return_stmt: + RETURN a_expr +{ + $$.val = &tree.RoutineReturn{ + ReturnVal: $2.expr(), + } +} + +routine_body_stmt: + stmt_without_legacy_transaction +| routine_return_stmt + +routine_body_stmt_list: + routine_body_stmt_list routine_body_stmt ';' + { + $$.val = append($1.stmts(), $2.stmt()) + } +| /* Empty */ + { + $$.val = tree.Statements{} + } + +opt_routine_body: + routine_return_stmt + { + $$.val = &tree.RoutineBody{ + Stmts: tree.Statements{$1.stmt()}, + } + } +| BEGIN ATOMIC routine_body_stmt_list END + { + $$.val = &tree.RoutineBody{ + Stmts: $3.stmts(), + } + } +| /* Empty */ + { + $$.val = (*tree.RoutineBody)(nil) + } + create_unsupported: CREATE ACCESS METHOD error { return unimplemented(sqllex, "create access method") } | CREATE AGGREGATE error { return unimplementedWithIssueDetail(sqllex, 74775, "create aggregate") } @@ -3806,8 +4090,6 @@ create_unsupported: | CREATE DEFAULT CONVERSION error { return unimplemented(sqllex, "create def conv") } | CREATE FOREIGN TABLE error { return unimplemented(sqllex, "create foreign table") } | CREATE FOREIGN DATA error { return unimplemented(sqllex, "create fdw") } -| CREATE FUNCTION error { return unimplementedWithIssueDetail(sqllex, 17511, "create function") } -| CREATE OR REPLACE FUNCTION error { return unimplementedWithIssueDetail(sqllex, 17511, "create function") } | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE name error { return unimplementedWithIssueDetail(sqllex, 17511, "create language " + $6) } | CREATE OPERATOR error { return unimplementedWithIssue(sqllex, 65017) } | CREATE PUBLICATION error { return unimplemented(sqllex, "create publication") } @@ -3818,10 +4100,6 @@ create_unsupported: | CREATE TEXT error { return unimplementedWithIssueDetail(sqllex, 7821, "create text") } | CREATE TRIGGER error { return unimplementedWithIssueDetail(sqllex, 28296, "create trigger") } -opt_or_replace: - OR REPLACE {} -| /* EMPTY */ {} - opt_trusted: TRUSTED {} | /* EMPTY */ {} @@ -3862,6 +4140,7 @@ create_ddl_stmt: | create_type_stmt // EXTEND WITH HELP: CREATE TYPE | create_view_stmt // EXTEND WITH HELP: CREATE VIEW | create_sequence_stmt // EXTEND WITH HELP: CREATE SEQUENCE +| create_func_stmt // %Help: CREATE STATISTICS - create a new table statistic // %Category: Misc @@ -9388,12 +9667,7 @@ transaction_stmt: // // %SeeAlso: COMMIT, ROLLBACK, WEBDOCS/begin-transaction.html begin_stmt: - BEGIN opt_transaction begin_transaction - { - $$.val = $3.stmt() - } -| BEGIN error // SHOW HELP: BEGIN -| START TRANSACTION begin_transaction + START TRANSACTION begin_transaction { $$.val = $3.stmt() } @@ -9411,11 +9685,6 @@ commit_stmt: $$.val = &tree.CommitTransaction{} } | COMMIT error // SHOW HELP: COMMIT -| END opt_transaction - { - $$.val = &tree.CommitTransaction{} - } -| END error // SHOW HELP: COMMIT abort_stmt: ABORT opt_abort_mod @@ -9445,6 +9714,25 @@ rollback_stmt: } | ROLLBACK error // SHOW HELP: ROLLBACK +legacy_transaction_stmt: + legacy_begin_stmt // EXTEND WITH HELP: BEGIN +| legacy_end_stmt // EXTEND WITH HELP: COMMIT + +legacy_begin_stmt: + BEGIN opt_transaction begin_transaction + { + $$.val = $3.stmt() + } +| BEGIN error // SHOW HELP: BEGIN + +legacy_end_stmt: + END opt_transaction + { + $$.val = &tree.CommitTransaction{} + } +| END error // SHOW HELP: COMMIT + + opt_transaction: TRANSACTION {} | /* EMPTY */ {} @@ -14056,6 +14344,9 @@ type_function_name_no_crdb_extra: | unreserved_keyword | type_func_name_no_crdb_extra_keyword +param_name: + type_function_name + // Any not-fully-reserved word --- these names can be, eg, variable names. non_reserved_word: IDENT @@ -14094,6 +14385,7 @@ unreserved_keyword: | ALWAYS | ASENSITIVE | AT +| ATOMIC | ATTRIBUTE | AUTOMATIC | AVAILABILITY @@ -14107,6 +14399,7 @@ unreserved_keyword: | BUNDLE | BY | CACHE +| CALLED | CANCEL | CANCELQUERY | CASCADE @@ -14151,6 +14444,7 @@ unreserved_keyword: | DELETE | DEFAULTS | DEFERRED +| DEFINER | DELIMITER | DESTINATION | DETACHED @@ -14177,6 +14471,7 @@ unreserved_keyword: | EXPLAIN | EXPORT | EXTENSION +| EXTERNAL | FAILURE | FILES | FILTER @@ -14209,6 +14504,7 @@ unreserved_keyword: | HOUR | IDENTITY | IMMEDIATE +| IMMUTABLE | IMPORT | INCLUDE | INCLUDING @@ -14218,10 +14514,12 @@ unreserved_keyword: | INDEXES | INHERITS | INJECT +| INPUT | INSERT | INTO_DB | INVERTED | ISOLATION +| INVOKER | JOB | JOBS | JSON @@ -14234,6 +14532,7 @@ unreserved_keyword: | LATEST | LC_COLLATE | LC_CTYPE +| LEAKPROOF | LEASE | LESS | LEVEL @@ -14311,6 +14610,7 @@ unreserved_keyword: | OVER | OWNED | OWNER +| PARALLEL | PARENT | PARTIAL | PARTITION @@ -14366,6 +14666,8 @@ unreserved_keyword: | RESTRICTED | RESUME | RETRY +| RETURN +| RETURNS | REVISION_HISTORY | REVOKE | ROLE @@ -14390,6 +14692,7 @@ unreserved_keyword: | SCRUB | SEARCH | SECOND +| SECURITY | SERIALIZABLE | SEQUENCE | SEQUENCES @@ -14411,6 +14714,7 @@ unreserved_keyword: | SPLIT | SQL | SQLLOGIN +| STABLE | START | STATE | STATEMENTS @@ -14424,6 +14728,7 @@ unreserved_keyword: | STRICT | SUBSCRIPTION | SUPER +| SUPPORT | SURVIVE | SURVIVAL | SYNTAX @@ -14442,6 +14747,7 @@ unreserved_keyword: | TRANSACTION | TRANSACTIONS | TRANSFER +| TRANSFORM | TRIGGER | TRUNCATE | TRUSTED @@ -14468,6 +14774,7 @@ unreserved_keyword: | VIEWACTIVITYREDACTED | VIEWCLUSTERSETTING | VISIBLE +| VOLATILE | VOTERS | WITHIN | WITHOUT @@ -14508,6 +14815,7 @@ col_name_keyword: | IF | IFERROR | IFNULL +| INOUT | INT | INTEGER | INTERVAL @@ -14523,6 +14831,7 @@ col_name_keyword: | PRECISION | REAL | ROW +| SETOF | SMALLINT | STRING | SUBSTRING diff --git a/pkg/sql/parser/testdata/create_function b/pkg/sql/parser/testdata/create_function new file mode 100644 index 000000000000..7025a24e94b8 --- /dev/null +++ b/pkg/sql/parser/testdata/create_function @@ -0,0 +1,73 @@ +parse +CREATE OR REPLACE FUNCTION f(a int = 7) RETURNS INT AS 'SELECT 1' LANGUAGE SQL +---- +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT 7) RETURNS INT8 LANGUAGE SQL AS $$SELECT 1$$ -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT (7)) RETURNS INT8 LANGUAGE SQL AS $$SELECT 1$$ -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT _) RETURNS INT8 LANGUAGE SQL AS $$SELECT 1$$ -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8 DEFAULT 7) RETURNS INT8 LANGUAGE SQL AS $$SELECT 1$$ -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(IN a INT=7) RETURNS INT CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS 'SELECT 1' +---- +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT 7) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT (7)) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT _) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8 DEFAULT 7) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(IN a INT=7) RETURNS INT AS 'SELECT 1' CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL +---- +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT 7) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT (7)) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT _) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8 DEFAULT 7) RETURNS INT8 CALLED ON NULL INPUT IMMUTABLE LEAKPROOF LANGUAGE SQL AS $$SELECT 1$$ -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(a INT DEFAULT 10) RETURNS INT RETURNS NULL ON NULL INPUT LANGUAGE SQL AS 'SELECT 1' +---- +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT 10) RETURNS INT8 RETURNS NULL ON NULL INPUT LANGUAGE SQL AS $$SELECT 1$$ -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT (10)) RETURNS INT8 RETURNS NULL ON NULL INPUT LANGUAGE SQL AS $$SELECT 1$$ -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8 DEFAULT _) RETURNS INT8 RETURNS NULL ON NULL INPUT LANGUAGE SQL AS $$SELECT 1$$ -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8 DEFAULT 10) RETURNS INT8 RETURNS NULL ON NULL INPUT LANGUAGE SQL AS $$SELECT 1$$ -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT a; END +---- +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT a; END -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT (1); SELECT (a); END -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT _; SELECT a; END -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT _; END -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT $1; END +---- +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT $1; END -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT (1); SELECT ($1); END -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT _; SELECT $1; END -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; SELECT $1; END -- identifiers removed + +parse +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1; CREATE OR REPLACE FUNCTION g() RETURNS INT BEGIN ATOMIC SELECT 2; END; END +---- +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; CREATE OR REPLACE FUNCTION g() RETURNS INT8 BEGIN ATOMIC SELECT 2; END; END -- normalized! +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT (1); CREATE OR REPLACE FUNCTION g() RETURNS INT8 BEGIN ATOMIC SELECT (2); END; END -- fully parenthesized +CREATE OR REPLACE FUNCTION f(IN a INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT _; CREATE OR REPLACE FUNCTION g() RETURNS INT8 BEGIN ATOMIC SELECT _; END; END -- literals removed +CREATE OR REPLACE FUNCTION _(IN _ INT8) RETURNS INT8 LANGUAGE SQL BEGIN ATOMIC SELECT 1; CREATE OR REPLACE FUNCTION _() RETURNS INT8 BEGIN ATOMIC SELECT 2; END; END -- identifiers removed + +error +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1 END +---- +at or near "end": syntax error +DETAIL: source SQL: +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1 END + ^ +HINT: try \h CREATE + +error +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1; CREATE OR REPLACE FUNCTION g() RETURNS INT BEGIN ATOMIC SELECT 2; END; +---- +at or near "EOF": syntax error +DETAIL: source SQL: +CREATE OR REPLACE FUNCTION f(a INT) RETURNS INT LANGUAGE SQL BEGIN ATOMIC SELECT 1; CREATE OR REPLACE FUNCTION g() RETURNS INT BEGIN ATOMIC SELECT 2; END; + ^ +HINT: try \h CREATE diff --git a/pkg/sql/scanner/scan.go b/pkg/sql/scanner/scan.go index ed303af6c22f..b590b9136650 100644 --- a/pkg/sql/scanner/scan.go +++ b/pkg/sql/scanner/scan.go @@ -1038,12 +1038,21 @@ func (s *Scanner) scanOne(lval *fakeSym) (done, hasToks bool, err error) { } } + var preValID int32 + curFuncBodyCnt := 0 for { if lval.id == lexbase.ERROR { return true, true, fmt.Errorf("scan error: %s", lval.s) } + preValID = lval.id s.Scan(lval) - if lval.id == 0 || lval.id == ';' { + if preValID == lexbase.BEGIN && lval.id == lexbase.ATOMIC { + curFuncBodyCnt++ + } + if curFuncBodyCnt > 0 && lval.id == lexbase.END { + curFuncBodyCnt-- + } + if lval.id == 0 || (curFuncBodyCnt == 0 && lval.id == ';') { return (lval.id == 0), true, nil } } diff --git a/pkg/sql/sem/tree/BUILD.bazel b/pkg/sql/sem/tree/BUILD.bazel index 5716e63d179a..74edb0590bf7 100644 --- a/pkg/sql/sem/tree/BUILD.bazel +++ b/pkg/sql/sem/tree/BUILD.bazel @@ -100,6 +100,7 @@ go_library( "type_check.go", "type_name.go", "typing.go", + "udf.go", "union.go", "unsupported_error.go", "update.go", diff --git a/pkg/sql/sem/tree/object_name.go b/pkg/sql/sem/tree/object_name.go index 52f706bf3c10..1a7797bf270f 100644 --- a/pkg/sql/sem/tree/object_name.go +++ b/pkg/sql/sem/tree/object_name.go @@ -205,13 +205,11 @@ func (u *UnresolvedObjectName) Format(ctx *FmtCtx) { func (u *UnresolvedObjectName) String() string { return AsString(u) } -// ToTableName converts the unresolved name to a table name. -// // TODO(radu): the schema and catalog names might not be in the right places; we // would only figure that out during name resolution. This method is temporary, // while we change all the code paths to only use TableName after resolution. -func (u *UnresolvedObjectName) ToTableName() TableName { - return TableName{objName{ +func (u *UnresolvedObjectName) toObjName() objName { + return objName{ ObjectName: Name(u.Parts[0]), ObjectNamePrefix: ObjectNamePrefix{ SchemaName: Name(u.Parts[1]), @@ -219,7 +217,17 @@ func (u *UnresolvedObjectName) ToTableName() TableName { ExplicitSchema: u.NumParts >= 2, ExplicitCatalog: u.NumParts >= 3, }, - }} + } +} + +// ToTableName converts the unresolved name to a table name. +func (u *UnresolvedObjectName) ToTableName() TableName { + return TableName{u.toObjName()} +} + +// ToFunctionName converts the unresolved name to a function name. +func (u *UnresolvedObjectName) ToFunctionName() FunctionName { + return FunctionName{u.toObjName()} } // ToUnresolvedName converts the unresolved object name to the more general diff --git a/pkg/sql/sem/tree/stmt.go b/pkg/sql/sem/tree/stmt.go index 9c703d9bd973..155f49c6cd58 100644 --- a/pkg/sql/sem/tree/stmt.go +++ b/pkg/sql/sem/tree/stmt.go @@ -85,6 +85,9 @@ const ( TypeTCL ) +// Statements represent a list of statements. +type Statements []Statement + // Statement represents a statement. type Statement interface { fmt.Stringer @@ -1779,6 +1782,24 @@ func (*ValuesClause) StatementType() StatementType { return TypeDML } // StatementTag returns a short string identifying the type of statement. func (*ValuesClause) StatementTag() string { return "VALUES" } +// StatementReturnType implements the Statement interface. +func (*CreateFunction) StatementReturnType() StatementReturnType { return DDL } + +// StatementType implements the Statement interface. +func (*CreateFunction) StatementType() StatementType { return TypeDDL } + +// StatementTag returns a short string identifying the type of statement. +func (*CreateFunction) StatementTag() string { return "CREATE FUNCTION" } + +// StatementReturnType implements the Statement interface. +func (*RoutineReturn) StatementReturnType() StatementReturnType { return Rows } + +// StatementType implements the Statement interface. +func (*RoutineReturn) StatementType() StatementType { return TypeDML } + +// StatementTag returns a short string identifying the type of statement. +func (*RoutineReturn) StatementTag() string { return "RETURN" } + func (n *AlterChangefeed) String() string { return AsString(n) } func (n *AlterChangefeedCmds) String() string { return AsString(n) } func (n *AlterBackup) String() string { return AsString(n) } @@ -1836,6 +1857,7 @@ func (n *CopyFrom) String() string { return AsString(n) } func (n *CreateChangefeed) String() string { return AsString(n) } func (n *CreateDatabase) String() string { return AsString(n) } func (n *CreateExtension) String() string { return AsString(n) } +func (n *CreateFunction) String() string { return AsString(n) } func (n *CreateIndex) String() string { return AsString(n) } func (n *CreateRole) String() string { return AsString(n) } func (n *CreateTable) String() string { return AsString(n) } @@ -1878,6 +1900,7 @@ func (n *ReparentDatabase) String() string { return AsString(n) } func (n *RenameIndex) String() string { return AsString(n) } func (n *RenameTable) String() string { return AsString(n) } func (n *Restore) String() string { return AsString(n) } +func (n *RoutineReturn) String() string { return AsString(n) } func (n *Revoke) String() string { return AsString(n) } func (n *RevokeRole) String() string { return AsString(n) } func (n *RollbackToSavepoint) String() string { return AsString(n) } diff --git a/pkg/sql/sem/tree/udf.go b/pkg/sql/sem/tree/udf.go new file mode 100644 index 000000000000..f7181e9ee223 --- /dev/null +++ b/pkg/sql/sem/tree/udf.go @@ -0,0 +1,291 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package tree + +import ( + "strings" + + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" + "github.com/cockroachdb/errors" +) + +// FunctionName represent a function name in a UDF relevant statement, either +// DDL or DML statement. Similar to TableName, it is constructed for incoming +// SQL queries from an UnresolvedObjectName. +type FunctionName struct { + objName +} + +// Format implements the NodeFormatter interface. +func (f *FunctionName) Format(ctx *FmtCtx) { + f.ObjectNamePrefix.Format(ctx) + if f.ExplicitSchema || ctx.alwaysFormatTablePrefix() { + ctx.WriteByte('.') + } + ctx.FormatNode(&f.ObjectName) +} + +// CreateFunction represents a CREATE FUNCTION statement. +type CreateFunction struct { + IsProcedure bool + Replace bool + FuncName FunctionName + Args FuncArgs + ReturnType FuncReturnType + Options FunctionOptions + RoutineBody *RoutineBody +} + +// Format implements the NodeFormatter interface. +func (node *CreateFunction) Format(ctx *FmtCtx) { + ctx.WriteString("CREATE ") + if node.Replace { + ctx.WriteString("OR REPLACE ") + } + ctx.WriteString("FUNCTION ") + ctx.FormatNode(&node.FuncName) + ctx.WriteString("(") + ctx.FormatNode(node.Args) + ctx.WriteString(") ") + ctx.WriteString("RETURNS ") + if node.ReturnType.IsSet { + ctx.WriteString("SETOF ") + } + ctx.WriteString(node.ReturnType.Type.SQLString()) + ctx.WriteString(" ") + var funcBody FunctionBodyStr + for _, option := range node.Options { + switch t := option.(type) { + case FunctionBodyStr: + funcBody = t + continue + } + ctx.FormatNode(option) + ctx.WriteString(" ") + } + if len(funcBody) > 0 { + ctx.FormatNode(funcBody) + } + if node.RoutineBody != nil { + ctx.WriteString("BEGIN ATOMIC ") + for _, stmt := range node.RoutineBody.Stmts { + ctx.FormatNode(stmt) + ctx.WriteString("; ") + } + ctx.WriteString("END") + } +} + +// RoutineBody represent a list of statements in a UDF body. +type RoutineBody struct { + Stmts Statements +} + +// RoutineReturn represent a RETURN statement in a UDF body. +type RoutineReturn struct { + ReturnVal Expr +} + +// Format implements the NodeFormatter interface. +func (node *RoutineReturn) Format(ctx *FmtCtx) { + ctx.WriteString("RETURN ") + ctx.FormatNode(node.ReturnVal) +} + +// FunctionOptions represent a list of function options. +type FunctionOptions []FunctionOption + +// FunctionOption is an interface representing UDF properties. +type FunctionOption interface { + functionOption() + NodeFormatter +} + +func (FunctionNullInputBehavior) functionOption() {} +func (FunctionVolatility) functionOption() {} +func (FunctionLeakProof) functionOption() {} +func (FunctionBodyStr) functionOption() {} +func (FunctionLanguage) functionOption() {} + +// FunctionNullInputBehavior represent UDF property on null parameters. +type FunctionNullInputBehavior int + +const ( + // FunctionCalledOnNullInput indicates that the function will be given the + // chance to execute when presented with NULL input. + FunctionCalledOnNullInput FunctionNullInputBehavior = iota + // FunctionReturnsNullOnNullInput indicates that the function will result in + // NULL given any NULL parameter. + FunctionReturnsNullOnNullInput + // FunctionStrict is the same as FunctionReturnsNullOnNullInput + FunctionStrict +) + +// Format implements the NodeFormatter interface. +func (node FunctionNullInputBehavior) Format(ctx *FmtCtx) { + switch node { + case FunctionCalledOnNullInput: + ctx.WriteString("CALLED ON NULL INPUT") + case FunctionReturnsNullOnNullInput: + ctx.WriteString("RETURNS NULL ON NULL INPUT") + case FunctionStrict: + ctx.WriteString("STRICT") + default: + panic(pgerror.New(pgcode.InvalidParameterValue, "Unknown function option")) + } +} + +// FunctionVolatility represent UDF volatility property. +type FunctionVolatility int + +const ( + // FunctionVolatile see volatility.Volatile + FunctionVolatile FunctionVolatility = iota + // FunctionImmutable see volatility.Immutable + FunctionImmutable + // FunctionStable see volatility.Stable + FunctionStable +) + +// Format implements the NodeFormatter interface. +func (node FunctionVolatility) Format(ctx *FmtCtx) { + switch node { + case FunctionVolatile: + ctx.WriteString("VOLATILE") + case FunctionImmutable: + ctx.WriteString("IMMUTABLE") + case FunctionStable: + ctx.WriteString("STABLE") + default: + panic(pgerror.New(pgcode.InvalidParameterValue, "Unknown function option")) + } +} + +// FunctionLeakProof indicates whether if a UDF is leakproof or not. +type FunctionLeakProof bool + +// Format implements the NodeFormatter interface. +func (node FunctionLeakProof) Format(ctx *FmtCtx) { + if !node { + ctx.WriteString("NOT ") + } + ctx.WriteString("LEAKPROOF") +} + +// FunctionLanguage indicates the language of the statements in the UDF function +// body. +type FunctionLanguage int + +const ( + _ FunctionLanguage = iota + // FunctionLangSQL represent SQL language. + FunctionLangSQL +) + +// Format implements the NodeFormatter interface. +func (node FunctionLanguage) Format(ctx *FmtCtx) { + ctx.WriteString("LANGUAGE ") + switch node { + case FunctionLangSQL: + ctx.WriteString("SQL") + default: + panic(pgerror.New(pgcode.InvalidParameterValue, "Unknown function option")) + } +} + +// AsFunctionLanguage converts a string to a FunctionLanguage if applicable. +// Error is returned if string does not represent a valid UDF language. +func AsFunctionLanguage(lang string) (FunctionLanguage, error) { + switch strings.ToLower(lang) { + case "sql": + return FunctionLangSQL, nil + } + return 0, errors.Newf("language %q does not exist", lang) +} + +// FunctionBodyStr is a string containing all statements in a UDF body. +type FunctionBodyStr string + +// Format implements the NodeFormatter interface. +func (node FunctionBodyStr) Format(ctx *FmtCtx) { + ctx.WriteString("AS ") + ctx.WriteString("$$") + ctx.WriteString(string(node)) + ctx.WriteString("$$") +} + +// FuncArgs represents a list of FuncArg. +type FuncArgs []FuncArg + +// Format implements the NodeFormatter interface. +func (node FuncArgs) Format(ctx *FmtCtx) { + for i, arg := range node { + if i > 0 { + ctx.WriteString(", ") + } + ctx.FormatNode(&arg) + } +} + +// FuncArg represents an argument from a UDF signature. +type FuncArg struct { + Name Name + Type ResolvableTypeReference + Class FuncArgClass + DefaultVal Expr +} + +// Format implements the NodeFormatter interface. +func (node *FuncArg) Format(ctx *FmtCtx) { + switch node.Class { + case FunctionArgIn: + ctx.WriteString("IN") + case FunctionArgOut: + ctx.WriteString("OUT") + case FunctionArgInOut: + ctx.WriteString("INOUT") + case FunctionArgVariadic: + ctx.WriteString("VARIADIC") + default: + panic(pgerror.New(pgcode.InvalidParameterValue, "Unknown function option")) + } + ctx.WriteString(" ") + if node.Name != "" { + ctx.FormatNode(&node.Name) + ctx.WriteString(" ") + } + ctx.WriteString(node.Type.SQLString()) + if node.DefaultVal != nil { + ctx.WriteString(" DEFAULT ") + ctx.FormatNode(node.DefaultVal) + } +} + +// FuncArgClass indicates what type of argument an arg is. +type FuncArgClass int + +const ( + // FunctionArgIn args can only be used as input. + FunctionArgIn FuncArgClass = iota + // FunctionArgOut args can only be used as output. + FunctionArgOut + // FunctionArgInOut args can be used as both input and output. + FunctionArgInOut + // FunctionArgVariadic args are variadic. + FunctionArgVariadic +) + +// FuncReturnType represent the return type of UDF. +type FuncReturnType struct { + Type ResolvableTypeReference + IsSet bool +}