@mason-data/cli 0.8.0 → 0.9.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 +13 -4
- 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 +283 -0
- package/dist/src/api/postgres/schema.js +6 -0
- package/dist/src/api/postgres/server.js +505 -99
- 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 +169 -11
- package/dist/src/cli/commands.js +207 -51
- package/dist/src/cli/credentials.js +182 -0
- package/dist/src/cli/demo.js +2 -2
- package/dist/src/cli/deploy.js +574 -0
- package/dist/src/cli/dev.js +148 -27
- package/dist/src/cli/doctor.js +116 -33
- package/dist/src/cli/generated-files.js +143 -0
- package/dist/src/cli/guides.js +97 -6
- package/dist/src/cli/hosted.js +850 -0
- package/dist/src/cli/init.js +47 -11
- package/dist/src/cli/login.js +289 -0
- package/dist/src/cli/machine.js +1 -1
- package/dist/src/cli/main.js +8 -2
- package/dist/src/cli/mason.cjs +17 -0
- package/dist/src/cli/mcp.js +2 -2
- 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 +99 -0
- package/dist/src/cli/readiness.js +42 -6
- package/dist/src/cli/registry.js +12 -4
- package/dist/src/cli/replica-hosted.js +665 -0
- package/dist/src/cli/replica.js +76 -0
- package/dist/src/cli/roles.js +2 -1
- package/dist/src/cli/run-server.js +154 -7
- package/dist/src/cli/security.js +57 -10
- package/dist/src/cli/serve.js +63 -7
- package/dist/src/cli/share-run.js +51 -8
- package/dist/src/cli/share.js +3 -3
- package/dist/src/cli/source-edit.js +10 -4
- package/dist/src/cli/source-role.js +344 -0
- package/dist/src/cli/sources.js +30 -20
- package/dist/src/cli/style.js +2 -1
- package/dist/src/cli/supervise.js +15 -23
- package/dist/src/cli/token-hosted.js +337 -0
- package/dist/src/cli/ui-ssh.js +460 -0
- package/dist/src/cli/ui.js +115 -11
- package/dist/src/cli/user-hosted.js +299 -0
- package/dist/src/cli/user.js +126 -74
- 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 +5 -0
- package/dist/src/duck/verified.js +159 -2
- package/dist/src/duck/views.js +4 -3
- 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 +24 -9
- 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
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
* defined is never in the catalog Postgres, so it never reaches here.
|
|
15
15
|
*/
|
|
16
16
|
import { exprType } from "./pgtree.js";
|
|
17
|
+
import { HELD_TYPE_INPUT } from "../api/postgres/schema.js";
|
|
18
|
+
import { heldType, isHeldArray } from "./held-types.js";
|
|
19
|
+
/** the key of a type the source made, held as its text (src/duck/held-types.ts) */
|
|
20
|
+
const HELD_KEY = "made";
|
|
17
21
|
const isNode = (v) => v !== null && v !== undefined && typeof v === "object" && !Array.isArray(v);
|
|
18
22
|
/** an OID a customer's database made (an extension's type or operator, a table's row type) differs between databases:
|
|
19
23
|
* marked `u<oid>` here, and named by the catalog before it is a key (`nameUserKeys`: `ext:=(citext,citext)`) */
|
|
@@ -42,7 +46,9 @@ export async function nameUserKeys(rows, keys) {
|
|
|
42
46
|
const read = async (set, sql) => set.size === 0 ? new Map() : new Map((await rows(sql([...set].join(", ")))).map((r) => [r[0], r[1]]));
|
|
43
47
|
const proc = await read(oids.proc, (l) => `SELECT oid::text, 'ext:' || oid::regprocedure::text FROM pg_proc WHERE oid IN (${l})`);
|
|
44
48
|
const op = await read(oids.op, (l) => `SELECT oid::text, 'ext:' || oid::regoperator::text FROM pg_operator WHERE oid IN (${l})`);
|
|
45
|
-
|
|
49
|
+
// a type the source made that the copy holds as its text is one key, whichever: what is verified over one is the text's
|
|
50
|
+
const type = await read(oids.type, (l) => `SELECT t.oid::text, CASE WHEN t.typtype = 'c' THEN 'rowtype' WHEN t.typinput::text LIKE '%${HELD_TYPE_INPUT}%' THEN '${HELD_KEY}'
|
|
51
|
+
WHEN e.typinput::text LIKE '%${HELD_TYPE_INPUT}%' THEN '${HELD_KEY}[]' ELSE 'ext:' || t.typname END FROM pg_type t LEFT JOIN pg_type e ON e.oid = t.typelem WHERE t.oid IN (${l})`);
|
|
46
52
|
const name = (kind, p) => { const m = USER.exec(p); return m === null ? p : (kind === "operator" ? op : kind === "type" || kind === "cast" ? type : proc).get(m[1]) ?? p; };
|
|
47
53
|
return new Set(list.map((k) => {
|
|
48
54
|
const kind = k.slice(0, k.indexOf(":")), value = k.slice(k.indexOf(":") + 1);
|
|
@@ -84,6 +90,9 @@ export function resolvedKeys(tree) {
|
|
|
84
90
|
const t = exprType(v);
|
|
85
91
|
if (t !== null && t.type !== 0)
|
|
86
92
|
keys.add(`type:${oidKey(t.type)}`);
|
|
93
|
+
// a system column (ctid, xmin, tableoid): a copy has none, whatever its type's being verified says. Never verified
|
|
94
|
+
if (name === "VAR" && Number(v.varattno) < 0)
|
|
95
|
+
keys.add("field:VAR.system");
|
|
87
96
|
const resolved = RESOLVED[name];
|
|
88
97
|
if (resolved) {
|
|
89
98
|
const oid = v[resolved[0]];
|
|
@@ -120,6 +129,111 @@ export function resolvedKeys(tree) {
|
|
|
120
129
|
visit(tree);
|
|
121
130
|
return keys;
|
|
122
131
|
}
|
|
132
|
+
/** Each function, aggregate and window function an analysed tree calls, with the type of each argument it is given, as
|
|
133
|
+
* `<function oid>:<type oid>` (the catalog's OIDs): what a function marked stable only for its arguments' text reads
|
|
134
|
+
* (src/duck/generated-plan.ts `sessionReads`). */
|
|
135
|
+
export function argumentTypes(tree) {
|
|
136
|
+
const out = new Set();
|
|
137
|
+
const visit = (v) => {
|
|
138
|
+
if (Array.isArray(v)) {
|
|
139
|
+
for (const x of v)
|
|
140
|
+
visit(x);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!isNode(v))
|
|
144
|
+
return;
|
|
145
|
+
const r = RESOLVED[v._node];
|
|
146
|
+
const oid = r === undefined || r[1] === "operator" ? undefined : v[r[0]];
|
|
147
|
+
if (typeof oid === "string" && Array.isArray(v.args)) {
|
|
148
|
+
for (const a of v.args) {
|
|
149
|
+
const e = isNode(a) && a._node === "TARGETENTRY" ? a.expr : a;
|
|
150
|
+
const t = isNode(e) ? exprType(e) : null;
|
|
151
|
+
if (t !== null && t.type !== 0)
|
|
152
|
+
out.add(`${oid}:${String(t.type)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
for (const [k, x] of Object.entries(v))
|
|
156
|
+
if (k !== "_node")
|
|
157
|
+
visit(x);
|
|
158
|
+
};
|
|
159
|
+
visit(tree);
|
|
160
|
+
return [...out];
|
|
161
|
+
}
|
|
162
|
+
/** What a statement does with a value the copy holds as its text (src/duck/held-types.ts): what the text cannot answer
|
|
163
|
+
* as the type would. Each is `operator:<oid>`, `function:<oid>` … as a key; the planner reads an operator's name. */
|
|
164
|
+
export function heldUses(tree) {
|
|
165
|
+
const out = [];
|
|
166
|
+
const typeOf = (x) => {
|
|
167
|
+
if (!isNode(x))
|
|
168
|
+
return null;
|
|
169
|
+
if (x._node === "TARGETENTRY")
|
|
170
|
+
return typeOf(x.expr);
|
|
171
|
+
// a literal is the statement's, not the copy's (`to_tsvector('simple', x)` reads a regconfig)
|
|
172
|
+
if (x._node === "CONST")
|
|
173
|
+
return null;
|
|
174
|
+
// a binary-compatible cast is its value (a cidr read by inet's operators)
|
|
175
|
+
if (x._node === "RELABELTYPE")
|
|
176
|
+
return typeOf(x.arg);
|
|
177
|
+
return exprType(x)?.type ?? null;
|
|
178
|
+
};
|
|
179
|
+
const visit = (v) => {
|
|
180
|
+
if (Array.isArray(v)) {
|
|
181
|
+
for (const x of v)
|
|
182
|
+
visit(x);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!isNode(v))
|
|
186
|
+
return;
|
|
187
|
+
const resolved = RESOLVED[v._node];
|
|
188
|
+
if (resolved !== undefined && Array.isArray(v.args)) {
|
|
189
|
+
const oid = v[resolved[0]];
|
|
190
|
+
const type = v.args.map(typeOf).find((t) => heldType(t) !== undefined);
|
|
191
|
+
if (type != null && typeof oid === "string")
|
|
192
|
+
out.push({ kind: resolved[1] === "operator" ? "operator" : "function", oid, type });
|
|
193
|
+
}
|
|
194
|
+
if (v._node === "COERCEVIAIO" || v._node === "ARRAYCOERCEEXPR") {
|
|
195
|
+
const from = typeOf(v.arg), to = exprType(v)?.type ?? null;
|
|
196
|
+
// to another type, or a value read as one (a text cast to macaddr), which only Postgres's input function reads
|
|
197
|
+
if (heldType(from) !== undefined && to !== 25 && to !== 1043 && from !== to)
|
|
198
|
+
out.push({ kind: "cast", oid: String(to), type: from });
|
|
199
|
+
else if (heldType(to) !== undefined && from !== to)
|
|
200
|
+
out.push({ kind: "cast", oid: String(to), type: to });
|
|
201
|
+
}
|
|
202
|
+
// a regclass read as its oid: the copy holds its name
|
|
203
|
+
if (v._node === "RELABELTYPE") {
|
|
204
|
+
const from = typeOf(v.arg), to = exprType(v)?.type ?? null;
|
|
205
|
+
if (heldType(from) !== undefined && to !== 869 && heldType(to) === undefined && from !== to)
|
|
206
|
+
out.push({ kind: "cast", oid: String(to), type: from });
|
|
207
|
+
}
|
|
208
|
+
// a "char" read as a text by min and max, under the database's collation: the copy's is a byte's order
|
|
209
|
+
if (v._node === "AGGREF" && Array.isArray(v.args) && v.args.some((a) => isNode(a) && isNode(a.expr) && a.expr.funcid === "946"))
|
|
210
|
+
out.push({ kind: "cast", oid: "25", type: 18 });
|
|
211
|
+
// a point's x, a vector's element: a subscript of a type that is not an array
|
|
212
|
+
if (v._node === "SUBSCRIPTINGREF") {
|
|
213
|
+
const of = typeOf(v.refexpr);
|
|
214
|
+
if (heldType(of) !== undefined && !isHeldArray(of))
|
|
215
|
+
out.push({ kind: "cast", oid: String(of), type: of });
|
|
216
|
+
}
|
|
217
|
+
for (const [k, x] of Object.entries(v))
|
|
218
|
+
if (k !== "_node" && k !== "joinaliasvars")
|
|
219
|
+
visit(x);
|
|
220
|
+
};
|
|
221
|
+
visit(tree);
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
/** whether a statement tests a value of one of `types` with IS [NOT] NULL */
|
|
225
|
+
export function nullTested(tree, types) {
|
|
226
|
+
const visit = (v) => {
|
|
227
|
+
if (Array.isArray(v))
|
|
228
|
+
return v.some(visit);
|
|
229
|
+
if (!isNode(v))
|
|
230
|
+
return false;
|
|
231
|
+
if (v._node === "NULLTEST" && isNode(v.arg) && types.has(exprType(v.arg)?.type ?? 0))
|
|
232
|
+
return true;
|
|
233
|
+
return Object.entries(v).some(([k, x]) => k !== "_node" && k !== "joinaliasvars" && visit(x));
|
|
234
|
+
};
|
|
235
|
+
return visit(tree);
|
|
236
|
+
}
|
|
123
237
|
// --------------------------------------------------------------------------------------------- what a key names
|
|
124
238
|
/** each kind field's values in SQL's words (the enums of Postgres 18's primnodes.h / parsenodes.h, as outfuncs.c writes them) */
|
|
125
239
|
const VALUES = {
|
|
@@ -148,6 +262,7 @@ const VALUES = {
|
|
|
148
262
|
"COMMONTABLEEXPR.ctematerialized": { 0: "WITH", 1: "WITH … AS MATERIALIZED", 2: "WITH … AS NOT MATERIALIZED" },
|
|
149
263
|
};
|
|
150
264
|
const FLAGS = {
|
|
265
|
+
"VAR.system": "a system column (ctid, xmin, tableoid …), which a synced table does not have",
|
|
151
266
|
"AGGREF.aggdistinct": "an aggregate of DISTINCT values", "AGGREF.aggorder": "an aggregate's own ORDER BY", "AGGREF.aggfilter": "an aggregate's FILTER (WHERE …)",
|
|
152
267
|
"AGGREF.aggdirectargs": "an ordered-set aggregate's direct argument", "WINDOWFUNC.aggfilter": "a window aggregate's FILTER (WHERE …)",
|
|
153
268
|
"QUERY.distinctClause": "SELECT DISTINCT", "QUERY.groupingSets": "GROUPING SETS / ROLLUP / CUBE", "QUERY.havingQual": "HAVING",
|
|
@@ -207,7 +322,7 @@ export async function describeKeys(rows, keys) {
|
|
|
207
322
|
const proc = await read(procs, (l) => `SELECT oid::text, oid::regprocedure::text FROM pg_proc WHERE oid IN (${l})`);
|
|
208
323
|
const op = await read(ops, (l) => `SELECT oid::text, oid::regoperator::text FROM pg_operator WHERE oid IN (${l})`);
|
|
209
324
|
const type = await read(types, (l) => `SELECT oid::text, format_type(oid, NULL) FROM pg_type WHERE oid IN (${l})`);
|
|
210
|
-
const typeName = (t) => t === "rowtype" ? "a table's row type" : t.startsWith("ext:") ? `${t.slice(4)} (an extension's)` : type.get(t) ?? `type OID ${t}`;
|
|
325
|
+
const typeName = (t) => t === "rowtype" ? "a table's row type" : t === HELD_KEY ? "a type the source made" : t === `${HELD_KEY}[]` ? "an array of a type the source made" : t.startsWith("ext:") ? `${t.slice(4)} (an extension's)` : type.get(t) ?? `type OID ${t}`;
|
|
211
326
|
const extName = (v) => v.startsWith("ext:") ? `${v.slice(4)} (an extension's)` : `OID ${v}`;
|
|
212
327
|
for (const k of list) {
|
|
213
328
|
const kind = k.slice(0, k.indexOf(":")), value = k.slice(k.indexOf(":") + 1);
|