Использование pg_notify в триггерной функции PostgreSQL - PullRequest
17 голосов
/ 24 марта 2011

Я пытаюсь выдать уведомление от триггерной функции PostgreSQL. Я могу успешно использовать команду NOTIFY, но мне не повезло с pg_notify. Несмотря на то, что я получаю уведомление при вызове функции pg_notify из консоли psql, я никогда не получаю уведомления при ее вызове из моей функции триггера.

Эта версия моей функции триггера работает должным образом. У меня есть программа на Java, которая СЛУШАЕТ «mymessage», и получает уведомление с полезной нагрузкой «fired by NOTIFY».

-- Function: conversation_notify()

-- DROP FUNCTION conversation_notify();

CREATE OR REPLACE FUNCTION conversation_notify()
  RETURNS trigger AS
$BODY$
    BEGIN
        --SELECT pg_notify('mymessage', 'fired by FUNCTION');
        NOTIFY mymessage, 'fired by NOTIFY';
        RETURN NULL;
    END; 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION conversation_notify() OWNER TO postgres;

Эта версия моей триггерной функции НЕ работает должным образом. Единственными изменениями являются раскомментирование строки pg_notify и комментирование строки NOTIFY ниже. (Я не изменял приложение Java, которое LISTENing.) Я ожидаю, что мое приложение LISTENing to 'mymessage' должно получить уведомление с полезной нагрузкой 'fired by FUNCTION'. Реальное поведение заключается в том, что ничего не получено, даже через 30 секунд после изменения соответствующей таблицы.

-- Function: conversation_notify()

-- DROP FUNCTION conversation_notify();

CREATE OR REPLACE FUNCTION conversation_notify()
  RETURNS trigger AS
$BODY$
    BEGIN
        SELECT pg_notify('mymessage', 'fired by FUNCTION');
        --NOTIFY mymessage, 'fired by NOTIFY';
        RETURN NULL;
    END; 
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION conversation_notify() OWNER TO postgres;

Однако я действительно растерялся, потому что та же команда pg_notify работает, как и ожидалось, из консоли psql! Когда я выполняю следующую команду, мое Java-приложение получает уведомление с полезная нагрузка 'fired by CONSOLE':

select pg_notify('mymessage', 'fired by CONSOLE');

Для полноты вот мое определение триггера:

-- Trigger: conversation_notify on ofconversation

-- DROP TRIGGER conversation_notify ON ofconversation;

CREATE TRIGGER conversation_notify
  AFTER INSERT OR UPDATE
  ON ofconversation
  FOR EACH ROW
  EXECUTE PROCEDURE conversation_notify();

Я пытаюсь использовать pg_notify, потому что мне нужна динамическая полезная нагрузка. Прямо сейчас, это спорный вопрос. :) Руководство Postgres 9.0 указывает, что это должно быть возможно. NOTIFY docs для состояния параметра «полезная нагрузка»:

(Если необходимо передать двоичные данные или большие объемы информации, лучше всего поместить их в таблицу базы данных и отправить ключ записи.)

Я также ссылался на связанный вопрос переполнения стека и думаю, что я избежал этой проблемы: СЛУШАТЬ / УВЕДОМИТЬ, используя pg_notify (text, text) в PostgreSQL .

Версия базы данных:

PostgreSQL 9.0.3, скомпилированный Visual C ++ build 1500, 32-bit

Моя ОС - Windows XP Professional, версия 2002, SP3.

Заранее спасибо.

РЕДАКТИРОВАТЬ: Добавлен мой код слушателя Java ниже. Он основан на этом примере из документации PostgreSQL: http://jdbc.postgresql.org/documentation/81/listennotify.html.

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.postgresql.PGConnection;
import org.postgresql.PGNotification;

public class ConversationListener extends Thread
{   
    private Connection conn;
    private PGConnection pgConn;

    public ConversationListener(Connection conn) throws SQLException
    {
        this.conn = conn;
        this.pgConn = (PGConnection) conn;
        Statement listenStatement = conn.createStatement();
        listenStatement.execute("LISTEN mymessage");
        listenStatement.close();
    }

