dzql 0.5.33 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.sample +28 -0
- package/compose.yml +28 -0
- package/dist/client/index.ts +1 -0
- package/dist/client/stores/useMyProfileStore.ts +114 -0
- package/dist/client/stores/useOrgDashboardStore.ts +131 -0
- package/dist/client/stores/useVenueDetailStore.ts +117 -0
- package/dist/client/ws.ts +716 -0
- package/dist/db/migrations/000_core.sql +92 -0
- package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
- package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
- package/dist/runtime/manifest.json +1562 -0
- package/docs/README.md +293 -36
- package/docs/feature-requests/applyPatch-bug-report.md +85 -0
- package/docs/feature-requests/connection-ready-profile.md +57 -0
- package/docs/feature-requests/hidden-bug-report.md +111 -0
- package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
- package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
- package/docs/feature-requests/todo.md +146 -0
- package/docs/for_ai.md +641 -0
- package/docs/project-setup.md +432 -0
- package/examples/blog.ts +50 -0
- package/examples/invalid.ts +18 -0
- package/examples/venues.js +485 -0
- package/package.json +23 -60
- package/src/cli/codegen/client.ts +99 -0
- package/src/cli/codegen/manifest.ts +95 -0
- package/src/cli/codegen/pinia.ts +174 -0
- package/src/cli/codegen/realtime.ts +58 -0
- package/src/cli/codegen/sql.ts +698 -0
- package/src/cli/codegen/subscribable_sql.ts +547 -0
- package/src/cli/codegen/subscribable_store.ts +184 -0
- package/src/cli/codegen/types.ts +142 -0
- package/src/cli/compiler/analyzer.ts +52 -0
- package/src/cli/compiler/graph_rules.ts +251 -0
- package/src/cli/compiler/ir.ts +233 -0
- package/src/cli/compiler/loader.ts +132 -0
- package/src/cli/compiler/permissions.ts +227 -0
- package/src/cli/index.ts +164 -0
- package/src/client/index.ts +1 -0
- package/src/client/ws.ts +286 -0
- package/src/create/.env.example +8 -0
- package/src/create/README.md +101 -0
- package/src/create/compose.yml +14 -0
- package/src/create/domain.ts +153 -0
- package/src/create/package.json +24 -0
- package/src/create/server.ts +18 -0
- package/src/create/setup.sh +11 -0
- package/src/create/tsconfig.json +15 -0
- package/src/runtime/auth.ts +39 -0
- package/src/runtime/db.ts +33 -0
- package/src/runtime/errors.ts +51 -0
- package/src/runtime/index.ts +98 -0
- package/src/runtime/js_functions.ts +63 -0
- package/src/runtime/manifest_loader.ts +29 -0
- package/src/runtime/namespace.ts +483 -0
- package/src/runtime/server.ts +87 -0
- package/src/runtime/ws.ts +197 -0
- package/src/shared/ir.ts +197 -0
- package/tests/client.test.ts +38 -0
- package/tests/codegen.test.ts +71 -0
- package/tests/compiler.test.ts +45 -0
- package/tests/graph_rules.test.ts +173 -0
- package/tests/integration/db.test.ts +174 -0
- package/tests/integration/e2e.test.ts +65 -0
- package/tests/integration/features.test.ts +922 -0
- package/tests/integration/full_stack.test.ts +262 -0
- package/tests/integration/setup.ts +45 -0
- package/tests/ir.test.ts +32 -0
- package/tests/namespace.test.ts +395 -0
- package/tests/permissions.test.ts +55 -0
- package/tests/pinia.test.ts +48 -0
- package/tests/realtime.test.ts +22 -0
- package/tests/runtime.test.ts +80 -0
- package/tests/subscribable_gen.test.ts +72 -0
- package/tests/subscribable_reactivity.test.ts +258 -0
- package/tests/venues_gen.test.ts +25 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/README.md +0 -90
- package/bin/cli.js +0 -727
- package/docs/compiler/ADVANCED_FILTERS.md +0 -183
- package/docs/compiler/CODING_STANDARDS.md +0 -415
- package/docs/compiler/COMPARISON.md +0 -673
- package/docs/compiler/QUICKSTART.md +0 -326
- package/docs/compiler/README.md +0 -134
- package/docs/examples/README.md +0 -38
- package/docs/examples/blog.sql +0 -160
- package/docs/examples/venue-detail-simple.sql +0 -8
- package/docs/examples/venue-detail-subscribable.sql +0 -45
- package/docs/for-ai/claude-guide.md +0 -1210
- package/docs/getting-started/quickstart.md +0 -125
- package/docs/getting-started/subscriptions-quick-start.md +0 -203
- package/docs/getting-started/tutorial.md +0 -1104
- package/docs/guides/atomic-updates.md +0 -299
- package/docs/guides/client-stores.md +0 -730
- package/docs/guides/composite-primary-keys.md +0 -158
- package/docs/guides/custom-functions.md +0 -362
- package/docs/guides/drop-semantics.md +0 -554
- package/docs/guides/field-defaults.md +0 -240
- package/docs/guides/interpreter-vs-compiler.md +0 -237
- package/docs/guides/many-to-many.md +0 -929
- package/docs/guides/subscriptions.md +0 -537
- package/docs/reference/api.md +0 -1373
- package/docs/reference/client.md +0 -224
- package/src/client/stores/index.js +0 -8
- package/src/client/stores/useAppStore.js +0 -285
- package/src/client/stores/useWsStore.js +0 -289
- package/src/client/ws.js +0 -762
- package/src/compiler/cli/compile-example.js +0 -33
- package/src/compiler/cli/compile-subscribable.js +0 -43
- package/src/compiler/cli/debug-compile.js +0 -44
- package/src/compiler/cli/debug-parse.js +0 -26
- package/src/compiler/cli/debug-path-parser.js +0 -18
- package/src/compiler/cli/debug-subscribable-parser.js +0 -21
- package/src/compiler/cli/index.js +0 -174
- package/src/compiler/codegen/auth-codegen.js +0 -153
- package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
- package/src/compiler/codegen/graph-rules-codegen.js +0 -450
- package/src/compiler/codegen/notification-codegen.js +0 -232
- package/src/compiler/codegen/operation-codegen.js +0 -1382
- package/src/compiler/codegen/permission-codegen.js +0 -318
- package/src/compiler/codegen/subscribable-codegen.js +0 -827
- package/src/compiler/compiler.js +0 -371
- package/src/compiler/index.js +0 -11
- package/src/compiler/parser/entity-parser.js +0 -440
- package/src/compiler/parser/path-parser.js +0 -290
- package/src/compiler/parser/subscribable-parser.js +0 -244
- package/src/database/dzql-core.sql +0 -161
- package/src/database/migrations/001_schema.sql +0 -60
- package/src/database/migrations/002_functions.sql +0 -890
- package/src/database/migrations/003_operations.sql +0 -1135
- package/src/database/migrations/004_search.sql +0 -581
- package/src/database/migrations/005_entities.sql +0 -730
- package/src/database/migrations/006_auth.sql +0 -94
- package/src/database/migrations/007_events.sql +0 -133
- package/src/database/migrations/008_hello.sql +0 -18
- package/src/database/migrations/008a_meta.sql +0 -172
- package/src/database/migrations/009_subscriptions.sql +0 -240
- package/src/database/migrations/010_atomic_updates.sql +0 -157
- package/src/database/migrations/010_fix_m2m_events.sql +0 -94
- package/src/index.js +0 -40
- package/src/server/api.js +0 -9
- package/src/server/db.js +0 -442
- package/src/server/index.js +0 -317
- package/src/server/logger.js +0 -259
- package/src/server/mcp.js +0 -594
- package/src/server/meta-route.js +0 -251
- package/src/server/namespace.js +0 -292
- package/src/server/subscriptions.js +0 -351
- package/src/server/ws.js +0 -573
|
@@ -1,730 +0,0 @@
|
|
|
1
|
-
-- DZQL Entity Management - Version 3.0.0
|
|
2
|
-
-- Entity registration, API functions creation, and graph rules execution
|
|
3
|
-
|
|
4
|
-
-- ============================================================================
|
|
5
|
-
-- DYNAMIC TRIGGER CREATION (for "execution": "trigger" actions)
|
|
6
|
-
-- ============================================================================
|
|
7
|
-
|
|
8
|
-
CREATE OR REPLACE FUNCTION dzql.create_event_trigger(
|
|
9
|
-
p_table_name text,
|
|
10
|
-
p_trigger_op text, -- 'on_create', 'on_update', 'on_delete'
|
|
11
|
-
p_action_config jsonb
|
|
12
|
-
) RETURNS void
|
|
13
|
-
LANGUAGE plpgsql AS $$
|
|
14
|
-
DECLARE
|
|
15
|
-
l_trigger_name text;
|
|
16
|
-
l_function_name text;
|
|
17
|
-
l_target_function text;
|
|
18
|
-
l_params jsonb;
|
|
19
|
-
l_param_key text;
|
|
20
|
-
l_param_value text;
|
|
21
|
-
l_param_list text[];
|
|
22
|
-
l_record_ref text;
|
|
23
|
-
l_sql text;
|
|
24
|
-
BEGIN
|
|
25
|
-
l_target_function := p_action_config->>'function';
|
|
26
|
-
l_params := p_action_config->'params';
|
|
27
|
-
|
|
28
|
-
-- Determine if we should use NEW or OLD record
|
|
29
|
-
IF p_trigger_op = 'on_delete' THEN
|
|
30
|
-
l_record_ref := 'OLD';
|
|
31
|
-
ELSE
|
|
32
|
-
l_record_ref := 'NEW';
|
|
33
|
-
END IF;
|
|
34
|
-
|
|
35
|
-
-- Construct the parameter list for the function call
|
|
36
|
-
l_param_list := array[]::text[];
|
|
37
|
-
IF l_params IS NOT NULL THEN
|
|
38
|
-
FOR l_param_key, l_param_value IN SELECT * FROM jsonb_each_text(l_params)
|
|
39
|
-
LOOP
|
|
40
|
-
-- e.g., p_streak_id => NEW.streak_id
|
|
41
|
-
l_param_list := l_param_list || format('%I => %s.%I', l_param_key, l_record_ref, right(l_param_value, -1));
|
|
42
|
-
END LOOP;
|
|
43
|
-
END IF;
|
|
44
|
-
|
|
45
|
-
-- Generate unique names for the trigger and its function
|
|
46
|
-
l_function_name := format('dzql_trigger_%s_%s_%s', p_table_name, p_trigger_op, l_target_function);
|
|
47
|
-
l_trigger_name := format('dzql_managed_%s_%s', p_table_name, p_trigger_op);
|
|
48
|
-
|
|
49
|
-
-- Create the trigger function
|
|
50
|
-
l_sql := format(
|
|
51
|
-
$SQL$
|
|
52
|
-
CREATE OR REPLACE FUNCTION %I()
|
|
53
|
-
RETURNS TRIGGER AS $trigger_func$
|
|
54
|
-
BEGIN
|
|
55
|
-
PERFORM %I(%s);
|
|
56
|
-
RETURN NULL; -- Result is ignored for AFTER trigger
|
|
57
|
-
END;
|
|
58
|
-
$trigger_func$ LANGUAGE plpgsql;
|
|
59
|
-
$SQL$,
|
|
60
|
-
l_function_name,
|
|
61
|
-
l_target_function,
|
|
62
|
-
array_to_string(l_param_list, ', ')
|
|
63
|
-
);
|
|
64
|
-
EXECUTE l_sql;
|
|
65
|
-
|
|
66
|
-
-- Create the trigger
|
|
67
|
-
l_sql := format(
|
|
68
|
-
$SQL$
|
|
69
|
-
DROP TRIGGER IF EXISTS %I ON %I;
|
|
70
|
-
CREATE TRIGGER %I
|
|
71
|
-
AFTER %s ON %I
|
|
72
|
-
FOR EACH ROW
|
|
73
|
-
EXECUTE FUNCTION %I();
|
|
74
|
-
$SQL$,
|
|
75
|
-
l_trigger_name, p_table_name,
|
|
76
|
-
l_trigger_name,
|
|
77
|
-
CASE p_trigger_op WHEN 'on_create' THEN 'INSERT' WHEN 'on_update' THEN 'UPDATE' ELSE 'DELETE' END,
|
|
78
|
-
p_table_name,
|
|
79
|
-
l_function_name
|
|
80
|
-
);
|
|
81
|
-
EXECUTE l_sql;
|
|
82
|
-
|
|
83
|
-
RAISE NOTICE 'DZQL: Created trigger % on % for % event', l_trigger_name, p_table_name, p_trigger_op;
|
|
84
|
-
END;
|
|
85
|
-
$$;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
-- ============================================================================
|
|
89
|
-
-- GRAPH RULES EXECUTION ENGINE
|
|
90
|
-
-- ============================================================================
|
|
91
|
-
|
|
92
|
-
-- Execute graph insert action
|
|
93
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_insert(
|
|
94
|
-
p_entity text,
|
|
95
|
-
p_data jsonb,
|
|
96
|
-
p_user_id int
|
|
97
|
-
) RETURNS void
|
|
98
|
-
LANGUAGE plpgsql AS $$
|
|
99
|
-
DECLARE
|
|
100
|
-
l_cols text[];
|
|
101
|
-
l_vals text[];
|
|
102
|
-
l_col_name text;
|
|
103
|
-
l_sql_stmt text;
|
|
104
|
-
BEGIN
|
|
105
|
-
-- Graph rules are trusted server-side operations, skip permission checks
|
|
106
|
-
|
|
107
|
-
-- Build column and value lists
|
|
108
|
-
FOR l_col_name IN SELECT * FROM jsonb_object_keys(p_data)
|
|
109
|
-
LOOP
|
|
110
|
-
l_cols := l_cols || l_col_name;
|
|
111
|
-
l_vals := l_vals || quote_literal(p_data->>l_col_name);
|
|
112
|
-
END LOOP;
|
|
113
|
-
|
|
114
|
-
-- Build and execute INSERT statement
|
|
115
|
-
l_sql_stmt := format('INSERT INTO %I (%s) VALUES (%s)',
|
|
116
|
-
p_entity,
|
|
117
|
-
array_to_string(l_cols, ', '),
|
|
118
|
-
array_to_string(l_vals, ', ')
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
EXECUTE l_sql_stmt;
|
|
122
|
-
|
|
123
|
-
-- Create event for graph rule action
|
|
124
|
-
INSERT INTO dzql.events (
|
|
125
|
-
table_name,
|
|
126
|
-
op,
|
|
127
|
-
pk,
|
|
128
|
-
data,
|
|
129
|
-
user_id,
|
|
130
|
-
notify_users
|
|
131
|
-
) VALUES (
|
|
132
|
-
p_entity,
|
|
133
|
-
'insert',
|
|
134
|
-
jsonb_build_object('id', p_data->>'id'),
|
|
135
|
-
p_data,
|
|
136
|
-
p_user_id,
|
|
137
|
-
dzql.resolve_notification_paths(p_entity, p_data)
|
|
138
|
-
);
|
|
139
|
-
END $$;
|
|
140
|
-
|
|
141
|
-
-- Execute graph update action
|
|
142
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_update(
|
|
143
|
-
p_entity text,
|
|
144
|
-
p_match jsonb,
|
|
145
|
-
p_data jsonb,
|
|
146
|
-
p_user_id int
|
|
147
|
-
) RETURNS void
|
|
148
|
-
LANGUAGE plpgsql AS $$
|
|
149
|
-
DECLARE
|
|
150
|
-
l_set_clauses text[];
|
|
151
|
-
l_where_clauses text[];
|
|
152
|
-
l_col_name text;
|
|
153
|
-
l_sql_stmt text;
|
|
154
|
-
BEGIN
|
|
155
|
-
-- Check permissions before executing graph rule action
|
|
156
|
-
-- Graph rules are trusted server-side operations, skip permission checks
|
|
157
|
-
|
|
158
|
-
-- Build SET clauses
|
|
159
|
-
FOR l_col_name IN SELECT * FROM jsonb_object_keys(p_data)
|
|
160
|
-
LOOP
|
|
161
|
-
l_set_clauses := l_set_clauses || format('%I = %L', l_col_name, p_data->>l_col_name);
|
|
162
|
-
END LOOP;
|
|
163
|
-
|
|
164
|
-
-- Build WHERE clauses
|
|
165
|
-
FOR l_col_name IN SELECT * FROM jsonb_object_keys(p_match)
|
|
166
|
-
LOOP
|
|
167
|
-
l_where_clauses := l_where_clauses || format('%I = %L', l_col_name, p_match->>l_col_name);
|
|
168
|
-
END LOOP;
|
|
169
|
-
|
|
170
|
-
-- Build and execute UPDATE statement
|
|
171
|
-
l_sql_stmt := format('UPDATE %I SET %s WHERE %s',
|
|
172
|
-
p_entity,
|
|
173
|
-
array_to_string(l_set_clauses, ', '),
|
|
174
|
-
array_to_string(l_where_clauses, ' AND ')
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
EXECUTE l_sql_stmt;
|
|
178
|
-
|
|
179
|
-
-- Create event for graph rule action
|
|
180
|
-
INSERT INTO dzql.events (
|
|
181
|
-
table_name,
|
|
182
|
-
op,
|
|
183
|
-
pk,
|
|
184
|
-
data,
|
|
185
|
-
user_id,
|
|
186
|
-
notify_users
|
|
187
|
-
) VALUES (
|
|
188
|
-
p_entity,
|
|
189
|
-
'update',
|
|
190
|
-
p_match,
|
|
191
|
-
p_data,
|
|
192
|
-
p_user_id,
|
|
193
|
-
'[]'::int[] -- Graph rule updates don't have notification paths
|
|
194
|
-
);
|
|
195
|
-
END $$;
|
|
196
|
-
|
|
197
|
-
-- Execute graph delete action
|
|
198
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_delete(
|
|
199
|
-
p_entity text,
|
|
200
|
-
p_match jsonb,
|
|
201
|
-
p_user_id int
|
|
202
|
-
) RETURNS void
|
|
203
|
-
LANGUAGE plpgsql AS $$
|
|
204
|
-
DECLARE
|
|
205
|
-
l_where_clauses text[];
|
|
206
|
-
l_col_name text;
|
|
207
|
-
l_sql_stmt text;
|
|
208
|
-
BEGIN
|
|
209
|
-
-- Check permissions before executing graph rule action
|
|
210
|
-
-- Graph rules are trusted server-side operations, skip permission checks
|
|
211
|
-
-- Build WHERE clauses
|
|
212
|
-
FOR l_col_name IN SELECT * FROM jsonb_object_keys(p_match)
|
|
213
|
-
LOOP
|
|
214
|
-
l_where_clauses := l_where_clauses || format('%I = %L', l_col_name, p_match->>l_col_name);
|
|
215
|
-
END LOOP;
|
|
216
|
-
|
|
217
|
-
-- Build and execute DELETE statement
|
|
218
|
-
l_sql_stmt := format('DELETE FROM %I WHERE %s',
|
|
219
|
-
p_entity,
|
|
220
|
-
array_to_string(l_where_clauses, ' AND ')
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
EXECUTE l_sql_stmt;
|
|
224
|
-
|
|
225
|
-
-- Create event for graph rule action
|
|
226
|
-
INSERT INTO dzql.events (
|
|
227
|
-
table_name,
|
|
228
|
-
op,
|
|
229
|
-
pk,
|
|
230
|
-
data,
|
|
231
|
-
user_id,
|
|
232
|
-
notify_users
|
|
233
|
-
) VALUES (
|
|
234
|
-
p_entity,
|
|
235
|
-
'delete',
|
|
236
|
-
p_match,
|
|
237
|
-
NULL,
|
|
238
|
-
p_user_id,
|
|
239
|
-
'[]'::int[] -- Graph rule deletes don't have notification paths
|
|
240
|
-
);
|
|
241
|
-
END $$;
|
|
242
|
-
|
|
243
|
-
-- === Validate Action ===
|
|
244
|
-
-- Calls a validation function and raises exception if it returns false
|
|
245
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_validate(
|
|
246
|
-
p_function_name text,
|
|
247
|
-
p_params jsonb,
|
|
248
|
-
p_error_message text DEFAULT 'Validation failed'
|
|
249
|
-
) RETURNS void
|
|
250
|
-
LANGUAGE plpgsql AS $$
|
|
251
|
-
DECLARE
|
|
252
|
-
l_result boolean;
|
|
253
|
-
l_sql text;
|
|
254
|
-
l_param_list text[];
|
|
255
|
-
l_key text;
|
|
256
|
-
l_value text;
|
|
257
|
-
BEGIN
|
|
258
|
-
-- Validate function name (prevent SQL injection)
|
|
259
|
-
IF NOT p_function_name ~ '^[a-z_][a-z0-9_]*$' THEN
|
|
260
|
-
RAISE EXCEPTION 'Invalid function name: %', p_function_name;
|
|
261
|
-
END IF;
|
|
262
|
-
|
|
263
|
-
-- Build parameter list
|
|
264
|
-
l_param_list := array[]::text[];
|
|
265
|
-
FOR l_key, l_value IN SELECT * FROM jsonb_each_text(p_params)
|
|
266
|
-
LOOP
|
|
267
|
-
l_param_list := l_param_list || (l_key || ' => ' || quote_literal(l_value));
|
|
268
|
-
END LOOP;
|
|
269
|
-
|
|
270
|
-
-- Build and execute function call
|
|
271
|
-
IF array_length(l_param_list, 1) > 0 THEN
|
|
272
|
-
l_sql := format('SELECT %I(%s)', p_function_name, array_to_string(l_param_list, ', '));
|
|
273
|
-
ELSE
|
|
274
|
-
l_sql := format('SELECT %I()', p_function_name);
|
|
275
|
-
END IF;
|
|
276
|
-
|
|
277
|
-
EXECUTE l_sql INTO l_result;
|
|
278
|
-
|
|
279
|
-
-- Raise exception if validation failed
|
|
280
|
-
IF NOT COALESCE(l_result, false) THEN
|
|
281
|
-
RAISE EXCEPTION '%', p_error_message;
|
|
282
|
-
END IF;
|
|
283
|
-
END $$;
|
|
284
|
-
|
|
285
|
-
-- === Execute Action ===
|
|
286
|
-
-- Calls a custom function with parameters (fire-and-forget)
|
|
287
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_function(
|
|
288
|
-
p_function_name text,
|
|
289
|
-
p_params jsonb
|
|
290
|
-
) RETURNS jsonb
|
|
291
|
-
LANGUAGE plpgsql AS $$
|
|
292
|
-
DECLARE
|
|
293
|
-
l_result jsonb;
|
|
294
|
-
l_sql text;
|
|
295
|
-
l_param_list text[];
|
|
296
|
-
l_key text;
|
|
297
|
-
l_value text;
|
|
298
|
-
BEGIN
|
|
299
|
-
-- Validate function name (prevent SQL injection)
|
|
300
|
-
IF NOT p_function_name ~ '^[a-z_][a-z0-9_]*$' THEN
|
|
301
|
-
RAISE EXCEPTION 'Invalid function name: %', p_function_name;
|
|
302
|
-
END IF;
|
|
303
|
-
|
|
304
|
-
-- Build parameter list
|
|
305
|
-
l_param_list := array[]::text[];
|
|
306
|
-
FOR l_key, l_value IN SELECT * FROM jsonb_each_text(p_params)
|
|
307
|
-
LOOP
|
|
308
|
-
l_param_list := l_param_list || (l_key || ' => ' || quote_literal(l_value));
|
|
309
|
-
END LOOP;
|
|
310
|
-
|
|
311
|
-
-- Build and execute function call
|
|
312
|
-
IF array_length(l_param_list, 1) > 0 THEN
|
|
313
|
-
l_sql := format('SELECT %I(%s)', p_function_name, array_to_string(l_param_list, ', '));
|
|
314
|
-
ELSE
|
|
315
|
-
l_sql := format('SELECT %I()', p_function_name);
|
|
316
|
-
END IF;
|
|
317
|
-
|
|
318
|
-
EXECUTE l_sql INTO l_result;
|
|
319
|
-
|
|
320
|
-
RETURN COALESCE(l_result, '{}'::jsonb);
|
|
321
|
-
EXCEPTION WHEN OTHERS THEN
|
|
322
|
-
-- Log error but don't fail the transaction
|
|
323
|
-
RAISE WARNING 'Graph rule function execution failed: % (function: %)', SQLERRM, p_function_name;
|
|
324
|
-
RETURN jsonb_build_object('error', SQLERRM);
|
|
325
|
-
END $$;
|
|
326
|
-
|
|
327
|
-
-- Main graph rules execution engine
|
|
328
|
-
CREATE OR REPLACE FUNCTION dzql.execute_graph_rules(
|
|
329
|
-
p_table_name text,
|
|
330
|
-
p_operation text, -- 'insert', 'update', 'delete'
|
|
331
|
-
p_record_before jsonb,
|
|
332
|
-
p_record_after jsonb,
|
|
333
|
-
p_user_id int
|
|
334
|
-
) RETURNS jsonb
|
|
335
|
-
LANGUAGE plpgsql AS $$
|
|
336
|
-
DECLARE
|
|
337
|
-
l_entity_config record;
|
|
338
|
-
l_graph_rules jsonb;
|
|
339
|
-
l_trigger_key text;
|
|
340
|
-
l_trigger_rules jsonb;
|
|
341
|
-
l_rule_name text;
|
|
342
|
-
l_rule_config jsonb;
|
|
343
|
-
l_action jsonb;
|
|
344
|
-
l_action_type text;
|
|
345
|
-
l_target_entity text;
|
|
346
|
-
l_action_data jsonb;
|
|
347
|
-
l_action_match jsonb;
|
|
348
|
-
l_resolved_data jsonb;
|
|
349
|
-
l_resolved_match jsonb;
|
|
350
|
-
l_execution_log jsonb := '[]'::jsonb;
|
|
351
|
-
l_condition text;
|
|
352
|
-
l_condition_result boolean;
|
|
353
|
-
l_function_name text;
|
|
354
|
-
l_function_params jsonb;
|
|
355
|
-
l_error_message text;
|
|
356
|
-
l_function_result jsonb;
|
|
357
|
-
BEGIN
|
|
358
|
-
-- Get entity configuration
|
|
359
|
-
SELECT * INTO l_entity_config FROM dzql.entities WHERE table_name = p_table_name;
|
|
360
|
-
|
|
361
|
-
IF l_entity_config IS NULL THEN
|
|
362
|
-
RETURN jsonb_build_object('status', 'entity_not_found');
|
|
363
|
-
END IF;
|
|
364
|
-
|
|
365
|
-
l_graph_rules := l_entity_config.graph_rules;
|
|
366
|
-
|
|
367
|
-
-- Early exit if no graph rules
|
|
368
|
-
IF l_graph_rules IS NULL OR l_graph_rules = '{}' THEN
|
|
369
|
-
RETURN jsonb_build_object('status', 'no_rules');
|
|
370
|
-
END IF;
|
|
371
|
-
|
|
372
|
-
-- Map operation to trigger key
|
|
373
|
-
l_trigger_key := CASE p_operation
|
|
374
|
-
WHEN 'insert' THEN 'on_create'
|
|
375
|
-
WHEN 'update' THEN 'on_update'
|
|
376
|
-
WHEN 'delete' THEN 'on_delete'
|
|
377
|
-
ELSE NULL
|
|
378
|
-
END;
|
|
379
|
-
|
|
380
|
-
IF l_trigger_key IS NULL THEN
|
|
381
|
-
RETURN jsonb_build_object('status', 'invalid_operation', 'operation', p_operation);
|
|
382
|
-
END IF;
|
|
383
|
-
|
|
384
|
-
-- Get rules for this trigger
|
|
385
|
-
l_trigger_rules := l_graph_rules->l_trigger_key;
|
|
386
|
-
|
|
387
|
-
IF l_trigger_rules IS NULL OR l_trigger_rules = '{}' THEN
|
|
388
|
-
RETURN jsonb_build_object('status', 'no_rules_for_trigger', 'trigger', l_trigger_key);
|
|
389
|
-
END IF;
|
|
390
|
-
|
|
391
|
-
-- Execute each rule
|
|
392
|
-
FOR l_rule_name, l_rule_config IN SELECT * FROM jsonb_each(l_trigger_rules)
|
|
393
|
-
LOOP
|
|
394
|
-
-- Check condition if present
|
|
395
|
-
l_condition := l_rule_config->>'condition';
|
|
396
|
-
l_condition_result := true; -- Default to true if no condition
|
|
397
|
-
|
|
398
|
-
IF l_condition IS NOT NULL THEN
|
|
399
|
-
l_condition_result := dzql.evaluate_condition(l_condition, p_record_before, p_record_after, p_user_id);
|
|
400
|
-
END IF;
|
|
401
|
-
|
|
402
|
-
IF l_condition_result THEN
|
|
403
|
-
-- Execute each action in the rule
|
|
404
|
-
FOR l_action IN SELECT * FROM jsonb_array_elements(l_rule_config->'actions')
|
|
405
|
-
LOOP
|
|
406
|
-
-- If action is handled by a native trigger, skip it in the immediate executor
|
|
407
|
-
IF l_action->>'execution' = 'trigger' THEN
|
|
408
|
-
CONTINUE;
|
|
409
|
-
END IF;
|
|
410
|
-
|
|
411
|
-
l_action_type := l_action->>'type';
|
|
412
|
-
|
|
413
|
-
-- Execute the action based on type
|
|
414
|
-
BEGIN
|
|
415
|
-
CASE l_action_type
|
|
416
|
-
-- Existing action types
|
|
417
|
-
WHEN 'create' THEN
|
|
418
|
-
l_target_entity := l_action->>'entity';
|
|
419
|
-
l_action_data := l_action->'data';
|
|
420
|
-
l_resolved_data := dzql.resolve_graph_data(l_action_data, p_record_before, p_record_after, p_user_id);
|
|
421
|
-
PERFORM dzql.execute_graph_insert(l_target_entity, l_resolved_data, p_user_id);
|
|
422
|
-
|
|
423
|
-
WHEN 'update' THEN
|
|
424
|
-
l_target_entity := l_action->>'entity';
|
|
425
|
-
l_action_data := l_action->'data';
|
|
426
|
-
l_action_match := l_action->'match';
|
|
427
|
-
l_resolved_data := dzql.resolve_graph_data(l_action_data, p_record_before, p_record_after, p_user_id);
|
|
428
|
-
l_resolved_match := dzql.resolve_graph_data(l_action_match, p_record_before, p_record_after, p_user_id);
|
|
429
|
-
PERFORM dzql.execute_graph_update(l_target_entity, l_resolved_match, l_resolved_data, p_user_id);
|
|
430
|
-
|
|
431
|
-
WHEN 'delete' THEN
|
|
432
|
-
l_target_entity := l_action->>'entity';
|
|
433
|
-
l_action_match := l_action->'match';
|
|
434
|
-
l_resolved_match := dzql.resolve_graph_data(l_action_match, p_record_before, p_record_after, p_user_id);
|
|
435
|
-
PERFORM dzql.execute_graph_delete(l_target_entity, l_resolved_match, p_user_id);
|
|
436
|
-
|
|
437
|
-
-- NEW: Validation action
|
|
438
|
-
WHEN 'validate' THEN
|
|
439
|
-
l_function_name := l_action->>'function';
|
|
440
|
-
l_function_params := l_action->'params';
|
|
441
|
-
l_error_message := COALESCE(l_action->>'error_message', 'Validation failed');
|
|
442
|
-
l_resolved_data := dzql.resolve_graph_data(l_function_params, p_record_before, p_record_after, p_user_id);
|
|
443
|
-
PERFORM dzql.execute_graph_validate(l_function_name, l_resolved_data, l_error_message);
|
|
444
|
-
|
|
445
|
-
-- NEW: Execute function action
|
|
446
|
-
WHEN 'execute' THEN
|
|
447
|
-
l_function_name := l_action->>'function';
|
|
448
|
-
l_function_params := l_action->'params';
|
|
449
|
-
l_resolved_data := dzql.resolve_graph_data(l_function_params, p_record_before, p_record_after, p_user_id);
|
|
450
|
-
l_function_result := dzql.execute_graph_function(l_function_name, l_resolved_data);
|
|
451
|
-
|
|
452
|
-
ELSE
|
|
453
|
-
RAISE WARNING 'Unknown graph rule action type: %', l_action_type;
|
|
454
|
-
END CASE;
|
|
455
|
-
|
|
456
|
-
-- Log successful execution
|
|
457
|
-
l_execution_log := l_execution_log || jsonb_build_object(
|
|
458
|
-
'rule', l_rule_name,
|
|
459
|
-
'action', l_action_type,
|
|
460
|
-
'status', 'success'
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
EXCEPTION WHEN OTHERS THEN
|
|
464
|
-
-- Log error and re-raise for validate actions, otherwise just log
|
|
465
|
-
l_execution_log := l_execution_log || jsonb_build_object(
|
|
466
|
-
'rule', l_rule_name,
|
|
467
|
-
'action', l_action_type,
|
|
468
|
-
'status', 'error',
|
|
469
|
-
'error', SQLERRM
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
-- Re-raise exceptions from validate actions to prevent operation
|
|
473
|
-
IF l_action_type = 'validate' THEN
|
|
474
|
-
RAISE;
|
|
475
|
-
END IF;
|
|
476
|
-
END;
|
|
477
|
-
END LOOP;
|
|
478
|
-
END IF;
|
|
479
|
-
END LOOP;
|
|
480
|
-
|
|
481
|
-
RETURN jsonb_build_object(
|
|
482
|
-
'status', 'success',
|
|
483
|
-
'trigger', l_trigger_key,
|
|
484
|
-
'executed_actions', l_execution_log
|
|
485
|
-
);
|
|
486
|
-
END $$;
|
|
487
|
-
|
|
488
|
-
-- ============================================================================
|
|
489
|
-
-- API FUNCTION CREATION
|
|
490
|
-
-- ============================================================================
|
|
491
|
-
|
|
492
|
-
-- Create API functions for an entity
|
|
493
|
-
CREATE OR REPLACE FUNCTION dzql.create_entity_functions(p_table_name text)
|
|
494
|
-
RETURNS void
|
|
495
|
-
LANGUAGE plpgsql AS $$
|
|
496
|
-
DECLARE
|
|
497
|
-
l_get_fn_name text;
|
|
498
|
-
l_save_fn_name text;
|
|
499
|
-
l_delete_fn_name text;
|
|
500
|
-
l_lookup_fn_name text;
|
|
501
|
-
l_search_fn_name text;
|
|
502
|
-
BEGIN
|
|
503
|
-
-- Generate function names
|
|
504
|
-
l_get_fn_name := 'get_' || p_table_name;
|
|
505
|
-
l_save_fn_name := 'save_' || p_table_name;
|
|
506
|
-
l_delete_fn_name := 'delete_' || p_table_name;
|
|
507
|
-
l_lookup_fn_name := 'lookup_' || p_table_name;
|
|
508
|
-
l_search_fn_name := 'search_' || p_table_name;
|
|
509
|
-
|
|
510
|
-
-- Create GET function
|
|
511
|
-
EXECUTE format('
|
|
512
|
-
CREATE OR REPLACE FUNCTION dzql.%I(p_args jsonb, p_user_id int)
|
|
513
|
-
RETURNS jsonb
|
|
514
|
-
LANGUAGE sql
|
|
515
|
-
AS $func$
|
|
516
|
-
SELECT dzql.generic_get(%L, p_args, p_user_id);
|
|
517
|
-
$func$;
|
|
518
|
-
', l_get_fn_name, p_table_name);
|
|
519
|
-
|
|
520
|
-
-- Create SAVE function
|
|
521
|
-
EXECUTE format('
|
|
522
|
-
CREATE OR REPLACE FUNCTION dzql.%I(p_args jsonb, p_user_id int)
|
|
523
|
-
RETURNS jsonb
|
|
524
|
-
LANGUAGE sql
|
|
525
|
-
AS $func$
|
|
526
|
-
SELECT dzql.generic_save(%L, p_args, p_user_id);
|
|
527
|
-
$func$;
|
|
528
|
-
', l_save_fn_name, p_table_name);
|
|
529
|
-
|
|
530
|
-
-- Create DELETE function
|
|
531
|
-
EXECUTE format('
|
|
532
|
-
CREATE OR REPLACE FUNCTION dzql.%I(p_args jsonb, p_user_id int)
|
|
533
|
-
RETURNS jsonb
|
|
534
|
-
LANGUAGE sql
|
|
535
|
-
AS $func$
|
|
536
|
-
SELECT dzql.generic_delete(%L, p_args, p_user_id);
|
|
537
|
-
$func$;
|
|
538
|
-
', l_delete_fn_name, p_table_name);
|
|
539
|
-
|
|
540
|
-
-- Create LOOKUP function
|
|
541
|
-
EXECUTE format('
|
|
542
|
-
CREATE OR REPLACE FUNCTION dzql.%I(p_args jsonb, p_user_id int)
|
|
543
|
-
RETURNS jsonb
|
|
544
|
-
LANGUAGE sql
|
|
545
|
-
AS $func$
|
|
546
|
-
SELECT dzql.generic_lookup(%L, p_args, p_user_id);
|
|
547
|
-
$func$;
|
|
548
|
-
', l_lookup_fn_name, p_table_name);
|
|
549
|
-
|
|
550
|
-
-- Create SEARCH function
|
|
551
|
-
EXECUTE format('
|
|
552
|
-
CREATE OR REPLACE FUNCTION dzql.%I(p_args jsonb, p_user_id int)
|
|
553
|
-
RETURNS jsonb
|
|
554
|
-
LANGUAGE sql
|
|
555
|
-
AS $func$
|
|
556
|
-
SELECT dzql.generic_search(%L, p_args, p_user_id);
|
|
557
|
-
$func$;
|
|
558
|
-
', l_search_fn_name, p_table_name);
|
|
559
|
-
END $$;
|
|
560
|
-
|
|
561
|
-
-- ============================================================================
|
|
562
|
-
-- ENTITY REGISTRATION
|
|
563
|
-
-- ============================================================================
|
|
564
|
-
|
|
565
|
-
-- Register entity function with full graph rules support
|
|
566
|
-
CREATE OR REPLACE FUNCTION dzql.register_entity(
|
|
567
|
-
p_table_name text,
|
|
568
|
-
p_label_field text,
|
|
569
|
-
p_searchable_fields text[],
|
|
570
|
-
p_fk_includes jsonb DEFAULT '{}',
|
|
571
|
-
p_soft_delete boolean DEFAULT false,
|
|
572
|
-
p_temporal_fields jsonb DEFAULT '{}',
|
|
573
|
-
p_notification_paths jsonb DEFAULT '{}',
|
|
574
|
-
p_permission_paths jsonb DEFAULT '{}',
|
|
575
|
-
p_graph_rules jsonb DEFAULT '{}',
|
|
576
|
-
p_field_defaults jsonb DEFAULT '{}'
|
|
577
|
-
) RETURNS void
|
|
578
|
-
LANGUAGE plpgsql AS $$
|
|
579
|
-
DECLARE
|
|
580
|
-
l_trigger_op text;
|
|
581
|
-
l_rule_name text;
|
|
582
|
-
l_rule_config jsonb;
|
|
583
|
-
l_action jsonb;
|
|
584
|
-
l_many_to_many jsonb;
|
|
585
|
-
BEGIN
|
|
586
|
-
-- Validate permission paths if provided
|
|
587
|
-
IF p_permission_paths IS NOT NULL AND p_permission_paths != '{}' THEN
|
|
588
|
-
IF NOT dzql.validate_permission_paths(p_table_name, p_permission_paths) THEN
|
|
589
|
-
RAISE EXCEPTION 'Invalid permission paths for entity %', p_table_name;
|
|
590
|
-
END IF;
|
|
591
|
-
END IF;
|
|
592
|
-
|
|
593
|
-
-- Validate graph rules if provided
|
|
594
|
-
IF p_graph_rules IS NOT NULL AND p_graph_rules != '{}' THEN
|
|
595
|
-
IF NOT dzql.validate_graph_rules(p_graph_rules) THEN
|
|
596
|
-
RAISE EXCEPTION 'Invalid graph rules for entity %', p_table_name;
|
|
597
|
-
END IF;
|
|
598
|
-
END IF;
|
|
599
|
-
|
|
600
|
-
-- Extract many_to_many from graph_rules if present
|
|
601
|
-
l_many_to_many := COALESCE(p_graph_rules->'many_to_many', '{}'::jsonb);
|
|
602
|
-
|
|
603
|
-
-- Insert or update entity configuration
|
|
604
|
-
INSERT INTO dzql.entities
|
|
605
|
-
(table_name, label_field, searchable_fields, fk_includes, soft_delete, temporal_fields, notification_paths, permission_paths, graph_rules, field_defaults, many_to_many)
|
|
606
|
-
VALUES
|
|
607
|
-
(p_table_name, p_label_field, p_searchable_fields, p_fk_includes, p_soft_delete, p_temporal_fields, p_notification_paths, p_permission_paths, p_graph_rules, p_field_defaults, l_many_to_many)
|
|
608
|
-
ON CONFLICT (table_name) DO UPDATE SET
|
|
609
|
-
label_field = EXCLUDED.label_field,
|
|
610
|
-
searchable_fields = EXCLUDED.searchable_fields,
|
|
611
|
-
fk_includes = EXCLUDED.fk_includes,
|
|
612
|
-
soft_delete = EXCLUDED.soft_delete,
|
|
613
|
-
temporal_fields = EXCLUDED.temporal_fields,
|
|
614
|
-
notification_paths = EXCLUDED.notification_paths,
|
|
615
|
-
permission_paths = EXCLUDED.permission_paths,
|
|
616
|
-
graph_rules = EXCLUDED.graph_rules,
|
|
617
|
-
field_defaults = EXCLUDED.field_defaults,
|
|
618
|
-
many_to_many = EXCLUDED.many_to_many;
|
|
619
|
-
|
|
620
|
-
-- Create API functions for this entity
|
|
621
|
-
PERFORM dzql.create_entity_functions(p_table_name);
|
|
622
|
-
|
|
623
|
-
-- Create managed triggers for any 'execution: trigger' actions
|
|
624
|
-
IF p_graph_rules IS NOT NULL AND p_graph_rules != '{}' THEN
|
|
625
|
-
FOREACH l_trigger_op IN ARRAY ARRAY['on_create', 'on_update', 'on_delete']
|
|
626
|
-
LOOP
|
|
627
|
-
IF p_graph_rules ? l_trigger_op THEN
|
|
628
|
-
FOR l_rule_name, l_rule_config IN SELECT * FROM jsonb_each(p_graph_rules->l_trigger_op)
|
|
629
|
-
LOOP
|
|
630
|
-
FOR l_action IN SELECT * FROM jsonb_array_elements(l_rule_config->'actions')
|
|
631
|
-
LOOP
|
|
632
|
-
IF l_action->>'execution' = 'trigger' THEN
|
|
633
|
-
PERFORM dzql.create_event_trigger(p_table_name, l_trigger_op, l_action);
|
|
634
|
-
END IF;
|
|
635
|
-
END LOOP;
|
|
636
|
-
END LOOP;
|
|
637
|
-
END IF;
|
|
638
|
-
END LOOP;
|
|
639
|
-
END IF;
|
|
640
|
-
|
|
641
|
-
-- Log successful registration
|
|
642
|
-
RAISE NOTICE 'DZQL: Entity % registered successfully with graph rules support', p_table_name;
|
|
643
|
-
END $$;
|
|
644
|
-
|
|
645
|
-
-- ============================================================================
|
|
646
|
-
-- ENTITY UTILITIES
|
|
647
|
-
-- ============================================================================
|
|
648
|
-
|
|
649
|
-
-- Unregister an entity (removes configuration and API functions)
|
|
650
|
-
CREATE OR REPLACE FUNCTION dzql.unregister_entity(p_table_name text)
|
|
651
|
-
RETURNS void
|
|
652
|
-
LANGUAGE plpgsql AS $$
|
|
653
|
-
DECLARE
|
|
654
|
-
l_fn_names text[] := ARRAY['get_', 'save_', 'delete_', 'lookup_', 'search_'];
|
|
655
|
-
l_fn_name text;
|
|
656
|
-
BEGIN
|
|
657
|
-
-- Remove entity configuration
|
|
658
|
-
DELETE FROM dzql.entities WHERE table_name = p_table_name;
|
|
659
|
-
|
|
660
|
-
-- Drop API functions
|
|
661
|
-
FOREACH l_fn_name IN ARRAY l_fn_names
|
|
662
|
-
LOOP
|
|
663
|
-
EXECUTE format('DROP FUNCTION IF EXISTS dzql.%I(jsonb, int)', l_fn_name || p_table_name);
|
|
664
|
-
END LOOP;
|
|
665
|
-
|
|
666
|
-
RAISE NOTICE 'DZQL: Entity % unregistered successfully', p_table_name;
|
|
667
|
-
END $$;
|
|
668
|
-
|
|
669
|
-
-- List all registered entities
|
|
670
|
-
CREATE OR REPLACE FUNCTION dzql.list_entities()
|
|
671
|
-
RETURNS TABLE(
|
|
672
|
-
table_name text,
|
|
673
|
-
label_field text,
|
|
674
|
-
searchable_fields text[],
|
|
675
|
-
has_fk_includes boolean,
|
|
676
|
-
soft_delete boolean,
|
|
677
|
-
has_temporal_fields boolean,
|
|
678
|
-
has_notification_paths boolean,
|
|
679
|
-
has_permission_paths boolean,
|
|
680
|
-
has_graph_rules boolean
|
|
681
|
-
)
|
|
682
|
-
LANGUAGE sql AS $$
|
|
683
|
-
SELECT
|
|
684
|
-
e.table_name,
|
|
685
|
-
e.label_field,
|
|
686
|
-
e.searchable_fields,
|
|
687
|
-
(e.fk_includes IS NOT NULL AND e.fk_includes != '{}') as has_fk_includes,
|
|
688
|
-
e.soft_delete,
|
|
689
|
-
(e.temporal_fields IS NOT NULL AND e.temporal_fields != '{}') as has_temporal_fields,
|
|
690
|
-
(e.notification_paths IS NOT NULL AND e.notification_paths != '{}') as has_notification_paths,
|
|
691
|
-
(e.permission_paths IS NOT NULL AND e.permission_paths != '{}') as has_permission_paths,
|
|
692
|
-
(e.graph_rules IS NOT NULL AND e.graph_rules != '{}') as has_graph_rules
|
|
693
|
-
FROM dzql.entities e
|
|
694
|
-
ORDER BY e.table_name;
|
|
695
|
-
$$;
|
|
696
|
-
|
|
697
|
-
-- Get detailed entity configuration
|
|
698
|
-
CREATE OR REPLACE FUNCTION dzql.get_entity_config(p_table_name text)
|
|
699
|
-
RETURNS jsonb
|
|
700
|
-
LANGUAGE sql AS $$
|
|
701
|
-
SELECT to_jsonb(e.*)
|
|
702
|
-
FROM dzql.entities e
|
|
703
|
-
WHERE e.table_name = p_table_name;
|
|
704
|
-
$$;
|
|
705
|
-
|
|
706
|
-
-- Update entity graph rules only
|
|
707
|
-
CREATE OR REPLACE FUNCTION dzql.update_entity_graph_rules(
|
|
708
|
-
p_table_name text,
|
|
709
|
-
p_graph_rules jsonb
|
|
710
|
-
) RETURNS void
|
|
711
|
-
LANGUAGE plpgsql AS $$
|
|
712
|
-
BEGIN
|
|
713
|
-
-- Validate graph rules
|
|
714
|
-
IF p_graph_rules IS NOT NULL AND p_graph_rules != '{}' THEN
|
|
715
|
-
IF NOT dzql.validate_graph_rules(p_graph_rules) THEN
|
|
716
|
-
RAISE EXCEPTION 'Invalid graph rules for entity %', p_table_name;
|
|
717
|
-
END IF;
|
|
718
|
-
END IF;
|
|
719
|
-
|
|
720
|
-
-- Update only graph rules
|
|
721
|
-
UPDATE dzql.entities
|
|
722
|
-
SET graph_rules = p_graph_rules
|
|
723
|
-
WHERE table_name = p_table_name;
|
|
724
|
-
|
|
725
|
-
IF NOT FOUND THEN
|
|
726
|
-
RAISE EXCEPTION 'Entity % not found', p_table_name;
|
|
727
|
-
END IF;
|
|
728
|
-
|
|
729
|
-
RAISE NOTICE 'DZQL: Graph rules updated for entity %', p_table_name;
|
|
730
|
-
END $$;
|