pg_notify und Trigger

AnfängerDB

Benutzer
Beiträge
19
Hallo liebe Community,

ich habe mir ein Back-End in ASP NET Core erstellt, welches eine PostgreSQL-Datenbank mit dem Entity Framework anspricht. Jetzt möchte ich einen Trigger schreiben, der mir beim erstellen eines neuen Tupels eine Meldung ausgibt.

Dies habe ich (da ich noch keine große erfahrung hab) so (in pgAdmin 4) gemacht:

BEGIN
PERFORM pg_notify('new_Id', Id);
RETURN new;
END

aber wenn ich jetzt etwas zu meiner Datenbank hinzufügen will erhalte ich folgende Meldung von meinem Back-End:

An error occurred while updating the entries. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 42703: Spalte »id« existiert nicht
at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)

atMicrosoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
atMicrosoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
atMicrosoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Exception data:
Severity: FEHLER
SqlState: 42703
MessageText: Spalte »id« existiert nicht
InternalPosition: 28
InternalQuery: SELECT pg_notify('new_Id', Id)
Where: PL/pgSQL-Funktion "Test"() Zeile 3 bei PERFORM
File: d:\pginstaller_12.auto\postgres.windows-x64\src\backend\parser\parse_relation.c
Line: 3359
Routine: errorMissingColumn
--- End of inner exception stack trace ---


BTW: Eine Spalte Id existiert ;-)

Seit bitte gnädig ;-) ich habe wie schon geschreiben keine große Erfahrung von Triggern

Über hilfe würde ich mich sehr freuen.

lg
 
Werbung:
...und das ist mein eigentlicher Trigger:
CREATE TRIGGER data_modified
AFTER insert or update
on public."DatabaseName"
for each row
execute procedure notify_id_trigger();
 
du möchtest den Trigger auf einfügen in die Datenbank zünden.

Falsche angehensweise, einfach ändern auf:
Code:
create trigger data_modified
after insert or update on tablename
for each row
execute procedure notify_id_trigger();

Der Trigger kann nicht ausgelöst werden bei irgendeinem insert in irgendeine Tabelle der Datenbank.
Wenn du ein und dieselbe Trigger-Procedure bei verschiedenen inserts oder updates bei mehreren Tabellen zünden möchtest, musst du mehrere Trigger erstellen, die auf die selbe Trigger-Procedure zurückgreifen.
 
Hier ein Beispiel:

Tabelle erstellen:
Code:
create table auto_filled(id integer, filled text);

Trigger-Procedure erstellen:
Code:
create function auto_filled() returns trigger as $$ begin
new.filled = 'Alberto';
return new;
end;
$$ language plpgsql;

Trigger erstellen:
Code:
create trigger auto_filled before insert on auto_filled for each row execute procedure auto_filled();

Datensatz einfügen:
Code:
insert into auto_filled(id) values (1);

Datensatz abrufen:
Code:
select * from auto_filled;

Ergebnis:
Code:
 id | filled
----+---------
  1 | Alberto
(1 Zeile)
 
BTW: Eine Spalte Id existiert ;-)

Wenn die Spalte "Id" benannt ist, wird sie NICHT als "id" gefunden.

Code:
edb=# create table anfaenger_db("Id" int);
CREATE TABLE
edb=*# select id from anfaenger_db ;
FEHLER:  Spalte »id« existiert nicht
LINE 1: select id from anfaenger_db ;
               ^
HINT:  Vielleicht wurde beabsichtigt, auf die Spalte »anfaenger_db.Id« zu verweisen.
edb=*# select Id from anfaenger_db ;
FEHLER:  Spalte »id« existiert nicht
LINE 1: select Id from anfaenger_db ;
               ^
HINT:  Vielleicht wurde beabsichtigt, auf die Spalte »anfaenger_db.Id« zu verweisen.
edb=*# select "Id" from anfaenger_db ;
 Id
----
(0 rows)

edb=*#
 
da sind noch mehr komische Dinge:

  • welchen Datentyp hat Deine Spalte "Id"?
  • Fehlermeldung ist "PL/pgSQL-Funktion "Test"() Zeile 3 bei PERFORM", aber Dein Trigger rift auf: "execute procedure notify_id_trigger();", also einmal "Test()" und einmal "notify_id_trigger()"
 
da sind noch mehr komische Dinge:

  • welchen Datentyp hat Deine Spalte "Id"?
  • Fehlermeldung ist "PL/pgSQL-Funktion "Test"() Zeile 3 bei PERFORM", aber Dein Trigger rift auf: "execute procedure notify_id_trigger();", also einmal "Test()" und einmal "notify_id_trigger()"

Hallo, danke für die Antwort :)

In C# ist der Typ Guid Id (und es ist der PK) und das Entity Framework erstellte die Tabelle (ähnlich wie bei Spring Data JPA).

zu Punkt 2: Ja da hab ich versucht mit der GUI es zu erstellen, was auch nicht geklappt hat, und habe es mit dem Query Tool versucht:

