5.9FUNCTION

A stored function is a user-defined function stored in the metadata of a database, and running on the server. Stored functions can be called by stored procedures, stored functions (including the function itself), triggers and client programs. When a stored function calls itself, such a stored function is called a recursive function.

Unlike stored procedures, stored functions always return a single scalar value. To return a value from a stored functions, use the RETURN statement, which immediately ends the function.

See alsoEXTERNAL FUNCTION

5.9.1CREATE FUNCTION

Used forCreating a new stored function

Available inDSQL

Syntax

   |CREATE FUNCTION funcname [ ( [ <in_params> ] ) ]
   |  RETURNS <domain_or_non_array_type> [COLLATE collation]
   |  [DETERMINISTIC]
   |  <module-body>
   | 
   |<module-body> ::=
   |  !! See Syntax of Module Body !!
   | 
   |<in_params> ::= <inparam> [, <inparam> ... ]
   | 
   |<inparam> ::= <param-decl> [ { = | DEFAULT } <value> ]
   | 
   |<value> ::= { <literal> | NULL | <context-var> }
   | 
   |<param-decl> ::= paramname <domain_or_non_array_type> [NOT NULL]
   |  [COLLATE collation]
   | 
   |<domain_or_non_array_type> ::=
   |  !! See Scalar Data Types Syntax !!

Table 5.25CREATE FUNCTION Statement Parameters
ParameterDescription

funcname

Stored function name consisting of up to 31 characters. Must be unique among all function names in the database.

inparam

Input parameter description

collation

Collation sequence

literal

A literal value that is assignment-compatible with the data type of the parameter

context-var

Any context variable whose type is compatible with the data type of the parameter

paramname

The name of an input parameter of the function. It may consist of up to 31 characters. The name of the parameter must be unique among input parameters of the function and its local variables.

The CREATE FUNCTION statement creates a new stored function. The stored function name must be unique among the names of all stored and external (legacy) functions, excluding sub-functions or functions in packages. For sub-functions or functions in packages, the name must be unique within its module (package, stored procedure, stored function, trigger).

Note

It is advisable to not reuse function names between global stored functions and stored functions in packages, although this is legal. At the moment, it is not possible to call a function or procedure from the global namespace from inside a package, if that package defines a function or procedure with the same name. In that situation, the function or procedure of the package will be called.

CREATE FUNCTION is a compound statement with a header and a body. The header defines the name of the stored function, and declares input parameters and return type.

The function body consists of optional declarations of local variables, named cursors, and subroutines (sub-functions and sub-procedures), and one or more statements or statement blocks, enclosed in an outer block that starts with the keyword BEGIN and ends with the keyword END. Declarations and statements inside the function body must be terminated with a semicolon (;).

5.9.1.1Statement Terminators

Some SQL statement editors — specifically the isql utility that comes with Firebird and possibly some third-party editors — employ an internal convention that requires all statements to be terminated with a semi-colon. This creates a conflict with PSQL syntax when coding in these environments. If you are unacquainted with this problem and its solution, please study the details in the PSQL chapter in the section entitled Switching the Terminator in isql.

5.9.1.2Parameters

Each parameter has a data type.

A collation sequence can be specified for string-type parameters, using the COLLATE clause.

Input Parameters

Input parameters are presented as a parenthesized list following the name of the function. They are passed by value into the function, so any changes inside the function has no effect on the parameters in the caller. The NOT NULL constraint can also be specified for any input parameter, to prevent NULL being passed or assigned to it. Input parameters may have default values. Parameters with default values specified must be added at the end of the list of parameters.

Output Parameter

The RETURNS clause specifies the return type of the stored function. If a function returns a string value, then it is possible to specify the collation using the COLLATE clause. As a return type, you can specify a data type, a domain name, the type of a domain (using TYPE OF), or the type of a column of a table or view (using TYPE OF COLUMN).

5.9.1.3Deterministic functions

