panda-core 0.1.11 → 0.1.12
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.
- checksums.yaml +4 -4
- data/Rakefile +5 -0
- data/db/migrate/20250121012333_logidze_install.rb +577 -0
- data/db/migrate/20250121012334_enable_hstore.rb +5 -0
- data/lib/panda/core/configuration.rb +4 -1
- data/lib/panda/core/engine.rb +3 -0
- data/lib/panda/core/railtie.rb +2 -0
- data/lib/panda/core/version.rb +1 -1
- metadata +59 -659
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc545a266b233a065a0ad1400d61566033d676509364cbb0b3264cfebf1f937c
|
4
|
+
data.tar.gz: 1328107817e05bf72dd11dd0b3b27a3d39813b792ef2cd19b7e3d545d12a9df8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 221e370f59805011182520d1846637996517b080639dae1273814b8d44409bcc21b5af9b60f668acae9519a849407bb47f590f8c5ec29be35c67d743ad87dff5
|
7
|
+
data.tar.gz: 07bf703d37be3e11224154713566817eebe59e99660a902d52c2c7b77f050049fb8334ea501ba939e71537e12d33c6dc38130f02f7dbd6c9dd17713fcf4bbc9c
|
data/Rakefile
CHANGED
@@ -0,0 +1,577 @@
|
|
1
|
+
class LogidzeInstall < ActiveRecord::Migration[8.0]
|
2
|
+
def up
|
3
|
+
execute <<~SQL
|
4
|
+
CREATE OR REPLACE FUNCTION logidze_capture_exception(error_data jsonb) RETURNS boolean AS $body$
|
5
|
+
-- version: 1
|
6
|
+
BEGIN
|
7
|
+
-- Feel free to change this function to change Logidze behavior on exception.
|
8
|
+
--
|
9
|
+
-- Return `false` to raise exception or `true` to commit record changes.
|
10
|
+
--
|
11
|
+
-- `error_data` contains:
|
12
|
+
-- - returned_sqlstate
|
13
|
+
-- - message_text
|
14
|
+
-- - pg_exception_detail
|
15
|
+
-- - pg_exception_hint
|
16
|
+
-- - pg_exception_context
|
17
|
+
-- - schema_name
|
18
|
+
-- - table_name
|
19
|
+
-- Learn more about available keys:
|
20
|
+
-- https://www.postgresql.org/docs/9.6/plpgsql-control-structures.html#PLPGSQL-EXCEPTION-DIAGNOSTICS-VALUES
|
21
|
+
--
|
22
|
+
|
23
|
+
return false;
|
24
|
+
END;
|
25
|
+
$body$
|
26
|
+
LANGUAGE plpgsql;
|
27
|
+
|
28
|
+
CREATE OR REPLACE FUNCTION logidze_compact_history(log_data jsonb, cutoff integer DEFAULT 1) RETURNS jsonb AS $body$
|
29
|
+
-- version: 1
|
30
|
+
DECLARE
|
31
|
+
merged jsonb;
|
32
|
+
BEGIN
|
33
|
+
LOOP
|
34
|
+
merged := jsonb_build_object(
|
35
|
+
'ts',
|
36
|
+
log_data#>'{h,1,ts}',
|
37
|
+
'v',
|
38
|
+
log_data#>'{h,1,v}',
|
39
|
+
'c',
|
40
|
+
(log_data#>'{h,0,c}') || (log_data#>'{h,1,c}')
|
41
|
+
);
|
42
|
+
|
43
|
+
IF (log_data#>'{h,1}' ? 'm') THEN
|
44
|
+
merged := jsonb_set(merged, ARRAY['m'], log_data#>'{h,1,m}');
|
45
|
+
END IF;
|
46
|
+
|
47
|
+
log_data := jsonb_set(
|
48
|
+
log_data,
|
49
|
+
'{h}',
|
50
|
+
jsonb_set(
|
51
|
+
log_data->'h',
|
52
|
+
'{1}',
|
53
|
+
merged
|
54
|
+
) - 0
|
55
|
+
);
|
56
|
+
|
57
|
+
cutoff := cutoff - 1;
|
58
|
+
|
59
|
+
EXIT WHEN cutoff <= 0;
|
60
|
+
END LOOP;
|
61
|
+
|
62
|
+
return log_data;
|
63
|
+
END;
|
64
|
+
$body$
|
65
|
+
LANGUAGE plpgsql;
|
66
|
+
|
67
|
+
CREATE OR REPLACE FUNCTION logidze_filter_keys(obj jsonb, keys text[], include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
|
68
|
+
-- version: 1
|
69
|
+
DECLARE
|
70
|
+
res jsonb;
|
71
|
+
key text;
|
72
|
+
BEGIN
|
73
|
+
res := '{}';
|
74
|
+
|
75
|
+
IF include_columns THEN
|
76
|
+
FOREACH key IN ARRAY keys
|
77
|
+
LOOP
|
78
|
+
IF obj ? key THEN
|
79
|
+
res = jsonb_insert(res, ARRAY[key], obj->key);
|
80
|
+
END IF;
|
81
|
+
END LOOP;
|
82
|
+
ELSE
|
83
|
+
res = obj;
|
84
|
+
FOREACH key IN ARRAY keys
|
85
|
+
LOOP
|
86
|
+
res = res - key;
|
87
|
+
END LOOP;
|
88
|
+
END IF;
|
89
|
+
|
90
|
+
RETURN res;
|
91
|
+
END;
|
92
|
+
$body$
|
93
|
+
LANGUAGE plpgsql;
|
94
|
+
|
95
|
+
CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
96
|
+
-- version: 4
|
97
|
+
DECLARE
|
98
|
+
changes jsonb;
|
99
|
+
version jsonb;
|
100
|
+
full_snapshot boolean;
|
101
|
+
log_data jsonb;
|
102
|
+
new_v integer;
|
103
|
+
size integer;
|
104
|
+
history_limit integer;
|
105
|
+
debounce_time integer;
|
106
|
+
current_version integer;
|
107
|
+
k text;
|
108
|
+
iterator integer;
|
109
|
+
item record;
|
110
|
+
columns text[];
|
111
|
+
include_columns boolean;
|
112
|
+
ts timestamp with time zone;
|
113
|
+
ts_column text;
|
114
|
+
err_sqlstate text;
|
115
|
+
err_message text;
|
116
|
+
err_detail text;
|
117
|
+
err_hint text;
|
118
|
+
err_context text;
|
119
|
+
err_table_name text;
|
120
|
+
err_schema_name text;
|
121
|
+
err_jsonb jsonb;
|
122
|
+
err_captured boolean;
|
123
|
+
BEGIN
|
124
|
+
ts_column := NULLIF(TG_ARGV[1], 'null');
|
125
|
+
columns := NULLIF(TG_ARGV[2], 'null');
|
126
|
+
include_columns := NULLIF(TG_ARGV[3], 'null');
|
127
|
+
|
128
|
+
IF NEW.log_data is NULL OR NEW.log_data = '{}'::jsonb
|
129
|
+
THEN
|
130
|
+
IF columns IS NOT NULL THEN
|
131
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
132
|
+
ELSE
|
133
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
134
|
+
END IF;
|
135
|
+
|
136
|
+
IF log_data#>>'{h, -1, c}' != '{}' THEN
|
137
|
+
NEW.log_data := log_data;
|
138
|
+
END IF;
|
139
|
+
|
140
|
+
ELSE
|
141
|
+
|
142
|
+
IF TG_OP = 'UPDATE' AND (to_jsonb(NEW.*) = to_jsonb(OLD.*)) THEN
|
143
|
+
RETURN NEW; -- pass
|
144
|
+
END IF;
|
145
|
+
|
146
|
+
history_limit := NULLIF(TG_ARGV[0], 'null');
|
147
|
+
debounce_time := NULLIF(TG_ARGV[4], 'null');
|
148
|
+
|
149
|
+
log_data := NEW.log_data;
|
150
|
+
|
151
|
+
current_version := (log_data->>'v')::int;
|
152
|
+
|
153
|
+
IF ts_column IS NULL THEN
|
154
|
+
ts := statement_timestamp();
|
155
|
+
ELSEIF TG_OP = 'UPDATE' THEN
|
156
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
157
|
+
IF ts IS NULL OR ts = (to_jsonb(OLD.*) ->> ts_column)::timestamp with time zone THEN
|
158
|
+
ts := statement_timestamp();
|
159
|
+
END IF;
|
160
|
+
ELSEIF TG_OP = 'INSERT' THEN
|
161
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
162
|
+
IF ts IS NULL OR (extract(epoch from ts) * 1000)::bigint = (NEW.log_data #>> '{h,-1,ts}')::bigint THEN
|
163
|
+
ts := statement_timestamp();
|
164
|
+
END IF;
|
165
|
+
END IF;
|
166
|
+
|
167
|
+
full_snapshot := (coalesce(current_setting('logidze.full_snapshot', true), '') = 'on') OR (TG_OP = 'INSERT');
|
168
|
+
|
169
|
+
IF current_version < (log_data#>>'{h,-1,v}')::int THEN
|
170
|
+
iterator := 0;
|
171
|
+
FOR item in SELECT * FROM jsonb_array_elements(log_data->'h')
|
172
|
+
LOOP
|
173
|
+
IF (item.value->>'v')::int > current_version THEN
|
174
|
+
log_data := jsonb_set(
|
175
|
+
log_data,
|
176
|
+
'{h}',
|
177
|
+
(log_data->'h') - iterator
|
178
|
+
);
|
179
|
+
END IF;
|
180
|
+
iterator := iterator + 1;
|
181
|
+
END LOOP;
|
182
|
+
END IF;
|
183
|
+
|
184
|
+
changes := '{}';
|
185
|
+
|
186
|
+
IF full_snapshot THEN
|
187
|
+
BEGIN
|
188
|
+
changes = hstore_to_jsonb_loose(hstore(NEW.*));
|
189
|
+
EXCEPTION
|
190
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
191
|
+
changes = row_to_json(NEW.*)::jsonb;
|
192
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
193
|
+
LOOP
|
194
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
195
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
196
|
+
END IF;
|
197
|
+
END LOOP;
|
198
|
+
END;
|
199
|
+
ELSE
|
200
|
+
BEGIN
|
201
|
+
changes = hstore_to_jsonb_loose(
|
202
|
+
hstore(NEW.*) - hstore(OLD.*)
|
203
|
+
);
|
204
|
+
EXCEPTION
|
205
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
206
|
+
changes = (SELECT
|
207
|
+
COALESCE(json_object_agg(key, value), '{}')::jsonb
|
208
|
+
FROM
|
209
|
+
jsonb_each(row_to_json(NEW.*)::jsonb)
|
210
|
+
WHERE NOT jsonb_build_object(key, value) <@ row_to_json(OLD.*)::jsonb);
|
211
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
212
|
+
LOOP
|
213
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
214
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
215
|
+
END IF;
|
216
|
+
END LOOP;
|
217
|
+
END;
|
218
|
+
END IF;
|
219
|
+
|
220
|
+
changes = changes - 'log_data';
|
221
|
+
|
222
|
+
IF columns IS NOT NULL THEN
|
223
|
+
changes = logidze_filter_keys(changes, columns, include_columns);
|
224
|
+
END IF;
|
225
|
+
|
226
|
+
IF changes = '{}' THEN
|
227
|
+
RETURN NEW; -- pass
|
228
|
+
END IF;
|
229
|
+
|
230
|
+
new_v := (log_data#>>'{h,-1,v}')::int + 1;
|
231
|
+
|
232
|
+
size := jsonb_array_length(log_data->'h');
|
233
|
+
version := logidze_version(new_v, changes, ts);
|
234
|
+
|
235
|
+
IF (
|
236
|
+
debounce_time IS NOT NULL AND
|
237
|
+
(version->>'ts')::bigint - (log_data#>'{h,-1,ts}')::text::bigint <= debounce_time
|
238
|
+
) THEN
|
239
|
+
-- merge new version with the previous one
|
240
|
+
new_v := (log_data#>>'{h,-1,v}')::int;
|
241
|
+
version := logidze_version(new_v, (log_data#>'{h,-1,c}')::jsonb || changes, ts);
|
242
|
+
-- remove the previous version from log
|
243
|
+
log_data := jsonb_set(
|
244
|
+
log_data,
|
245
|
+
'{h}',
|
246
|
+
(log_data->'h') - (size - 1)
|
247
|
+
);
|
248
|
+
END IF;
|
249
|
+
|
250
|
+
log_data := jsonb_set(
|
251
|
+
log_data,
|
252
|
+
ARRAY['h', size::text],
|
253
|
+
version,
|
254
|
+
true
|
255
|
+
);
|
256
|
+
|
257
|
+
log_data := jsonb_set(
|
258
|
+
log_data,
|
259
|
+
'{v}',
|
260
|
+
to_jsonb(new_v)
|
261
|
+
);
|
262
|
+
|
263
|
+
IF history_limit IS NOT NULL AND history_limit <= size THEN
|
264
|
+
log_data := logidze_compact_history(log_data, size - history_limit + 1);
|
265
|
+
END IF;
|
266
|
+
|
267
|
+
NEW.log_data := log_data;
|
268
|
+
END IF;
|
269
|
+
|
270
|
+
RETURN NEW; -- result
|
271
|
+
EXCEPTION
|
272
|
+
WHEN OTHERS THEN
|
273
|
+
GET STACKED DIAGNOSTICS err_sqlstate = RETURNED_SQLSTATE,
|
274
|
+
err_message = MESSAGE_TEXT,
|
275
|
+
err_detail = PG_EXCEPTION_DETAIL,
|
276
|
+
err_hint = PG_EXCEPTION_HINT,
|
277
|
+
err_context = PG_EXCEPTION_CONTEXT,
|
278
|
+
err_schema_name = SCHEMA_NAME,
|
279
|
+
err_table_name = TABLE_NAME;
|
280
|
+
err_jsonb := jsonb_build_object(
|
281
|
+
'returned_sqlstate', err_sqlstate,
|
282
|
+
'message_text', err_message,
|
283
|
+
'pg_exception_detail', err_detail,
|
284
|
+
'pg_exception_hint', err_hint,
|
285
|
+
'pg_exception_context', err_context,
|
286
|
+
'schema_name', err_schema_name,
|
287
|
+
'table_name', err_table_name
|
288
|
+
);
|
289
|
+
err_captured = logidze_capture_exception(err_jsonb);
|
290
|
+
IF err_captured THEN
|
291
|
+
return NEW;
|
292
|
+
ELSE
|
293
|
+
RAISE;
|
294
|
+
END IF;
|
295
|
+
END;
|
296
|
+
$body$
|
297
|
+
LANGUAGE plpgsql;
|
298
|
+
|
299
|
+
CREATE OR REPLACE FUNCTION logidze_logger_after() RETURNS TRIGGER AS $body$
|
300
|
+
-- version: 4
|
301
|
+
|
302
|
+
|
303
|
+
DECLARE
|
304
|
+
changes jsonb;
|
305
|
+
version jsonb;
|
306
|
+
full_snapshot boolean;
|
307
|
+
log_data jsonb;
|
308
|
+
new_v integer;
|
309
|
+
size integer;
|
310
|
+
history_limit integer;
|
311
|
+
debounce_time integer;
|
312
|
+
current_version integer;
|
313
|
+
k text;
|
314
|
+
iterator integer;
|
315
|
+
item record;
|
316
|
+
columns text[];
|
317
|
+
include_columns boolean;
|
318
|
+
ts timestamp with time zone;
|
319
|
+
ts_column text;
|
320
|
+
err_sqlstate text;
|
321
|
+
err_message text;
|
322
|
+
err_detail text;
|
323
|
+
err_hint text;
|
324
|
+
err_context text;
|
325
|
+
err_table_name text;
|
326
|
+
err_schema_name text;
|
327
|
+
err_jsonb jsonb;
|
328
|
+
err_captured boolean;
|
329
|
+
BEGIN
|
330
|
+
ts_column := NULLIF(TG_ARGV[1], 'null');
|
331
|
+
columns := NULLIF(TG_ARGV[2], 'null');
|
332
|
+
include_columns := NULLIF(TG_ARGV[3], 'null');
|
333
|
+
|
334
|
+
IF NEW.log_data is NULL OR NEW.log_data = '{}'::jsonb
|
335
|
+
THEN
|
336
|
+
IF columns IS NOT NULL THEN
|
337
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
338
|
+
ELSE
|
339
|
+
log_data = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
340
|
+
END IF;
|
341
|
+
|
342
|
+
IF log_data#>>'{h, -1, c}' != '{}' THEN
|
343
|
+
NEW.log_data := log_data;
|
344
|
+
END IF;
|
345
|
+
|
346
|
+
ELSE
|
347
|
+
|
348
|
+
IF TG_OP = 'UPDATE' AND (to_jsonb(NEW.*) = to_jsonb(OLD.*)) THEN
|
349
|
+
RETURN NULL;
|
350
|
+
END IF;
|
351
|
+
|
352
|
+
history_limit := NULLIF(TG_ARGV[0], 'null');
|
353
|
+
debounce_time := NULLIF(TG_ARGV[4], 'null');
|
354
|
+
|
355
|
+
log_data := NEW.log_data;
|
356
|
+
|
357
|
+
current_version := (log_data->>'v')::int;
|
358
|
+
|
359
|
+
IF ts_column IS NULL THEN
|
360
|
+
ts := statement_timestamp();
|
361
|
+
ELSEIF TG_OP = 'UPDATE' THEN
|
362
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
363
|
+
IF ts IS NULL OR ts = (to_jsonb(OLD.*) ->> ts_column)::timestamp with time zone THEN
|
364
|
+
ts := statement_timestamp();
|
365
|
+
END IF;
|
366
|
+
ELSEIF TG_OP = 'INSERT' THEN
|
367
|
+
ts := (to_jsonb(NEW.*) ->> ts_column)::timestamp with time zone;
|
368
|
+
IF ts IS NULL OR (extract(epoch from ts) * 1000)::bigint = (NEW.log_data #>> '{h,-1,ts}')::bigint THEN
|
369
|
+
ts := statement_timestamp();
|
370
|
+
END IF;
|
371
|
+
END IF;
|
372
|
+
|
373
|
+
full_snapshot := (coalesce(current_setting('logidze.full_snapshot', true), '') = 'on') OR (TG_OP = 'INSERT');
|
374
|
+
|
375
|
+
IF current_version < (log_data#>>'{h,-1,v}')::int THEN
|
376
|
+
iterator := 0;
|
377
|
+
FOR item in SELECT * FROM jsonb_array_elements(log_data->'h')
|
378
|
+
LOOP
|
379
|
+
IF (item.value->>'v')::int > current_version THEN
|
380
|
+
log_data := jsonb_set(
|
381
|
+
log_data,
|
382
|
+
'{h}',
|
383
|
+
(log_data->'h') - iterator
|
384
|
+
);
|
385
|
+
END IF;
|
386
|
+
iterator := iterator + 1;
|
387
|
+
END LOOP;
|
388
|
+
END IF;
|
389
|
+
|
390
|
+
changes := '{}';
|
391
|
+
|
392
|
+
IF full_snapshot THEN
|
393
|
+
BEGIN
|
394
|
+
changes = hstore_to_jsonb_loose(hstore(NEW.*));
|
395
|
+
EXCEPTION
|
396
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
397
|
+
changes = row_to_json(NEW.*)::jsonb;
|
398
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
399
|
+
LOOP
|
400
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
401
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
402
|
+
END IF;
|
403
|
+
END LOOP;
|
404
|
+
END;
|
405
|
+
ELSE
|
406
|
+
BEGIN
|
407
|
+
changes = hstore_to_jsonb_loose(
|
408
|
+
hstore(NEW.*) - hstore(OLD.*)
|
409
|
+
);
|
410
|
+
EXCEPTION
|
411
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
412
|
+
changes = (SELECT
|
413
|
+
COALESCE(json_object_agg(key, value), '{}')::jsonb
|
414
|
+
FROM
|
415
|
+
jsonb_each(row_to_json(NEW.*)::jsonb)
|
416
|
+
WHERE NOT jsonb_build_object(key, value) <@ row_to_json(OLD.*)::jsonb);
|
417
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
418
|
+
LOOP
|
419
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
420
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
421
|
+
END IF;
|
422
|
+
END LOOP;
|
423
|
+
END;
|
424
|
+
END IF;
|
425
|
+
|
426
|
+
changes = changes - 'log_data';
|
427
|
+
|
428
|
+
IF columns IS NOT NULL THEN
|
429
|
+
changes = logidze_filter_keys(changes, columns, include_columns);
|
430
|
+
END IF;
|
431
|
+
|
432
|
+
IF changes = '{}' THEN
|
433
|
+
RETURN NULL;
|
434
|
+
END IF;
|
435
|
+
|
436
|
+
new_v := (log_data#>>'{h,-1,v}')::int + 1;
|
437
|
+
|
438
|
+
size := jsonb_array_length(log_data->'h');
|
439
|
+
version := logidze_version(new_v, changes, ts);
|
440
|
+
|
441
|
+
IF (
|
442
|
+
debounce_time IS NOT NULL AND
|
443
|
+
(version->>'ts')::bigint - (log_data#>'{h,-1,ts}')::text::bigint <= debounce_time
|
444
|
+
) THEN
|
445
|
+
-- merge new version with the previous one
|
446
|
+
new_v := (log_data#>>'{h,-1,v}')::int;
|
447
|
+
version := logidze_version(new_v, (log_data#>'{h,-1,c}')::jsonb || changes, ts);
|
448
|
+
-- remove the previous version from log
|
449
|
+
log_data := jsonb_set(
|
450
|
+
log_data,
|
451
|
+
'{h}',
|
452
|
+
(log_data->'h') - (size - 1)
|
453
|
+
);
|
454
|
+
END IF;
|
455
|
+
|
456
|
+
log_data := jsonb_set(
|
457
|
+
log_data,
|
458
|
+
ARRAY['h', size::text],
|
459
|
+
version,
|
460
|
+
true
|
461
|
+
);
|
462
|
+
|
463
|
+
log_data := jsonb_set(
|
464
|
+
log_data,
|
465
|
+
'{v}',
|
466
|
+
to_jsonb(new_v)
|
467
|
+
);
|
468
|
+
|
469
|
+
IF history_limit IS NOT NULL AND history_limit <= size THEN
|
470
|
+
log_data := logidze_compact_history(log_data, size - history_limit + 1);
|
471
|
+
END IF;
|
472
|
+
|
473
|
+
NEW.log_data := log_data;
|
474
|
+
END IF;
|
475
|
+
|
476
|
+
EXECUTE format('UPDATE %I.%I SET "log_data" = $1 WHERE ctid = %L', TG_TABLE_SCHEMA, TG_TABLE_NAME, NEW.CTID) USING NEW.log_data;
|
477
|
+
RETURN NULL;
|
478
|
+
EXCEPTION
|
479
|
+
WHEN OTHERS THEN
|
480
|
+
GET STACKED DIAGNOSTICS err_sqlstate = RETURNED_SQLSTATE,
|
481
|
+
err_message = MESSAGE_TEXT,
|
482
|
+
err_detail = PG_EXCEPTION_DETAIL,
|
483
|
+
err_hint = PG_EXCEPTION_HINT,
|
484
|
+
err_context = PG_EXCEPTION_CONTEXT,
|
485
|
+
err_schema_name = SCHEMA_NAME,
|
486
|
+
err_table_name = TABLE_NAME;
|
487
|
+
err_jsonb := jsonb_build_object(
|
488
|
+
'returned_sqlstate', err_sqlstate,
|
489
|
+
'message_text', err_message,
|
490
|
+
'pg_exception_detail', err_detail,
|
491
|
+
'pg_exception_hint', err_hint,
|
492
|
+
'pg_exception_context', err_context,
|
493
|
+
'schema_name', err_schema_name,
|
494
|
+
'table_name', err_table_name
|
495
|
+
);
|
496
|
+
err_captured = logidze_capture_exception(err_jsonb);
|
497
|
+
IF err_captured THEN
|
498
|
+
return NEW;
|
499
|
+
ELSE
|
500
|
+
RAISE;
|
501
|
+
END IF;
|
502
|
+
END;
|
503
|
+
$body$
|
504
|
+
LANGUAGE plpgsql;
|
505
|
+
|
506
|
+
|
507
|
+
CREATE OR REPLACE FUNCTION logidze_snapshot(item jsonb, ts_column text DEFAULT NULL, columns text[] DEFAULT NULL, include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
|
508
|
+
-- version: 3
|
509
|
+
DECLARE
|
510
|
+
ts timestamp with time zone;
|
511
|
+
k text;
|
512
|
+
BEGIN
|
513
|
+
item = item - 'log_data';
|
514
|
+
IF ts_column IS NULL THEN
|
515
|
+
ts := statement_timestamp();
|
516
|
+
ELSE
|
517
|
+
ts := coalesce((item->>ts_column)::timestamp with time zone, statement_timestamp());
|
518
|
+
END IF;
|
519
|
+
|
520
|
+
IF columns IS NOT NULL THEN
|
521
|
+
item := logidze_filter_keys(item, columns, include_columns);
|
522
|
+
END IF;
|
523
|
+
|
524
|
+
FOR k IN (SELECT key FROM jsonb_each(item))
|
525
|
+
LOOP
|
526
|
+
IF jsonb_typeof(item->k) = 'object' THEN
|
527
|
+
item := jsonb_set(item, ARRAY[k], to_jsonb(item->>k));
|
528
|
+
END IF;
|
529
|
+
END LOOP;
|
530
|
+
|
531
|
+
return json_build_object(
|
532
|
+
'v', 1,
|
533
|
+
'h', jsonb_build_array(
|
534
|
+
logidze_version(1, item, ts)
|
535
|
+
)
|
536
|
+
);
|
537
|
+
END;
|
538
|
+
$body$
|
539
|
+
LANGUAGE plpgsql;
|
540
|
+
|
541
|
+
CREATE OR REPLACE FUNCTION logidze_version(v bigint, data jsonb, ts timestamp with time zone) RETURNS jsonb AS $body$
|
542
|
+
-- version: 2
|
543
|
+
DECLARE
|
544
|
+
buf jsonb;
|
545
|
+
BEGIN
|
546
|
+
data = data - 'log_data';
|
547
|
+
buf := jsonb_build_object(
|
548
|
+
'ts',
|
549
|
+
(extract(epoch from ts) * 1000)::bigint,
|
550
|
+
'v',
|
551
|
+
v,
|
552
|
+
'c',
|
553
|
+
data
|
554
|
+
);
|
555
|
+
IF coalesce(current_setting('logidze.meta', true), '') <> '' THEN
|
556
|
+
buf := jsonb_insert(buf, '{m}', current_setting('logidze.meta')::jsonb);
|
557
|
+
END IF;
|
558
|
+
RETURN buf;
|
559
|
+
END;
|
560
|
+
$body$
|
561
|
+
LANGUAGE plpgsql;
|
562
|
+
|
563
|
+
SQL
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
def down
|
568
|
+
execute <<~SQL
|
569
|
+
DROP FUNCTION IF EXISTS logidze_capture_exception(jsonb) CASCADE;
|
570
|
+
DROP FUNCTION IF EXISTS logidze_compact_history(jsonb, integer) CASCADE;
|
571
|
+
DROP FUNCTION IF EXISTS logidze_filter_keys(jsonb, text[], boolean) CASCADE;
|
572
|
+
DROP FUNCTION IF EXISTS logidze_logger() CASCADE;
|
573
|
+
DROP FUNCTION IF EXISTS logidze_logger_after() CASCADE;
|
574
|
+
DROP FUNCTION IF EXISTS logidze_snapshot(jsonb, text, text[], boolean) CASCADE;
|
575
|
+
DROP FUNCTION IF EXISTS logidze_version(bigint, jsonb, timestamp with time zone) CASCADE;
|
576
|
+
SQL
|
577
|
+
end
|
@@ -2,6 +2,7 @@ module Panda
|
|
2
2
|
module Core
|
3
3
|
class Configuration
|
4
4
|
attr_accessor :user_class,
|
5
|
+
:user_identity_class,
|
5
6
|
:storage_provider,
|
6
7
|
:cache_store,
|
7
8
|
:parent_controller,
|
@@ -11,13 +12,15 @@ module Panda
|
|
11
12
|
:session_token_cookie
|
12
13
|
|
13
14
|
def initialize
|
15
|
+
@user_class = "Panda::Core::User"
|
16
|
+
@user_identity_class = "Panda::Core::UserIdentity"
|
14
17
|
@storage_provider = :active_storage
|
15
18
|
@cache_store = :memory_store
|
16
19
|
@parent_controller = "ActionController::API"
|
17
20
|
@parent_mailer = "ActionMailer::Base"
|
18
21
|
@mailer_sender = "support@example.com"
|
19
22
|
@mailer_default_url_options = {host: "localhost:3000"}
|
20
|
-
@session_token_cookie = :
|
23
|
+
@session_token_cookie = :panda_session
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
data/lib/panda/core/engine.rb
CHANGED
@@ -7,10 +7,13 @@ require "dry-configurable"
|
|
7
7
|
require "faraday"
|
8
8
|
require "faraday/multipart"
|
9
9
|
require "faraday/retry"
|
10
|
+
require "fx"
|
10
11
|
require "image_processing"
|
11
12
|
require "importmap-rails"
|
13
|
+
require "logidze"
|
12
14
|
require "lookbook"
|
13
15
|
require "omniauth"
|
16
|
+
require "omniauth/rails_csrf_protection"
|
14
17
|
require "propshaft"
|
15
18
|
require "redis"
|
16
19
|
require "silencer"
|
data/lib/panda/core/railtie.rb
CHANGED