WITH FUNCTION/PROCEDURE
1. Purpose
This document explains the WITH FUNCTION and WITH PROCEDURE features in IvorySQL,
which implement Oracle-style inline PL/SQL functions and procedures inside SQL statements.
WITH FUNCTION/PROCEDURE is Oracle’s Subquery Factoring with PL/SQL Declarations feature.
It allows PL/SQL functions and procedures to be defined directly inside the WITH clause
(Common Table Expression, CTE) of a SQL statement.
2. Feature Description
2.1. Basic Syntax
In Oracle-compatible mode (compatible_db = ORA_PARSER), the WITH clause supports the
following extended syntax:
WITH
FUNCTION func_name ( [ param_list ] ) RETURN return_type
{ IS | AS }
[ declare_section ]
BEGIN
statements
END [ func_name ] ;
PROCEDURE proc_name ( [ param_list ] )
{ IS | AS }
[ declare_section ]
BEGIN
statements
END [ proc_name ] ;
cte_name AS ( SELECT ... )
SELECT ...
2.2. Key Characteristics
-
Scope-limited: Functions/procedures defined in the WITH clause are visible only within the current SQL statement.
-
Mixed usage: Function/procedure definitions can be interleaved with standard CTEs (
AS (SELECT …)). -
Full PL/SQL syntax: The function body supports the complete PL/SQL syntax (
BEGIN…END). -
No system catalog writes: Definitions are automatically destroyed after execution; they are not persisted to
pg_proc. -
SELECT context only: Valid in SELECT statements and INSERT…SELECT statements. Using WITH FUNCTION before UPDATE, DELETE, or MERGE raises an error (consistent with Oracle behavior).
3. Supported Statement Types
WITH inline functions/procedures are only allowed in the following top-level statements:
-
SELECTstatements (most common) -
INSERT … SELECTstatements (Oracle-compatible form; WITH FUNCTION appears afterINSERT INTO)
The following statements are not supported (Oracle does not allow them;
ERRCODE_FEATURE_NOT_SUPPORTED is raised):
-
WITH FUNCTION … UPDATE …: WITH FUNCTION cannot precede UPDATE -
WITH FUNCTION … DELETE …: WITH FUNCTION cannot precede DELETE -
WITH FUNCTION … MERGE …: WITH FUNCTION cannot precede MERGE
To share reusable logic inside DML statements, define a schema-level function with
CREATE FUNCTION instead.
4. Syntax Examples
4.1. Simplest Inline Function
WITH
FUNCTION double_it(n NUMBER) RETURN NUMBER AS
BEGIN RETURN n * 2; END;
SELECT double_it(5) FROM dual;
-- Expected output: 10
4.2. Function Mixed with a CTE
WITH
FUNCTION tax(amt NUMBER) RETURN NUMBER AS
BEGIN RETURN amt * 0.1; END;
orders AS (SELECT 100 AS amount)
SELECT amount, tax(amount) FROM orders;
-- Expected output: 100 | 10
4.3. Multiple Inline Functions
WITH
FUNCTION add1(n NUMBER) RETURN NUMBER AS BEGIN RETURN n+1; END;
FUNCTION mul2(n NUMBER) RETURN NUMBER AS BEGIN RETURN n*2; END;
SELECT mul2(add1(3)) FROM dual;
-- Expected output: 8
4.4. Recursive Function
WITH
FUNCTION factorial(n NUMBER) RETURN NUMBER AS
BEGIN
IF n <= 1 THEN RETURN 1; END IF;
RETURN n * factorial(n-1);
END;
SELECT factorial(5) FROM dual;
-- Expected output: 120
4.5. OUT Parameters (PROCEDURE Only)
WITH FUNCTION does not allow OUT or IN OUT parameters (consistent with Oracle
ORA-06572 behavior). Only WITH PROCEDURE may declare OUT / IN OUT parameters.
-- Correct: PROCEDURE may declare OUT parameters
WITH
PROCEDURE swap(val IN NUMBER, result OUT NUMBER) AS
BEGIN
result := val * 10;
END;
SELECT 1 FROM dual; -- the procedure is called by another subprogram inside the same
-- WITH block, not directly from a SELECT expression
-- Error: FUNCTION does not allow OUT parameters
WITH
FUNCTION bad_func(val NUMBER, result OUT NUMBER) RETURN NUMBER AS
BEGIN RETURN val; END;
SELECT bad_func(5) FROM dual;
-- Expected output: ERROR: WITH FUNCTION "bad_func" cannot declare OUT or IN OUT parameters
4.6. Default Parameter Values
WITH
FUNCTION calc(n NUMBER DEFAULT 10) RETURN NUMBER AS
BEGIN RETURN n * 2; END;
SELECT calc() FROM dual;
-- Expected output: 20
4.7. Exception Handling
WITH
FUNCTION safe_div(a NUMBER, b NUMBER) RETURN NUMBER AS
BEGIN
RETURN a / b;
EXCEPTION
WHEN OTHERS THEN RETURN NULL;
END;
SELECT safe_div(1, 0) FROM dual;
-- Expected output: NULL
4.8. Integration with DML
-- Allowed: INSERT INTO ... WITH FUNCTION ... SELECT ... (Oracle-compatible form)
WITH
FUNCTION get_bonus(sal NUMBER) RETURN NUMBER AS
BEGIN RETURN sal * 1.2; END;
INSERT INTO emp_bonus (empno, bonus)
SELECT empno, get_bonus(sal) FROM emp WHERE deptno = 10;
Oracle does not allow WITH FUNCTION to precede UPDATE, DELETE, or MERGE.
IvorySQL enforces the same restriction; such usage raises ERRCODE_FEATURE_NOT_SUPPORTED.
|
5. Scope and Visibility
5.1. Scope Rules
-
The scope of an inline function/procedure is limited to the SQL statement in which it is defined (including subqueries within that statement).
-
Functions/procedures may reference each other regardless of declaration order (mutual recursion is supported).
-
An inline definition shadows any existing database function with the same name and signature (WITH definition takes precedence).
-
Defining two functions/procedures with the same name and parameter types in a single WITH clause is not allowed.
6. Relationship with Existing Features
| Existing Feature | Relationship |
|---|---|
PL/iSQL nested subprograms |
Directly reused: leverages the existing compile/execute infrastructure. |
Standard CTE ( |
Coexists: can be mixed in the same WITH clause. |
|
Coexists: WITH RECURSIVE and inline functions can appear together. |
Oracle Package |
Similar: package procedures/functions are also registered temporarily at session level. |
PL/iSQL |
Different: WITH inline functions are not persisted and are not written to the system catalog. |
7. Error Handling
7.1. Duplicate Definition
WITH
FUNCTION dup(n NUMBER) RETURN NUMBER AS BEGIN RETURN n; END;
FUNCTION dup(n NUMBER) RETURN NUMBER AS BEGIN RETURN n * 2; END;
SELECT dup(1) FROM dual;
-- Expected output: ERROR: WITH clause function "dup" is defined more than once with the same argument types
7.2. Usage in PG_PARSER Mode
-- Attempting to use WITH FUNCTION syntax in PG_PARSER mode
SET compatible_db = PG_PARSER;
WITH FUNCTION foo(n NUMBER) RETURN NUMBER AS BEGIN RETURN n; END;
SELECT foo(1);
-- Expected output: ERROR: syntax error at or near "FUNCTION"
7.3. Function Body Syntax Error
WITH
FUNCTION broken(n NUMBER) RETURN NUMBER AS
BEGIN
RETRUN n; -- typo
END;
SELECT broken(1) FROM dual;
-- Expected output: ERROR: syntax error at or near "RETRUN"
8. EXPLAIN Output
9. Restrictions and Constraints
-
Oracle parser mode only: This feature is active only when
compatible_db = ORA_PARSER. -
No system catalog writes: Inline functions/procedures are registered dynamically during statement execution and deregistered immediately after; nothing is written to
pg_proc. -
Transaction-safe: Registration and deregistration are fully transparent to transactions; no WAL is generated.
-
Concurrency-safe: Each concurrent session has its own independent inline function/procedure registration context.
-
Scope-limited: An inline function cannot be called outside the statement that defines it.
-
No polymorphic parameters: Declaring parameters with types such as
ANYELEMENTwill fail at call time. -
Not usable as a table function:
SELECT * FROM with_func(…)is rejected.