The optional DETERMINISTIC clause indicates that the function is deterministic. Deterministic functions always return the same result for the same set of inputs. Non-deterministic functions can return different results for each invocation, even for the same set of inputs. If a function is specified as deterministic, then such a function might not be called again if it has already been called once with the given set of inputs, and instead takes the result from a metadata cache.

Note

Current versions of Firebird do not actually cache results of deterministic functions.

Specifying the DETERMINISTIC clause is actually something like a promise that the function will return the same thing for equal inputs. At the moment, a deterministic function is considered an invariant, and works like other invariants. That is, they are computed and cached at the current execution level of a given statement.

This is easily demonstrated with an example:

   |CREATE FUNCTION FN_T
   |RETURNS DOUBLE PRECISION DETERMINISTIC
   |AS
   |BEGIN
   |  RETURN rand();
   |END;
   | 
   |-- the function will be evaluated twice and will return 2 different values
   |SELECT fn_t() FROM rdb$database
   |UNION ALL
   |SELECT fn_t() FROM rdb$database;
   | 
   |-- the function will be evaluated once and will return 2 identical values
   |WITH t (n) AS (
   |  SELECT 1 FROM rdb$database
   |  UNION ALL
   |  SELECT 2 FROM rdb$database
   |)
   |SELECT n, fn_t() FROM t;

5.9.1.4Variable, Cursor and Sub-Routine Declarations

The optional declarations section, located at the start of the body of the function definition, defines variables (including cursors) and sub-routines local to the function. Local variable declarations follow the same rules as parameters regarding specification of the data type. See details in the PSQL chapter for DECLARE VARIABLE, DECLARE CURSOR, DECLARE FUNCTION, and DECLARE PROCEDURE.

5.9.1.5Function Body

The header section is followed by the function body, consisting of one or more PSQL statements enclosed between the outer keywords BEGIN and END. Multiple BEGIN …​ END blocks of terminated statements may be embedded inside the procedure body.

5.9.1.6External UDR Functions

A stored function can also be located in an external module. In this case, instead of a function body, the CREATE FUNCTION specifies the location of the function in the external module using the EXTERNAL clause. The optional NAME clause specifies the name of the external module, the name of the function inside the module, and — optionally — user-defined information. The required ENGINE clause specifies the name of the UDR engine that handles communication between Firebird and the external module. The optional AS clause accepts a string literal body, which can be used by the engine or module for various purposes.

🛑
Warning

External UDR (User Defined Routine) functions created using CREATE FUNCTION …​ EXTERNAL …​ should not be confused with legacy UDFs (User Defined Functions) declared using DECLARE EXTERNAL FUNCTION.

UDFs are deprecated, and a legacy from previous Firebird functions. Their capabilities are significantly inferior to the capabilities to the new type of external UDR functions.

5.9.1.7Who Can Create a Function

The CREATE FUNCTION statement can be executed by:

The user who created the stored function becomes its owner.

