Search code examples
postgresqlplpgsql

How to simplify running a function if it exists, falling back on other code?


I've got some code like this, using an extension :

DO $$
    BEGIN
        -- If table is versioned, use table_version API to add column
        IF EXISTS (
            SELECT 1
            FROM pg_catalog.pg_proc
            JOIN pg_catalog.pg_namespace ON pg_namespace.oid = pg_proc.pronamespace
            WHERE pg_proc.proname = 'ver_is_table_versioned'
            AND pg_namespace.nspname = 'table_version'
        ) THEN
            IF table_version.ver_is_table_versioned('my_schema', 'my_table') THEN
                PERFORM table_version.ver_versioned_table_add_column(
                    'my_schema',
                    'my_table',
                    'my_column',
                    'CHAR(1)'
                );
                RETURN;
            ELSE
                ALTER TABLE my_schema.my_table ADD COLUMN my_column CHAR(1);
            END IF;
        END IF;
        -- Otherwise ALTER TABLE directly
        ALTER TABLE my_schema.my_table ADD COLUMN my_column CHAR(1);
    END;
$$

How can I avoid duplicating the ALTER TABLE statement? I can't use IF EXISTS(…) AND table_version.ver_is_table_versioned(…), because that is eagerly evaluated, and results in a

schema "table_version" does not exist

error if the extension is not installed.

Postgres execute function if exists does not answer my question.

Throwing a custom exception when table_version.ver_is_table_versioned(…) is false and catching both that and undefined_function to run the fallback code is a possibility, but pretty horrible in terms of complexity.


Solution

  • Drop the ALTER TABLE inside of the condition block and let the final statement execute unconditionally unless the versioning exists.

    DO $$
        BEGIN
            -- If table is versioned, use table_version API to add column
            IF EXISTS (
                SELECT 1
                FROM pg_catalog.pg_proc
                JOIN pg_catalog.pg_namespace ON pg_namespace.oid = pg_proc.pronamespace
                WHERE pg_proc.proname = 'ver_is_table_versioned'
                AND pg_namespace.nspname = 'table_version'
            ) THEN
                IF table_version.ver_is_table_versioned('my_schema', 'my_table') THEN
                    PERFORM table_version.ver_versioned_table_add_column(
                        'my_schema',
                        'my_table',
                        'my_column',
                        'CHAR(1)'
                    );
                    RETURN;
                END IF;
            END IF;
            -- Otherwise ALTER TABLE directly
            ALTER TABLE my_schema.my_table ADD COLUMN my_column CHAR(1);
        END;
    $$