- 7.7 Writing the Body Code
- 7.7.1 Assignment Statements
- 7.7.2 Management Statements in PSQL
- 7.7.3 DECLARE VARIABLE
- 7.7.4 DECLARE .. CURSOR
- 7.7.5 DECLARE FUNCTION
- 7.7.6 DECLARE PROCEDURE
- 7.7.7 BEGIN … END
- 7.7.8 IF … THEN … ELSE
- 7.7.9 WHILE … DO
- 7.7.10 BREAK
- 7.7.11 LEAVE
- 7.7.12 CONTINUE
- 7.7.13 EXIT
- 7.7.14 SUSPEND
- 7.7.15 EXECUTE STATEMENT
7.7 Writing the Body Code
This section takes a closer look at the procedural SQL language constructs and statements that are available for coding the body of a stored procedure, trigger or anonymous PSQL block.
Colon Marker (:)
The colon marker prefix (:) is used in PSQL to mark a reference to a variable in a DML statement. The colon marker is not required before variable names in other PSQL code.
Since Firebird 3.0, the colon prefix can also be used for the NEW and OLD contexts, and for cursor variables.
7.7.1 Assignment Statements
Used forAssigning a value to a variable
Available inPSQL
Syntax
1. varname = <value_expr>;
Table 7.7.1.1 Assignment Statement Parameters
| Argument | Description |
|---|---|
| varname | Name of a parameter or local variable |
| value_expr | An expression, constant or variable whose value resolves to the same data type as varname |
PSQL uses the equal symbol (=) as its assignment operator. The assignment statement assigns an SQL expression value on the right to the variable on the left of the operator. The expression can be any valid SQL expression: it may contain literals, internal variable names, arithmetic, logical and string operations, calls to internal functions, stored functions or external functions (UDFs).
7.7.1.1 Example using assignment statements
1. CREATE PROCEDURE MYPROC (
2. a INTEGER,
3. b INTEGER,
4. name VARCHAR (30)
5. )
6. RETURNS (
7. c INTEGER,
8. str VARCHAR(100))
9. AS
10. BEGIN
11. -- assigning a constant
12. c = 0;
13. str = '';
14. SUSPEND;
15. -- assigning expression values
16. c = a + b;
17. str = name || CAST(b AS VARCHAR(10));
18. SUSPEND;
19. -- assigning expression value
20. -- built by a query
21. c = (SELECT 1 FROM rdb$database);
22. -- assigning a value from a context variable
23. str = CURRENT_USER;
24. SUSPEND;
25. END
See alsoSection 7.7.3, DECLARE VARIABLE
7.7.2 Management Statements in PSQL
Management statement are allowed in PSQL blocks (triggers, procedures, functions and EXECUTE BLOCK), which is especially helpful for applications that need some management statements to be executed at the start of a session, specifically in ON CONNECT triggers.
The management statements permitted in PSQL are:
- ALTER SESSION RESET
- SET BIND
- SET DECFLOAT
- SET ROLE
- SET SESSION IDLE TIMEOUT
- SET STATEMENT TIMEOUT
- SET TIME ZONE
- SET TRUSTED ROLE
7.7.2.1 Example of Management Statements in PSQL
1. create or alter trigger on_connect on connect
2. as
3. begin
4. set bind of decfloat to double precision;
5. set time zone 'America/Sao_Paulo';
6. end
Caution
Although useful as a workaround, using ON CONNECT triggers to configure bind and time zone is usually not the right approach.
See alsoManagement Statements
7.7.3 DECLARE VARIABLE
Used forDeclaring a local variable
Available inPSQL
Syntax
1. DECLARE [VARIABLE] varname
2. <domain_or_non_array_type> [NOT NULL] [COLLATE collation]
3. [{DEFAULT | = } <initvalue>];
5. <domain_or_non_array_type> ::=
6. !! See Scalar Data Types Syntax !!
8. <initvalue> ::= <literal> | <context_var>
Table 7.7.3.1 DECLARE VARIABLE Statement Parameters
| Argument | Description |
|---|---|
| varname | Name of the local variable |
| collation | Collation sequence |
| initvalue | Initial value for this variable |
| literal | Literal of a type compatible with the type of the local variable |
| context_var | Any context variable whose type is compatible with the type of the local variable |
The statement DECLARE [VARIABLE] is used for declaring a local variable. The keyword VARIABLE can be omitted. One DECLARE [VARIABLE] statement is required for each local variable. Any number of DECLARE [VARIABLE] statements can be included and in any order. The name of a local variable must be unique among the names of local variables and input and output parameters declared for the module.
Note
A special case of DECLARE [VARIABLE] — declaring cursors — is covered separately in Section 7.7.4, DECLARE .. CURSOR
7.7.3.1 Data Type for Variables
A local variable can be of any SQL type other than an array.
- A domain name can be specified as the type; the variable will inherit all of its attributes.
- If the
TYPE OF *domain*clause is used instead, the variable will inherit only the domain’s data type, and, if applicable, its character set and collation attributes. Any default value or constraints such asNOT NULLorCHECKconstraints are not inherited. - If the
TYPE OF COLUMN *relation*.*column*>option is used to borrow from a column in a table or view, the variable will inherit only the column’s data type, and, if applicable, its character set and collation attributes. Any other attributes are ignored.
7.7.3.2 NOT NULL Constraint
For local variables, you can specify the NOT NULL constraint, disallowing NULL values for the variable. If a domain has been specified as the data type and the domain already has the NOT NULL constraint, the declaration is unnecessary. For other forms, including use of a domain that is nullable, the NOT NULL constraint can be included if needed.
7.7.3.3 CHARACTER SET and COLLATE clauses
Unless specified, the character set and collation sequence of a string variable will be the database defaults. A CHARACTER SET clause can be included, if required, to handle string data that is going to be in a different character set. A valid collation sequence (COLLATE clause) can also be included, with or without the character set clause.
7.7.3.4 Initializing a Variable
Local variables are NULL when execution of the module begins. They can be initialized so that a starting or default value is available when they are first referenced. The DEFAULT <initvalue> form can be used, or just the assignment operator, =: = <initvalue>. The value can be any type-compatible literal or context variable, including NULL.
Tip
Be sure to use this clause for any variables that have a NOT NULL constraint and do not otherwise have a default value available.
7.7.3.5 Examples of various ways to declare local variables
1. CREATE OR ALTER PROCEDURE SOME_PROC
2. AS
3. -- Declaring a variable of the INT type
4. DECLARE I INT;
5. -- Declaring a variable of the INT type that does not allow NULL
6. DECLARE VARIABLE J INT NOT NULL;
7. -- Declaring a variable of the INT type with the default value of 0
8. DECLARE VARIABLE K INT DEFAULT 0;
9. -- Declaring a variable of the INT type with the default value of 1
10. DECLARE VARIABLE L INT = 1;
11. -- Declaring a variable based on the COUNTRYNAME domain
12. DECLARE FARM_COUNTRY COUNTRYNAME;
13. -- Declaring a variable of the type equal to the COUNTRYNAME domain
14. DECLARE FROM_COUNTRY TYPE OF COUNTRYNAME;
15. -- Declaring a variable with the type of the CAPITAL column in the COUNTRY table
16. DECLARE CAPITAL TYPE OF COLUMN COUNTRY.CAPITAL;
17. BEGIN
18. /* PSQL statements */
19. END
See alsoData Types and Subtypes, Custom Data Types — Domains, CREATE DOMAIN
7.7.4 DECLARE .. CURSOR
Used forDeclaring a named cursor
Available inPSQL
Syntax
1. DECLARE [VARIABLE] cursor_name
2. [[NO] SCROLL] CURSOR
3. FOR (<select>);
Table 7.7.4.1 DECLARE … CURSOR Statement Parameters
| Argument | Description |
|---|---|
| cursor_name | Cursor name |
| select | SELECT statement |
The DECLARE … CURSOR … FOR statement binds a named cursor to the result set obtained in the SELECT statement specified in the FOR clause. In the body code, the cursor can be opened, used to iterate row-by-row through the result set, and closed. While the cursor is open, the code can perform positioned updates and deletes using the WHERE CURRENT OF in the UPDATE or DELETE statement.
Note
Syntactically, the DECLARE … CURSOR statement is a special case of Section 7.7.3, DECLARE VARIABLE.
7.7.4.1 Forward-Only and Scrollable Cursors
The cursor can be forward-only (unidirectional) or scrollable. The optional clause SCROLL makes the cursor scrollable, the NO SCROLL clause, forward-only. By default, cursors are forward-only.
Forward-only cursors can — as the name implies — only move forward in the dataset. Forward-only cursors only support the FETCH [NEXT FROM] statement, other commands raise an error. Scrollable cursors allow you to move not only forward in the dataset, but also back, asl well as N positions relative to the current position.
Warning
Scrollable cursors are materialized as a temporary dataset, as such, they consume additional memory or disk space, so use them only when you really need them.
7.7.4.2 Cursor Idiosyncrasies
- The optional
FOR UPDATEclause can be included in theSELECTstatement, but its absence does not prevent successful execution of a positioned update or delete - Care should be taken to ensure that the names of declared cursors do not conflict with any names used subsequently in statements for
AS CURSORclauses - If the cursor is needed only to walk the result set, it is nearly always easier and less error-prone to use a
FOR SELECTstatement with theAS CURSORclause. Declared cursors must be explicitly opened, used to fetch data, and closed. The context variableROW_COUNThas to be checked after each fetch and, if its value is zero, the loop has to be terminated. AFOR SELECTstatement does this automatically.
Nevertheless, declared cursors provide a high level of control over sequential events and allow several cursors to be managed in parallel.
- The SELECT statement may contain parameters. For instance:
1. SELECT NAME || :SFX FROM NAMES WHERE NUMBER = :NUM
Each parameter has to have been declared beforehand as a PSQL variable, even if they originate as input and output parameters. When the cursor is opened, the parameter is assigned the current value of the variable.
Unstable Variables and Cursors
If the value of the PSQL variable used in the SELECT statement of the cursor changes during the execution of the loop, then its new value may — but not always — be used when selecting the next rows. It is better to avoid such situations. If you really need this behaviour, then you should thoroughly test your code and make sure you understand how changes to the variable affect the query results.
Note particularly that the behaviour may depend on the query plan, specifically on the indexes being used. Currently, there are no strict rules for this behaviour, and this may change in future versions of Firebird.
7.7.4.3 Examples Using Named Cursors
- Declaring a named cursor in the trigger.
1. CREATE OR ALTER TRIGGER TBU_STOCK
2. BEFORE UPDATE ON STOCK
3. AS
4. DECLARE C_COUNTRY CURSOR FOR (
5. SELECT
6. COUNTRY,
7. CAPITAL
8. FROM COUNTRY
9. );
10. BEGIN
11. /* PSQL statements */
12. END
2. Declaring a scrollable cursor
1. EXECUTE BLOCK
2. RETURNS (
3. N INT,
4. RNAME CHAR(63))
5. AS
6. - Declaring a scrollable cursor
7. DECLARE C SCROLL CURSOR FOR (
8. SELECT
9. ROW_NUMBER() OVER (ORDER BY RDB$RELATION_NAME) AS N,
10. RDB$RELATION_NAME
11. FROM RDB$RELATIONS
12. ORDER BY RDB$RELATION_NAME);
13. BEGIN
14. / * PSQL statements * /
15. END
3. A collection of scripts for creating views with a PSQL block using named cursors.
``` 1. EXECUTE BLOCK 2. RETURNS ( 3. SCRIPT BLOB SUB_TYPE TEXT) 4. AS 5. DECLARE VARIABLE FIELDS VARCHAR(8191); 6. DECLARE VARIABLE FIELD_NAME TYPE OF RDB$FIELD_NAME; 7. DECLARE VARIABLE RELATION RDB$RELATION_NAME; 8. DECLARE VARIABLE SOURCE TYPE OF COLUMN RDB$RELATIONS.RDB$VIEW_SOURCE; 9. DECLARE VARIABLE CUR_R CURSOR FOR ( 10. SELECT 11. RDB$RELATION_NAME, 12. RDB$VIEW_SOURCE 13. FROM 14. RDB$RELATIONS 15. WHERE 16. RDB$VIEW_SOURCE IS NOT NULL); 17. -- Declaring a named cursor where 18. -- a local variable is used 19. DECLARE CUR_F CURSOR FOR ( 20. SELECT 21. RDB$FIELD_NAME 22. FROM 23. RDB$RELATION_FIELDS 24. WHERE 25. -- It is important that the variable must be declared earlier 26. RDB$RELATION_NAME = :RELATION); 27. BEGIN 28. OPEN CUR_R; 29. WHILE (1 = 1) DO 30. BEGIN 31. FETCH CUR_R 32. INTO :RELATION, :SOURCE; 33. IF (ROW_COUNT = 0) THEN 34. LEAVE;
- FIELDS = NULL;
- -- The CUR_F cursor will use the value
- -- of the RELATION variable initiated above
- OPEN CUR_F;
- WHILE (1 = 1) DO
- BEGIN
- FETCH CUR_F
- INTO :FIELD_NAME;
- IF (ROW_COUNT = 0) THEN
- LEAVE;
- IF (FIELDS IS NULL) THEN
- FIELDS = TRIM(FIELD_NAME);
- ELSE
- FIELDS = FIELDS || ', ' || TRIM(FIELD_NAME);
- END
-
CLOSE CUR_F;
-
SCRIPT = 'CREATE VIEW ' || RELATION;
-
IF (FIELDS IS NOT NULL) THEN
-
SCRIPT = SCRIPT || ' (' || FIELDS || ')';
-
SCRIPT = SCRIPT || ' AS ' || ASCII_CHAR(13);
-
SCRIPT = SCRIPT || SOURCE;
-
SUSPEND;
- END
- CLOSE CUR_R;
- END ```
See alsoSection 7.7.18, OPEN, Section 7.7.19, FETCH, Section 7.7.20, CLOSE
7.7.5 DECLARE FUNCTION
Used forDeclaring a sub-function
Available inPSQL
Syntax
1. <declare-subfunc> ::= <subfunc-forward> | <subfunc-def>
3. <subfunc-forward> ::= <subfunc-header>;
5. <subfunc-def> ::= <subfunc-header> <psql-module-body>
7. <subfunc-header> ::=
8. DECLARE FUNCTION subfuncname [ ( [ <in_params> ] ) ]
9. RETURNS <domain_or_non_array_type> [COLLATE collation]
10. [DETERMINISTIC]
12. <in_params> ::=
13. !! See CREATE FUNCTION Syntax !!
15. <domain_or_non_array_type> ::=
16. !! See Scalar Data Types Syntax !!
18. <psql-module-body> ::=
19. !! See Syntax of Module Body !!
Table 7.7.5.1 DECLARE FUNCTION Statement Parameters
| Argument | Description |
|---|---|
| subfuncname | Sub-function name |
| collation | Collation name |
The DECLARE FUNCTION statement declares a sub-function. A sub-function is only visible to the PSQL module that defined the sub-function.
Sub-functions have a number of restrictions:
- A sub-function cannot be nested in another subroutine. Subroutines are only supported in top-level PSQL modules (stored procedures, stored functions, triggers and anonymous PSQL blocks). This restriction is not enforced by the syntax, but attempts to create nested sub-functions will raise an error feature is not supported with detail message nested sub function.
- Currently, the sub-function has no direct access to use variables and cursors from its parent module. However, it can access other routines from its parent modules, including recursive calls to itself. In some cases a forward declaration of the routine may be necessary.
A sub-function can be forward declared to resolve mutual dependencies between subroutine, and must be followed by its actual definition. When a sub-function is forward declared and has parameters with default values, the default values should only be specified in the forward declaration, and should not be repeated in subfunc_def.
Note
Declaring a sub-function with the same name as a stored function will hide that stored function from your module. It will not be possible to call that stored function.
Note
Contrary to DECLARE [VARIABLE], a DECLARE FUNCTION is not terminated by a semicolon. The END of its main BEGIN … END block is considered its terminator.
7.7.5.1 Examples of Sub-Functions
- Subfunction within a stored function
1. CREATE OR ALTER FUNCTION FUNC1 (n1 INTEGER, n2 INTEGER)
2. RETURNS INTEGER
3. AS
4. - Subfunction
5. DECLARE FUNCTION SUBFUNC (n1 INTEGER, n2 INTEGER)
6. RETURNS INTEGER
7. AS
8. BEGIN
9. RETURN n1 + n2;
10. END
11. BEGIN
12. RETURN SUBFUNC (n1, n2);
13. END
2. Recursive function call
``` 1. execute block returns (i integer, o integer) 2. as 3. -- Recursive function without forward declaration. 4. declare function fibonacci(n integer) returns integer 5. as 6. begin 7. if (n = 0 or n = 1) then 8. return n; 9. else 10. return fibonacci(n - 1) + fibonacci(n - 2); 11. end 12. begin 13. i = 0;
- while (i < 10)
- do
- begin
- o = fibonacci(i);
- suspend;
- i = i + 1;
- end
- end ```
See alsoSection 7.7.6, DECLARE PROCEDURE, CREATE FUNCTION
7.7.6 DECLARE PROCEDURE
Used forDeclaring a sub-procedure
Available inPSQL
Syntax
1. <declare-subproc> ::= <subproc-forward> | <subproc-def>
3. <subproc-forward> ::= <subproc-header>;
5. <subproc-def> ::= <subproc-header> <psql-module-body>
7. <subproc-header> ::=
8. DECLARE subprocname [ ( [ <in_params> ] ) ]
9. [RETURNS (<out_params>)]
11. <in_params> ::=
12. !! See CREATE PROCEDURE Syntax !!
14. <domain_or_non_array_type> ::=
15. !! See Scalar Data Types Syntax !!
17. <psql-module-body> ::=
18. !! See Syntax of Module Body !!
Table 7.7.6.1 DECLARE PROCEDURE Statement Parameters
| Argument | Description |
|---|---|
| subprocname | Sub-procedure name |
| collation | Collation name |
The DECLARE PROCEDURE statement declares a sub-procedure. A sub-procedure is only visible to the PSQL module that defined the sub-procedure.
Sub-procedures have a number of restrictions:
- A sub-procedure cannot be nested in another subroutine. Subroutines are only supported in top-level PSQL modules (stored procedures, stored functions, triggers and anonymous PSQL blocks). This restriction is not enforced by the syntax, but attempts to create nested sub-procedures will raise an error feature is not supported with detail message nested sub procedure.
- Currently, the sub-procedure has no direct access to use variables and cursors from its parent module. It can access other routines from its parent modules. In some cases a forward declaration may be necessary.
A sub-procedure can be forward declared to resolve mutual dependencies between subroutines, and must be followed by its actual definition. When a sub-procedure is forward declared and has parameters with default values, the default values should only be specified in the forward declaration, and should not be repeated in subproc_def.
Note
Declaring a sub-procedure with the same name as a stored procedure, table or view will hide that stored procedure, table or view from your module. It will not be possible to call that stored procedure, table or view.
Note
Contrary to DECLARE [VARIABLE], a DECLARE PROCEDURE is not terminated by a semicolon. The END of its main BEGIN … END block is considered its terminator.
7.7.6.1 Examples of Sub-Procedures
- Subroutines in
EXECUTE BLOCK
1. EXECUTE BLOCK
2. RETURNS (name VARCHAR(63))
3. AS
4. -- Sub-procedure returning a list of tables
5. DECLARE PROCEDURE get_tables
6. RETURNS (table_name VARCHAR(63))
7. AS
8. BEGIN
9. FOR SELECT RDB$RELATION_NAME
10. FROM RDB$RELATIONS
11. WHERE RDB$VIEW_BLR IS NULL
12. INTO table_name
13. DO SUSPEND;
14. END
15. -- Sub-procedure returning a list of views
16. DECLARE PROCEDURE get_views
17. RETURNS (view_name VARCHAR(63))
18. AS
19. BEGIN
20. FOR SELECT RDB$RELATION_NAME
21. FROM RDB$RELATIONS
22. WHERE RDB$VIEW_BLR IS NOT NULL
23. INTO view_name
24. DO SUSPEND;
25. END
26. BEGIN
27. FOR SELECT table_name
28. FROM get_tables
29. UNION ALL
30. SELECT view_name
31. FROM get_views
32. INTO name
33. DO SUSPEND;
34. END
2. With forward declaration and parameter with default value
``` 1. execute block returns (o integer) 2. as 3. -- Forward declaration of P1. 4. declare procedure p1(i integer = 1) returns (o integer);
- -- Forward declaration of P2.
-
declare procedure p2(i integer) returns (o integer);
-
-- Implementation of P1 should not re-declare parameter default value.
- declare procedure p1(i integer) returns (o integer)
- as
- begin
- execute procedure p2(i) returning_values o;
-
end
-
declare procedure p2(i integer) returns (o integer)
- as
- begin
- o = i;
- end
- begin
- execute procedure p1 returning_values o;
- suspend;
- end ```
See alsoSection 7.7.5, DECLARE FUNCTION, CREATE PROCEDURE
7.7.7 BEGIN … END
Used forDelimiting a block of statements
Available inPSQL
Syntax
1. <block> ::=
2. BEGIN
3. [<compound_statement> ...]
4. END
6. <compound_statement> ::= {<block> | <statement>}
The BEGIN … END construct is a two-part statement that wraps a block of statements that are executed as one unit of code. Each block starts with the half-statement BEGIN and ends with the other half-statement END. Blocks can be nested a maximum depth of 512 nested blocks. A block can be empty, allowing them to act as stubs, without the need to write dummy statements.
The BEGIN and END statements have no line terminators (semicolon). However, when defining or altering a PSQL module in the isql utility, that application requires that the last END statement be followed by its own terminator character, that was previously switched — using SET TERM — to some string other than a semicolon. That terminator is not part of the PSQL syntax.
The final, or outermost, END statement in a trigger terminates the trigger. What the final END statement does in a stored procedure depends on the type of procedure:
- In a selectable procedure, the final
ENDstatement returns control to the caller, returning SQLCODE 100, indicating that there are no more rows to retrieve - In an executable procedure, the final
ENDstatement returns control to the caller, along with the current values of any output parameters defined.
7.7.7.1 BEGIN … END Examples
A sample procedure from the employee.fdb database, showing simple usage of BEGIN…END blocks:
1. SET TERM ^;
2. CREATE OR ALTER PROCEDURE DEPT_BUDGET (
3. DNO CHAR(3))
4. RETURNS (
5. TOT DECIMAL(12,2))
6. AS
7. DECLARE VARIABLE SUMB DECIMAL(12,2);
8. DECLARE VARIABLE RDNO CHAR(3);
9. DECLARE VARIABLE CNT INTEGER;
10. BEGIN
11. TOT = 0;
13. SELECT BUDGET
14. FROM DEPARTMENT
15. WHERE DEPT_NO = :DNO
16. INTO :TOT;
18. SELECT COUNT(BUDGET)
19. FROM DEPARTMENT
20. WHERE HEAD_DEPT = :DNO
21. INTO :CNT;
23. IF (CNT = 0) THEN
24. SUSPEND;
26. FOR SELECT DEPT_NO
27. FROM DEPARTMENT
28. WHERE HEAD_DEPT = :DNO
29. INTO :RDNO
30. DO
31. BEGIN
32. EXECUTE PROCEDURE DEPT_BUDGET(:RDNO)
33. RETURNING_VALUES :SUMB;
34. TOT = TOT + SUMB;
35. END
37. SUSPEND;
38. END^
39. SET TERM ;^
See alsoSection 7.7.13, EXIT, SET TERM
7.7.8 IF … THEN … ELSE
Used forConditional branching
Available inPSQL
Syntax
1. IF (<condition>)
2. THEN <compound_statement>
3. [ELSE <compound_statement>]
Table 7.7.8.1 IF … THEN … ELSE Parameters
| Argument | Description |
|---|---|
| condition | A logical condition returning TRUE, FALSE or UNKNOWN |
| compound_statement | A single statement, or two or more statements wrapped in BEGIN … END |
The conditional branch statement IF … THEN is used to branch the execution process in a PSQL module. The condition is always enclosed in parentheses. If the condition returns the value TRUE, execution branches to the statement or the block of statements after the keyword THEN. If an ELSE is present, and the condition returns FALSE or UNKNOWN, execution branches to the statement or the block of statements after it.
Multi-Branch Decisions
PSQL does not provide more advanced multi-branch jumps, such as CASE or SWITCH. However, it is possible to chain IF … THEN … ELSE statements, see the example section below. Alternatively, the CASE statement from DSQL is available in PSQL and is able to satisfy at least some use cases in the manner of a switch:
1. CASE <test_expr>
2. WHEN <expr> THEN <result>
3. [WHEN <expr> THEN <result> ...]
4. [ELSE <defaultresult>]
5. END
7. CASE
8. WHEN <bool_expr> THEN <result>
9. [WHEN <bool_expr> THEN <result> ...]
10. [ELSE <defaultresult>]
11. END
Example in PSQL
1. ...
2. C = CASE
3. WHEN A=2 THEN 1
4. WHEN A=1 THEN 3
5. ELSE 0
6. END;
7. ...
7.7.8.1 IF Examples
- An example using the
IFstatement. Assume that theFIRST,LINE2andLASTvariables were declared earlier.
1. ...
2. IF (FIRST IS NOT NULL) THEN
3. LINE2 = FIRST || ' ' || LAST;
4. ELSE
5. LINE2 = LAST;
6. ...
2. Given IF … THEN … ELSE is a statement, it is possible to chain them together. Assume that the INT_VALUE and STRING_VALUE variables were declared earlier.
1. IF (INT_VALUE = 1) THEN
2. STRING_VALUE = 'one';
3. ELSE IF (INT_VALUE = 2) THEN
4. STRING_VALUE = 'two';
5. ELSE IF (INT_VALUE = 3) THEN
6. STRING_VALUE = 'three';
7. ELSE
8. STRING_VALUE = 'too much';
This specific example can be replaced with a simple CASE or the DECODE function.
See alsoSection 7.7.9, WHILE … DO, CASE
7.7.9 WHILE … DO
Used forLooping constructs
Available inPSQL
Syntax
1. [label:]
2. WHILE <condition> DO
3. <compound_statement>
Table 7.7.9.1 WHILE … DO Parameters
| Argument | Description |
|---|---|
| label | Optional label for LEAVE and CONTINUE. Follows the rules for identifiers. |
| condition | A logical condition returning TRUE, FALSE or UNKNOWN |
| compound_statement | A single statement, or two or more statements wrapped in BEGIN … END |
A WHILE statement implements the looping construct in PSQL. The statement or the block of statements will be executed until the condition returns TRUE. Loops can be nested to any depth.
7.7.9.1 WHILE … DO Examples
A procedure calculating the sum of numbers from 1 to I shows how the looping construct is used.
1. CREATE PROCEDURE SUM_INT (I INTEGER)
2. RETURNS (S INTEGER)
3. AS
4. BEGIN
5. s = 0;
6. WHILE (i > 0) DO
7. BEGIN
8. s = s + i;
9. i = i - 1;
10. END
11. END
Executing the procedure in isql:
1. EXECUTE PROCEDURE SUM_INT(4);
the result is:
1. S
2. ==========
3. 10
See alsoSection 7.7.8, IF … THEN … ELSE, Section 7.7.10, BREAK, Section 7.7.11, LEAVE, Section 7.7.12, CONTINUE, Section 7.7.13, EXIT, Section 7.7.16, FOR SELECT, Section 7.7.17, FOR EXECUTE STATEMENT
7.7.10 BREAK
Used forExiting a loop
Available inPSQL
Syntax
1. [label:]
2. <loop_stmt>
3. BEGIN
4. ...
5. BREAK;
6. ...
7. END
9. <loop_stmt> ::=
10. FOR <select_stmt> INTO <var_list> DO
11. | FOR EXECUTE STATEMENT ... INTO <var_list> DO
12. | WHILE (<condition>)} DO
Table 7.7.10.1 BREAK Statement Parameters
| Argument | Description |
|---|---|
| label | Label |
| select_stmt | SELECT statement |
| condition | A logical condition returning TRUE, FALSE or UNKNOWN |
The BREAK statement immediately terminates the inner loop of a WHILE or FOR looping statement. Code continues to be executed from the first statement after the terminated loop block.
BREAK is similar to LEAVE, except it doesn’t support a label.
See alsoSection 7.7.11, LEAVE
7.7.11 LEAVE
Used forExiting a loop
Available inPSQL
Syntax
1. [label:]
2. <loop_stmt>
3. BEGIN
4. ...
5. LEAVE [label];
6. ...
7. END
9. <loop_stmt> ::=
10. FOR <select_stmt> INTO <var_list> DO
11. | FOR EXECUTE STATEMENT ... INTO <var_list> DO
12. | WHILE (<condition>)} DO
Table 7.7.11.1 LEAVE Statement Parameters
| Argument | Description |
|---|---|
| label | Label |
| select_stmt | SELECT statement |
| condition | A logical condition returning TRUE, FALSE or UNKNOWN |
The LEAVE statement immediately terminates the inner loop of a WHILE or FOR looping statement. Using the optional label parameter, LEAVE can also exit an outer loop, that is, the loop labelled with label. Code continues to be executed from the first statement after the terminated loop block.
7.7.11.1 LEAVE Examples
- Leaving a loop if an error occurs on an insert into the
NUMBERStable. The code continues to be executed from the lineC = 0.
1. ...
2. WHILE (B < 10) DO
3. BEGIN
4. INSERT INTO NUMBERS(B)
5. VALUES (:B);
6. B = B + 1;
7. WHEN ANY DO
8. BEGIN
9. EXECUTE PROCEDURE LOG_ERROR (
10. CURRENT_TIMESTAMP,
11. 'ERROR IN B LOOP');
12. LEAVE;
13. END
14. END
15. C = 0;
16. ...
2. An example using labels in the LEAVE statement. LEAVE LOOPA terminates the outer loop and LEAVE LOOPB terminates the inner loop. Note that the plain LEAVE statement would be enough to terminate the inner loop.
1. ...
2. STMT1 = 'SELECT NAME FROM FARMS';
3. LOOPA:
4. FOR EXECUTE STATEMENT :STMT1
5. INTO :FARM DO
6. BEGIN
7. STMT2 = 'SELECT NAME ' || 'FROM ANIMALS WHERE FARM = ''';
8. LOOPB:
9. FOR EXECUTE STATEMENT :STMT2 || :FARM || ''''
10. INTO :ANIMAL DO
11. BEGIN
12. IF (ANIMAL = 'FLUFFY') THEN
13. LEAVE LOOPB;
14. ELSE IF (ANIMAL = FARM) THEN
15. LEAVE LOOPA;
16. ELSE
17. SUSPEND;
18. END
19. END
20. ...
See alsoSection 7.7.10, BREAK, Section 7.7.12, CONTINUE, Section 7.7.13, EXIT
7.7.12 CONTINUE
Used forContinuing with the next iteration of a loop
Available inPSQL
Syntax
1. [label:]
2. <loop_stmt>
3. BEGIN
4. ...
5. CONTINUE [label];
6. ...
7. END
9. <loop_stmt> ::=
10. FOR <select_stmt> INTO <var_list> DO
11. | FOR EXECUTE STATEMENT ... INTO <var_list> DO
12. | WHILE (<condition>)} DO
Table 7.7.12.1 CONTINUE Statement Parameters
| Argument | Description |
|---|---|
| label | Label |
| select_stmt | SELECT statement |
| condition | A logical condition returning TRUE, FALSE or UNKNOWN |
The CONTINUE statement skips the remainer of the current block of a loop and starts the next iteration of the current WHILE or FOR loop. Using the optional label parameter, CONTINUE can also start the next iteration of an outer loop, that is, the loop labelled with label.
7.7.12.1 CONTINUE Examples
Using the CONTINUE statement
1. FOR SELECT A, D
2. FROM ATABLE INTO achar, ddate
3. DO
4. BEGIN
5. IF (ddate < current_date - 30) THEN
6. CONTINUE;
7. ELSE
8. BEGIN
9. /* do stuff */
10. END
11. END
See alsoSection 7.7.10, BREAK, Section 7.7.11, LEAVE, Section 7.7.13, EXIT
7.7.13 EXIT
Used forTerminating module execution
Available inPSQL
Syntax
1. EXIT;
The EXIT statement causes execution of the current PSQL module to jump to the final END statement from any point in the code, thus terminating the program.
Calling EXIT in a function will result in the function returning NULL.
7.7.13.1 EXIT Examples
Using the EXIT statement in a selectable procedure
1. CREATE PROCEDURE GEN_100
2. RETURNS (I INTEGER)
3. AS
4. BEGIN
5. I = 1;
6. WHILE (1=1) DO
7. BEGIN
8. SUSPEND;
9. IF (I=100) THEN
10. EXIT;
11. I = I + 1;
12. END
13. END
See alsoSection 7.7.10, BREAK, Section 7.7.11, LEAVE, Section 7.7.12, CONTINUE, Section 7.7.14, SUSPEND
7.7.14 SUSPEND
Used forPassing output to the buffer and suspending execution while waiting for caller to fetch it
Available inPSQL
Syntax
1. SUSPEND;
The SUSPEND statement is used in a selectable stored procedure to pass the values of output parameters to a buffer and suspend execution. Execution remains suspended until the calling application fetches the contents of the buffer. Execution resumes from the statement directly after the SUSPEND statement. In practice, this is likely to be a new iteration of a looping process.
Important Notes
- The
SUSPENDstatement can only occur in stored procedures or sub-procedures - The presence of the
SUSPENDkeyword defines a stored procedure as a selectable procedure - Applications using interfaces that wrap the API perform the fetches from selectable procedures transparently.
- If a selectable procedure is executed using
EXECUTE PROCEDURE, it behaves as an executable procedure. When aSUSPENDstatement is executed in such a stored procedure, it is the same as executing theEXITstatement, resulting in immediate termination of the procedure. SUSPENDbreaks the atomicity of the block in which it is located. If an error occurs in a selectable procedure, statements executed after the finalSUSPENDstatement will be rolled back. Statements that executed before the finalSUSPENDstatement will not be rolled back unless the transaction is rolled back.
7.7.14.1 SUSPEND Examples
Using the SUSPEND statement in a selectable procedure
1. CREATE PROCEDURE GEN_100
2. RETURNS (I INTEGER)
3. AS
4. BEGIN
5. I = 1;
6. WHILE (1=1) DO
7. BEGIN
8. SUSPEND;
9. IF (I=100) THEN
10. EXIT;
11. I = I + 1;
12. END
13. END
See alsoSection 7.7.13, EXIT
7.7.15 EXECUTE STATEMENT
Used forExecuting dynamically created SQL statements
Available inPSQL
Syntax
1. <execute_statement> ::= EXECUTE STATEMENT <argument>
2. [<option> ...]
3. [INTO <variables>];
5. <argument> ::= <paramless_stmt>
6. | (<paramless_stmt>)
7. | (<stmt_with_params>) (<param_values>)
9. <param_values> ::= <named_values> | <positional_values>
11. <named_values> ::= <named_value> [, <named_value> ...]
13. <named_value> ::= [EXCESS] paramname := <value_expr>
15. <positional_values> ::= <value_expr> [, <value_expr> ...]
17. <option> ::=
18. WITH {AUTONOMOUS | COMMON} TRANSACTION
19. | WITH CALLER PRIVILEGES
20. | AS USER user
21. | PASSWORD password
22. | ROLE role
23. | ON EXTERNAL [DATA SOURCE] <connection_string>
25. <connection_string> ::=
26. !! See <filespec> in the CREATE DATABASE syntax !!
28. <variables> ::= [:]varname [, [:]varname ...]
Table 7.7.15.1 EXECUTE STATEMENT Statement Parameters
| Argument | Description |
|---|---|
| paramless_stmt | Literal string or variable containing a non-parameterized SQL query |
| stmt_with_params | Literal string or variable containing a parameterized SQL query |
| paramname | SQL query parameter name |
| value_expr | SQL expression resolving to a value |
| user | Username. It can be a string, CURRENT_USER or a string variable |
| password | Password. It can be a string or a string variable |
| role | Role. It can be a string, CURRENT_ROLE or a string variable |
| connection_string | Connection string. It can be a string literal or a string variable |
| varname | Variable |
The statement EXECUTE STATEMENT takes a string parameter and executes it as if it were a DSQL statement. If the statement returns data, it can be passed to local variables by way of an INTO clause.
Note
EXECUTE STATEMENT can only produce a single row of data. Statements producing multiple rows of data must be executed with Section 7.7.17, FOR EXECUTE STATEMENT.
7.7.15.1 Parameterized Statements
You can use parameters — either named or positional — in the DSQL statement string. Each parameter must be assigned a value.
7.7.15.1.1 Special Rules for Parameterized Statements
- Named and positional parameters cannot be mixed in one query
- Each parameter must be used in the statement text.
To relax this rule, named parameters can be prefixed with the keyword EXCESS to indicate that the parameter may be absent from the statement text. This option is useful for dynamically generated statements that conditionally include or exclude certain parameters.
3. If the statement has parameters, they must be enclosed in parentheses when EXECUTE STATEMENT is called, regardless of whether they come directly as strings, as variable names or as expressions
4. Each named parameter must be prefixed by a colon (:) in the statement string itself, but not when the parameter is assigned a value
5. Positional parameters must be assigned their values in the same order as they appear in the query text
6. The assignment operator for parameters is the special operator :=, similar to the assignment operator in Pascal
7. Each named parameter can be used in the statement more than once, but its value must be assigned only once
8. With positional parameters, the number of assigned values must match the number of parameter placeholders (question marks) in the statement exactly
9. A named parameter in the statement text can only be a regular identifier (it cannot be a quoted identifier)
7.7.15.1.2 Examples of EXECUTE STATEMENT with parameters
- With named parameters:
1. ...
2. DECLARE license_num VARCHAR(15);
3. DECLARE connect_string VARCHAR (100);
4. DECLARE stmt VARCHAR (100) =
5. 'SELECT license
6. FROM cars
7. WHERE driver = :driver AND location = :loc';
8. BEGIN
9. ...
10. SELECT connstr
11. FROM databases
12. WHERE cust_id = :id
13. INTO connect_string;
14. ...
15. FOR
16. SELECT id
17. FROM drivers
18. INTO current_driver
19. DO
20. BEGIN
21. FOR
22. SELECT location
23. FROM driver_locations
24. WHERE driver_id = :current_driver
25. INTO current_location
26. DO
27. BEGIN
28. ...
29. EXECUTE STATEMENT (stmt)
30. (driver := current_driver,
31. loc := current_location)
32. ON EXTERNAL connect_string
33. INTO license_num;
34. ...
2. The same code with positional parameters:
1. DECLARE license_num VARCHAR (15);
2. DECLARE connect_string VARCHAR (100);
3. DECLARE stmt VARCHAR (100) =
4. 'SELECT license
5. FROM cars
6. WHERE driver = ? AND location = ?';
7. BEGIN
8. ...
9. SELECT connstr
10. FROM databases
11. WHERE cust_id = :id
12. into connect_string;
13. ...
14. FOR
15. SELECT id
16. FROM drivers
17. INTO current_driver
18. DO
19. BEGIN
20. FOR
21. SELECT location
22. FROM driver_locations
23. WHERE driver_id = :current_driver
24. INTO current_location
25. DO
26. BEGIN
27. ...
28. EXECUTE STATEMENT (stmt)
29. (current_driver, current_location)
30. ON EXTERNAL connect_string
31. INTO license_num;
32. ...
3. Use of EXCESS to allow named parameters to be unused (note: this is a FOR EXECUTE STATEMENT):
1. CREATE PROCEDURE P_EXCESS (A_ID INT, A_TRAN INT = NULL, A_CONN INT = NULL)
2. RETURNS (ID INT, TRAN INT, CONN INT)
3. AS
4. DECLARE S VARCHAR(255);
5. DECLARE W VARCHAR(255) = '';
6. BEGIN
7. S = 'SELECT * FROM TTT WHERE ID = :ID';
9. IF (A_TRAN IS NOT NULL)
10. THEN W = W || ' AND TRAN = :a';
12. IF (A_CONN IS NOT NULL)
13. THEN W = W || ' AND CONN = :b';
15. IF (W <> '')
16. THEN S = S || W;
18. -- could raise error if TRAN or CONN is null
19. -- FOR EXECUTE STATEMENT (:S) (a := :A_TRAN, b := A_CONN, id := A_ID)
21. -- OK in all cases
22. FOR EXECUTE STATEMENT (:S) (EXCESS a := :A_TRAN, EXCESS b := A_CONN, id := A_ID)
23. INTO :ID, :TRAN, :CONN
24. DO SUSPEND;
25. END
7.7.15.2 WITH {AUTONOMOUS | COMMON} TRANSACTION
By default, the executed SQL statement runs within the current transaction. Using WITH AUTONOMOUS TRANSACTION causes a separate transaction to be started, with the same parameters as the current transaction. This separate transaction will be committed when the statement was executed without errors and rolled back otherwise.
The clause WITH COMMON TRANSACTION uses the current transaction whenever possible; this is the default behaviour. If the statement must run in a separate connection, an already started transaction within that connection is used, if available. Otherwise, a new transaction is started with the same parameters as the current transaction. Any new transactions started under the COMMON regime are committed or rolled back with the current transaction.
7.7.15.3 WITH CALLER PRIVILEGES
By default, the SQL statement is executed with the privileges of the current user. Specifying WITH CALLER PRIVILEGES combines the privileges of the calling procedure or trigger with those of the user, just as if the statement were executed directly by the routine. WITH CALLER PRIVILEGES has no effect if the ON EXTERNAL clause is also present.
7.7.15.4 ON EXTERNAL [DATA SOURCE]
With ON EXTERNAL [DATA SOURCE], the SQL statement is executed in a separate connection to the same or another database, possibly even on another server. If connection_string is NULL or '' (empty string), the entire ON EXTERNAL [DATA SOURCE] clause is considered absent, and the statement is executed against the current database.
7.7.15.4.1 Connection Pooling
- External connections made by statements
WITH COMMON TRANSACTION(the default) will remain open until the current transaction ends. They can be reused by subsequent calls toEXECUTE STATEMENT, but only if connection_string is exactly the same, including case - External connections made by statements
WITH AUTONOMOUS TRANSACTIONare closed as soon as the statement has been executed - Statements using
WITH AUTONOMOUS TRANSACTIONcan and will re-use connections that were opened earlier by statementsWITH COMMON TRANSACTION. If this happens, the reused connection will be left open after the statement has been executed. (It must be, because it has at least one active transaction!)
7.7.15.4.2 Transaction Pooling
- If
WITH COMMON TRANSACTIONis in effect, transactions will be reused as much as possible. They will be committed or rolled back together with the current transaction - If
WITH AUTONOMOUS TRANSACTIONis specified, a fresh transaction will always be started for the statement. This transaction will be committed or rolled back immediately after the statement’s execution
7.7.15.4.3 Exception Handling
When ON EXTERNAL is used, the extra connection is always made via a so-called external provider, even if the connection is to the current database. One of the consequences is that exceptions cannot be caught in the usual way. Every exception caused by the statement is wrapped in either an eds_connection or an eds_statement error. In order to catch them in your PSQL code, you have to use WHEN GDSCODE eds_connection, WHEN GDSCODE eds_statement or WHEN ANY.
Note
Without ON EXTERNAL, exceptions are caught in the usual way, even if an extra connection is made to the current database.
7.7.15.4.4 Miscellaneous Notes
- The character set used for the external connection is the same as that for the current connection
- Two-phase commits are not supported
7.7.15.5 AS USER, PASSWORD and ROLE
The optional AS USER, PASSWORD and ROLE clauses allow specification of which user will execute the SQL statement and with which role. The method of user login, and whether a separate connection is opened, depends on the presence and values of the `O
(本文超出站点单文档体积上限,已截断)