5.9.1.8CREATE FUNCTION Examples

  1. Creating a stored function

      |CREATE FUNCTION ADD_INT (A INT, B INT DEFAULT 0)
      |RETURNS INT
      |AS
      |BEGIN
      |  RETURN A + B;
      |END
    

    Calling in a select:

      |SELECT ADD_INT(2, 3) AS R FROM RDB$DATABASE
    

    Call inside PSQL code, the second optional parameter is not specified:

      |MY_VAR = ADD_INT(A);
    
  2. Creating a deterministic stored function

      |CREATE FUNCTION FN_E()
      |RETURNS DOUBLE PRECISION DETERMINISTIC
      |AS
      |BEGIN
      |  RETURN EXP(1);
      |END
    
  3. Creating a stored function with table column type parameters

    Returns the name of a type by field name and value

       |CREATE FUNCTION GET_MNEMONIC (
       |  AFIELD_NAME TYPE OF COLUMN RDB$TYPES.RDB$FIELD_NAME,
       |  ATYPE TYPE OF COLUMN RDB$TYPES.RDB$TYPE)
       |RETURNS TYPE OF COLUMN RDB$TYPES.RDB$TYPE_NAME
       |AS
       |BEGIN
       |  RETURN (SELECT RDB$TYPE_NAME
       |          FROM RDB$TYPES
       |          WHERE RDB$FIELD_NAME = :AFIELD_NAME
       |          AND RDB$TYPE = :ATYPE);
       |END
    
  4. Creating an external stored function

    Create a function located in an external module (UDR). Function implementation is located in the external module udrcpp_example. The name of the function inside the module is wait_event.

      |CREATE FUNCTION wait_event (
      |  event_name varchar (31) CHARACTER SET ascii
      |) RETURNS INTEGER
      |EXTERNAL NAME 'udrcpp_example!Wait_event'
      |ENGINE udr
    
  5. Creating a stored function containing a sub-function

    Creating a function to convert a number to hexadecimal format.

       |CREATE FUNCTION INT_TO_HEX (
       |  ANumber BIGINT ,
       |  AByte_Per_Number SMALLINT = 8)
       |RETURNS CHAR (66)
       |AS
       |DECLARE VARIABLE xMod SMALLINT ;
       |DECLARE VARIABLE xResult VARCHAR (64);
       |DECLARE FUNCTION TO_HEX (ANum SMALLINT ) RETURNS CHAR
       |  AS
       |  BEGIN
       |    RETURN CASE ANum
       |      WHEN 0 THEN '0'
       |      WHEN 1 THEN '1'
       |      WHEN 2 THEN '2'
       |      WHEN 3 THEN '3'
       |      WHEN 4 THEN '4'
       |      WHEN 5 THEN '5'
       |      WHEN 6 THEN '6'
       |      WHEN 7 THEN '7'
       |      WHEN 8 THEN '8'
       |      WHEN 9 THEN '9'
       |      WHEN 10 THEN 'A'
       |      WHEN 11 THEN 'B'
       |      WHEN 12 THEN 'C'
       |      WHEN 13 THEN 'D'
       |      WHEN 14 THEN 'E'
       |      WHEN 15 THEN 'F'
       |      ELSE NULL
       |    END;
       |  END
       |BEGIN
       |  xMod = MOD (ANumber, 16);
       |  ANumber = ANumber / 16;
       |  xResult = TO_HEX (xMod);
       |  WHILE (ANUMBER> 0) DO
       |  BEGIN
       |    xMod = MOD (ANumber, 16);
       |    ANumber = ANumber / 16;
       |    xResult = TO_HEX (xMod) || xResult;
       |  END
       |  RETURN '0x' || LPAD (xResult, AByte_Per_Number * 2, '0' );
       |END
    

See alsoSection 5.9.3, “CREATE OR ALTER FUNCTION, Section 5.9.2, “ALTER FUNCTION, Section 5.9.5, “RECREATE FUNCTION, Section 5.9.4, “DROP FUNCTION, Section 5.10.1, “DECLARE EXTERNAL FUNCTION

5.9.2ALTER FUNCTION

Used forModifying an existing stored function

Available inDSQL

Syntax

  |ALTER FUNCTION funcname
  |  [ ( [ <in_params> ] ) ]
  |  RETURNS <domain_or_non_array_type> [COLLATE collation]
  |  [DETERMINISTIC]
  |  <module-body>
  | 
  |!! See syntax of CREATE FUNCTION for further rules !!

The ALTER FUNCTION statement allows the following changes to a stored function definition:

  • the set and characteristics of input and output type

  • local variables, named cursors, and sub-routines

  • code in the body of the stored procedure

For external functions (UDR), you can change the entry point and engine name. For legacy external functions declared using DECLARE EXTERNAL FUNCTION — also known as UDFs — it is not possible to convert to PSQL and vice versa.

After ALTER FUNCTION executes, existing privileges remain intact and dependencies are not affected.

Caution

Take care about changing the number and type of input parameters and the output type of a stored function. Existing application code and procedures, functions and triggers that call it could become invalid because the new description of the parameters is incompatible with the old calling format. For information on how to troubleshoot such a situation, see the article The RDB$VALID_BLR Field in the Appendix.

5.9.2.1Who Can Alter a Function

