триггер postgresql с dblink ничего не возвращает

Я создал триггер для репликации вставок из таблицы mytable_db1 в базе данных 1 в ту же таблицу mytable_db2 в базе данных 2. Обе базы данных находятся на одном сервере.

CREATE OR REPLACE FUNCTION trigger_osm_test_insert()
RETURNS trigger AS
$BODY$
BEGIN
  PERFORM dblink_connect('db2', 'dbname=xxx port=5432 user=myusr password=xxx');
  PERFORM dblink_exec('db2', 
  'insert into test.mytable_db2 (osm_id, name, name_eng, name_int, type, z_order, population, last_update, country, iso3, shape)
  values ('||new.osm_id||', '''||new.name||''', '''||new.name_eng||''', '''||new.name_int||''', '''||new.type||''', '||new.z_order||',
  '||new.population||', '''||new.last_update||''', '''||new.country||''', '''||new.iso3||''',
  st_geometry((st_AsText('''||new.shape::text||'''))))');
  PERFORM dblink_disconnect('db2');
  RETURN new;
END;
$BODY$
 LANGUAGE plpgsql VOLATILE
 COST 100;
ALTER FUNCTION trigger_osm_test_insert()
 OWNER TO myusr;

CREATE TRIGGER osm_insert_test
 AFTER INSERT
 ON mytable_db1
 FOR EACH ROW
 EXECUTE PROCEDURE trigger_osm_test_insert();

Однако когда я делаю тестовую вставку, например:

insert into test.mytable_db1 (name, shape) values ('test', '0101000020E6100000E0979950035F4A40404B2751B0861CC0');

Вставленная строка вставляется в mytable_db1, но кажется, что триггер не работает, так как у меня ничего нет в mytable_db2. Вставка не выдает мне сообщения об ошибке от триггера.

Я использую postgresql 9.2.2. В обеих базах данных установлен dblink 1.0, а также postgis 2.0.6.

Есть ли у кого-нибудь предложения по поводу того, что я делаю не так?

Спасибо!


person Thierry    schedule 09.02.2016    source источник


Ответы (2)


Проблема в том, что вы не передаете все столбцы при выполнении INSERT. Из-за этого некоторые столбцы в NEW имеют значение NULL, а поскольку вы используете конкатенацию строк, вся ваша строка INSERT INTO... имеет значение NULL.

Например, этот запрос возвращает NULL:

select NULL || 'some text';

Вы должны проверить каждый столбец на отсутствие NULL, например так:

CREATE OR REPLACE FUNCTION trigger_osm_test_insert()
RETURNS trigger AS
$BODY$
DECLARE insert_statement TEXT;
BEGIN

  IF NOT ARRAY['db2'] <@ dblink_get_connections() OR dblink_get_connections() IS NULL THEN
    PERFORM dblink_connect('db2', 'dbname=xxx port=5432 user=xxx password=xxx');
  END IF;

  insert_statement =  format('insert into mytable_db2 (
      osm_id, name, name_eng, name_int, type, z_order, 
      population, last_update, country, iso3, shape
      )
      values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
  coalesce(new.osm_id::text,'NULL'), 
  coalesce(quote_literal(new.name), 'NULL'), 
  coalesce(quote_literal(new.name_eng), 'NULL'), 
  coalesce(new.name_int::text, 'NULL'), 
  coalesce(quote_literal(new.type),'NULL'), 
  coalesce(new.z_order::text,'NULL'),
  coalesce(new.population::text,'NULL'), 
  coalesce(quote_literal(new.last_update::text),'NULL'), 
  coalesce(quote_literal(new.country),'NULL'), 
  coalesce(quote_literal(new.iso3::text), 'NULL'),
  coalesce(quote_literal(new.shape::text),'NULL')
  );
  PERFORM dblink_exec('db2', insert_statement);
  PERFORM dblink_disconnect('db2');
  RETURN new;
END;
$BODY$
 LANGUAGE plpgsql VOLATILE;

Я не уверен в типах столбцов, поэтому проверьте это. Вставка значений, которые могут быть NULL, немного сложнее ... Используйте это:

  coalesce(new.osm_id::text,'NULL'), 

Для вставки целочисленных значений и этого:

  coalesce(quote_literal(new.name), 'NULL'), 

для вставки значений текста, метки времени и геометрии. В операторе вставки всегда используйте %s.

В дополнение к этому я внес следующие изменения в функцию:

  • Проверить, существует ли уже соединение
  • Функция st_geometry((st_AsText()) не нужна, поскольку вы уже вставляете геометрию в формате WKB.
  • Я использую функцию format() вместо оператора конкатенации ||
person Tom-db    schedule 10.02.2016
comment
Большое спасибо, Томмазо! Теперь работает !! Единственное, что мне пришлось повторно ввести функцию st_geometry (st_AsText ()) для геометрии. Это выдавало мне сообщение об ошибке из-за разного типа геометрии между двумя таблицами. Таблица db1 находится в геометрии PostGIS, а таблица db2 - в геометрии ArcSDE. - person Thierry; 10.02.2016

Есть лучшее решение для этого

Вот еще пример

-- Function: flux_tresorerie_historique_backup_row()

-- DROP FUNCTION flux_tresorerie_historique_backup_row();

CREATE OR REPLACE FUNCTION flux_tresorerie_historique_backup_row()
  RETURNS trigger AS
$BODY$
BEGIN
  perform dblink_connect('dbname=gtr_bd_archive user=postgres password=postgres');
  perform dblink_exec('insert into flux_tresorerie_historique values('||
    concat_ws(', ', quote_nullable(OLD.id_flux_historique),
                    quote_nullable(OLD.date_operation_flux),
                    quote_nullable(OLD.date_valeur_flux),
                    quote_nullable(OLD.date_rapprochement_flux),
                    quote_nullable(OLD.libelle_flux),
                    quote_nullable(OLD.montant_flux),
                    quote_nullable(OLD.contre_valeur_dzd),
                    quote_nullable(OLD.rib_compte_bancaire),
                    quote_nullable(OLD.frais_flux),
                    quote_nullable(OLD.sens_flux),
                    quote_nullable(OLD.statut_flux),
                    quote_nullable(OLD.code_devise),
                    quote_nullable(OLD.code_mode_paiement),
                    quote_nullable(OLD.code_agence),
                    quote_nullable(OLD.code_compte),
                    quote_nullable(OLD.code_banque),
                    quote_nullable(OLD.date_maj_flux),
                    quote_nullable(OLD.statut_frais),
                    quote_nullable(OLD.reference_flux),
                    quote_nullable(OLD.code_commission),
                    quote_nullable(OLD.id_flux)
           )||');');
  perform dblink_disconnect();
  RETURN NULL;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION flux_tresorerie_historique_backup_row()
  OWNER TO postgres;

если ваши таблицы идентичны, вы можете использовать такой "формат"

CREATE OR REPLACE FUNCTION flux_tresorerie_historique_backup_row()
  RETURNS trigger AS
$func$
BEGIN
   PERFORM dblink_connect('myserver');  -- name of foreign server

   PERFORM dblink_exec( format(
   $$
   INSERT INTO flux_tresorerie_historique
   SELECT (%L::flux_tresorerie_historique).*
   $$
   , OLD::text));

   PERFORM dblink_disconnect();
   RETURN NULL;  -- only for AFTER trigger
END
$func$  LANGUAGE plpgsql;
person hacene abdessamed    schedule 04.05.2016
comment
Это лучшее и простое решение. Мы можем использовать его в простой конкатенации, как в вопросе, как ... '|| quote_nullable (new.name_eng) || '... - person Vinner; 16.04.2020