PostgreSQLトリガー関数から通知を発行しようとしています。 NOTIFYコマンドは正常に使用できますが、pg_notifyでうまくいきません。 psqlコンソールからpg_notify関数を呼び出すと通知を受け取りますが、トリガー関数から呼び出すと通知を受け取りません。
このバージョンのトリガー関数は期待どおりに動作します。私は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であるアプリケーションを変更しませんでした。)私のアプリケーションが 'mymessage'をLISTENingすると、「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コンソールから期待どおりに機能するため、私は本当に混乱しています!次のコマンドを実行すると、my 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 「ペイロード」パラメータの状態:
(バイナリデータまたは大量の情報を通信する必要がある場合は、データベーステーブルに入れて、レコードのキーを送信することをお勧めします。)
関連するスタックオーバーフローの質問も参照しましたが、この問題は回避できたと思います: LISTEN/NOTIFY using pg_notify(text、text)in PostgreSQL 。
データベースのバージョンは次のとおりです。
Visual C++ビルド1500、32ビットでコンパイルされたPostgreSQL 9.0.3
私のOSはWindowsですXP Professional、Version 2002、SP3。
前もって感謝します。
編集:以下のmy 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ライブラリ(クラスパスに1つのみ)とJDK 1.6.0_22を使用しています。
上記から要約すると、Javaコードは、psqlとトリガーからのNOTIFY、およびpsqlからのpg_notifyで正常に動作します。
これは手遅れになるかもしれませんが、おそらく他の誰かがそれを使用できるようになるでしょう。 SELECT pg_notify( ''、 '');の使用トリガーでDBが応答します
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に変更すると、この問題の解決に役立ち、期待どおりに通知が配信されます。おそらくこれが問題だったのかもしれません。
私は同じ設定で同じ問題を抱えていました。
それはそこにいる誰かにとって役に立つかもしれません。行全体を「オブザーバー」に渡したい場合は、行全体を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();
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();
これらがあなたの問題を解決するかどうかはわかりませんが、私がヒットしたいくつかの落とし穴は次のとおりです:
LISTEN
コマンドを使用してトランザクションをコミットする必要があります。私はJavaに詳しくないので、自動コミットモードになっているかどうかはわかりません。pg_notify
の呼び出しをトリガーしたトランザクションがコミットされなかったか、ロールバックされた可能性がありますか?ただし、NOTIFYが機能し、pg_notifyが機能しない理由を説明できるものはありません。
次のコードをcreateトリガー関数に直接使用できます。
EXECUTE 'NOTIFY your_declared_notify';
OR
PERFORM pg_notify(CAST('your_declared_notify' AS text), CAST(NEW.nameAS text));
多分あなたは次の構文が好きでしょう:
RAISE notice 'hstore %, patrm %, dt %, v% ', new_g, _param_id, _dt, new.v ;