postgres - how to test if peer of foreign table actually exists - postgresql

I have two databases: let's call them primary (which holds actual data) and fdw (which contains foreign-data-wrapper of data in primary db).
I create simple table in primary db:
create schema myschema;
create table myschema.foo (id bigint, whatever text);
create table myschema.foov as select * from foo;
I create foreign table in fdw db accessing primary table through view:
create extension postgres_fdw;
create server remote_docker foreign data wrapper postgres_fdw options (host 'primary', dbname 'postgres', port '5432');
create schema remote_myschema;
create user mapping for current_user server remote_docker options (user 'postgres');
create foreign table remote_myschema.foo (id bigint, whatever text) server remote_docker options (schema_name 'myschema', table_name 'foov');
When executing select * from remote_myschema.foo query, everything works ok.
The problem: if I didn't create view in primary db, the create foreign table command in fdw db passes without error anyway. I am able to discover the nonexistency of view in primary db only at time of query execution on fdw db.
The question: is somehow possible to detect that foreign table is bound to nonexistent original? I compared pg_class data of foreign table in both cases and didn't find any difference nor anything in documentation. The only way I know at this moment is catching exception
do $$
declare
ex boolean;
begin
begin
execute 'select null from remote_myschema.foo';
ex := true;
exception when others then
ex := false;
end;
raise notice '%', ex::text;
end;
$$;
which is awful.
Thanks!

Catching the exception is the only way. Unless views are in the habit of suddenly disappearing at your site, you don't have to test it every time you use the foreign table. Testing once, right after you created it, is good enough.

Related

Postgresql: trigger on foreign table to execute function to truncate/insert into local table

I would like to create trigger to execute function to truncate local database table and insert new data.
Trigger execution must start after new row have insert in foreign database table.
I have read a lot about creating triggers on foreign table, but for me its not working. Trigger seems to not execute function when new row will be inserted in foreign table. It seems like trigger cant see this new row insert event.
What I did:
Created foreign table in my local database, lets call it 'foreign_table'. I tested, I can read data.
Created function to truncate local table and insert new data:
CREATE or replace FUNCTION public.reset_insert_table()
RETURNS TRIGGER
LANGUAGE 'plpgsql'
SET search_path=public
AS $BODY$
BEGIN
create temporary table temporary_table_tmp
as select * from public.table1;
TRUNCATE TABLE public.table2;
insert into table2
select * from temporary_table_tmp;
DROP table temporary_table_tmp;
END;
$BODY$;
Created trigger to launch function 'reset_insert_table()'
CREATE TRIGGER local_table_update
AFTER INSERT
ON 'foreign_table'
FOR EACH ROW EXECUTE PROCEDURE reset_insert_table();
Made test: inserted new row in foreign database table 'foreign_table', but I cant see that table is truncated and new data is not inserted. Insertion to foreign_tale was done in foreign database.
Problem was also testing does this trigger function work, executing manually will produce error:
EXECUTE PROCEDURE reset_insert_table();
ERROR: syntax error at or near "execute"
Tried also CALL and SELECT.
I created same function for testing but instead defining 'RETURNS TRIGGER'used 'RETURNS VOID' and function is working.
Can anyone tell why my solution is not working and does trigger on foreign tables must see events happening in foreign tables?
According to your comments, you seem to be using logical replication.
While data modifications are replayed on the standby with logical replication, the parameter session_replication_role is set to replica to keep triggers and foreign key constraints from working.
If you want a trigger to be triggered by the replay of data via logical replication, you have to declare it as a replica trigger:
ALTER TABLE a2 ENABLE REPLICA TRIGGER trigger_name;

Postgres: Trigger on FOREIGN TABLE

I would like to use postgres_fdw and house a FOREIGN TABLE in my database. Is it possible to define a trigger on the local server for this FOREIGN TABLE that recognizes an INSERT event on the remote server. If so, please provide an example.
Data Flow:
Insert data into table on remote server.
Recognize insert on local server's foreign table which fires a trigger.
Trigger function writes data into some other table.
Upon write success, post back to the foreign table
Idea as a crude diagram:
No error is reported but the write to table_b seems unsuccessful.
Here is what I've tried:
CREATE FOREIGN TABLE x.table_a -- note the foreign table is in a different schema than the local table
( id BIGINT NOT NULL
, data_ts TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
, xchg_ts TIMESTAMPTZ DEFAULT NULL
)
SERVER remote_server
OPTIONS (schema_name 'schema_a', table_name 'table_a')
;
CREATE TABLE y.table_b
( xchg_id BIGINT
, error_msg TEXT DEFAULT NULL
);
CREATE OR REPLACE FUNCTION func_foreign_table_a_after_insert()
RETURNS TRIGGER
AS $$
BEGIN
INSERT INTO y.table_b
(xchg_id)
VALUES
(NEW.id)
;
RETURN NEW;
END;
$$ LANGUAGE PLPGSQL
;
CREATE TRIGGER trig_foreign_table_a_after_insert
AFTER INSERT ON x.table_a
FOR EACH ROW EXECUTE PROCEDURE func_foreign_table_a_after_insert();

