-
Notifications
You must be signed in to change notification settings - Fork 17
/
alkis-trigger.sql
297 lines (260 loc) · 11 KB
/
alkis-trigger.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/******************************************************************************
*
* Projekt: norGIS ALKIS Import
* Zweck: Trigger des ALKIS-Schema
* Author: Jürgen E. Fischer <[email protected]>
*
******************************************************************************/
SET client_encoding = 'UTF8';
SET default_with_oids = false;
SET search_path = :"alkis_schema", public;
--- Tabelle "delete" für Lösch- und Fortführungsdatensätze
CREATE TABLE "delete" (
ogc_fid serial NOT NULL,
typename varchar,
featureid varchar,
context varchar, -- delete/replace/update
safetoignore varchar, -- replace.safetoignore 'true'/'false'
replacedBy varchar, -- gmlid
anlass varchar[], -- update.anlass
endet character(20), -- update.endet
ignored boolean DEFAULT false, -- Satz wurde nicht verarbeitet
PRIMARY KEY (ogc_fid)
);
CREATE INDEX delete_fid ON "delete"(featureid);
COMMENT ON TABLE delete IS 'BASE: Lösch- und Fortführungsdatensätze';
COMMENT ON COLUMN delete.context IS 'Operation ''delete'', ''replace'' oder ''update''.';
COMMENT ON COLUMN delete.safetoignore IS 'Attribut safeToIgnore von wfsext:Replace';
COMMENT ON COLUMN delete.replacedBy IS 'gml_id des Objekts, das featureid ersetzt';
COMMENT ON COLUMN delete.anlass IS 'Anlaß des Endes';
COMMENT ON COLUMN delete.endet IS 'Zeitpunkt des Endes';
COMMENT ON COLUMN delete.ignored IS 'Löschsatz wurde ignoriert';
-- Löschsatz verarbeiten (MIT Historie)
-- context='delete' => "endet" auf aktuelle Zeit setzen
-- context='replace' => "endet" des ersetzten auf "beginnt" des neuen Objekts setzen
-- context='update' => "endet" auf übergebene Zeit setzen und "anlass" festhalten
CREATE OR REPLACE FUNCTION delete_feature_hist() RETURNS TRIGGER AS $$
DECLARE
n INTEGER;
beginnt TEXT;
s TEXT;
BEGIN
NEW.context := coalesce(lower(NEW.context),'delete');
IF length(NEW.featureid)=32 THEN
beginnt := substr(NEW.featureid, 17, 4) || '-'
|| substr(NEW.featureid, 21, 2) || '-'
|| substr(NEW.featureid, 23, 2) || 'T'
|| substr(NEW.featureid, 26, 2) || ':'
|| substr(NEW.featureid, 28, 2) || ':'
|| substr(NEW.featureid, 30, 2) || 'Z'
;
ELSIF length(NEW.featureid)=16 THEN
-- Ältestes nicht gelöschtes Objekt
EXECUTE 'SELECT min(beginnt) FROM ' || NEW.typename
|| ' WHERE gml_id=''' || NEW.featureid || ''''
|| ' AND endet IS NULL'
INTO beginnt;
IF beginnt IS NULL THEN
RAISE EXCEPTION '%: Keinen Kandidaten zum Löschen gefunden.', NEW.featureid;
END IF;
ELSE
RAISE EXCEPTION '%: Identifikator gescheitert.', NEW.featureid;
END IF;
IF NEW.context='delete' THEN
SELECT endet INTO NEW.endet FROM pg_temp.deletedate;
ELSIF NEW.context='update' THEN
IF NEW.endet IS NULL THEN
RAISE EXCEPTION '%: Endedatum nicht gesetzt', NEW.featureid;
END IF;
ELSIF NEW.context='replace' THEN
NEW.safetoignore := lower(NEW.safetoignore);
IF NEW.safetoignore IS NULL THEN
RAISE EXCEPTION '%: safeToIgnore nicht gesetzt.', NEW.featureid;
ELSIF NEW.safetoignore<>'true' AND NEW.safetoignore<>'false' THEN
RAISE EXCEPTION '%: safeToIgnore ''%'' ungültig (''true'' oder ''false'' erwartet).', NEW.featureid, NEW.safetoignore;
END IF;
IF length(NEW.replacedby)=32 AND NEW.replacedby<>NEW.featureid THEN
NEW.endet := substr(NEW.replacedby, 17, 4) || '-'
|| substr(NEW.replacedby, 21, 2) || '-'
|| substr(NEW.replacedby, 23, 2) || 'T'
|| substr(NEW.replacedby, 26, 2) || ':'
|| substr(NEW.replacedby, 28, 2) || ':'
|| substr(NEW.replacedby, 30, 2) || 'Z'
;
END IF;
IF NEW.endet IS NULL THEN
-- Beginn des ersten Nachfolgeobjektes
EXECUTE 'SELECT min(beginnt) FROM ' || NEW.typename || ' a'
|| ' WHERE gml_id=''' || substr(NEW.replacedby, 1, 16) || ''''
|| ' AND beginnt>''' || beginnt || ''''
INTO NEW.endet;
ELSE
EXECUTE 'SELECT count(*) FROM ' || NEW.typename
|| ' WHERE gml_id=''' || substr(NEW.replacedby, 1, 16) || ''''
|| ' AND beginnt=''' || NEW.endet || ''''
INTO n;
IF n<>1 THEN
RAISE EXCEPTION '%: Ersatzobjekt % % nicht gefunden.', NEW.featureid, NEW.replacedby, NEW.endet;
END IF;
END IF;
IF NEW.endet IS NULL THEN
-- Abbrechen, wenn Austausch nicht ignoriert werden
-- darf, aber nicht wenn ein Objekt (sinnloserweise?)
-- gegen selbst getauscht werden soll.
IF NEW.safetoignore='false' AND NEW.featureid<>NEW.replacedby THEN
RAISE EXCEPTION '%: Beginn des Ersatzobjekts % nicht gefunden.', NEW.featureid, NEW.replacedby;
-- RAISE NOTICE '%: Beginn des ersetzenden Objekts % nicht gefunden.', NEW.featureid, NEW.replacedby;
END IF;
NEW.ignored=true;
RETURN NEW;
END IF;
ELSE
RAISE EXCEPTION '%: Ungültiger Kontext % (''delete'', ''replace'' oder ''update'' erwartet).', NEW.featureid, NEW.context;
END IF;
s := 'UPDATE ' || NEW.typename || ' SET endet=''' || NEW.endet || '''';
IF NEW.context='update' AND NEW.anlass IS NOT NULL THEN
s := s || ',anlass=array_cat(anlass,''{' || array_to_string(NEW.anlass,',') || '}'')';
END IF;
s := s || ' WHERE gml_id=''' || substr(NEW.featureid, 1, 16) || ''''
|| ' AND beginnt=''' || beginnt || ''''
;
EXECUTE s;
GET DIAGNOSTICS n = ROW_COUNT;
-- RAISE NOTICE 'SQL[%]:%', n, s;
IF n<>1 THEN
IF n=0 THEN
s := 'SELECT count(*),min(beginnt) FROM ' || NEW.typename || ' WHERE gml_id=''' || substr(NEW.featureid, 1, 16) || ''' AND endet IS NULL';
EXECUTE s INTO n, beginnt;
IF (n=0 AND NEW.context IN ('delete','update')) OR (n=1 AND NEW.context='replace') THEN
RAISE NOTICE '%: Kein Objekt gefunden [%:%]', NEW.featureid, NEW.context, n;
NEW.ignored=true;
RETURN NEW;
ELSIF n=2 AND beginnt IS NOT NULL THEN
s := 'UPDATE ' || NEW.typename || ' a SET endet=''' || NEW.endet || '''';
IF NEW.anlass IS NOT NULL THEN
s := s || ',anlass=array_cat(anlass,''{' || array_to_string(NEW.anlass,',') || '}'')';
END IF;
s := s || ' WHERE gml_id=''' || substr(NEW.featureid, 1, 16) || ''''
|| ' AND beginnt=''' || beginnt || ''''
;
EXECUTE s;
GET DIAGNOSTICS n = ROW_COUNT;
-- RAISE NOTICE 'SQL[%]:%', n, s;
IF n<>1 THEN
RAISE EXCEPTION '%: Aktualisierung des Vorgängerobjekts von % schlug fehl [%:%]', NEW.featureid, beginnt, NEW.context, n;
END IF;
ELSE
RAISE NOTICE '%: Kein eindeutiges Vorgängerobjekt gefunden [%:%]', NEW.featureid, NEW.context, n;
RETURN NEW;
END IF;
ELSE
RAISE EXCEPTION '%: % schlug fehl [%]', NEW.featureid, NEW.context, n;
END IF;
END IF;
NEW.ignored := false;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SET search_path = :"alkis_schema", public;
-- Abwandlung der Hist-Version als Kill-Version.
-- Die "gml_id" muss in der Datenbank das Format character(16) haben.
-- Dies kann auch Abgabeart 3100 verarbeiten. Historische Objekte werden aber sofort entfernt.
CREATE OR REPLACE FUNCTION delete_feature_kill() RETURNS TRIGGER AS $$
DECLARE
n INTEGER;
vbeginnt TEXT;
replgml TEXT;
featgml TEXT;
s TEXT;
BEGIN
-- Version 2014-09-23, replace führt auch zum Löschen des Vorgängerobjektes
NEW.context := coalesce(lower(NEW.context),'delete');
IF NEW.anlass IS NULL THEN
NEW.anlass := ARRAY[]::varchar[];
END IF;
featgml := substr(NEW.featureid, 1, 16); -- gml_id ohne Timestamp
IF length(NEW.featureid)=32 THEN
-- beginnt-Zeit der zu löschenden Vorgänger-Version des Objektes
vbeginnt := substr(NEW.featureid, 17, 4) || '-'
|| substr(NEW.featureid, 21, 2) || '-'
|| substr(NEW.featureid, 23, 2) || 'T'
|| substr(NEW.featureid, 26, 2) || ':'
|| substr(NEW.featureid, 28, 2) || ':'
|| substr(NEW.featureid, 30, 2) || 'Z' ;
ELSIF length(NEW.featureid)=16 THEN
-- Ältestes nicht gelöschtes Objekt
EXECUTE 'SELECT min(beginnt) FROM ' || NEW.typename
|| ' WHERE gml_id=''' || featgml || '''' || ' AND endet IS NULL'
INTO vbeginnt;
IF vbeginnt IS NULL THEN
RAISE EXCEPTION '%: Keinen Kandidaten zum Löschen gefunden.', NEW.featureid;
END IF;
ELSE
RAISE EXCEPTION '%: Identifikator gescheitert.', NEW.featureid;
END IF;
IF NEW.context='replace' THEN
NEW.safetoignore := lower(NEW.safetoignore);
IF NEW.safetoignore IS NULL THEN
RAISE EXCEPTION '%: safeToIgnore nicht gesetzt.', NEW.featureid;
ELSIF NEW.safetoignore<>'true' AND NEW.safetoignore<>'false' THEN
RAISE EXCEPTION '%: safeToIgnore ''%'' ungültig (''true'' oder ''false'' erwartet).', NEW.featureid, NEW.safetoignore;
END IF;
ELSIF NEW.context NOT IN ('delete', 'update') THEN
RAISE EXCEPTION '%: Ungültiger Kontext % (''delete'', ''replace'' oder ''update'' erwartet).', NEW.featureid, NEW.context;
END IF;
-- Vorgänger-ALKIS-Objekt löschen
s := 'DELETE FROM ' || NEW.typename || ' WHERE gml_id=''' || featgml || ''' AND beginnt=''' || vbeginnt || '''' ;
EXECUTE s;
GET DIAGNOSTICS n = ROW_COUNT;
-- RAISE NOTICE 'SQL[%]:%', n, s;
IF n=1 THEN
NEW.ignored := false;
ELSE
RAISE NOTICE '%: % schlug fehl ignoriert [%]', NEW.featureid, NEW.context, n;
NEW.ignored := true;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SET search_path = :"alkis_schema", public;
CREATE FUNCTION pg_temp.create_trigger(hist BOOLEAN) RETURNS void AS $$
BEGIN
IF hist THEN
CREATE TRIGGER delete_feature_trigger
BEFORE INSERT ON delete
FOR EACH ROW
EXECUTE PROCEDURE delete_feature_hist();
RAISE NOTICE 'Historische Objekte werden geführt.';
ELSE
CREATE TRIGGER delete_feature_trigger
BEFORE INSERT ON delete
FOR EACH ROW
EXECUTE PROCEDURE delete_feature_kill();
RAISE NOTICE 'Historische Objekte werden gelöscht.';
END IF;
END;
$$ LANGUAGE plpgsql;
SELECT pg_temp.create_trigger(:alkis_hist);
CREATE TABLE alkis_beziehungen (
ogc_fid serial NOT NULL,
beziehung_von character(16) NOT NULL,
beziehungsart varchar,
beziehung_zu character(16) NOT NULL,
PRIMARY KEY (ogc_fid)
);
CREATE INDEX alkis_beziehungen_von_idx ON alkis_beziehungen USING btree (beziehung_von);
CREATE INDEX alkis_beziehungen_zu_idx ON alkis_beziehungen USING btree (beziehung_zu);
CREATE INDEX alkis_beziehungen_art_idx ON alkis_beziehungen USING btree (beziehungsart);
COMMENT ON TABLE alkis_beziehungen IS 'BASE: Objektbeziehungen';
COMMENT ON COLUMN alkis_beziehungen.beziehung_von IS 'Join auf Feld gml_id verschiedener Tabellen';
COMMENT ON COLUMN alkis_beziehungen.beziehung_zu IS 'Join auf Feld gml_id verschiedener Tabellen';
COMMENT ON COLUMN alkis_beziehungen.beziehungsart IS 'Typ der Beziehung zwischen der von- und zu-Tabelle';
-- Beziehungssätze aufräumen
CREATE OR REPLACE FUNCTION alkis_beziehung_inserted() RETURNS TRIGGER AS $$
BEGIN
DELETE FROM alkis_beziehungen WHERE ogc_fid<NEW.ogc_fid AND beziehung_von=NEW.beziehung_von AND beziehungsart=NEW.beziehungsart AND beziehung_zu=NEW.beziehung_zu;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SET search_path = :"alkis_schema", public;
CREATE TRIGGER insert_beziehung_trigger
AFTER INSERT ON alkis_beziehungen
FOR EACH ROW
EXECUTE PROCEDURE alkis_beziehung_inserted();