diff --git a/docs/src/main/sphinx/admin/properties-sql-environment.md b/docs/src/main/sphinx/admin/properties-sql-environment.md new file mode 100644 index 000000000000..21b3cbdf0f68 --- /dev/null +++ b/docs/src/main/sphinx/admin/properties-sql-environment.md @@ -0,0 +1,53 @@ +# SQL environment properties + +SQL environment properties allow you to globally configure parameters relevant +to all SQL queries and the context they are processed in. + +## `sql.forced-session-time-zone` + +- **Type:** [](prop-type-string) + +Force the time zone for any query processing to the configured value, and +therefore override the time zone of the client. The time zone must be specified +as a string such as `UTC` or [other valid +values](timestamp-p-with-time-zone-data-type). + +## `sql.default-catalog` + +- **Type:** [](prop-type-string) + +Set the default catalog for all clients. Any default catalog configuration +provided by a client overrides this default. + +## `sql.default-schema` + +- **Type:** [](prop-type-string) + +Set the default schema for all clients. Must be set to a schema name that is +valid for the default catalog. Any default schema configuration provided by a +client overrides this default. + +## `sql.default-function-catalog` + +- **Type:** [](prop-type-string) + +Set the default catalog for [SQL routine](/routines) storage for all clients. +The connector used in the catalog must support [](sql-routine-management). Any +usage of a fully qualified name for a routine overrides this default. + +## `sql.default-function-schema` + +- **Type:** [](prop-type-string) + +Set the default schema for SQL routine storage for all clients. Must be set to a +schema name that is valid for the default function catalog. Any usage of a fully +qualified name for a routine overrides this default. + +## `sql.path` + +- **Type:** [](prop-type-string) + +Define the default collection of paths to functions or table functions in +specific catalogs and schemas. Paths are specified as +`catalog_name.schema_name`. Multiple paths must be separated by commas. Find +more details about the path in [](/sql/set-path). diff --git a/docs/src/main/sphinx/admin/properties.md b/docs/src/main/sphinx/admin/properties.md index 88c2ad833cb2..c0965221a0ae 100644 --- a/docs/src/main/sphinx/admin/properties.md +++ b/docs/src/main/sphinx/admin/properties.md @@ -17,6 +17,7 @@ properties, refer to the {doc}`connector documentation `. General Resource management Query management +SQL environment Spilling Exchange Task diff --git a/docs/src/main/sphinx/connector/hive.md b/docs/src/main/sphinx/connector/hive.md index 093a9c19cbb6..3a4f630c9358 100644 --- a/docs/src/main/sphinx/connector/hive.md +++ b/docs/src/main/sphinx/connector/hive.md @@ -384,6 +384,7 @@ configured object storage system and metadata stores: - {ref}`sql-view-management`; see also {ref}`Hive-specific view management ` +- [](sql-routine-management) - {ref}`sql-security-operations`: see also {ref}`SQL standard-based authorization for object storage ` diff --git a/docs/src/main/sphinx/connector/memory.md b/docs/src/main/sphinx/connector/memory.md index 1a4fcdb62f98..c4d116d50231 100644 --- a/docs/src/main/sphinx/connector/memory.md +++ b/docs/src/main/sphinx/connector/memory.md @@ -67,6 +67,7 @@ statements, the connector supports the following features: - {doc}`/sql/create-schema` - {doc}`/sql/drop-schema` - {doc}`/sql/comment` +- [](sql-routine-management) ### DROP TABLE diff --git a/docs/src/main/sphinx/index.md b/docs/src/main/sphinx/index.md index 8c485a678dbd..694534859bae 100644 --- a/docs/src/main/sphinx/index.md +++ b/docs/src/main/sphinx/index.md @@ -13,6 +13,7 @@ connector functions language sql +routines develop glossary appendix diff --git a/docs/src/main/sphinx/language/sql-support.md b/docs/src/main/sphinx/language/sql-support.md index 419ef207cd06..209bbd1a6924 100644 --- a/docs/src/main/sphinx/language/sql-support.md +++ b/docs/src/main/sphinx/language/sql-support.md @@ -116,6 +116,15 @@ connector to connector: - {doc}`/sql/drop-view` - {doc}`/sql/alter-view` +(sql-routine-management)= +### Routine management + +The following statements are used to manage [catalog routines](routine-catalog): + +- [](/sql/create-function) +- [](/sql/drop-function) +- [](/sql/show-functions) + (sql-security-operations)= ## Security operations diff --git a/docs/src/main/sphinx/language/types.md b/docs/src/main/sphinx/language/types.md index 97bf46da3b19..bf95d07ff3ba 100644 --- a/docs/src/main/sphinx/language/types.md +++ b/docs/src/main/sphinx/language/types.md @@ -271,12 +271,13 @@ SELECT cast(TIMESTAMP '2020-06-10 15:55:23.383345' as TIMESTAMP(12)); `TIMESTAMP WITH TIME ZONE` is an alias for `TIMESTAMP(3) WITH TIME ZONE` (millisecond precision). +(timestamp-p-with-time-zone-data-type)= ### `TIMESTAMP(P) WITH TIME ZONE` Instant in time that includes the date and time of day with `P` digits of -precision for the fraction of seconds and with a time zone. Values of this -type are rendered using the time zone from the value. -Time zones can be expressed in the following ways: +precision for the fraction of seconds and with a time zone. Values of this type +are rendered using the time zone from the value. Time zones can be expressed in +the following ways: - `UTC`, with `GMT`, `Z`, or `UT` usable as aliases for UTC. - `+hh:mm` or `-hh:mm` with `hh:mm` as an hour and minute offset from UTC. diff --git a/docs/src/main/sphinx/routines.md b/docs/src/main/sphinx/routines.md new file mode 100644 index 000000000000..42e5b202a794 --- /dev/null +++ b/docs/src/main/sphinx/routines.md @@ -0,0 +1,23 @@ +# SQL routines + +A SQL routine is a custom, user-defined function authored by a user of Trino in +a client and written in SQL. More details are available in the following sections: + +```{toctree} +:maxdepth: 1 + +Introduction +Examples +routines/begin +routines/case +routines/declare +routines/function +routines/if +routines/iterate +routines/leave +routines/loop +routines/repeat +routines/return +routines/set +routines/while +``` diff --git a/docs/src/main/sphinx/routines/begin.md b/docs/src/main/sphinx/routines/begin.md new file mode 100644 index 000000000000..f9dab3d72302 --- /dev/null +++ b/docs/src/main/sphinx/routines/begin.md @@ -0,0 +1,57 @@ +# BEGIN + +## Synopsis + +```text +BEGIN + [ DECLARE ... ] + statements +END +``` + +## Description + +Marks the start and end of a block in a [SQL routine](/routines/introduction). +`BEGIN` can be used wherever a statement can be used to group multiple +statements together and to declare variables local to the block. A typical use +case is as first statement within a [](/routines/function). Blocks can also be +nested. + +After the `BEGIN` keyword, you can add variable declarations using +[/routines/declare] statements, followed by one or more statements that define +the main body of the routine, separated by `;`. The following statements can be +used: + +* [](/routines/case) +* [](/routines/if) +* [](/routines/iterate) +* [](/routines/leave) +* [](/routines/loop) +* [](/routines/repeat) +* [](/routines/return) +* [](/routines/set) +* [](/routines/while) +* Nested [](/routines/begin) blocks + +## Examples + +The following example computes the value `42`: + +```sql +FUNCTION meaning_of_life() + RETURNS tinyint + BEGIN + DECLARE a tinyint DEFAULT 6; + DECLARE b tinyint DEFAULT 7; + RETURN a * b; + END +``` + +Further examples of varying complexity that cover usage of the `BEGIN` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/function) diff --git a/docs/src/main/sphinx/routines/case.md b/docs/src/main/sphinx/routines/case.md new file mode 100644 index 000000000000..36251cc9b7e5 --- /dev/null +++ b/docs/src/main/sphinx/routines/case.md @@ -0,0 +1,61 @@ +# CASE + +## Synopsis + +Simple case: + +```text +CASE + WHEN condition THEN statements + [ ... ] + [ ELSE statements ] +END CASE +``` + +Searched case: + +```text +CASE expression + WHEN expression THEN statements + [ ... ] + [ ELSE statements ] +END +``` + +## Description + +The `CASE` statement is an optional construct to allow conditional processing +in [SQL routines](/routines/introduction). + +The `WHEN` clauses are evaluated sequentially, stopping after the first match, +and therefore the order of the statements is significant. The statements of the +`ELSE` clause are executed if none of the `WHEN` clauses match. + +Unlike other languages like C or Java, SQL does not support case fall through, +so processing stops at the end of the first matched case. + +One or more `WHEN` clauses can be used. + +## Examples + +The following example shows a simple `CASE` statement usage: + +```sql +FUNCTION simple_case(a bigint) + RETURNS varchar + BEGIN + CASE a + WHEN 0 THEN RETURN 'zero'; + WHEN 1 THEN RETURN 'one'; + ELSE RETURN 'more than one or negative'; + END CASE; + END +``` + +Further examples of varying complexity that cover usage of the `CASE` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) diff --git a/docs/src/main/sphinx/routines/declare.md b/docs/src/main/sphinx/routines/declare.md new file mode 100644 index 000000000000..4ddcddf24e3a --- /dev/null +++ b/docs/src/main/sphinx/routines/declare.md @@ -0,0 +1,52 @@ +# DECLARE + +## Synopsis + +```text +DECLARE identifier [, ...] type [ DEFAULT expression ] +``` + +## Description + +Use the `DECLARE` statement directly after the [](/routines/begin) keyword in +[](/routines) to define one or more variables with an `identifier` as name. Each +statement must specify the [data type](/language/types) of the variable with +`type`. It can optionally include a default, initial value defined by an +`expression`. The default value is `NULL` if not specified. + +## Examples + +A simple declaration of the variable `x` with the `tinyint` data type and the +implicit default value of `null`: + +```sql +DECLARE x tinyint; +``` + +A declaration of multiple string variables with length restricted to 25 +characters: + +```sql +DECLARE first_name, last_name, middle_name varchar(25); +``` + +A declaration of a fixed-precision decimal number with a default value: + +```sql +DECLARE uptime_requirement decimal DEFAULT 99.999; +``` + +A declaration with a default value from an expression: + +```sql +DECLARE start_time timestamp(3) with time zone DEFAULT now(); +``` + +Further examples of varying complexity that cover usage of the `DECLARE` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/language/types) diff --git a/docs/src/main/sphinx/routines/examples.md b/docs/src/main/sphinx/routines/examples.md new file mode 100644 index 000000000000..0d3ed0726153 --- /dev/null +++ b/docs/src/main/sphinx/routines/examples.md @@ -0,0 +1,371 @@ +# Example SQL routines + + +After learning about [SQL routines from the +introduction](/routines/introduction), the following sections show numerous +examples of valid SQL routines. The routines are suitable as [inline +routines](routine-inline) or [catalog routines](routine-catalog), after +adjusting the name and adjusting the example invocations. + +The examples combine numerous supported statements. Refer to the specific +statement documentation for further details: + +* [](/routines/function) for general SQL routine declaration +* [](/routines/begin) and [](/routines/declare) for routine blocks +* [](/routines/set) for assigning values to variables +* [](/routines/return) for returning routine results +* [](/routines/case) and [](/routines/if) for conditional flows +* [](/routines/loop), [](/routines/repeat), and [](/routines/while) for looping constructs +* [](/routines/iterate) and [](/routines/leave) for flow control + +A very simple routine that returns a static value without requiring any input: + +```sql +FUNCTION answer() +RETURNS BIGINT +RETURN 42 +``` + +## Inline and catalog routines + +A full example of this routine as inline routine and usage in a string +concatenation with a cast: + +```sql +WITH + FUNCTION answer() + RETURNS BIGINT + RETURN 42 +SELECT 'The answer is ' || CAST(answer() as varchar); +-- The answer is 42 +``` + +Provided the catalog `example` supports routine storage in the `default` schema, +you can use the following: + +```sql +USE example.default; +CREATE FUNCTION example.default.answer() + RETURNS BIGINT + RETURN 42; +``` + +With the routine stored in the catalog, you can run the routine multiple times +without repeated definition: + +```sql +SELECT example.default.answer() + 1; -- 43 +SELECT 'The answer is' || CAST(example.default.answer() as varchar); -- The answer is 42 +``` + +Alternatively, you can configure the SQL environment in the +[](config-properties) to a catalog and schema that support SQL routine storage: + +```properties +sql.default-function-catalog=example +sql.default-function-schema=default +``` + +Now you can manage SQL routines without the full path: + +```sql +CREATE FUNCTION answer() + RETURNS BIGINT + RETURN 42; +``` + +SQL routine invocation works without the full path: + +```sql +SELECT answer() + 5; -- 47 +``` + +## Declaration examples + +The result of calling the routine `answer()` is always identical, so you can +declare it as deterministic, and add some other information: + +```sql +FUNCTION answer() +LANGUAGE SQL +DETERMINISTIC +RETURNS BIGINT +COMMENT 'Provide the answer to the question about life, the universe, and everything.' +RETURN 42 +``` + +The comment and other information about the routine is visible in the output of +[](/sql/show-functions). + +A simple routine that returns a greeting back to the input string `fullname` +concatenating two strings and the input value: + +```sql +FUNCTION hello(fullname VARCHAR) +RETURNS VARCHAR +RETURN 'Hello, ' || fullname || '!' +``` + +Following is an example invocation: + +```sql +SELECT hello('Jane Doe'); -- Hello, Jane Doe! +``` + +A first example routine, that uses multiple statements in a `BEGIN` block. It +calculates the result of a multiplication of the input integer with `99`. The +`bigint` data type is used for all variables and values. The value of integer +`99` is cast to `bigint` in the default value assignment for the variable `x`. + +```sql +FUNCTION times_ninety_nine(a bigint) +RETURNS bigint +BEGIN + DECLARE x bigint DEFAULT CAST(99 AS bigint); + RETURN x * a; +END +``` + +Following is an example invocation: + +```sql +SELECT times_ninety_nine(CAST(2 as bigint)); -- 198 +``` + +## Conditional flows + +A first example of conditional flow control in a routine using the `CASE` +statement. The simple `bigint` input value is compared to a number of values. + +```sql +FUNCTION simple_case(a bigint) +RETURNS varchar +BEGIN + CASE a + WHEN 0 THEN RETURN 'zero'; + WHEN 1 THEN RETURN 'one'; + WHEN 10 THEN RETURN 'ten'; + WHEN 20 THEN RETURN 'twenty'; + ELSE RETURN 'other'; + END CASE; + RETURN NULL; +END +``` + +Following are a couple of example invocations with result and explanation: + +```sql +SELECT simple_case(0); -- zero +SELECT simple_case(1); -- one +SELECT simple_case(-1); -- other (from else clause) +SELECT simple_case(10); -- ten +SELECT simple_case(11); -- other (from else clause) +SELECT simple_case(20); -- twenty +SELECT simple_case(100); -- other (from else clause) +SELECT simple_case(null); -- null .. but really?? +``` + +A second example of a routine with a `CASE` statement, this time with two +parameters, showcasing the importance of the order of the conditions. + +```sql +FUNCTION search_case(a bigint, b bigint) +RETURNS varchar +BEGIN + CASE + WHEN a = 0 THEN RETURN 'zero'; + WHEN b = 1 THEN RETURN 'one'; + WHEN a = DECIMAL '10.0' THEN RETURN 'ten'; + WHEN b = 20.0E0 THEN RETURN 'twenty'; + ELSE RETURN 'other'; + END CASE; + RETURN NULL; +END +``` + +Following are a couple of example invocations with result and explanation: + +```sql +SELECT search_case(0,0); -- zero +SELECT search_case(1,1); -- one +SELECT search_case(0,1); -- zero (not one since the second check is never reached) +SELECT search_case(10,1); -- one (not ten since the third check is never reached) +SELECT search_case(10,2); -- ten +SELECT search_case(10,20); -- ten (not twenty) +SELECT search_case(0,20); -- zero (not twenty) +SELECT search_case(3,20); -- twenty +SELECT search_case(3,21); -- other +SELECT simple_case(null,null); -- null .. but really?? +``` + +## Fibonacci example + +This routine calculates the `n`-th value in the Fibonacci series, in which each +number is the sum of the two preceding ones. The two initial values are are set +to `1` as the defaults for `a` and `b`. The routine uses an `IF` statement +condition to return `1` for all input values of `2` or less. The `WHILE` block +then starts to calculate each number in the series, starting with `a=1` and +`b=1` and iterates until it reaches the `n`-th position. In each iteration is +sets `a` and `b` for the preceding to values, so it can calculate the sum, and +finally return it. Note that processing the routine takes longer and longer with +higher `n` values, and the result it deterministic. + +```sql +FUNCTION fib(n bigint) +RETURNS bigint +BEGIN + DECLARE a, b bigint DEFAULT 1; + DECLARE c bigint; + IF n <= 2 THEN + RETURN 1; + END IF; + WHILE n > 2 DO + SET n = n - 1; + SET c = a + b; + SET a = b; + SET b = c; + END WHILE; + RETURN c; +END +``` + +Following are a couple of example invocations with result and explanation: + +```sql +SELECT fib(-1); -- 1 +SELECT fib(0); -- 1 +SELECT fib(1); -- 1 +SELECT fib(2); -- 1 +SELECT fib(3); -- 2 +SELECT fib(4); -- 3 +SELECT fib(5); -- 5 +SELECT fib(6); -- 8 +SELECT fib(7); -- 13 +SELECT fib(8); -- 21 +``` + +## Labels and loops + +This routing uses the `top` label to name the `WHILE` block, and then controls +the flow with conditional statements, `ITERATE`, and `LEAVE`. For the values of +`a=1` and `a=2` in the first two iterations of the loop the `ITERATE` call moves +the flow up to `top` before `b` is ever increased. Then `b` is increased for the +values `a=3`, `a=4`, `a=5`, `a=6`, and `a=7`, resulting in `b=5`. The `LEAVE` +call then causes the exit of the block before a is increased further to `10` and +therefore the result of the routine is `5`. + +```sql +FUNCTION labels() +RETURNS bigint +BEGIN + DECLARE a, b int DEFAULT 0; + top: WHILE a < 10 DO + SET a = a + 1; + IF a < 3 THEN + ITERATE top; + END IF; + SET b = b + 1; + IF a > 6 THEN + LEAVE top; + END IF; + END WHILE; + RETURN b; +END +``` + +This routine implements calculating the `n` to the power of `p` by repeated +multiplication and keeping track of the number of multiplications performed. +Note that this routine does not return the correct `0` for `p=0` since the `top` +block is merely escaped and the value of `n` is returned. The same incorrect +behavior happens for negative values of `p`: + +```sql +FUNCTION power(n int, p int) +RETURNS int + BEGIN + DECLARE r int DEFAULT n; + top: LOOP + IF p <= 1 THEN + LEAVE top; + END IF; + SET r = r * n; + SET p = p - 1; + END LOOP; + RETURN r; + END +``` + +Following are a couple of example invocations with result and explanation: + +```sql +SELECT power(2, 2); -- 4 +SELECT power(2, 8); -- 256 +SELECT power(3, 3); -- 256 +SELECT power(3, 0); -- 3, which is wrong +SELECT power(3, -2); -- 3, which is wrong +``` + +This routine returns `7` as a result of the increase of `b` in the loop from +`a=3` to `a=10`: + +```sql +FUNCTION test_repeat_continue() +RETURNS bigint +BEGIN + DECLARE a int DEFAULT 0; + DECLARE b int DEFAULT 0; + top: REPEAT + SET a = a + 1; + IF a <= 3 THEN + ITERATE top; + END IF; + SET b = b + 1; + UNTIL a >= 10 + END REPEAT; + RETURN b; +END +``` + +This routine returns `2` and shows that labels can be repeated and label usage +within a block refers to the label of that block: + +```sql +FUNCTION test() +RETURNS int +BEGIN + DECLARE r int DEFAULT 0; + abc: LOOP + SET r = r + 1; + LEAVE abc; + END LOOP; + abc: LOOP + SET r = r + 1; + LEAVE abc; + END LOOP; + RETURN r; +END +``` + +## Routines and built-in functions + +This routine show that multiple data types and built-in functions like +`length()` and `cardinality()` can be used in a routine. The two nested `BEGIN` +blocks also show how variable names are local within these blocks `x`, but the +global `r` from the top-level block can be accessed in the nested blocks: + +```sql +FUNCTION test() +RETURNS bigint +BEGIN + DECLARE r bigint DEFAULT 0; + BEGIN + DECLARE x varchar DEFAULT 'hello'; + SET r = r + length(x); + END; + BEGIN + DECLARE x array(int) DEFAULT array[1, 2, 3]; + SET r = r + cardinality(x); + END; + RETURN r; +END +``` diff --git a/docs/src/main/sphinx/routines/function.md b/docs/src/main/sphinx/routines/function.md new file mode 100644 index 000000000000..65c4448c4eb4 --- /dev/null +++ b/docs/src/main/sphinx/routines/function.md @@ -0,0 +1,100 @@ +# FUNCTION + +## Synopsis + +```text +FUNCTION name ( [ parameter_name data_type [, ...] ] ) + RETURNS type + [ LANGUAGE langauge] + [ NOT? DETERMINISTIC ] + [ RETURNS NULL ON NULL INPUT ] + [ CALLED ON NULL INPUT ] + [ SECURITY { DEFINER | INVOKER } ] + [ COMMENT description] + statements +``` + +## Description + +Declare a SQL routine. + +The `name` of the routine. [Inline routines](routine-inline) can use a simple +string. [Catalog routines](routine-catalog) must qualify the name of the catalog +and schema, delimited by `.`, to store the routine or rely on the [default +catalog and schema for routine storage](/admin/properties-sql-environment). + +The list of parameters is a comma-separated list of names `parameter_name` and +data types `data_type`, see [data type](/language/types). An empty list, specified as +`()` is also valid. + +The `type` value after the `RETURNS` keyword identifies the [data +type](/language/types) of the routine output. + +The optional `LANGUAGE` characteristic identifies the language used for the +routine definition with `language`. Only `SQL` is supported. + +The optional `DETERMINISTIC` or `NOT DETERMINISTIC` characteristic declares that +the routine is deterministic. This means that repeated routine calls with +identical input parameters yield the same result. For SQL language routines, a +routine is non-deterministic if it calls any non-deterministic routines and +[functions](/functions). By default, routines are assume to have a deterministic +behavior. + +The optional `RETURNS NULL ON NULL INPUT` characteristic declares that the +routine returns a `NULL` value when any of the input parameters are `NULL`. +The routine is not invoked with a `NULL` input value. + +The `CALLED ON NULL INPUT` characteristic declares that the routine is invoked +with `NULL` input parameter values. + +The `RETURNS NULL ON NULL INPUT` and `CALLED ON NULL INPUT` characteristics are +mutually exclusive, with `CALLED ON NULL INPUT` as the default. + +The security declaration of `SECURITY INVOKER` or `SECURITY DEFINER` is only +valid for catalog routines. It sets the mode for processing the routine with the +permissions of the user who calls the routine (`INVOKER`) or the user who +created the routine (`DEFINER`). + +The `COMMENT` characteristic can be used to provide information about the +function to other users as `description`. The information is accessible with +[](/sql/show-functions). + +The body of the routine can either be a simple single `RETURN` statement with an +expression, or compound list of `statements` in a `BEGIN` block. + +## Examples + +A simple catalog function: + +```sql +CREATE FUNCTION example.default.meaning_of_life() + RETURNS BIGINT + RETURN 42; +``` + +And used: + +```sql +SELECT example.default.meaning_of_life(); -- returns 42 +``` + +Equivalent usage with an inline function: + +```sql +WITH FUNCTION meaning_of_life() + RETURNS BIGINT + RETURN 42 +SELECT meaning_of_life(); +``` + +Further examples of varying complexity that cover usage of the `FUNCTION` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/begin) +* [](/routines/return) +* [](/sql/create-function) + diff --git a/docs/src/main/sphinx/routines/if.md b/docs/src/main/sphinx/routines/if.md new file mode 100644 index 000000000000..a02c9e659dc2 --- /dev/null +++ b/docs/src/main/sphinx/routines/if.md @@ -0,0 +1,47 @@ +# IF + +## Synopsis + +```text +IF condition + THEN statements + [ ELSEIF condition THEN statements ] + [ ... ] + [ ELSE statements ] +END IF +``` + +## Description + +The `IF THEN` statement is an optional construct to allow conditional processing +in [SQL routines](/routines/introduction). Each `condition` following an `IF` +or `ELSEIF` must evaluate to a boolean. The result of processing the expression +must result in a boolean `true` value to process the `statements` in the `THEN` +block. A result of `false` results in skipping the `THEN` block and moving to +evaluate the next `ELSEIF` and `ELSE` blocks in order. + +The `ELSEIF` and `ELSE` segments are optional. + +## Examples + +```sql +FUNCTION simple_if(a bigint) + RETURNS varchar + BEGIN + IF a = 0 THEN + RETURN 'zero'; + ELSEIF a = 1 THEN + RETURN 'one'; + ELSE + RETURN 'more than one or negative'; + END IF; + END +``` + +Further examples of varying complexity that cover usage of the `IF` statement in +combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) diff --git a/docs/src/main/sphinx/routines/introduction.md b/docs/src/main/sphinx/routines/introduction.md new file mode 100644 index 000000000000..d1191ae7dab1 --- /dev/null +++ b/docs/src/main/sphinx/routines/introduction.md @@ -0,0 +1,177 @@ +# Introduction to SQL routines + +A SQL routine is a custom, user-defined function authored by a user of Trino and +written in the SQL routine language. You can declare the routine body within a +[](/routines/function) block as [inline routines](routine-inline) or [catalog +routines](routine-catalog). + +(routine-inline)= +## Inline routines + +An inline routine declares and uses the routine within a query processing +context. The routine is declared in a `WITH` block before the query: + +```sql +WITH + FUNCTION abc(x integer) + RETURNS integer + RETURN x * 2 +SELECT abc(21); +``` + +Inline routine names must follow SQL identifier naming conventions, and cannot +contain `.` characters. + +The routine declaration is only valid within the context of the query. A +separate later invocation of the routine is not possible. If this is desired, +use a [catalog routine](routine-catalog). + +Multiple inline routine declarations are comma-separated, and can include +routines calling each other, as long as a called routine is declared before +the first invocation. + +```sql +WITH + FUNCTION abc(x integer) + RETURNS integer + RETURN x * 2, + FUNCTION xyz(x integer) + RETURNS integer + RETURN abc(x) + 1 +SELECT xyz(21); +``` + +Note that inline routines can mask and override the meaning of a built-in function: + +```sql +WITH + FUNCTION abs(x integer) + RETURNS integer + RETURN x * 2 +SELECT abs(-10); -- -20, not 10! +``` + +(routine-catalog)= +## Catalog routines + +You can store a routine in the context of a catalog, if the connector used in +the catalog supports routine storage. In this scenario, the following commands +can be used: + +* [](/sql/create-function) to create and store a routine. +* [](/sql/drop-function) to remove a routine. +* [](/sql/show-functions) to display a list of routines in a catalog. + +Catalog routines must use a name that combines the catalog name and schema name +with the routine name, such as `example.default.power` for the `power` routine +in the `default` schema of the `example` catalog. + +Invocation must use the fully qualified name, such as `example.default.power`. + +(routine-sql-environment)= +## SQL environment configuration + +Configuration of the `sql.default-function-catalog` and +`sql.default-function-schema` [](/admin/properties-sql-environment) allows you +to set the default storage for SQL routines. The catalog and schema must be +added to the `sql.path` as well. This enables users to call SQL routines and +perform all [SQL routine management](sql-routine-management) without specifying +the full path to the routine. + +:::{note} +Use the [](/connector/memory) in a catalog for simple storing and +testing of your SQL routines. +::: + +## Routine declaration + +Refer to the documentation for the [](/routines/function) keyword for more +details about declaring the routine overall. The routine body is composed with +statements from the following list: + +* [](/routines/begin) +* [](/routines/case) +* [](/routines/declare) +* [](/routines/if) +* [](/routines/iterate) +* [](/routines/leave) +* [](/routines/loop) +* [](/routines/repeat) +* [](/routines/return) +* [](/routines/set) +* [](/routines/while) + +Statements can also use [built-in functions and operators](/functions) as well +as other routines, although recursion is not supported for routines. + +Find simple examples in each statement documentation, and refer to the [example +documentation](/routines/examples) for more complex use cases that combine +multiple statements. + +:::{note} +User-defined functions can alternatively be written in Java and deployed as a +plugin. Details are available in the [developer guide](/develop/functions). +::: + +(routine-label)= +## Labels + +Routines can contain labels as markers for a specific block in the declaration +before the following keywords: + +* `CASE` +* `IF` +* `LOOP` +* `REPEAT` +* `WHILE` + +The label is used to name the block in order to continue processing with the +`ITERATE` statement or exit the block with the `LEAVE` statement. This flow +control is supported for nested blocks, allowing to continue or exit an outer +block, not just the innermost block. For example, the following snippet uses the +label `top` to name the complete block from `REPEAT` to `END REPEAT`: + +```sql +top: REPEAT + SET a = a + 1; + IF a <= 3 THEN + ITERATE top; + END IF; + SET b = b + 1; + UNTIL a >= 10 +END REPEAT; +``` + +Labels can be used with the `ITERATE` and `LEAVE` statements to continue +processing the block or leave the block. This flow control is also supported for +nested blocks and labels. + +## Recommendations + +Processing routines can potentially be resource intensive on the cluster in +terms of memory and processing. Take the following considerations into account +when writing and running SQL routines: + +* Some checks for the runtime behavior of routines are in place. For example, + routines that use take longer to process than a hardcoded threshold are automatically + terminated. +* Avoid creation of arrays in a looping construct. Each iteration creates a + separate new array with all items and copies the data for each modification, + leaving the prior array in memory for automated clean up later. Use a [lambda + expression](/functions/lambda) instead of the loop. +* Avoid concatenating strings in a looping construct. Each iteration creates a + separate new string and copying the old string for each modification, leaving + the prior string in memory for automated clean up later. Use a [lambda + expression](/functions/lambda) instead of the loop. +* Most routines should declare the `RETURNS NULL ON NULL INPUT` characteristics + unless the code has some special handling for null values. You must declare + this explicitly since `CALLED ON NULL INPUT` is the default characteristic. + +## Limitations + +The following limitations apply to SQL routines. + +* Routines must be declared before than can be referenced. +* Recursion cannot be declared or processed. +* Mutual recursion can not be declared or processed. +* Queries cannot be processed in a routine. diff --git a/docs/src/main/sphinx/routines/iterate.md b/docs/src/main/sphinx/routines/iterate.md new file mode 100644 index 000000000000..7be395881459 --- /dev/null +++ b/docs/src/main/sphinx/routines/iterate.md @@ -0,0 +1,41 @@ +# ITERATE + +## Synopsis + +```text +ITERATE label +``` + +## Description + +The `ITERATE` statement allows processing of blocks in [SQL +routines](/routines/introduction) to move processing back to the start of a +context block. Contexts are defined by a [`label`](routine-label). If no label +is found, the functions fails with an error message. + +## Examples + +```sql +FUNCTION count() +RETURNS bigint +BEGIN + DECLARE a int DEFAULT 0; + DECLARE b int DEFAULT 0; + top: REPEAT + SET a = a + 1; + IF a <= 3 THEN + ITERATE top; + END IF; + SET b = b + 1; + RETURN b; +END +``` + +Further examples of varying complexity that cover usage of the `ITERATE` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/leave) diff --git a/docs/src/main/sphinx/routines/leave.md b/docs/src/main/sphinx/routines/leave.md new file mode 100644 index 000000000000..196855509921 --- /dev/null +++ b/docs/src/main/sphinx/routines/leave.md @@ -0,0 +1,46 @@ +# LEAVE + +## Synopsis + +```text +LEAVE label +``` + +## Description + +The `LEAVE` statement allows processing of blocks in [SQL +routines](/routines/introduction) to move out of a specified context. Contexts +are defined by a [`label`](routine-label). If no label is found, the functions +fails with an error message. + +## Examples + +The following function includes a `LOOP` labelled `top`. The conditional `IF` +statement inside the loop can cause the exit from processing the loop when the +value for the parameter `p` is 1 or less. This can be the case if the value is +passed in as 1 or less or after a number of iterations through the loop. + +```sql +FUNCTION my_pow(n int, p int) +RETURNS int +BEGIN + DECLARE r int DEFAULT n; + top: LOOP + IF p <= 1 THEN + LEAVE top; + END IF; + SET r = r * n; + SET p = p - 1; + END LOOP; + RETURN r; +END +``` + +Further examples of varying complexity that cover usage of the `LEAVE` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/iterate) diff --git a/docs/src/main/sphinx/routines/loop.md b/docs/src/main/sphinx/routines/loop.md new file mode 100644 index 000000000000..3bb27745f0ff --- /dev/null +++ b/docs/src/main/sphinx/routines/loop.md @@ -0,0 +1,63 @@ +# LOOP + +## Synopsis + +```text +[label :] LOOP + statements +END LOOP +``` + +## Description + +The `LOOP` statement is an optional construct in [SQL +routines](/routines/introduction) to allow processing of a block of statements +repeatedly. + +The block of statements is processed at least once. After the first, and every +subsequent processing the expression `condidtion` is validated. If the result is +`true`, processing moves to END REPEAT and continues with the next statement in +the function. If the result is `false`, the statements are processed again +repeatedly. + +The optional `label` before the `REPEAT` keyword can be used to [name the +block](routine-label). + +Note that a `WHILE` statement is very similar, with the difference that for +`REPEAT` the statements are processed at least once, and for `WHILE` blocks the +statements might not be processed at all. + + +## Examples + + +The following function counts up to `100` in a loop starting from the input +value `i` and returns the number of incremental steps in the loop to get to +`100`. + +```sql +FUNCTION to_one_hundred(i int) + RETURNS int + BEGIN + DECLARE count int DEFAULT 0; + abc: LOOP + IF i >= 100 THEN + LEAVE abc; + END IF + SET count = count + 1; + SET i = i + 1; + END LOOP; + RETURN; + END +``` + +Further examples of varying complexity that cover usage of the `LOOP` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/repeat) +* [](/routines/while) + diff --git a/docs/src/main/sphinx/routines/repeat.md b/docs/src/main/sphinx/routines/repeat.md new file mode 100644 index 000000000000..5d750554c172 --- /dev/null +++ b/docs/src/main/sphinx/routines/repeat.md @@ -0,0 +1,70 @@ +# REPEAT + +## Synopsis + +```text +[label :] REPEAT + statements +UNTIL condition +END REPEAT +``` + +## Description + +The `REPEAT UNTIL` statement is an optional construct in [SQL +routines](/routines/introduction) to allow processing of a block of statements +as long as a condition is met. The condition is validated as a last step of each +iteration. + +The block of statements is processed at least once. After the first, and every +subsequent processing the expression `condidtion` is validated. If the result is +`true`, processing moves to `END REPEAT` and continues with the next statement in +the function. If the result is `false`, the statements are processed again. + +The optional `label` before the `REPEAT` keyword can be used to [name the +block](routine-label). + +Note that a `WHILE` statement is very similar, with the difference that for +`REPEAT` the statements are processed at least once, and for `WHILE` blocks the +statements might not be processed at all. + +## Examples + +The following routine shows a routine with a `REPEAT` statement that runs until +the value of `a` is greater or equal to `10`. + +```sql +FUNCTION test_repeat(a bigint) + RETURNS bigint + BEGIN + REPEAT + SET a = a + 1; + UNTIL a >= 10 + END REPEAT; + RETURN a; + END +``` + +Since `a` is also the input value and it is increased before the check the +routine always returns `10` for input values of `9` or less, and the input value ++ 1 for all higher values. + +Following are a couple of example invocations with result and explanation: + +```sql +SELECT test_repeat(5); -- 10 +SELECT test_repeat(9); -- 10 +SELECT test_repeat(10); -- 11 +SELECT test_repeat(11); -- 12 +SELECT test_repeat(12); -- 13 +``` + +Further examples of varying complexity that cover usage of the `REPEAT` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/loop) +* [](/routines/while) diff --git a/docs/src/main/sphinx/routines/return.md b/docs/src/main/sphinx/routines/return.md new file mode 100644 index 000000000000..18db948e112e --- /dev/null +++ b/docs/src/main/sphinx/routines/return.md @@ -0,0 +1,32 @@ +# RETURN + +## Synopsis + +```text +RETURN expression +``` + +## Description + +Provide the value from a [SQL routines](/routines/introduction) to the caller. +The value is the result of evaluating the expression. It can be a static value, +a declared variable or a more complex expression. + +## Examples + +The following examples return a static value, the result of an expression, and +the value of the variable x: + +```sql +RETURN 42; +RETURN 6 * 7; +RETURN x; +``` + +Further examples of varying complexity that cover usage of the `RETURN` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/routines/introduction) diff --git a/docs/src/main/sphinx/routines/set.md b/docs/src/main/sphinx/routines/set.md new file mode 100644 index 000000000000..d9c24414e049 --- /dev/null +++ b/docs/src/main/sphinx/routines/set.md @@ -0,0 +1,43 @@ +# SET + +## Synopsis + +```text +SET identifier = expression +``` + +## Description + +Use the `SET` statement in [SQL routines](/routines/introduction) to assign a +value to a variable, referenced by comma-separated `identifier`s. The +value is determined by evaluating the `expression` after the `=` sign. + +Before the assignment the variable must be defined with a `DECLARE` statement. +The data type of the variable must be identical to the data type of evaluating +the `expression`. + +## Examples + +The following functions returns the value `1` after setting the counter variable +multiple times to different values: + +```sql +FUNCTION one() + RETURNS bigint + BEGIN + DECLARE counter tinyint DEFAULT 1; + SET counter = 0; + SET counter = counter + 2; + SET counter = counter / counter; + RETURN counter; + END +``` + +Further examples of varying complexity that cover usage of the `SET` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/declare) diff --git a/docs/src/main/sphinx/routines/while.md b/docs/src/main/sphinx/routines/while.md new file mode 100644 index 000000000000..6e252482d4b8 --- /dev/null +++ b/docs/src/main/sphinx/routines/while.md @@ -0,0 +1,47 @@ +# WHILE + +## Synopsis + +```text +[label :] WHILE condition DO + statements +END WHILE +``` + +## Description + +The `WHILE` statement is an optional construct in [SQL +routines](/routines/introduction) to allow processing of a block of statements +as long as a condition is met. The condition is validated as a first step of +each iteration. + +The expression that defines the `condition` is evaluated at least once. If the +result is `true`, processing moves to `DO`, through following `statements` and +back to `WHILE` and the `condition`. If the result is `false`, processing moves +to `END WHILE` and continues with the next statement in the function. + +The optional `label` before the `WHILE` keyword can be used to [name the +block](routine-label). + +Note that a `WHILE` statement is very similar, with the difference that for +`REPEAT` the statements are processed at least once, and for `WHILE` blocks the +statements might not be processed at all. + +## Examples + +```sql +WHILE p > 1 DO + SET r = r * n; + SET p = p - 1; +END WHILE; +``` + +Further examples of varying complexity that cover usage of the `WHILE` statement +in combination with other statements are available in the [SQL routines examples +documentation](/routines/examples). + +## See also + +* [](/routines/introduction) +* [](/routines/loop) +* [](/routines/repeat) diff --git a/docs/src/main/sphinx/sql.md b/docs/src/main/sphinx/sql.md index 59a65fa9d41c..da54bc0e1bea 100644 --- a/docs/src/main/sphinx/sql.md +++ b/docs/src/main/sphinx/sql.md @@ -19,6 +19,7 @@ sql/analyze sql/call sql/comment sql/commit +sql/create-function sql/create-materialized-view sql/create-role sql/create-schema @@ -31,6 +32,7 @@ sql/deny sql/describe sql/describe-input sql/describe-output +sql/drop-function sql/drop-materialized-view sql/drop-role sql/drop-schema diff --git a/docs/src/main/sphinx/sql/create-function.md b/docs/src/main/sphinx/sql/create-function.md new file mode 100644 index 000000000000..403cc19f9209 --- /dev/null +++ b/docs/src/main/sphinx/sql/create-function.md @@ -0,0 +1,52 @@ +# CREATE FUNCTION + +## Synopsis + +```text +CREATE [OR REPLACE] FUNCTION + routine_definition +``` + +## Description + +Create or replace a [](routine-catalog). The `routine_definition` is composed of +the usage of [](/routines/function) and nested statements. The name of the +routine must be fully qualified with catalog and schema location, unless the +[default SQL routine storage catalog and +schema](/admin/properties-sql-environment) are configured. The connector used in +the catalog must support routine storage. + +The optional `OR REPLACE` clause causes the routine to be replaced if it already +exists rather than raising an error. + +## Examples + +The following example creates the `meaning_of_life` routine in the `default` +schema of the `example` catalog: + +```sql +CREATE FUNCTION example.default.meaning_of_life() + RETURNS bigint + BEGIN + RETURN 42; + END; +``` + +If the [default catalog and schema for routine +storage](/admin/properties-sql-environment) is configured, you can use the +following more compact syntax: + +```sql +CREATE FUNCTION meaning_of_life() RETURNS bigint RETURN 42; +``` + +Further examples of varying complexity that cover usage of the `FUNCTION` +statement in combination with other statements are available in the [SQL +routines examples documentation](/routines/examples). + +## See also + +* [](/sql/drop-function) +* [](/sql/show-functions) +* [](/routines/introduction) +* [](/admin/properties-sql-environment) diff --git a/docs/src/main/sphinx/sql/drop-function.md b/docs/src/main/sphinx/sql/drop-function.md new file mode 100644 index 000000000000..0e8792231e18 --- /dev/null +++ b/docs/src/main/sphinx/sql/drop-function.md @@ -0,0 +1,51 @@ +# DROP FUNCTION + +## Synopsis + +```text +DROP FUNCTION [ IF EXISTS ] routine_name ( [ [ parameter_name ] data_type [, ...] ] ) +``` + +## Description + +Removes a [](routine-catalog). The value of `routine_name` +must be fully qualified with catalog and schema location of the routine, unless +the [default SQL routine storage catalog and +schema](/admin/properties-sql-environment) are configured. + +The `data_type`s must be included for routines that use parameters to ensure the +routine with the correct name and parameter signature is removed. + +The optional `IF EXISTS` clause causes the error to be suppressed if +the function does not exist. + +## Examples + +The following example removes the `meaning_of_life` routine in the `default` +schema of the `example` catalog: + +```sql +DROP FUNCTION example.default.meaning_of_life(); +``` + +If the routine uses a input parameter, the type must be added: + +```sql +DROP FUNCTION multiply_by_two(bigint); +``` + +If the [default catalog and schema for routine +storage](/admin/properties-sql-environment) is configured, you can use the +following more compact syntax: + +```sql +DROP FUNCTION meaning_of_life(); +``` + +## See also + +* [](/sql/create-function) +* [](/sql/show-functions) +* [](/routines/introduction) +* [](/admin/properties-sql-environment) + diff --git a/docs/src/main/sphinx/sql/select.md b/docs/src/main/sphinx/sql/select.md index c3d9fc092816..c552dee7ea1a 100644 --- a/docs/src/main/sphinx/sql/select.md +++ b/docs/src/main/sphinx/sql/select.md @@ -3,6 +3,7 @@ ## Synopsis ```text +[ WITH FUNCTION sql_routines ] [ WITH [ RECURSIVE ] with_query [, ...] ] SELECT [ ALL | DISTINCT ] select_expression [, ...] [ FROM from_item [, ...] ] @@ -67,6 +68,28 @@ ROLLUP ( column [, ...] ) Retrieve rows from zero or more tables. +## WITH FUNCTION clause + +The `WITH FUNCTION` clause allows you to define a list of inline SQL routines +that are available for use in the rest of the query. + +The following example declares and uses two inline routines: + +```sql +WITH + FUNCTION hello(name varchar) + RETURNS varchar + RETURN format('Hello %s!', 'name'), + FUNCTION bye(name varchar) + RETURNS varchar + RETURN format('Bye %s!', 'name') +SELECT hello('Finn') || ' and ' || bye('Joe'); +-- Hello Finn! and Bye Joe! +``` + +Find further information about routines in general, inline routines, all +supported statements, and examples in [](/routines). + ## WITH clause The `WITH` clause defines named relations for use within a query. diff --git a/docs/src/main/sphinx/sql/set-path.md b/docs/src/main/sphinx/sql/set-path.md index cbb9d33eae8b..5735e5bb9375 100644 --- a/docs/src/main/sphinx/sql/set-path.md +++ b/docs/src/main/sphinx/sql/set-path.md @@ -46,4 +46,6 @@ FROM ## See also -{doc}`use` +* [](/sql/use) +* [](/admin/properties-sql-environment) + diff --git a/docs/src/main/sphinx/sql/show-functions.md b/docs/src/main/sphinx/sql/show-functions.md index de286d797a98..013dfd0d98a3 100644 --- a/docs/src/main/sphinx/sql/show-functions.md +++ b/docs/src/main/sphinx/sql/show-functions.md @@ -8,7 +8,10 @@ SHOW FUNCTIONS [ FROM schema ] [ LIKE pattern ] ## Description -List functions in `schema` or all functions in the current session path. +List functions in `schema` or all functions in the current session path. This +can include built-in functions, [functions from a custom +plugin](/develop/functions), and [SQL routines](/routines). + For each function returned, the following information is displayed: - Function name @@ -18,24 +21,48 @@ For each function returned, the following information is displayed: - Deterministic - Description +Use the optional `FROM` keyword to only list functions in a specific catalog and +schema. The location in `schema` must be specified as +`cataglog_name.schema_name`. + {ref}`Specify a pattern ` in the optional `LIKE` clause to -filter the results to the desired subset. For example, the following query -allows you to find functions beginning with `array`: +filter the results to the desired subset. + +## Examples +List all SQL routines and plugin functions in the `default` schema of the +`example` catalog: + +```sql +SHOW FUNCTIONS FROM example.default; ``` + +List all functions with a name beginning with `array`: + +```sql SHOW FUNCTIONS LIKE 'array%'; ``` -`SHOW FUNCTIONS` works with built-in functions as well as with {doc}`custom -functions `. In the following example, three custom -functions beginning with `cf` are available: +List all functions with a name beginning with `cf`: -```text +```sql SHOW FUNCTIONS LIKE 'cf%'; +``` + +Example output: +```text Function | Return Type | Argument Types | Function Type | Deterministic | Description ------------------+-------------+----------------+---------------+---------------+----------------------------------------- cf_getgroups | varchar | | scalar | true | Returns the current session's groups cf_getprincipal | varchar | | scalar | true | Returns the current session's principal cf_getuser | varchar | | scalar | true | Returns the current session's user ``` + +## See also + +* [](/functions) +* [](/routines) +* [](/develop/functions) +* [](/sql/create-function) +* [](/sql/drop-function)