Executing dynamically created SQL statements
Available in
PSQL
Syntax
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.
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
Named and positional parameters cannot be mixed in one query
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 expressionsEach named parameter must be prefixed by a colon (‘
:
’) in the statement string itself, but not when the parameter is assigned a valuePositional parameters must be assigned their values in the same order as they appear in the query text
The assignment operator for parameters is the special operator “
:=
”, similar to the assignment operator in PascalEach named parameter can be used in the statement more than once, but its value must be assigned only once
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:
The same code with positional parameters:
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.
By default, the SQL statement is executed with the privileges of the current user. Specifying 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 toEXECUTE STATEMENT
, but only if the connect string is exactly the same, including caseExternal connections made by statements
WITH AUTONOMOUS TRANSACTION
are closed as soon as the statement has been executed
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
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
.
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
The optional , 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
andROLE
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 parametersIf 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
andROLE
is present, a new connection to the current database is opened with the suppled parameter values. No defaults are used for missing parametersIf all three are absent, the statement is executed within the current connection
Caveats with EXECUTE STATEMENT
There is no way to validate the syntax of the enclosed statement
There are no dependency checks to discover whether tables or columns have been dropped
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