skip to Main Content

I have a view as a union of several external tables (postgres_fdw). When selected in this view, some of the external servers may be unavailable (there is no network connection), and the select raise an error. Is it possible to ignore such errors and return only data from available tables?

2

Answers


  1. There isn’t a mechanism that would allow a view to ignore missing foreign tables using pure SQL. Conceptually, foreign tables that are inaccessible due to network connection issues aren’t any different than tables that can’t be accessed because the storage they are on is unavailable. Silently ignoring inaccessible tables seems illogical. How would one know that data is missing? A lack of data because tables are empty isn’t the same as a lack of data due to tables being unavailable.

    If you insist on silently ignoring unavailable tables, then you could create table functions that return the results of querying the foreign tables or empty result sets when exceptions occur. Your view could then use the results of these functions instead of directly querying the foreign tables. Just because one can do this, doesn’t mean that one should.

    Login or Signup to reply.
  2. You could try a function or stored procedure with error handling, along these lines:

    CREATE OR REPLACE FUNCTION union_foreign_tables(ft_info text[][])
    RETURNS SETOF record
    AS $$
    DECLARE
        ft_row text[];
        ft_name text;
        ft_col1 text;
        ft_col2 text;
        ft_col3 text;
        ft_connected boolean;
        first_table boolean := true;
        query text := '';
        i integer;
    BEGIN
        FOR i IN 1..array_length(ft_info, 1)
        LOOP
            ft_row := ft_info[i];
            ft_name := ft_row[1];
            ft_col1 := ft_row[2];
            ft_col2 := ft_row[3];
            ft_col3 := ft_row[4];
    
            ft_connected := true;
            BEGIN
                EXECUTE format('SELECT 1 FROM %I LIMIT 1', ft_name);
            EXCEPTION WHEN others THEN
                ft_connected := false;
            END;
    
            IF ft_connected THEN
                IF first_table THEN
                    query := format('SELECT %I AS col1, %I AS col2, %I AS col3 FROM %I', ft_col1, ft_col2, ft_col3, ft_name);
                    first_table := false;
                ELSE
                    query := query || format(' UNION SELECT %I AS col1, %I AS col2, %I AS col3 FROM %I', ft_col1, ft_col2, ft_col3, ft_name);
                END IF;
            END IF;
        END LOOP;
    
        IF query <> '' THEN
            RETURN QUERY EXECUTE query;
        END IF;
    END;
    $$ LANGUAGE plpgsql;
    

    Note as I have no idea what your current view query looks like, I have allowed for both multiple tables and different column names sequences per table which are passed into that function as an array of arrays:

    SELECT union_foreign_tables(ARRAY[
        ARRAY['foreign_table1', 'col1', 'col2', 'col3'],
        ARRAY['foreign_table2', 'col1a', 'col2a', 'col3a'],
        ARRAY['foreign_table3', 'col1b', 'col2b', 'col3b']
    ]);
    

    see unit test: https://dbfiddle.uk/yBRur0Jr (Postgres 15)

    nb: Using a procedure may suit better if data volumes are very large.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search