@mason-data/cli 0.8.0 → 0.10.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 +19 -8
- package/dist/src/api/error.js +4 -0
- package/dist/src/api/postgres/catalog-worker.js +49 -2
- package/dist/src/api/postgres/catalog.js +166 -32
- package/dist/src/api/postgres/listener.js +11 -3
- package/dist/src/api/postgres/proxy.js +129 -0
- package/dist/src/api/postgres/schema.js +6 -0
- package/dist/src/api/postgres/server.js +527 -103
- package/dist/src/api/postgres/translate.js +47 -2
- package/dist/src/cli/background.js +18 -19
- package/dist/src/cli/bridge-stdin.js +101 -0
- package/dist/src/cli/command.js +337 -15
- package/dist/src/cli/commands.js +458 -169
- package/dist/src/cli/credentials.js +182 -0
- package/dist/src/cli/demo.js +2 -2
- package/dist/src/cli/deploy.js +680 -0
- package/dist/src/cli/dev.js +148 -27
- package/dist/src/cli/doctor.js +122 -36
- package/dist/src/cli/generated-files.js +143 -0
- package/dist/src/cli/guides.js +116 -16
- package/dist/src/cli/hosted.js +850 -0
- package/dist/src/cli/init.js +154 -33
- package/dist/src/cli/login.js +292 -0
- package/dist/src/cli/machine.js +125 -40
- package/dist/src/cli/main.js +59 -10
- package/dist/src/cli/mason.cjs +17 -0
- package/dist/src/cli/mcp.js +17 -14
- package/dist/src/cli/output-gate.js +176 -0
- package/dist/src/cli/page-share.js +96 -0
- package/dist/src/cli/pg-tls.js +1 -1
- package/dist/src/cli/pin.js +6 -2
- package/dist/src/cli/pool-size.cjs +85 -0
- package/dist/src/cli/ports.js +2 -0
- package/dist/src/cli/preflight.js +130 -39
- package/dist/src/cli/project.js +100 -1
- package/dist/src/cli/readiness.js +42 -6
- package/dist/src/cli/registry.js +89 -36
- package/dist/src/cli/replica-hosted.js +1324 -0
- package/dist/src/cli/replica.js +114 -0
- package/dist/src/cli/replication-mode.js +5 -0
- package/dist/src/cli/request-filter.js +16 -6
- package/dist/src/cli/roles.js +2 -1
- package/dist/src/cli/run-server.js +179 -9
- package/dist/src/cli/secret-input.js +193 -0
- package/dist/src/cli/security.js +57 -10
- package/dist/src/cli/serve.js +65 -9
- package/dist/src/cli/share-run.js +51 -8
- package/dist/src/cli/share.js +23 -18
- package/dist/src/cli/source-edit.js +10 -4
- package/dist/src/cli/source-role.js +343 -0
- package/dist/src/cli/sources.js +50 -23
- package/dist/src/cli/style.js +16 -1
- package/dist/src/cli/supervise.js +15 -23
- package/dist/src/cli/sync.js +17 -8
- package/dist/src/cli/token-hosted.js +343 -0
- package/dist/src/cli/tunnel.js +1 -1
- package/dist/src/cli/ui-hosted.js +163 -0
- package/dist/src/cli/ui-ssh.js +460 -0
- package/dist/src/cli/ui.js +162 -17
- package/dist/src/cli/usage.js +19 -15
- package/dist/src/cli/user-hosted.js +364 -0
- package/dist/src/cli/user.js +275 -105
- package/dist/src/demo/seed.js +26 -0
- package/dist/src/duck/analyze.js +36 -20
- package/dist/src/duck/collation.js +266 -0
- package/dist/src/duck/constant-key.js +94 -0
- package/dist/src/duck/distinct-key.js +283 -26
- package/dist/src/duck/events.js +39 -8
- package/dist/src/duck/executor-worker.js +2 -2
- package/dist/src/duck/executor.js +6 -5
- package/dist/src/duck/explain.js +4 -0
- package/dist/src/duck/functions.js +448 -49
- package/dist/src/duck/gaps.js +34 -16
- package/dist/src/duck/generated-plan.js +1070 -0
- package/dist/src/duck/generated.js +906 -0
- package/dist/src/duck/grants.js +1 -1
- package/dist/src/duck/held-types.js +64 -0
- package/dist/src/duck/history.js +4 -0
- package/dist/src/duck/jsonb-order.js +9 -12
- package/dist/src/duck/kept-route.js +6 -5
- package/dist/src/duck/kept.js +9 -7
- package/dist/src/duck/late.js +1469 -40
- package/dist/src/duck/libc-bound.js +341 -0
- package/dist/src/duck/libc-order.js +892 -126
- package/dist/src/duck/pg-eval-worker.js +7 -1
- package/dist/src/duck/pg-eval.js +6 -4
- package/dist/src/duck/pgtree.js +774 -23
- package/dist/src/duck/plan.js +275 -30
- package/dist/src/duck/publication.js +14 -0
- package/dist/src/duck/requests.js +9 -5
- package/dist/src/duck/resolved.js +117 -2
- package/dist/src/duck/rewrite.js +4567 -583
- package/dist/src/duck/row-filter.js +12 -6
- package/dist/src/duck/statement.js +19 -0
- package/dist/src/duck/store.js +294 -68
- package/dist/src/duck/stream.js +9 -5
- package/dist/src/duck/sync.js +602 -126
- package/dist/src/duck/token.js +4 -1
- package/dist/src/duck/types.js +63 -7
- package/dist/src/duck/usage.js +6 -4
- package/dist/src/duck/users.js +110 -9
- package/dist/src/duck/verified.js +159 -2
- package/dist/src/duck/views.js +4 -3
- package/dist/src/lib/cidr.js +177 -0
- package/dist/src/lib/database-url.js +55 -10
- package/dist/src/lib/instant.js +50 -0
- package/dist/src/lib/log-buffer.js +13 -4
- package/dist/src/lib/log-read.js +196 -0
- package/dist/src/lib/pg-numeric.js +387 -49
- package/dist/src/lib/secret-mask.js +142 -0
- package/dist/src/lib/table-sync.js +84 -19
- package/dist/src/web/app.js +9 -4
- package/dist/src/web/pages/status.js +178 -13
- package/dist/src/web/pages/syncs.js +1 -1
- package/dist/src/web/pages/tables.js +8 -3
- package/dist/src/web/pages/users.js +4 -3
- package/dist/src/web/shared.js +19 -3
- package/package.json +8 -7
package/dist/src/duck/grants.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* table the role has only part of is refused, never narrowed (a narrowed `*` would change the result's columns); a
|
|
23
23
|
* system column (xmin, ctid) needs the whole table.
|
|
24
24
|
*/
|
|
25
|
-
import { StatementError } from "
|
|
25
|
+
import { StatementError } from "./statement.js";
|
|
26
26
|
import { catalogRows } from "./analyze.js";
|
|
27
27
|
/** no roles file: every session reads every copied table, as before; one object, so the listener sees no change */
|
|
28
28
|
export const NO_ROLES = { file: null, grants: new Map(), problem: null, default: null };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHAT THE COPY HOLDS FOR A POSTGRES TYPE (issues 126 and 129): the one list that says so, read by the copy
|
|
3
|
+
* (src/duck/sync.ts: what it reads and declares), the catalog (the column declared as the source spells it, always), the
|
|
4
|
+
* wire (src/duck/types.ts: the source's OID) and the rewrite (src/duck/rewrite.ts: a literal, a sort).
|
|
5
|
+
*
|
|
6
|
+
* THE CLASS: a type the copy holds as another type. The catalog said the wire's type, and the wire said what DuckDB
|
|
7
|
+
* happened to read (`timetz` as text over DuckDB's TIMETZ, `point` as text over a STRUCT, `box` not copied at all), so a
|
|
8
|
+
* tool believing the catalog sent text functions, and they failed in the engine's words.
|
|
9
|
+
*
|
|
10
|
+
* THE DECISION: the catalog reports the source's type, whatever the copy holds, so Postgres's own analysis decides every
|
|
11
|
+
* statement; and every type is one of two kinds:
|
|
12
|
+
* - `same` (`NATIVE`): DuckDB holds a value of the type itself, whose order and text are Postgres's;
|
|
13
|
+
* - `text`: DuckDB holds Postgres's text of the value (`c::text`, an array as `c::text[]`). That is anything `NATIVE`
|
|
14
|
+
* does not name, a type added to Postgres or made by a user included: nothing falls through to what DuckDB reads.
|
|
15
|
+
* What can be answered over a text is answered (the value, `::text`, `=` and GROUP BY where equal values have one text); an
|
|
16
|
+
* order is not the text's, so a sort is refused by name (`held-type-compare`), and every operator, aggregate and cast
|
|
17
|
+
* stays refused until the oracle has verified it (src/duck/verified.ts).
|
|
18
|
+
*
|
|
19
|
+
* `tests/duck-held-types.test.ts` holds this list to pg_type: a type of Postgres's named neither way fails it.
|
|
20
|
+
*/
|
|
21
|
+
/** the types DuckDB holds as themselves, as `format_type` spells them with their typmod left off */
|
|
22
|
+
const NATIVE = new Set(["smallint", "integer", "bigint", "real", "double precision", "numeric", "text", "character varying", "character", "name", '"char"',
|
|
23
|
+
"boolean", "uuid", "date", "time without time zone", "time with time zone", "timestamp without time zone", "timestamp with time zone", "interval",
|
|
24
|
+
"json", "jsonb", "bytea", "oid",
|
|
25
|
+
// held as their text from the first: an inet's output is the rewrite's and src/duck/types.ts's, a citext's its bytes
|
|
26
|
+
"inet", "citext"]);
|
|
27
|
+
const held = (oid, array, size, textEqual = true, literal = true) => ({ oid, array, size, textEqual, literal });
|
|
28
|
+
/** every built-in type a column can have that `NATIVE` does not name, by its pg_type.typname */
|
|
29
|
+
export const HELD = {
|
|
30
|
+
point: held(600, 1017, 16, false), lseg: held(601, 1018, 32, false), path: held(602, 1019, -1, false), box: held(603, 1020, 32, false), polygon: held(604, 1027, -1, false),
|
|
31
|
+
line: held(628, 629, 24, false), circle: held(718, 719, 24, false), money: held(790, 791, 8, true, false), macaddr: held(829, 1040, 6), macaddr8: held(774, 775, 8), cidr: held(650, 651, -1),
|
|
32
|
+
bit: held(1560, 1561, -1), varbit: held(1562, 1563, -1), tsvector: held(3614, 3643, -1), tsquery: held(3615, 3645, -1, false), xml: held(142, 143, -1, false),
|
|
33
|
+
jsonpath: held(4072, 4073, -1, false), tid: held(27, 1010, 6), xid: held(28, 1011, 4), cid: held(29, 1012, 4), xid8: held(5069, 271, 8), pg_lsn: held(3220, 3221, 8),
|
|
34
|
+
regclass: held(2205, 2210, 4), regtype: held(2206, 2211, 4), regproc: held(24, 1008, 4), regprocedure: held(2202, 2207, 4), regoper: held(2203, 2208, 4), regoperator: held(2204, 2209, 4),
|
|
35
|
+
regconfig: held(3734, 3735, 4), regdictionary: held(3769, 3770, 4), regnamespace: held(4089, 4090, 4), regrole: held(4096, 4097, 4), regcollation: held(4191, 4192, 4),
|
|
36
|
+
int4range: held(3904, 3905, -1), int8range: held(3926, 3927, -1), daterange: held(3912, 3913, -1), tsrange: held(3908, 3909, -1), tstzrange: held(3910, 3911, -1, false),
|
|
37
|
+
numrange: held(3906, 3907, -1, false), int4multirange: held(4451, 6150, -1), int8multirange: held(4536, 6157, -1), datemultirange: held(4535, 6155, -1), tsmultirange: held(4533, 6152, -1),
|
|
38
|
+
tstzmultirange: held(4534, 6153, -1, false), nummultirange: held(4532, 6151, -1, false), int2vector: held(22, 1006, -1), oidvector: held(30, 1013, -1), aclitem: held(1033, 1034, 16),
|
|
39
|
+
refcursor: held(1790, 2201, -1), pg_snapshot: held(5038, 5039, -1, false), txid_snapshot: held(2970, 2949, -1, false),
|
|
40
|
+
};
|
|
41
|
+
/** `oid`'s array: DuckDB reads no array of it, so the copy holds it as a list of its digits */
|
|
42
|
+
export const OID_ARRAY = 1028;
|
|
43
|
+
const HELD_OIDS = new Map(Object.values(HELD).map((h) => [h.oid, h]));
|
|
44
|
+
const HELD_ARRAYS = new Map([...Object.values(HELD).map((h) => [h.array, h]), [OID_ARRAY, held(26, OID_ARRAY, -1)]]);
|
|
45
|
+
/** a built-in type held as its text, by its OID or its array's */
|
|
46
|
+
export const heldType = (oid) => oid === null ? undefined : HELD_OIDS.get(oid) ?? HELD_ARRAYS.get(oid);
|
|
47
|
+
/** a held type's name, for a refusal */
|
|
48
|
+
export const heldName = (oid) => Object.entries(HELD).find(([, h]) => h.oid === oid || h.array === oid)?.[0] ?? (oid === 18 ? '"char"' : oid === OID_ARRAY ? "oid[]" : String(oid));
|
|
49
|
+
/** an array of a type held as its text: a list of each element's text */
|
|
50
|
+
export const isHeldArray = (oid) => oid !== null && HELD_ARRAYS.has(oid);
|
|
51
|
+
/** a time written in the session's zone, which the copy's text cannot be: written again where the statement executes */
|
|
52
|
+
export const ZONED_HELD = new Map([[3910, "tstzrange"], [4534, "tstzmultirange"]]);
|
|
53
|
+
/** what `format_type` writes, its typmod and `[]` left off (`time(3) with time zone`, `bit(8)`, `numeric(12,2)[]`) */
|
|
54
|
+
const baseOf = (spelled) => spelled.replace(/\[\]$/, "").replace(/\(\d+(,\d+)?\)/, "").replace(/\s+/g, " ").trim().replace(/^interval .*/, "interval");
|
|
55
|
+
/** whether DuckDB holds a value of this type as itself */
|
|
56
|
+
export const isNative = (spelled) => NATIVE.has(baseOf(spelled));
|
|
57
|
+
/** How the copy holds a column of the source's type `spelled` (`typeKind`: pg_type.typtype of the type or its element).
|
|
58
|
+
* An enum is DuckDB's ENUM; any other type a database made is its text. */
|
|
59
|
+
export function holdOf(spelled, typeKind) {
|
|
60
|
+
const array = spelled.endsWith("[]");
|
|
61
|
+
if (typeKind === "e" || (isNative(spelled) && !(array && baseOf(spelled) === "oid")))
|
|
62
|
+
return "same";
|
|
63
|
+
return array ? "text[]" : "text";
|
|
64
|
+
}
|
package/dist/src/duck/history.js
CHANGED
|
@@ -2,6 +2,10 @@ import { MASON_SCHEMA } from "./store.js";
|
|
|
2
2
|
/** How many days every history table keeps (the request log, the sync's events, the usage samples, the copies'
|
|
3
3
|
* sizes) unless MASON_HISTORY_DAYS says. */
|
|
4
4
|
export const DEFAULT_HISTORY_DAYS = 7;
|
|
5
|
+
/** The instant `days` days before `now` (ms since the epoch), ISO 8601 in UTC: what a history table's trim deletes
|
|
6
|
+
* before. `now` is the module's own clock, the one that stamps its rows, never the engine's `now()`: a module reads
|
|
7
|
+
* ONE clock, or a clock it is given (a test's, a run's) and the engine's disagree and the trim deletes what it keeps. */
|
|
8
|
+
export function cutoff(now, days) { return new Date(now - days * 86_400_000).toISOString(); }
|
|
5
9
|
const KEPT = "the request log, the sync's events, the usage samples and the synced tables' sizes";
|
|
6
10
|
/** The days MASON_HISTORY_DAYS keeps, or what is wrong with the text. Unset or empty: the default. */
|
|
7
11
|
export function historyDays(text) {
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
* under ICU, ICU's sort key (DuckDB's `icu_sort_key`, which the rewrite hands in) and the bytes' hex; under a libc
|
|
31
31
|
* locale the measured key (src/duck/libc-order.ts `libcKey`) and the bytes' hex, as the rewrite sorts text
|
|
32
32
|
* (src/duck/rewrite.ts `libcKey`).
|
|
33
|
-
* A string's collation key is computed by DuckDB's ICU for the ICU
|
|
33
|
+
* A string's collation key is computed by DuckDB's ICU for the ICU kind, which a JavaScript function cannot
|
|
34
34
|
* call: the rewrite passes the document's strings' ICU keys as a list (`jsonbStrings` lists the strings, in the order
|
|
35
35
|
* this walk meets them; `icuKeys` the list DuckDB made of them). Held to Postgres by tests/duck-jsonb-order.test.ts
|
|
36
36
|
* (PGlite's order) and the oracle (tests/integration/duck-oracle.itest.ts, every collation it is run under).
|
|
37
37
|
*/
|
|
38
38
|
import { Buffer } from "node:buffer";
|
|
39
39
|
import { parseJson, storedPairs } from "./jsonb.js";
|
|
40
|
-
import {
|
|
40
|
+
import { libcKey } from "./libc-order.js";
|
|
41
41
|
const TAG = { null: 0x10, str: 0x20, num: 0x30, bool: 0x40, arr: 0x50, obj: 0x60 };
|
|
42
42
|
const BIAS = 0x80000000;
|
|
43
43
|
/** every string of the value — each object's keys and every string value — in the order the key's walk meets them */
|
|
@@ -68,7 +68,7 @@ function eachString(v, out) {
|
|
|
68
68
|
export const JSONB_KEY_FUNCTIONS = {
|
|
69
69
|
bytes: { key: "__mason_jsonb_key", strings: null },
|
|
70
70
|
icu: { key: "__mason_jsonb_key_icu", strings: "__mason_jsonb_strings" },
|
|
71
|
-
libc: { key: "__mason_jsonb_key_libc"
|
|
71
|
+
libc: { key: "__mason_jsonb_key_libc" },
|
|
72
72
|
};
|
|
73
73
|
export const STORED_SUFFIX = "_stored";
|
|
74
74
|
/**
|
|
@@ -86,14 +86,11 @@ export function jsonbStrings(text) {
|
|
|
86
86
|
/** whether a jsonb's strings hold a character a libc order does not cover: what its key refuses by name
|
|
87
87
|
* (`libc-uncovered`, src/duck/libc-order.ts `libcKey`), told from any other failure of the key (src/duck/kept.ts) */
|
|
88
88
|
export function libcUncovered(order, text) {
|
|
89
|
-
return jsonbStrings(text).some((s) => {
|
|
89
|
+
return jsonbStrings(text).some((s) => { if (order.unread)
|
|
90
|
+
return true; for (const ch of s)
|
|
90
91
|
if (order.uncovered[ch.codePointAt(0)])
|
|
91
92
|
return true; return false; });
|
|
92
93
|
}
|
|
93
|
-
/** The strings ICU's accent and case levels read for a libc key of each (src/duck/libc-order.ts `libcKept`). */
|
|
94
|
-
export function jsonbLibcKept(order, text) {
|
|
95
|
-
return jsonbStrings(text).map((s) => libcKept(order, s));
|
|
96
|
-
}
|
|
97
94
|
const u32 = (n) => [(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff];
|
|
98
95
|
/**
|
|
99
96
|
* A JSON number's text as its key: zero, or its sign, exponent and digits (the value's, exactly: no rounding through a
|
|
@@ -120,15 +117,15 @@ export function jsonbKey(text, c) {
|
|
|
120
117
|
const out = [];
|
|
121
118
|
let at = 0;
|
|
122
119
|
const icuKey = () => {
|
|
123
|
-
if (c.kind
|
|
124
|
-
throw new Error("
|
|
120
|
+
if (c.kind !== "icu")
|
|
121
|
+
throw new Error("only ICU's key is handed in");
|
|
125
122
|
const k = c.icuKeys[at++];
|
|
126
123
|
if (k === undefined)
|
|
127
124
|
throw new Error(`the jsonb holds more strings than the ${c.icuKeys.length} ICU keys handed in: a Mason bug (the rewrite's list and this walk disagree)`);
|
|
128
125
|
return k;
|
|
129
126
|
};
|
|
130
127
|
const str = (s) => {
|
|
131
|
-
const key = c.kind === "bytes" ? s : c.kind === "icu" ? `${icuKey()} ${hex(s)}` : `${libcKey(c.order, s
|
|
128
|
+
const key = c.kind === "bytes" ? s : c.kind === "icu" ? `${icuKey()} ${hex(s)}` : `${libcKey(c.order, s)} ${hex(s)}`;
|
|
132
129
|
for (const b of Buffer.from(key, "utf8"))
|
|
133
130
|
out.push(b);
|
|
134
131
|
out.push(0x00);
|
|
@@ -169,7 +166,7 @@ export function jsonbKey(text, c) {
|
|
|
169
166
|
if (root.t !== "arr" && root.t !== "obj")
|
|
170
167
|
out.push(TAG.arr, ...u32(1), 0x00);
|
|
171
168
|
value(root);
|
|
172
|
-
if (c.kind
|
|
169
|
+
if (c.kind === "icu" && at !== c.icuKeys.length)
|
|
173
170
|
throw new Error(`the jsonb holds ${at} strings, ${c.icuKeys.length} ICU keys were handed in: a Mason bug (the rewrite's list and this walk disagree)`);
|
|
174
171
|
return Buffer.from(out);
|
|
175
172
|
}
|
|
@@ -149,14 +149,14 @@ export function pathText(r) {
|
|
|
149
149
|
* as a value: the exact read raises at it, by the column alone. A jsonb's key under a libc collation is marked where
|
|
150
150
|
* its strings hold a character the measured order does not cover, the gap the statement's own key refuses by name
|
|
151
151
|
* (`libc-uncovered`); any other mark is a Mason bug. Once counted (a minute at most), the CASE computes the row from its
|
|
152
|
-
* document, and raises the function's own error
|
|
152
|
+
* document, and raises the function's own error or answers. Raised by `MARKED_FUNCTIONS`, never
|
|
153
153
|
* DuckDB's `error()`, which is volatile (a window's ORDER BY refuses it: the eager oracle's `rank() OVER (ORDER BY j)`).
|
|
154
154
|
*/
|
|
155
155
|
export function markedError(r, libc) {
|
|
156
156
|
if (isOrderRead(r) && libc) {
|
|
157
|
-
return { code: "0A000", message: "a jsonb compared or sorted here holds a string Mason cannot order as the database's collation does
|
|
158
|
-
"
|
|
159
|
-
"in a minute, the
|
|
157
|
+
return { code: "0A000", message: "a jsonb compared or sorted here holds a string Mason cannot order as the database's collation does, " +
|
|
158
|
+
"in a row written since its stored keys were counted; asked again " +
|
|
159
|
+
"in a minute, the statement's own key answers or refuses it (Mason gap libc-uncovered)" };
|
|
160
160
|
}
|
|
161
161
|
return { code: "XX000", message: `a stored value of ${pathText(r)} could not be computed when its row was written (a Mason bug: report it, with mason logs); ` +
|
|
162
162
|
"asked again in a minute, it is computed from the row's document" };
|
|
@@ -224,7 +224,8 @@ export function exposes(tree) {
|
|
|
224
224
|
return false;
|
|
225
225
|
const k = kindOf(v);
|
|
226
226
|
const b = k === null ? v : bodyOf(v);
|
|
227
|
-
|
|
227
|
+
// (an arm of a set operation is a SelectStmt with no wrapper of its own: k is null)
|
|
228
|
+
if ((k === "SelectStmt" || k === null) && Array.isArray(b.targetList) && directTables(b.fromClause).length > 0
|
|
228
229
|
&& b.targetList.some((t) => kindOf(t) === "ResTarget" && star(bodyOf(t).val)))
|
|
229
230
|
return true;
|
|
230
231
|
if (k === "JoinExpr" && b.isNatural === true && directTables([b.larg, b.rarg]).length > 0)
|
package/dist/src/duck/kept.js
CHANGED
|
@@ -2,8 +2,8 @@ import { DUCKDB_VERSION, MASON_SCHEMA } from "./store.js";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { canonicalRead, EQUAL_STEPS, HIDDEN_PREFIX, HIDDEN_RE, hiddenName, isEqualRead, isKeyRead, isOrderRead, KEY_NULL_HEX, KEY_RECOMPUTE_HEX, knownSteps, ORDER_STEPS, pathKey, pathText, readOfKey, VISIBLE_CATALOG } from "./kept-route.js";
|
|
4
4
|
import { JSONB_KEY_FUNCTIONS, KEY_VERSION, libcUncovered, STORED_SUFFIX } from "./jsonb-order.js";
|
|
5
|
+
import { TURN_FUNCTION } from "./functions.js";
|
|
5
6
|
import { encodeLibcOrder } from "./libc-order.js";
|
|
6
|
-
import { icuNameOf } from "./rewrite.js";
|
|
7
7
|
/** a path read by this many statements in the request log is stored (a larger table's also needs `KEEP_FILL_SHARE`) */
|
|
8
8
|
export const KEEP_AFTER_STATEMENTS = 3;
|
|
9
9
|
/** a path of a SMALL table (`SMALL_FILL_SECONDS`) read by one statement that took this long (ms, the request log's
|
|
@@ -97,7 +97,7 @@ const tableOf = (source) => { const at = source.indexOf("."); return `${ident(so
|
|
|
97
97
|
/** the value a stored column takes from its JSON column's `value` (a column reference or an expression): the path's
|
|
98
98
|
* `->` answer, never NULL (a NULL document's is `KEPT_NULL` too: the column's NULL is a row not stored yet) */
|
|
99
99
|
export function keptValueSql(p, value) {
|
|
100
|
-
return `coalesce(__mason_json_kept(NULL, ${value}, ${literal(p.steps)}, ${p.either}, ${p.canonical}, false), ${NULL_SQL})`;
|
|
100
|
+
return `coalesce(__mason_json_kept(${TURN_FUNCTION}(NULL, ${value}), ${value}, ${literal(p.steps)}, ${p.either}, ${p.canonical}, false), ${NULL_SQL})`;
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* A JSONB'S STORED KEY, from its column's `value` (src/duck/kept-route.ts `ORDER_STEPS`): the key the statement would
|
|
@@ -106,9 +106,9 @@ export function keptValueSql(p, value) {
|
|
|
106
106
|
* `KEY_RECOMPUTE_HEX` where the key raised, which its read computes (and raises) again.
|
|
107
107
|
*/
|
|
108
108
|
export function orderKeySql(c, value) {
|
|
109
|
-
const call = (name, ...args) => `${name}${STORED_SUFFIX}(NULL, ${args.join(", ")})`;
|
|
110
|
-
const key = c.kind
|
|
111
|
-
: call(JSONB_KEY_FUNCTIONS
|
|
109
|
+
const call = (name, ...args) => `${name}${STORED_SUFFIX}(${TURN_FUNCTION}(NULL, ${value}), ${args.join(", ")})`;
|
|
110
|
+
const key = c.kind !== "icu" ? call(JSONB_KEY_FUNCTIONS[c.kind].key, value)
|
|
111
|
+
: call(JSONB_KEY_FUNCTIONS.icu.key, value, `list_transform(${call(JSONB_KEY_FUNCTIONS.icu.strings, value)}, __mason_s -> icu_sort_key(__mason_s, ${literal(c.name)}))`);
|
|
112
112
|
return `CASE WHEN (${value}) IS NULL THEN unhex('${KEY_NULL_HEX}') ELSE coalesce(${key}, unhex('${KEY_RECOMPUTE_HEX}')) END`;
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
@@ -124,7 +124,7 @@ export function orderId(c, libc, versions = `k${KEY_VERSION}:${DUCKDB_VERSION}`)
|
|
|
124
124
|
return `icu:${c.name}:${versions}`;
|
|
125
125
|
if (libc === undefined)
|
|
126
126
|
throw new Error("a libc collation's order was not read (Mason bug: report it)");
|
|
127
|
-
return `libc:${
|
|
127
|
+
return `libc:${createHash("sha256").update(encodeLibcOrder(libc)).digest("hex").slice(0, 16)}:${versions}`;
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
130
|
* THE C KEY'S ID, stored under a libc collation for equality (`EQUAL_STEPS:<id>`, src/duck/kept-route.ts): the C
|
|
@@ -879,7 +879,9 @@ export class KeptJsonPaths {
|
|
|
879
879
|
const of = read < marked ? ` (of the first ${read.toLocaleString("en-US")} read, of ${marked.toLocaleString("en-US")})` : "";
|
|
880
880
|
const said = [];
|
|
881
881
|
if (uncovered > 0)
|
|
882
|
-
said.push(
|
|
882
|
+
said.push(libc?.unread === true
|
|
883
|
+
? `the database's collation is one Mason could not read, so a sort of ${uncovered.toLocaleString("en-US")} row(s) is refused by name (Mason gap libc-locale-unread)`
|
|
884
|
+
: `${uncovered.toLocaleString("en-US")} row(s) hold a character the database's libc order is not measured for, so a sort of them is refused by name (Mason gap libc-uncovered)`);
|
|
883
885
|
if (read > uncovered)
|
|
884
886
|
said.push(`${(read - uncovered).toLocaleString("en-US")} row(s) its function could not read are computed from their document when read (a Mason bug: report it, with mason logs)`);
|
|
885
887
|
return said.join("; ") + of;
|