dzql 0.9.0 → 0.11.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/README.md +29 -13
- package/package.json +2 -2
- package/src/compile/cascades.ts +151 -0
- package/src/compile/columns.ts +143 -0
- package/src/compile/holders.ts +66 -0
- package/src/compile/holds.ts +90 -0
- package/src/compile/ledger.ts +72 -0
- package/src/compile/lookups.ts +118 -0
- package/src/compile/opens.ts +69 -23
- package/src/compile/resolve.ts +75 -0
- package/src/compile/root.ts +61 -0
- package/src/compile/rows.ts +123 -0
- package/src/compile/scope.ts +88 -0
- package/src/compile/told.ts +95 -0
- package/src/postgres/auth-jwt-sql.ts +1 -1
- package/src/postgres/auth-jwt.ts +2 -2
- package/src/postgres/bootstrap.ts +2 -2
- package/src/postgres/codegen.ts +13 -2
- package/src/postgres/index.ts +2 -2
- package/src/postgres/listener.ts +50 -35
- package/src/postgres/pglite.ts +5 -2
- package/src/sql/001a-delta-meta.sql +55 -15
- package/src/sql/001b-delta-scope.sql +19 -451
- package/src/sql/001c-delta-read.sql +18 -296
- package/src/sql/001d-delta-write.sql +104 -330
- package/src/sql/001e-delta-ops.sql +4 -62
- package/src/sql/001g-delta-ledger.sql +51 -30
- package/src/sql/auth-jwt.sql +1 -1
|
@@ -27,71 +27,13 @@ RETURNS TABLE(version BIGINT, ops JSONB, applied JSONB) AS $$
|
|
|
27
27
|
$$ LANGUAGE sql STABLE;
|
|
28
28
|
|
|
29
29
|
-- ---------------------------------------------------------------------------
|
|
30
|
-
-- delta_open_at: time-travel
|
|
30
|
+
-- delta_open_at: time-travel -- a document as it stood at a point in time.
|
|
31
|
+
-- generateSql sends each document to its own read as it stood
|
|
32
|
+
-- (compileLookups, dzql_open_at__<slug>); before it is applied there is none.
|
|
31
33
|
-- ---------------------------------------------------------------------------
|
|
32
34
|
|
|
33
35
|
CREATE OR REPLACE FUNCTION delta_open_at(p_doc_name TEXT, p_at TIMESTAMPTZ)
|
|
34
|
-
RETURNS JSONB AS $$
|
|
35
|
-
DECLARE
|
|
36
|
-
v_def _delta_docs;
|
|
37
|
-
v_resolved JSONB;
|
|
38
|
-
v_mode TEXT;
|
|
39
|
-
v_doc_id BIGINT;
|
|
40
|
-
v_root_coll RECORD;
|
|
41
|
-
v_root_row JSONB;
|
|
42
|
-
v_result JSONB;
|
|
43
|
-
v_coll_key TEXT;
|
|
44
|
-
BEGIN
|
|
45
|
-
v_def := _delta_find_doc(p_doc_name);
|
|
46
|
-
IF v_def.prefix IS NULL THEN
|
|
47
|
-
RAISE EXCEPTION 'no doc def for: %', p_doc_name
|
|
48
|
-
USING ERRCODE = 'P0001';
|
|
49
|
-
END IF;
|
|
50
|
-
|
|
51
|
-
v_resolved := _delta_resolve_scope(v_def, p_doc_name);
|
|
52
|
-
v_mode := v_resolved->>'mode';
|
|
53
|
-
|
|
54
|
-
SELECT * INTO v_root_coll FROM _delta_collections
|
|
55
|
-
WHERE collection_key = v_def.root_collection;
|
|
56
|
-
IF NOT FOUND THEN
|
|
57
|
-
RAISE EXCEPTION 'doc "%" references unknown root collection: %',
|
|
58
|
-
p_doc_name, v_def.root_collection
|
|
59
|
-
USING ERRCODE = 'P0001';
|
|
60
|
-
END IF;
|
|
61
|
-
|
|
62
|
-
-- List mode (todo #74): as delta_open reads a list, at p_at -- each row as it shows (todo #70).
|
|
63
|
-
IF v_mode = 'list' THEN RETURN _delta_show_doc(v_def, _delta_read_list(v_def, v_resolved->>'where', p_at), FALSE); END IF;
|
|
64
|
-
|
|
65
|
-
v_doc_id := (v_resolved->'values'->>'id')::BIGINT;
|
|
66
|
-
|
|
67
|
-
-- the root as it stood, where it met the whole of its name's scope then
|
|
68
|
-
-- (a column beside its id), as delta_open reads it now
|
|
69
|
-
IF v_root_coll.temporal THEN
|
|
70
|
-
EXECUTE format(
|
|
71
|
-
'SELECT to_jsonb(t.*) FROM %I t WHERE t.id = $1 AND %s AND %s',
|
|
72
|
-
v_root_coll.table_name, _delta_temporal_where(p_at), v_resolved->>'where'
|
|
73
|
-
) INTO v_root_row USING v_doc_id;
|
|
74
|
-
v_root_row := _delta_strip_temporal(v_root_row);
|
|
75
|
-
ELSE
|
|
76
|
-
EXECUTE format('SELECT to_jsonb(t.*) FROM %I t WHERE t.id = $1 AND %s', v_root_coll.table_name, v_resolved->>'where')
|
|
77
|
-
INTO v_root_row USING v_doc_id;
|
|
78
|
-
END IF;
|
|
79
|
-
|
|
80
|
-
IF v_root_row IS NULL THEN RETURN NULL; END IF;
|
|
81
|
-
|
|
82
|
-
v_result := jsonb_build_object(v_def.root_collection, v_root_row);
|
|
83
|
-
|
|
84
|
-
FOREACH v_coll_key IN ARRAY v_def.include
|
|
85
|
-
LOOP
|
|
86
|
-
v_result := v_result || jsonb_build_object(
|
|
87
|
-
v_coll_key,
|
|
88
|
-
_delta_load_collection(v_coll_key, v_def.root_collection, v_doc_id, p_at)
|
|
89
|
-
);
|
|
90
|
-
END LOOP;
|
|
91
|
-
|
|
92
|
-
RETURN _delta_show_doc(v_def, v_result, TRUE);
|
|
93
|
-
END;
|
|
94
|
-
$$ LANGUAGE plpgsql STABLE;
|
|
36
|
+
RETURNS JSONB LANGUAGE plpgsql STABLE AS $$ BEGIN RAISE EXCEPTION 'no doc def for: %', p_doc_name USING ERRCODE = 'P0001'; END $$;
|
|
95
37
|
|
|
96
38
|
-- ---------------------------------------------------------------------------
|
|
97
39
|
-- delta_prune_ops: clean up old log entries -- and the names nobody holds
|
|
@@ -29,6 +29,8 @@ CREATE TABLE IF NOT EXISTS _delta_ledger (
|
|
|
29
29
|
CREATE INDEX IF NOT EXISTS idx_delta_ledger_doc ON _delta_ledger (doc_name, version);
|
|
30
30
|
CREATE INDEX IF NOT EXISTS idx_delta_ledger_cursor ON _delta_ledger (cursor);
|
|
31
31
|
CREATE INDEX IF NOT EXISTS idx_delta_ledger_undoes ON _delta_ledger (undoes);
|
|
32
|
+
-- a cursor's entries newest first: where an undo or a redo looks for its tip (_delta_ledger_tip)
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_delta_ledger_cursor_id ON _delta_ledger (cursor, id);
|
|
32
34
|
|
|
33
35
|
-- ---------------------------------------------------------------------------
|
|
34
36
|
-- _delta_row_at: the row a document holds at a row's path, split -- the twin
|
|
@@ -204,25 +206,44 @@ $$ LANGUAGE plpgsql;
|
|
|
204
206
|
-- what could be redone; an entry not undoable (a fact) starts no chain.
|
|
205
207
|
-- ---------------------------------------------------------------------------
|
|
206
208
|
|
|
209
|
+
-- _delta_ledger_tip: the entry an undo (p_back) or a redo of a cursor takes --
|
|
210
|
+
-- the newest tip of the cursor's chains that goes the way asked: back, a tip at
|
|
211
|
+
-- an even depth (a write, or a redo); forward, one at an odd depth (an undo that
|
|
212
|
+
-- changed something) newer than the cursor's last write. Found newest first,
|
|
213
|
+
-- among the cursor's own entries, so it costs what was written since the
|
|
214
|
+
-- answer, not the cursor's whole history (todo #28): every entry of a chain
|
|
215
|
+
-- carries its write's cursor, as delta_walk records it (an entry recorded by
|
|
216
|
+
-- hand as walking another cursor's chain is not one of this cursor's tips).
|
|
207
217
|
CREATE OR REPLACE FUNCTION _delta_ledger_tip(p_cursor TEXT, p_back BOOLEAN)
|
|
208
218
|
RETURNS _delta_ledger AS $$
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
219
|
+
DECLARE
|
|
220
|
+
v_entry _delta_ledger;
|
|
221
|
+
v_up _delta_ledger;
|
|
222
|
+
v_depth INT;
|
|
223
|
+
BEGIN
|
|
224
|
+
-- a step at a time, each the next older entry (LIMIT 1 on the cursor's index): it reads as far as it goes
|
|
225
|
+
SELECT * INTO v_entry FROM _delta_ledger WHERE cursor = p_cursor ORDER BY id DESC LIMIT 1;
|
|
226
|
+
WHILE v_entry.id IS NOT NULL LOOP
|
|
227
|
+
-- the cursor's last write, met newest first: a redo is of nothing older (a fresh write ends what could be redone)
|
|
228
|
+
IF NOT p_back AND v_entry.undoes IS NULL AND v_entry.undoable THEN RETURN NULL; END IF;
|
|
229
|
+
IF NOT EXISTS (SELECT 1 FROM _delta_ledger w WHERE w.undoes = v_entry.id) THEN -- a tip: nothing has walked it
|
|
230
|
+
-- its depth along its chain, up to the write that began it
|
|
231
|
+
v_depth := 0;
|
|
232
|
+
v_up := v_entry;
|
|
233
|
+
WHILE v_up.undoes IS NOT NULL LOOP
|
|
234
|
+
SELECT * INTO v_up FROM _delta_ledger WHERE id = v_up.undoes;
|
|
235
|
+
v_depth := v_depth + 1;
|
|
236
|
+
END LOOP;
|
|
237
|
+
IF v_up.cursor IS NOT DISTINCT FROM p_cursor AND v_up.undoable THEN -- a chain this cursor's undoable write began
|
|
238
|
+
IF p_back AND v_depth % 2 = 0 THEN RETURN v_entry; END IF;
|
|
239
|
+
IF NOT p_back AND v_depth % 2 = 1 AND v_entry.undoable THEN RETURN v_entry; END IF; -- a walk recorded as changing nothing is never redone
|
|
240
|
+
END IF;
|
|
241
|
+
END IF;
|
|
242
|
+
SELECT * INTO v_entry FROM _delta_ledger WHERE cursor = p_cursor AND id < v_entry.id ORDER BY id DESC LIMIT 1;
|
|
243
|
+
END LOOP;
|
|
244
|
+
RETURN NULL;
|
|
245
|
+
END;
|
|
246
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
226
247
|
|
|
227
248
|
-- JSON values the same, with JSON null and a missing value the same.
|
|
228
249
|
CREATE OR REPLACE FUNCTION _delta_same(a JSONB, b JSONB)
|
|
@@ -233,15 +254,17 @@ $$;
|
|
|
233
254
|
-- A field of a collection's row the same in two values: a timestamptz by the
|
|
234
255
|
-- instant it names, whatever zone its text was written in (a ledger's entry
|
|
235
256
|
-- keeps the session's TimeZone of its write), anything else as _delta_same.
|
|
257
|
+
-- generateSql writes each collection's time fields in (compileLedger); before
|
|
258
|
+
-- it is applied there is no collection, and values are the same as JSON.
|
|
236
259
|
CREATE OR REPLACE FUNCTION _delta_same(p_coll TEXT, p_field TEXT, a JSONB, b JSONB)
|
|
237
|
-
RETURNS BOOLEAN LANGUAGE sql STABLE AS $$
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
$$;
|
|
260
|
+
RETURNS BOOLEAN LANGUAGE sql STABLE AS $$ SELECT _delta_same(a, b); $$;
|
|
261
|
+
|
|
262
|
+
-- _delta_row_standing: row `p_id` of a collection as it stands, without its
|
|
263
|
+
-- validity, as {row} (row null: not there); NULL for a collection there is not.
|
|
264
|
+
-- What a walk guards a row beyond its document by. generateSql writes one for
|
|
265
|
+
-- each collection (compileLedger).
|
|
266
|
+
CREATE OR REPLACE FUNCTION _delta_row_standing(p_coll TEXT, p_id BIGINT)
|
|
267
|
+
RETURNS JSONB LANGUAGE sql STABLE AS $$ SELECT NULL::jsonb; $$;
|
|
245
268
|
|
|
246
269
|
-- _delta_walk_key: a row's key in a walk -- the keyOf of planWalk in
|
|
247
270
|
-- src/server/ledger.ts. A single document's root is one row at two paths,
|
|
@@ -306,11 +329,9 @@ BEGIN
|
|
|
306
329
|
FOR v_parts IN SELECT DISTINCT _delta_split_path(o->>'path') FROM jsonb_array_elements(p_entry.ops || p_entry.inverse) AS o LOOP
|
|
307
330
|
CONTINUE WHEN array_length(v_parts, 1) <> 2 OR v_parts[2] !~ '^[0-9]+$' OR v_parts[2]::numeric > 9007199254740991
|
|
308
331
|
OR v_parts[1] = v_def.root_collection OR v_parts[1] = ANY(COALESCE(v_def.include, ARRAY[]::text[]));
|
|
309
|
-
|
|
310
|
-
CONTINUE WHEN
|
|
311
|
-
|
|
312
|
-
INTO v_row USING v_parts[2]::bigint;
|
|
313
|
-
v_beyond := v_beyond || jsonb_build_array(jsonb_build_object('coll', v_parts[1], 'id', v_parts[2]::bigint, 'row', v_row));
|
|
332
|
+
v_row := _delta_row_standing(v_parts[1], v_parts[2]::bigint);
|
|
333
|
+
CONTINUE WHEN v_row IS NULL; -- a collection there is not
|
|
334
|
+
v_beyond := v_beyond || jsonb_build_array(jsonb_build_object('coll', v_parts[1], 'id', v_parts[2]::bigint, 'row', v_row->'row'));
|
|
314
335
|
END LOOP;
|
|
315
336
|
v_doc := _delta_beside(v_doc, v_def, v_beyond);
|
|
316
337
|
|
package/src/sql/auth-jwt.sql
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
-- auth-jwt — reference users schema + login / register stored functions.
|
|
3
3
|
-- =========================================================================
|
|
4
4
|
--
|
|
5
|
-
-- Apply this alongside the
|
|
5
|
+
-- Apply this alongside the dzql/auth-jwt module. Consumers who
|
|
6
6
|
-- want a different identity schema (sessions, OAuth, custom tables) can
|
|
7
7
|
-- substitute their own `login` / `register` functions and pass the SQL
|
|
8
8
|
-- strings via `jwtAuth({ loginSql, registerSql })`.
|