Kurzfassung

Um Änderungen in einer Postgres-DB ein externes Skript auslösen zu lassen, kann man den trigger-Mechanismus sowie notify in Postgres nutzen und ein externes Skript ein entsprechendes listen auf der selben Datenbank durchführen lassen. Damit kann man z. B. bewerkstelligen, dass ein neuer Fertigungsauftrag aus Odoo einen curl an den SLF-Configurator auslöst.

Postgres: notify mit trigger auslösen

a) von Odoo an SLF

Beispiel: Um bei Einfügung eines neuen Fertigungsauftrags in die Tabelle mrp_production ein notify an den Kanal joachim_production_insert auszulösen, muss zuerst eine Funktion definiert werden, die einen trigger als Rückgabewert hat und die das notify aufruft:

create or replace function joachim_production_insert() returns trigger as $joachim_production_insert_trigger$
begin
	perform pg_notify('joachim_production_insert',row_to_json(NEW)::text);
	return new;
end;
$joachim_production_insert_trigger$ language plpgsql;
Jetzt muss noch festgelegt werden, dass ein insert in diese Tabelle obige Funktion triggert:
create trigger joachim_production_insert_trigger after insert on mrp_production
for each row execute function joachim_production_insert();
Bemerkungen:
  • Die Namen von Kanal, Funktion und Trigger sind logisch unabhängig; hier sind sie nur gleich bzw. ähnlich um einigermaßen selbsterklärend zu sein wenn man aus anderem Kontext darüber stolpert
  • Für mehrzeilige SQL-Eingabe kann man zum Beispiel pgadmin nutzen. (Aber nicht versuchen, die Trigger mit den dafür vorgesehenen Dialogen in pgadmin zu definieren; da habe ich mich im Kreis gedreht)
  • notify lässt standardmäßig eine Payload von bis zu 8000 Byte Länge zu. Wenn nicht sicher ist dass man damit hinkommt, dann bloß Primärschlüssel übergeben und das Python-Skript die Daten aus der Tabelle lesen lassen statt via notify-Payload zu schicken. Oder neue Zeile in dedizierte Tabelle kopieren, die dem Python-Skript als abzuarbeitende Queue dient.

b) von SLF an Odoo

In "SLF"-DB auf Workstation (Achtung: Posgres läuft auf Host, nicht auf VM, und zwar mit Benutzer DigiLLab):

creat function notify_update_for_odoo() returns trigger as $production_order_update_trigger$
begin
	perform pg_notify('update_for_odoo',row_to_json(NEW)::text);
	return new;
end;
$production_order_update_trigger$ language plpgsql;
create trigger production_order_update_trigger after update of state on production_order
for each row execute function notify_update_for_odoo();
````


## Python: mit listen auf notify reagieren

Um nun mit einem Python-Skript auf ein `notify` im fraglichen Kanal zu reagieren, sollte zunächst ein dedizierter Benutzer dafür angelegt werden (zumindest ging es mit dem einzigen Admin erstmal nicht).
import asyncio import psycopg2

conn = psycopg2.connect(host="localhost",dbname="my_first_database",user="python_test",password="1234") conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)

cursor = conn.cursor() cursor.execute('LISTEN joachim_production_trigger')

def handle_notify(): conn.poll() for notify in conn.notifies: print(notify.payload) conn.notifies.clear()

loop = asyncio.get_event_loop() loop.add_reader(conn, handle_notify) loop.run_forever() ``` Für eine Lösung ohne asyncio siehe hier. Das ISOLATION_LEVEL_AUTOCOMMIT ist nötig, damit jede Datenbankanfrage durch psycopg2 automatisch committed wird anstatt auf einen commit zu warten (gemäß irgendeinem in diesem Punkt nicht besonders sinnvollen Standard; finde die Diskussion auf Stackexchange gerade nicht wieder).

Auf Workstation (SLF-VM auf Workstation)

Lieber Powershell als Powershell ISE, denn erstere macht den print wie man ihn von Unix kennt einfach auf die Konsole selbst. Mit Powershell ISE landet diese Ausgabe irgendwo anders.