BEGIN...END Block

Named or anonymous block with optional declarations and exception handlers.

Category: control-flow

Syntax

[<<label>>]
[DECLARE
  <declarations>]
BEGIN
  <statements>
[EXCEPTION
  WHEN <condition> THEN <statements>]
END [<label>];

Description

## 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.

Examples

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;
$$;

Pitfalls

See Also

Open in interactive docs →   DeltaForge home →