Named or anonymous block with optional declarations and exception handlers.
[<<label>>]
[DECLARE
<declarations>]
BEGIN
<statements>
[EXCEPTION
WHEN <condition> THEN <statements>]
END [<label>];
## Overview The BEGIN...END block is the fundamental structural unit of PL/pgSQL code in DeltaForge. Every anonymous block (DO), stored procedure, and function body is a block. Blocks can be nested to create inner scopes with their own variable declarations and exception handlers. ## Behavior - On entry, a new variable scope is pushed onto the scope stack. All DECLARE variables are initialized in declaration order, with default expressions evaluated at that point. - Statements in the body execute sequentially. If any statement produces a control-flow signal (EXIT, CONTINUE, RETURN), the signal propagates up through enclosing blocks. - If an EXCEPTION section is present, the block body runs inside an implicit try/catch. On error, each WHEN clause is tested in order; the first matching handler executes. After handler completion, control continues after the END of the block. - When the block exits (normally or via exception handler), the variable scope is popped. Variables declared inside the block are no longer accessible. - Labels (<<label>>) are pushed onto a label stack on entry and popped on exit. EXIT and CONTINUE statements can target labeled blocks. ## Differences from PostgreSQL - In PostgreSQL, an EXCEPTION block creates a subtransaction (savepoint). DeltaForge does not create subtransactions; exception handlers catch errors but cannot roll back partial DML changes made before the error. - `%TYPE` and `%ROWTYPE` references in declarations are supported (e.g., `v_name table.column%TYPE`). - Nested blocks in DeltaForge share the same DataFusion session context, so temporary tables created in an inner block remain visible in outer blocks.
DO $$
DECLARE
x INT := 42;
BEGIN
PRINT x;
END;
$$;
DO $$
DECLARE
row_count INT;
BEGIN
SELECT count(*) INTO row_count FROM orders;
IF row_count = 0 THEN
RAISE EXCEPTION 'No orders found';
END IF;
PRINT 'Orders:', row_count;
EXCEPTION
WHEN OTHERS THEN
PRINT 'Error: ' || SQLERRM;
END;
$$;
DO $$
<<outer>>
DECLARE
total INT := 0;
BEGIN
<<inner>>
DECLARE
subtotal INT := 100;
BEGIN
total := total + subtotal;
END inner;
PRINT 'Total:', total;
END outer;
$$;
DO $$
DECLARE
v_msg TEXT := 'Pipeline started';
BEGIN
RAISE NOTICE '%', v_msg;
INSERT INTO pipeline_log (message) VALUES (v_msg);
RAISE NOTICE 'Pipeline complete';
EXCEPTION
WHEN unique_violation THEN
RAISE NOTICE 'Duplicate log entry, skipping';
WHEN OTHERS THEN
RAISE WARNING 'Unexpected error: %', SQLERRM;
END;
$$;