Trigger execution for foreign table in postgresql

I have two databases db1 and db2.db2 has foreign table, say tableA whose schema has been imported from db1. A trigger runs on tableA in db1 for every row update.
Now using postgres_fdw I can fetch the records from db2 but unable to update any record on tableA due to that trigger function.Update works fine in case I disable the trigger.I need that trigger for audit log.
Please suggest me a suitable suggestion to resolve the issue.I am using postgres 9.6.
Make sure the user establishing the link has access to the audit tables.
You could also add the required schema to the trigger function search path:
CREATE OR REPLACE FUNCTION abc.mytrigger() RETURNS trigger AS
$BODY$BEGIN
[...] -- do something in the xyz schema
RETURN NEW;
END;$BODY$
LANGUAGE plpgsql
SET search_path = xyz;

Foreign table inserts don't use the remote sequence

I have a set of applications accessing two different PostgreSQL 9.6 DBs on the same server. Due to some application limitations, one application accesses a handful of tables via FDW in one DB to the other.
Something like this:
DB1.fdw_table_a -> DB2.table_a
fdw_table_a is only used for inserts of log data. This table has an id column, which is a bigint sequence. The sequence exists in DB1 (on the foreign table) and in DB2 (the "real" table). This works as it should and all is well.
Now there's a need to have another application (again with limited access capabilities) perform inserts into the "real" table, DB2.table_a. In testing, I can see some inconsistencies in the id column, but no obvious issues have appeared.
I can see in the customer-facing environments that the DB1 FDW sequence is used as expected, but when inserts start directly on the DB2 'real' table, that sequence will start at 1 (as it has never been used).
Are there other things we should be considering in this environment?
Are there some issues that could arise from overlap in these two sequences inserting into the table?
The sequence only gets used if you omit the id column in the INSERT statement. But postgres_fdw will never omit a column, as you can see from the execution plan.
One way to solve the problem is to use a foreign table that does not contain the id column. Then any insert into that foreign table will use the sequence to populate that column.
The following plan from 2014 is still valid today.
=# CREATE SEQUENCE seq;
CREATE SEQUENCE
=# CREATE VIEW seq_view AS SELECT nextval('seq') as a;
CREATE VIEW
=# CREATE EXTENSION postgres_fdw;
CREATE EXTENSION
=# CREATE SERVER postgres_server
-# FOREIGN DATA WRAPPER postgres_fdw
-# OPTIONS (host 'localhost', port '5433', dbname 'postgres');
CREATE SERVER
=# CREATE USER MAPPING FOR PUBLIC SERVER postgres_server OPTIONS (password '');
CREATE USER MAPPING
=# CREATE FOREIGN TABLE foreign_seq_table (a bigint)
-# SERVER postgres_server OPTIONS (table_name 'seq_view');
CREATE FOREIGN TABLE
=# CREATE FUNCTION foreign_seq_nextval() RETURNS bigint AS
-# 'SELECT a FROM foreign_seq_table;' LANGUAGE SQL;
CREATE FUNCTION
=# CREATE TABLE tab (a int DEFAULT foreign_seq_nextval());
CREATE TABLE
=# INSERT INTO tab VALUES (DEFAULT), (DEFAULT), (DEFAULT);
INSERT 0 3
=# SELECT * FROM tab;
a
----
9
10
11
(3 rows)
https://paquier.xyz/postgresql-2/global-sequences-with-postgres_fdw-and-postgres-core/

Query to foreign table impossible due to index on custom stored function value in host database

I have a following situation.
In database A on server I, let's call it Host DB, there is a table, that has a following sample create script:
CREATE TABLE public.some_table (
id SERIAL PRIMARY KEY,
some_field TEXT
);
CREATE INDEX public.some_field_index
ON public.some_table USING btree
(my_custom_function(some_field));
As you can see, the index is created on a result of some custom, stored in database A, function my_custom_function.
Now I want to declare some_table as foreign table on other server, in database B. After creating the server, user mappings etc. I declare foreign table as:
CREATE FOREIGN TABLE public.some_table (
id SERIAL PRIMARY KEY,
some_field TEXT
)
SERVER host_server
OPTIONS (
schema_name 'public',
table_name 'some_table'
);
The table is created nicely, however I cannot query it. Instead I am getting following error:
ERROR: function my_custom_function(text) does not exist.
No function matches the given name and argument type.
You might need to add explcit type casts.
CONTEXT: Remote SQL command: SELECT id, some_field FROM public.some_table
SQL fuction my_custom_function during inlining.
I believe the problem is related to function my_custom_function not being declared on the server B, in the "guest" database. For some reasons i don't want to create this function. Is there any solution to overcome this problem?
Thanks for all your answers in advance.