Skip to content

Functions

PostgreSQL functions are reusable code blocks that can be called from SQL queries.

PostgreSQL Specification

CREATE FUNCTION Syntax

CREATE [ OR REPLACE ] FUNCTION
    name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } default_expr ] [, ...] ] )
    [ RETURNS rettype
      | RETURNS TABLE ( column_name column_type [, ...] ) ]
  { LANGUAGE lang_name
    | TRANSFORM { FOR TYPE type_name } [, ... ]
    | WINDOW
    | { IMMUTABLE | STABLE | VOLATILE }
    | [ NOT ] LEAKPROOF
    | { CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT }
    | { [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER }
    | PARALLEL { UNSAFE | RESTRICTED | SAFE }
    | COST execution_cost
    | ROWS result_rows
    | SUPPORT support_function
    | SET configuration_parameter { TO value | = value | FROM CURRENT }
    | AS 'definition'
    | AS 'obj_file', 'link_symbol'
    | sql_body
  } ...

Reference: PostgreSQL 17 CREATE FUNCTION

pgdelta Support

✅ Currently Supported (via pg_get_functiondef)

  • CREATE FUNCTION with complete definition
  • DROP FUNCTION
  • CREATE OR REPLACE FUNCTION
  • CREATE PROCEDURE
  • DROP PROCEDURE
  • All function languages (SQL, PL/pgSQL, Python, etc.)
  • All parameter modes (IN, OUT, INOUT, VARIADIC)
  • All return types including RETURNS TABLE
  • All function attributes (IMMUTABLE, STABLE, VOLATILE, etc.)
  • Security context (SECURITY DEFINER/INVOKER)
  • Cost and row estimates
  • Configuration parameter settings
  • All PostgreSQL function features
CREATE FUNCTION "public"."calculate_tax"(amount decimal, rate decimal)
RETURNS decimal
LANGUAGE sql
IMMUTABLE
AS $function$
    SELECT amount * rate;
$function$;

❌ Not Yet Supported

  • ALTER FUNCTION operations (planned)
  • ALTER PROCEDURE operations (planned)

Usage Examples

Basic SQL Function

target_sql = """
CREATE FUNCTION calculate_total(price decimal, tax_rate decimal)
RETURNS decimal
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT price * (1 + tax_rate);
$$;
"""

PL/pgSQL Function

target_sql = """
CREATE FUNCTION get_user_order_count(user_id integer)
RETURNS integer
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
    order_count integer;
BEGIN
    SELECT COUNT(*) INTO order_count
    FROM orders
    WHERE orders.user_id = $1;

    RETURN order_count;
END;
$$;
"""

Table-Returning Function

target_sql = """
CREATE FUNCTION get_recent_orders(days_back integer)
RETURNS TABLE(id integer, user_id integer, total decimal)
LANGUAGE sql
STABLE
AS $$
    SELECT id, user_id, total
    FROM orders
    WHERE created_at >= CURRENT_DATE - INTERVAL '1 day' * days_back;
$$;
"""

Implementation Details

Function Models

@dataclass(frozen=True)
class CreateFunction:
    stable_id: str          # Format: "f:schema.function_name"
    namespace: str          # Schema name
    proname: str           # Function name
    function_definition: str # Complete function definition

@dataclass(frozen=True)
class ReplaceFunction:
    stable_id: str          # Format: "f:schema.function_name"
    namespace: str          # Schema name
    proname: str           # Function name
    function_definition: str # New function definition

SQL Generation

def generate_create_function_sql(change: CreateFunction) -> str:
    """Generate CREATE FUNCTION SQL."""
    # Function definition contains the complete CREATE FUNCTION statement
    return change.function_definition + ";"

def generate_replace_function_sql(change: ReplaceFunction) -> str:
    """Generate CREATE OR REPLACE FUNCTION SQL."""
    # Replace CREATE with CREATE OR REPLACE
    definition = change.function_definition
    if definition.startswith("CREATE FUNCTION"):
        definition = definition.replace("CREATE FUNCTION", "CREATE OR REPLACE FUNCTION", 1)
    return definition + ";"

Function Types

SQL Functions

-- Simple calculation
CREATE FUNCTION add_numbers(a integer, b integer)
RETURNS integer
LANGUAGE sql
IMMUTABLE
AS $$
    SELECT a + b;
$$;

PL/pgSQL Functions

-- Complex logic with control structures
CREATE FUNCTION fibonacci(n integer)
RETURNS integer
LANGUAGE plpgsql
IMMUTABLE
AS $$
DECLARE
    a integer := 0;
    b integer := 1;
    temp integer;
BEGIN
    FOR i IN 1..n LOOP
        temp := a + b;
        a := b;
        b := temp;
    END LOOP;
    RETURN a;
END;
$$;

Trigger Functions

-- Function for trigger use
CREATE FUNCTION update_modified_time()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
    NEW.modified_at = NOW();
    RETURN NEW;
END;
$$;

Future Enhancements

Planned Features (v0.2.0)

  • Enhanced function overloading support
  • Better parameter type resolution
  • Function security context tracking

Best Practices

Function Naming

# Good function names
good_names = [
    "calculate_tax",          # Verb + noun
    "get_user_orders",        # Clear action
    "validate_email",         # Descriptive purpose
    "format_currency",        # Transformation function
]

# Function categories
categories = {
    "calculations": "calculate_*, compute_*",
    "queries": "get_*, find_*, search_*",
    "validations": "validate_*, check_*, verify_*",
    "transformations": "format_*, convert_*, transform_*"
}

Performance Considerations

# Function volatility
volatility_levels = {
    "IMMUTABLE": "Same input always produces same output",
    "STABLE": "Output doesn't change within single statement",
    "VOLATILE": "Output can change between calls (default)"
}

# Performance impact
performance_tips = {
    "mark_immutable": "Allows aggressive optimization",
    "use_stable": "For functions reading database state",
    "avoid_volatile": "Only when function has side effects"
}