The ALTER FUNCTION statement can be executed by:

  • Administrators

  • Owner of the stored function

  • Users with the ALTER ANY FUNCTION privilege

5.9.2.2Examples of ALTER FUNCTION

Altering a stored function

  |ALTER FUNCTION ADD_INT(A INT, B INT, C INT)
  |RETURNS INT
  |AS
  |BEGIN
  |  RETURN A + B + C;
  |END

See alsoSection 5.9.1, “CREATE FUNCTION, Section 5.9.3, “CREATE OR ALTER FUNCTION, Section 5.9.5, “RECREATE FUNCTION, Section 5.9.4, “DROP FUNCTION

5.9.3CREATE OR ALTER FUNCTION

Used forCreating a new or modifying an existing stored function

Available inDSQL

Syntax

  |CREATE OR ALTER FUNCTION funcname
  |  [ ( [ <in_params> ] ) ]
  |  RETURNS <domain_or_non_array_type> [COLLATE collation]
  |  [DETERMINISTIC]
  |  <module-body>
  | 
  |!! See syntax of CREATE FUNCTION for further rules !!

The CREATE OR ALTER FUNCTION statement creates a new stored function or alters an existing one. If the stored function does not exist, it will be created by invoking a CREATE FUNCTION statement transparently. If the function already exists, it will be altered and compiled (through ALTER FUNCTION) without affecting its existing privileges and dependencies.

5.9.3.1Examples of CREATE OR ALTER FUNCTION

Create a new or alter an existing stored function

  |CREATE OR ALTER FUNCTION ADD_INT(A INT, B INT DEFAULT 0)
  |RETURNS INT
  |AS
  |BEGIN
  |  RETURN A + B;
  |END

See alsoSection 5.9.1, “CREATE FUNCTION, Section 5.9.2, “ALTER FUNCTION, Section 5.9.4, “DROP FUNCTION

5.9.4DROP FUNCTION

Used forDropping a stored function

Available inDSQL

Syntax

  |DROP FUNCTION funcname

Table 5.26DROP FUNCTION Statement Parameters
ParameterDescription

funcname

Stored function name consisting of up to 31 characters. Must be unique among all function names in the database.

The DROP FUNCTION statement deletes an existing stored function. If the stored function has any dependencies, the attempt to delete it will fail and the appropriate error will be raised.

5.9.4.1Who Can Drop a Function

The DROP FUNCTION statement can be executed by:

  • Administrators

  • Owner of the stored function

  • Users with the DROP ANY FUNCTION privilege

5.9.4.2Examples of DROP FUNCTION

  |DROP FUNCTION ADD_INT;

See alsoSection 5.9.1, “CREATE FUNCTION, Section 5.9.3, “CREATE OR ALTER FUNCTION, Section 5.9.5, “RECREATE FUNCTION

5.9.5RECREATE FUNCTION

Used forCreating a new stored function or recreating an existing one

Available inDSQL

Syntax

  |RECREATE FUNCTION funcname
  |  [ ( [ <in_params> ] ) ]
  |  RETURNS <domain_or_non_array_type> [COLLATE collation]
  |  [DETERMINISTIC]
  |  <module-body>
  | 
  |!! See syntax of CREATE FUNCTION for further rules !!

The RECREATE FUNCTION statement creates a new stored function or recreates an existing one. If there is a function with this name already, the engine will try to drop it and then create a new one. Recreating an existing function will fail at COMMIT if the function has dependencies.

Note

Be aware that dependency errors are not detected until the COMMIT phase of this operation.

After a procedure is successfully recreated, existing privileges to execute the stored function and the privileges of the stored function itself are dropped.

5.9.5.1Examples of RECREATE FUNCTION

Creating or recreating a stored function

  |RECREATE FUNCTION ADD_INT(A INT, B INT DEFAULT 0)
  |RETURNS INT
  |AS
  |BEGIN
  |  RETURN A + B;
  |EN

See alsoSection 5.9.1, “CREATE FUNCTION, Section 5.9.4, “DROP FUNCTION