PL/SQLadvanced

Dynamic SQL and Ref Cursors in EBS Extensions

Explain when and how to safely use dynamic SQL and ref cursors in custom EBS reports and extensions

Dynamic SQL builds a statement at runtime. It is necessary when the shape of the query genuinely varies, and it is where injection vulnerabilities come from.

Building dynamic SQL safely is like a mail-merge letter template: you build the fixed wording once (the SQL skeleton) and drop distinct recipient details into placeholder slots (bind variables) rather than manually re-typing (concatenating) each recipient's name directly into a brand new letter every time — which would be both slower and more error/injection-prone.

Key Concepts

1
The common EBS case: optional report parameters.
    l_sql := 'SELECT * FROM xx_orders WHERE 1=1';
    IF p_customer IS NOT NULL THEN
        l_sql := l_sql || ' AND customer_id = :b_cust';
    END IF;
    IF p_from_date IS NOT NULL THEN
        l_sql := l_sql || ' AND order_date >= :b_from';
    END IF;
2
    OPEN l_cursor FOR l_sql USING p_customer, p_from_date;
3
Only the predicates the user supplied are added. A static query cannot do this without NVL tricks that defeat the indexes.
NVL
4
Bind variables, always — and here is what happens without them.
    -- vulnerable
    l_sql := 'SELECT * FROM xx_orders WHERE customer_id = ' || p_input;
5
    p_input = '1 OR 1=1'          returns every row
    p_input = '1; DROP TABLE ...' in some contexts, worse
6
    -- safe
    l_sql := 'SELECT * FROM xx_orders WHERE customer_id = :b_cust';
    OPEN l_cursor FOR l_sql USING p_input;
7
A bind variable is a value, never parsed as SQL. Concatenation makes the input part of the statement, which is the entire vulnerability.
8
Binds also help performance. Concatenated values produce a different statement text per execution, so every call is a hard parse and the shared pool fills with near-identical statements. One statement with binds is parsed once.
9
The one thing you cannot bind is an identifier — a table or column name. If those must vary, validate them against a known list rather than accepting input:
10
    IF p_column NOT IN ('ORDER_DATE','SHIP_DATE') THEN
        RAISE invalid_column;
    END IF;
11
Ref cursors return a result set without the caller knowing the query:
12
    TYPE ref_cur IS REF CURSOR;
13
    PROCEDURE get_orders(p_customer IN NUMBER, x_cursor OUT ref_cur) IS
    BEGIN
        OPEN x_cursor FOR SELECT ... WHERE customer_id = p_customer;
    END;
14
This is how BI Publisher data models and OAF pages take data from a package: the procedure opens the cursor, the consumer fetches from it.
15
Strong or weak. A strong ref cursor declares its return type, so the compiler checks the fetch. A weak one (SYS_REFCURSOR) accepts any query, which is what dynamic SQL needs and what gives up that checking. Use strong where the shape is fixed.
SYS_REFCURSOR
16
What the interviewer is probing.1. "When is dynamic SQL justified?" Probing: judgement. Stalls: "Whenever convenient." Moves up: when the structure genuinely varies — optional predicates, a table name from configuration — not merely to avoid writing branches.
17
2. "What is the injection risk?" Probing: the security point. Stalls: "None." Moves up: concatenating parameter values into the statement; bind variables with USING remove it and let the cursor be shared.
18
3. "Why do bind variables matter for performance here?" Probing: the depth. Stalls: "They do not." Moves up: literals produce a new hard parse per value, flooding the shared pool — binds let one plan be reused.
19
4. "What is a ref cursor used for?" Probing: the capability. Stalls: "A loop." Moves up: returning a result set to a caller, including BI Publisher data templates and Java or web service consumers.