CREATE OR REPLACE FUNCTION notify_id_trigger() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('new_id', NEW.ID);
RETURN new;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER data_modified AFTER insert or update on databaseName for each row execute procedure notify_id_trigger();

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 42703: Record »new« hat kein Feld »id«
at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Exception data:
Severity: FEHLER
SqlState: 42703
MessageText: Record »new« hat kein Feld »id«
Where: SQL-Anweisung »SELECT pg_notify('new_id', NEW.ID)«
PL/pgSQL-Funktion notify_id_trigger() Zeile 3 bei PERFORM
File: d:\pginstaller_12.auto\postgres.windows-x64\src\pl\plpgsql\src\pl_exec.c
Line: 5693
Routine: plpgsql_exec_get_datum_type_info
--- End of inner exception stack trace ---
 
Fangen wir mal langsam an: Du hast die Tabelle mit GUI erstellt, die Sprache war auch von PGAdmin. Wenn Du da eine Spalte Id nennst, macht PGAdmin von sich aus "Id" und damit heißt die Spalte dann nicht mehr id oder "id", sondern "Id". Das mag jetzt optisch kein großes Ding zu sein, ist es aber aus Sicht der DB.

Öffne psql, und schaue Dir dort die Tabelle an. Wie ist der eXaKtE Name der Spalte?
 
Wenn ich aber

CREATE OR REPLACE FUNCTION notify_id_trigger() RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('new_Id', NEW."Id");
RETURN new;
END;
$$ LANGUAGE plpgsql;

also "Id" bekomme ich folgenden Stacktrace:

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 42883: Funktion pg_notify(unknown, uuid) existiert nicht
at Npgsql.NpgsqlConnector.<ReadMessage>g__ReadMessageLong|194_0(NpgsqlConnector connector, Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Exception data:
Severity: FEHLER
SqlState: 42883
MessageText: Funktion pg_notify(unknown, uuid) existiert nicht
Hint: Keine Funktion stimmt mit dem angegebenen Namen und den Argumenttypen überein. Sie müssen möglicherweise ausdrückliche Typumwandlungen hinzufügen.
InternalPosition: 8
InternalQuery: SELECT pg_notify('new_Id', NEW."Id")
Where: PL/pgSQL-Funktion notify_id_trigger() Zeile 3 bei PERFORM
File: d:\pginstaller_12.auto\postgres.windows-x64\src\backend\parser\parse_func.c
Line: 631
Routine: ParseFuncOrColumn
--- End of inner exception stack trace ---
 
Du würdest es Dir und anderen leichter machen, das alles zuerst einmal in psql auszutesten, ohne dem PGAdmin und Micosoft-Gedöhns ...

Code:
edb=*# create table bla(id uuid DEFAULT gen_random_uuid (), data text);
CREATE TABLE
edb=*# create or replace function notify_id_trigger() RETURNS trigger AS $$ begin PERFORM pg_notify('new_Id'::text, NEW."id"::text); return new; end; $$ language plpgsql;
CREATE FUNCTION
edb=*# create trigger trg1 after insert or update on bla for each row execute procedure notify_id_trigger();
CREATE TRIGGER
edb=*# insert into bla (data) values ('test');
INSERT 0 1

Ein Listener auf 'new_Id' sollte nun eine Notification erhalten haben.

Exception data:
Severity: FEHLER
SqlState: 42883
MessageText: Funktion pg_notify(unknown, uuid) existiert nicht

ist der eigentliche Fehler. Deine Tabelle hat Spalte id mit Datentyp UUID, pg_notify erwartet aber (TEXT,TEXT). Das ::text in einer definierten Funktion führt daher einen explizieten CAST auf diesen Datentyp durch.

Wenn Du in der Anwendung den vergebenen ID-Wert wissen willst, könntest Du das einfach haben und den vergebenen Wert einfach als Returnwert definieren:

Code:
edb=*# drop table bla;
DROP TABLE
edb=*# create table bla(id uuid DEFAULT gen_random_uuid (), data text);
CREATE TABLE
edb=*# insert into bla (data) values ('test') returning id;;
                  id                 
--------------------------------------
 0da843d8-66f6-48f5-89b2-73c048a9c0b4
(1 row)

INSERT 0 1
edb=*#
 
du kannst z.B. in der Trigger-Funktion via RAISE NOTICE einen mehr oder weniger sinnvollen text der Nachwelt mitteiln.

Code:
edb=*# create or replace function notify_id_trigger() RETURNS trigger AS $$ begin PERFORM pg_notify('new_Id'::text, NEW."id"::text); raise notice 'im Trigger';return new; end; $$ language plpgsql;
CREATE FUNCTION
edb=*# create trigger trg1 after insert or update on bla for each row execute procedure notify_id_trigger();
CREATE TRIGGER
edb=*# insert into bla (data) values ('test') returning id;;
HINWEIS:  im Trigger
                  id                 
--------------------------------------
 7638675a-5c35-4a8d-a1b9-560aa4845ee4
(1 row)

INSERT 0 1
edb=*#
 
Werbung:
Zurück
Oben