POSTGRESQL - Insert into foreign table triggered insertion into another remote table - postgresql

I have a problem to update records into my foreign table.
It refers to a remote table which have a trigger to insert into another remote table (account_journals table).
I recreated same trigger and adding another foreign table which refers to the account_journals table, but i still have same error.
How i created my foreigns tables :
CREATE FOREIGN TABLE accounts (
...
)
SERVER fdw_server
OPTIONS (schema_name 'public', table_name 'remote_accounts');
CREATE FOREIGN TABLE
CREATE FOREIGN TABLE account_journals (
...
)
SERVER fdw_server
OPTIONS (schema_name 'public', table_name 'remote_account_journals');
CREATE FOREIGN TABLE
The trigger :
CREATE TRIGGER tg_account_journals AFTER INSERT OR DELETE OR UPDATE ON accounts FOR EACH row EXECUTE FUNCTION fill_account_journals();
fill_account_journals() function just insert record into account_journals.
When update record into accounts table :
new_database=# UPDATE accounts SET name = 'valou' WHERE id = 1;
ERROR: relation "remote_account_journals" does not exist
CONTEXT: PL/pgSQL function public.fill_remote_account_journals() line 8 at SQL statement
remote SQL command: UPDATE public.remote_accounts SET name = $2 WHERE ctid = $1 RETURNING id, name, ...
Any help is appreciated to solve my problem, thanks

Related

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

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.

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();

Can we use a select query in alter table command in db2 database

I have a foreign key constraint on my table created using the following command in db2
ALTER TABLE "ADDRESS" ADD FOREIGN KEY("CITY_ID") REFERENCES CITY("ID");
Now I am trying to drop the constraint. Since there was no name given to the constraint while creating, the alter command to drop the foreign key does not work.
Can I use a select command inside the alter table command so that I can query the SYSCAT.TABCONST table to get the constraint id?
Something like
ALTER TABLE ADDRESS DROP FOREIGN KEY
(SELECT CONSTNAME FROM SYSCAT.TABCONST where tabname='ADDRESS' and TYPE='F')
--#SET TERMINATOR #
BEGIN
EXECUTE IMMEDIATE (SELECT 'ALTER TABLE '||TABSCHEMA||'.'||TABNAME||' DROP CONSTRAINT '||CONSTNAME FROM SYSCAT.REFERENCES WHERE TABSCHEMA=USER AND TABNAME='ADDRESS' AND REFTABSCHEMA=USER AND REFTABNAME='CITY');
END#
Note that there may be multiple ADDRESS -> CITY references. But this will work if there is only one such a foreign key between these tables.
We assume here that both tables are in the current user's schema.
You can use compound SQL for this. Here is an example:
--#SET TERMINATOR #
begin
declare v_fkname varchar(128) default '';
declare v_sql varchar(1024);
declare v_not_found integer default 0;
declare not_found condition for sqlstate '02000';
declare continue handler for not_found set v_not_found=1 ;
set v_fkname = (select constname from syscat.tabconst where tabname='ACTORS' and tabschema='USER1' and type='F');
if v_not_found = 0
then
set v_sql='ALTER TABLE actors DROP FOREIGN KEY '||v_fkname ;
execute immediate(v_sql);
end if;
end#
Remember that you will also need to verify afterwards that no objects have become invalid as a result of this change.

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.

How to change constraint definition in PostgreSQL?

I created a table in PostgreSQL like this:
CREATE TABLE Table1 (
Id varchar(100) PRIMARY KEY CHECK (Id ~ '^[a-z0-9]{3,15}$'),
...
);
This will automatically create a constraint called table1_id_check.
Now I would like to change the check constraint to
(Id ~ '^[a-z0-9]{3,}$')
How can I do this in PostgreSQL as a single statement without dropping the constraint and recreating it again?
Using multiple statements within a transaction works on all SQL dbms that support using this DDL in a transaction.
begin transaction;
alter table table1
drop constraint table1_id_check;
alter table table1
add constraint table1_id_check CHECK (Id ~ '^[a-z0-9]{3,}$');
commit;
PostgreSQL lets you use multiple clauses within an ALTER TABLE statement.
alter table table1
drop constraint table1_id_check,
add constraint table1_id_check CHECK (Id ~ '^[a-z0-9]{3,}$');