7.6.10. EXECUTE STATEMENT

Used for

Executing dynamically created SQL statements

Available in

PSQL

Syntax

  1. <execute_statement> ::= EXECUTE STATEMENT <argument>
  2. [<option> …]
  3. [INTO <variables>]
  4. <argument> ::= <paramless_stmt>
  5. | (<paramless_stmt>)
  6. | (<stmt_with_params>) (<param_values>)
  7. <param_values> ::= <named_values> | <positional_values>
  8. <named_values> ::= paramname := <value_expr>
  9. [, paramname := <value_expr> ...]
  10. <positional_values> ::= <value_expr> [, <value_expr> ...]
  11. <option> ::= WITH {AUTONOMOUS | COMMON} TRANSACTION
  12. | WITH CALLER PRIVILEGES
  13. | AS USER user
  14. | PASSWORD password
  15. | ROLE role
  16. | ON EXTERNAL [DATA SOURCE] <connect_string>
  17. <connect_string> ::= [<hostspec>] {filepath | db_alias}
  18. <hostspec> ::= <tcpip_hostspec> | <NamedPipes_hostspec>
  19. <tcpip_hostspec> ::= hostname[/port]:
  20. <NamePipes_hostspec> ::= \\hostname\
  21. <variables> ::= [:]varname [, [:]varname ...]
Table 87. EXECUTE STATEMENT Statement Parameters
ArgumentDescription

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

User name. 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 or a string variable

filepath

Path to the primary database file

db_alias

Database alias

hostname

Computer name or IP address

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.

Parameterized Statements

You can use parameters — either named or positional — in the DSQL statement string. Each parameter must be assigned a value.

Special Rules for Parameterized Statements

  1. Named and positional parameters cannot be mixed in one query

  2. 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

  3. Each named parameter must be prefixed by a colon (‘:’) in the statement string itself, but not when the parameter is assigned a value

  4. Positional parameters must be assigned their values in the same order as they appear in the query text

  5. The assignment operator for parameters is the special operator “:=”, similar to the assignment operator in Pascal

  6. Each named parameter can be used in the statement more than once, but its value must be assigned only once

  7. With positional parameters, the number of assigned values must match the number of parameter placeholders (question marks) in the statement exactly

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. ...

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. ...

WITH {AUTONOMOUS | COMMON} TRANSACTION

Traditionally, the executed SQL statement always ran within the current transaction, and this is still the default. WITH AUTONOMOUS TRANSACTION causes a separate transaction to be started, with the same parameters as the current transaction. It will be committed if the statement runs to completion without errors and rolled back otherwise. WITH COMMON TRANSACTION uses the current transaction if possible.

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.

WITH CALLER PRIVILEGES

By default, the SQL statement is executed with the privileges of the current user. Specifying WITH CALLER PRIVILEGES adds to this the privileges of the calling procedure or trigger, 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.

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 the connect 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.

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 to EXECUTE STATEMENT, but only if the connect string is exactly the same, including case

  • External connections made by statements WITH AUTONOMOUS TRANSACTION are closed as soon as the statement has been executed

  • Notice that statements WITH AUTONOMOUS TRANSACTION can and will re-use connections that were opened earlier by statements WITH 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 un-committed transaction!)

Transaction Pooling

  • If WITH COMMON TRANSACTION is 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 TRANSACTION is 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

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.

Without ON EXTERNAL, exceptions are caught in the usual way, even if an extra connection is made to the current database.

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

AS USER, PASSWORD and ROLE

The optional AS USER, PASSWORD and ROLE clauses allow specificaton of which user will execute the SQL statement and with which role. The method of user log-in and whether a separate connection is open depend on the presence and values of the ON EXTERNAL [DATA SOURCE], AS USER, PASSWORD and ROLE clauses:

  • If ON EXTERNAL is present, a new connection is always opened, and:

    • If at least one of AS USER, PASSWORD and ROLE is present, native authentication is attempted with the given parameter values (locally or remotely, depending on the connect string). No defaults are used for missing parameters

    • If all three are absent and the connect string contains no hostname, then the new connection is established on the local host with the same user and role as the current connection. The term ‘local’ means “on the same machine as the server” here. This is not necessarily the location of the client

    • If all three are absent and the connect string contains a hostname, then trusted authentication is attempted on the remote host (again, ‘remote’ from the perspective of the server). If this succeeds, the remote operating system will provide the user name (usually the operating system account under which the Firebird process runs)

  • If ON EXTERNAL is absent:

    • If at least one of AS USER, PASSWORD and ROLE is present, a new connection to the current database is opened with the suppled parameter values. No defaults are used for missing parameters

    • If all three are absent, the statement is executed within the current connection

Notice

If a parameter value is NULL or “‘’” (empty string), the entire parameter is considered absent. Additionally, AS USER is considered absent if its value is equal to CURRENT_USER, and ROLE if it is the same as CURRENT_ROLE.

Caveats with EXECUTE STATEMENT

  1. There is no way to validate the syntax of the enclosed statement

  2. There are no dependency checks to discover whether tables or columns have been dropped

  3. Even though the performance in loops has been significantly improved in Firebird 2.5, execution is still considerably slower than when the same statements are launched directly

  4. Return values are strictly checked for data type in order to avoid unpredictable type-casting exceptions. For example, the string '1234' would convert to an integer, 1234, but 'abc' would give a conversion error

All in all, this feature is meant to be used very cautiously and you should always take the caveats into account. If you can achieve the same result with PSQL and/or DSQL, it will almost always be preferable.

See also

FOR EXECUTE STATEMENT