@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
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* string, a quoted name, a dollar-quoted body and a comment, never in a `::` cast.
|
|
19
19
|
*/
|
|
20
20
|
import { deparseSync } from "pgsql-deparser";
|
|
21
|
-
import { gapRefusal, StatementError } from "
|
|
21
|
+
import { gapRefusal, StatementError } from "./statement.js";
|
|
22
22
|
import { parseSql } from "../api/postgres/parser.js";
|
|
23
23
|
import { analyze, catalogRows } from "./analyze.js";
|
|
24
|
+
import { sessionSetSql } from "../api/postgres/catalog.js";
|
|
24
25
|
import { treeDifference } from "./rewrite.js";
|
|
25
26
|
/** an attribute's name: what `:name` reads and `--set name=value` sets */
|
|
26
27
|
export const ATTRIBUTE_NAME_RE = /^[a-z_][a-z0-9_]{0,62}$/;
|
|
@@ -183,7 +184,7 @@ export async function filterTypes(run, f, shapes) {
|
|
|
183
184
|
const message = (r.M ?? "Postgres refused it").replace(/\$(\d+)/g, (m, k) => names[Number(k) - 1] !== undefined ? `:${names[Number(k) - 1]}` : m);
|
|
184
185
|
return { problem: `${f.at}.where: ${message}${at} — the predicate is checked by Postgres as SELECT 1 FROM ${f.table} WHERE ${f.where.text}` };
|
|
185
186
|
};
|
|
186
|
-
const first = await analyze(run, sql, names.map(() => 0)
|
|
187
|
+
const first = await analyze(run, sql, names.map(() => 0));
|
|
187
188
|
if ("refused" in first)
|
|
188
189
|
return said(first.refused);
|
|
189
190
|
if (hasSubquery(first.tree))
|
|
@@ -215,7 +216,7 @@ export async function filterTypes(run, f, shapes) {
|
|
|
215
216
|
}
|
|
216
217
|
if (mismatch !== null) {
|
|
217
218
|
// Postgres's own refusal, the parameter declared of the shape the file says
|
|
218
|
-
const again = await analyze(run, sql, declared
|
|
219
|
+
const again = await analyze(run, sql, declared);
|
|
219
220
|
if ("refused" in again)
|
|
220
221
|
return { problem: `${said(again.refused).problem} (${mismatch})` };
|
|
221
222
|
return { problem: `${f.at}.where: ${mismatch}` };
|
|
@@ -278,7 +279,7 @@ export function bindFilter(f, types, values) {
|
|
|
278
279
|
export async function attributeRefused(run, rows, tables) {
|
|
279
280
|
for (const table of tables) {
|
|
280
281
|
for (const a of rows.get(table)?.attributes ?? []) {
|
|
281
|
-
const r = await analyze(run, `SELECT ${a.sql}`, []
|
|
282
|
+
const r = await analyze(run, `SELECT ${a.sql}`, []);
|
|
282
283
|
if ("refused" in r)
|
|
283
284
|
return { name: a.name, type: a.type, at: a.at, where: a.where, problem: r.refused.M ?? "Postgres refused it" };
|
|
284
285
|
}
|
|
@@ -544,7 +545,7 @@ async function carryKeyDependency(catalog, tree, reads, analysis, oids) {
|
|
|
544
545
|
* security: a filtered table's whole row (a subquery's is a record), a name both a WITH query and a filtered table, a
|
|
545
546
|
* TABLESAMPLE of one, a column qualified by its schema beside another item of the table's name.
|
|
546
547
|
*/
|
|
547
|
-
export async function expandRowFilters(catalog, stmt, analysis,
|
|
548
|
+
export async function expandRowFilters(catalog, stmt, analysis, under, filters) {
|
|
548
549
|
const read = relationsRead(analysis.tree);
|
|
549
550
|
if (read.size === 0 || filters.size === 0)
|
|
550
551
|
return null;
|
|
@@ -562,7 +563,7 @@ export async function expandRowFilters(catalog, stmt, analysis, searchPathSql, f
|
|
|
562
563
|
const ranges = all.filter((n) => n.kind === "RangeVar");
|
|
563
564
|
const ctes = new Set(all.filter((n) => n.kind === "CommonTableExpr").map((n) => n.body.ctename));
|
|
564
565
|
const spelled = ranges.map((r) => [r.body.catalogname, r.body.schemaname, r.body.relname].filter((x) => typeof x === "string").map(quoteIdent).join("."));
|
|
565
|
-
const resolved = spelled.length === 0 ? [] : await catalogRows(catalog,
|
|
566
|
+
const resolved = spelled.length === 0 ? [] : await catalogRows(catalog, `${sessionSetSql(under, true)}; SELECT v.i::text, c.oid::text FROM (VALUES ${spelled.map((s, i) => `(${i}, ${literal(s)})`).join(", ")}) AS v(i, name) ` +
|
|
566
567
|
`JOIN pg_class c ON c.oid = to_regclass(v.name) WHERE c.relkind = 'r'`);
|
|
567
568
|
const asFiltered = new Map();
|
|
568
569
|
for (const [i, oid] of resolved) {
|
|
@@ -591,6 +592,11 @@ export async function expandRowFilters(catalog, stmt, analysis, searchPathSql, f
|
|
|
591
592
|
if ([...filteredRead.values()].some((v) => !found.has(v.table))) {
|
|
592
593
|
throw refused("Mason could not tell which of the statement's names are the tables its role filters (a Mason gap: report it, with the statement)", "write each table's name qualified by its schema");
|
|
593
594
|
}
|
|
595
|
+
// the printer below (pgsql-deparser) writes FETCH … WITH TIES as a LIMIT, which the read-back refuses as a report-it
|
|
596
|
+
// gap: said by name here instead, at any depth (a set operation's arm is a body of its own, unwrapped)
|
|
597
|
+
if (JSON.stringify(tree).includes("\"limitOption\":\"LIMIT_OPTION_WITH_TIES\"")) {
|
|
598
|
+
throw refused("Mason cannot yet answer FETCH … WITH TIES in a statement that reads a table its role filters", "write ORDER BY … LIMIT n where the ties do not matter");
|
|
599
|
+
}
|
|
594
600
|
// a column qualified by its table's schema (`public.orders.id`, Metabase writes every column so) names the subquery by
|
|
595
601
|
// its alias alone: sound only where no other item of the statement has the table's name
|
|
596
602
|
const named = all.flatMap((n) => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { gapMark } from "./gaps.js";
|
|
2
|
+
/** A statement's error as Postgres raises it, from outside the listener (the DuckDB runner's): the fields, and the SQL
|
|
3
|
+
* its position is in. */
|
|
4
|
+
export class StatementError extends Error {
|
|
5
|
+
fields;
|
|
6
|
+
sql;
|
|
7
|
+
/** the registry entry (src/duck/gaps.ts) that refused the statement, as the refusal was built with it (`gapRefusal`,
|
|
8
|
+
* src/duck/store.ts pgErrorOf): what the request log records as `gap`; null for every other error */
|
|
9
|
+
gap;
|
|
10
|
+
constructor(fields, sql = "", gap = null) { super(fields.message); this.fields = fields; this.sql = sql; this.gap = gap; }
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A REFUSAL BY A REGISTRY ENTRY (src/duck/gaps.ts), the one way one is built: 0A000, `Mason gap <id>` in its DETAIL (what
|
|
14
|
+
* the oracle and a reader read), followed by `detail` (docs/SQL-SUPPORT.md, or what it names), and the id itself on the
|
|
15
|
+
* error, so the request log records which entry refused it without reading the text back.
|
|
16
|
+
*/
|
|
17
|
+
export function gapRefusal(gap, message, hint, sql = "", detail = " (docs/SQL-SUPPORT.md)") {
|
|
18
|
+
return new StatementError({ code: "0A000", message, hint, detail: `${gapMark(gap)}${detail}` }, sql, gap);
|
|
19
|
+
}
|