    @Override
    public void run()
    {
        while (true)
        {
            try
            {
                // issue a dummy query to contact the backend
                // and receive any pending notifications.
                Statement selectStatement = conn.createStatement();
                ResultSet rs = selectStatement.executeQuery("SELECT 1");
                rs.close();
                selectStatement.close();

                PGNotification notifications[] = pgConn.getNotifications();

                if (notifications != null)
                {
                    for (PGNotification pgNotification : notifications)
                    {
                        System.out.println("Got notification: " + pgNotification.getName() +
                            " with payload: " + pgNotification.getParameter());
                    }
                }

                // wait a while before checking again
                Thread.sleep(500);
            }
            catch (SQLException sqlException)
            {
                sqlException.printStackTrace();
            }
            catch (InterruptedException ie)
            {
                ie.printStackTrace();
            }
        }
    }
}

Это простое настольное приложение Java 1.6 SE, поэтому я управляю своим собственным соединением JDBC и всем остальным. Я загружаю драйвер через

Class.forName("org.postgresql.Driver");

Я использую библиотеку postgresql-9.0-801.jdbc3.jar (только одна на моем пути к классам) и JDK 1.6.0_22.

Просто для примера, код Java прекрасно работает с NOTIFY из psql и триггера, а также с pg_notify из psql.

Ответы [ 6 ]

28 голосов
/ 12 октября 2011

Это может быть слишком поздно, чтобы помочь, но, возможно, кто-то еще сможет использовать его.Использование SELECT pg_notify ('', '');в триггере побуждает БД ответить

ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.

Изменение SELECT на PERFORM, так как ошибка говорит, помогает решить эту проблему, и уведомление доставляется, как ожидалось.Возможно, это могло быть проблемой.

У меня такая же настройка, и у меня была та же проблема.

14 голосов
/ 17 июня 2015

Это может быть полезно кому-то там.Иногда вы хотите передать всю строку «наблюдателю», и тогда было бы неплохо сериализовать всю строку в JSON.Вы можете достичь этого с помощью row_to_json

-- Notify when record was inserted into 'prices' table
CREATE OR REPLACE FUNCTION notify_pricesinserted()
  RETURNS trigger AS $$
DECLARE
BEGIN
  PERFORM pg_notify(
    CAST('pricesinserted' AS text),
    row_to_json(NEW)::text);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER notify_pricesinserted
  AFTER INSERT ON prices
  FOR EACH ROW
  EXECUTE PROCEDURE notify_pricesinserted();
6 голосов
/ 02 марта 2012
CREATE OR REPLACE FUNCTION notifyshipment() RETURNS trigger AS $$
DECLARE
BEGIN
  PERFORM pg_notify(CAST('snc' AS text),CAST(NEW.id AS text)|| ' ' || CAST(NEW.tracking_number AS text));
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER shipmentnotify AFTER UPDATE ON shipments FOR EACH ROW EXECUTE PROCEDURE notifyshipment();
2 голосов
/ 24 марта 2011

Я не знаю, помогают ли они решить вашу проблему, но некоторые ошибки, с которыми я столкнулся:

  • Вы должны зафиксировать транзакцию с помощью команды LISTEN. Я не знаком с Java, я не знаю, находитесь ли вы в режиме автоматической фиксации или нет.
  • Уведомления отправляются при фиксации. Я полагаю, по какой-либо причине, что транзакция, вызвавшая вызов pg_notify, не была зафиксирована или откатана?
  • Может быть, соединение LISTEN подключается к другой базе данных, отличной от той, куда отправляется NOTIFY? :)

Однако ни один из них не может объяснить, почему NOTIFY работает, а pg_notify - нет.

1 голос
/ 01 февраля 2016

Вы можете использовать следующий код непосредственно в вашей функции создания триггера:

EXECUTE 'NOTIFY your_declared_notify';        

OR

 PERFORM pg_notify(CAST('your_declared_notify' AS text), CAST(NEW.nameAS text));
0 голосов
/ 11 октября 2013

Может быть, вам понравится следующий синтаксис:

RAISE notice 'hstore %, patrm %, dt %, v% ', new_g, _param_id, _dt, new.v ;
...