@intentius/chant-lexicon-sql 0.124.18 → 0.124.19
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/dist/clickhouse/import/live-export.d.ts +8 -0
- package/dist/clickhouse/import/live-export.d.ts.map +1 -1
- package/dist/clickhouse/live/undeclarable.d.ts +32 -0
- package/dist/clickhouse/live/undeclarable.d.ts.map +1 -0
- package/dist/clickhouse/testing/fake-server.d.ts +6 -0
- package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
- package/dist/codegen/coverage-page.d.ts +7 -0
- package/dist/codegen/coverage-page.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/core/undeclarable.d.ts +38 -0
- package/dist/core/undeclarable.d.ts.map +1 -0
- package/dist/coverage/clickhouse-commands.json +24 -0
- package/dist/coverage/clickhouse.json +25 -0
- package/dist/coverage/postgres-commands.json +245 -0
- package/dist/coverage/postgres.json +51 -0
- package/dist/coverage.d.ts +68 -0
- package/dist/coverage.d.ts.map +1 -1
- package/dist/files/clickhouse.d.ts.map +1 -1
- package/dist/files/common.d.ts +12 -0
- package/dist/files/common.d.ts.map +1 -1
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/import-parser.d.ts +7 -2
- package/dist/import-parser.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/postgres/import/live-export.d.ts +8 -0
- package/dist/postgres/import/live-export.d.ts.map +1 -1
- package/dist/postgres/live/catalog.d.ts +12 -1
- package/dist/postgres/live/catalog.d.ts.map +1 -1
- package/dist/postgres/live/undeclarable.d.ts +42 -0
- package/dist/postgres/live/undeclarable.d.ts.map +1 -0
- package/dist/postgres/testing/fake-client.d.ts +6 -0
- package/dist/postgres/testing/fake-client.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clickhouse/import/live-export.ts +17 -2
- package/src/clickhouse/live/undeclarable.e2e.test.ts +64 -0
- package/src/clickhouse/live/undeclarable.ts +66 -0
- package/src/clickhouse/testing/fake-server.ts +5 -0
- package/src/codegen/classifier-page.test.ts +6 -0
- package/src/codegen/coverage-page.ts +77 -0
- package/src/codegen/docs.ts +5 -0
- package/src/core/undeclarable.test.ts +138 -0
- package/src/core/undeclarable.ts +66 -0
- package/src/coverage/clickhouse-commands.json +24 -0
- package/src/coverage/clickhouse.json +25 -0
- package/src/coverage/postgres-commands.json +245 -0
- package/src/coverage/postgres.json +51 -0
- package/src/coverage.test.ts +78 -0
- package/src/coverage.ts +142 -0
- package/src/files/clickhouse.ts +6 -1
- package/src/files/common.ts +18 -0
- package/src/files/postgres.ts +6 -1
- package/src/import-parser.ts +12 -4
- package/src/plugin.ts +6 -1
- package/src/postgres/import/live-export.ts +25 -2
- package/src/postgres/live/catalog.ts +7 -3
- package/src/postgres/live/undeclarable.e2e.test.ts +81 -0
- package/src/postgres/live/undeclarable.ts +141 -0
- package/src/postgres/testing/fake-client.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/files/common.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/files/common.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1F;AAED,2GAA2G;AAC3G,qBAAa,YAAa,SAAQ,KAAK;IAEnC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE;gBAD3B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,SAAS,MAAM,EAAE;CAOvC;AAED,sHAAsH;AACtH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,OAAO,GAAG,KAAK,EAAE,EAAE,CAiBhG;AAED,eAAO,MAAM,UAAU,GAAI,QAAQ,SAAS,KAAK,EAAE,KAAG,MAA4C,CAAC;AAEnG,yGAAyG;AACzG,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yGAAyG;IACzG,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAED,kIAAkI;AAClI,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,EAC7C,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,QAAQ,CAAC,UAAU,CAAC,CAGtB;AAED,0DAA0D;AAC1D,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,EAAE,IAAI,MAAM,KAAG,MAA8C,CAAC;AAEjG,+CAA+C;AAC/C,eAAO,MAAM,SAAS,GAAI,GAAG,MAAM,KAAG,MAAgD,CAAC;AAEvF,6FAA6F;AAC7F,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,MAAoF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/files/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/files/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAM9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAA8F,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvJ,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC;AAE/E,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAGzD;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,oBAAoB,CAE9E;AAKD,8EAA8E;AAC9E,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,WAAqF,CAAC;AAsB5H;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,gBAAgB,GAAG,QAAQ,CAAC,CAgJtH"}
|
package/dist/import-parser.d.ts
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* anything else is ClickHouse.
|
|
10
10
|
*
|
|
11
11
|
* A statement that declares nothing, or does not parse, is an error naming
|
|
12
|
-
* it (`./sql-files.ts`); none is left out.
|
|
12
|
+
* it (`./sql-files.ts`); none is left out. So is a `CREATE` of a kind chant
|
|
13
|
+
* cannot declare (#3911, `./coverage.ts`): `--parser-option
|
|
14
|
+
* skipUndeclarable` imports the rest and lists those as warnings. A clause the parser does not know
|
|
13
15
|
* (#3872) is kept in the template as written.
|
|
14
16
|
*/
|
|
15
17
|
import type { ParserOptions, TemplateIR, TemplateParser } from "@intentius/chant/import/parser";
|
|
@@ -20,7 +22,10 @@ export declare function guessDialect(content: string): SqlDialect;
|
|
|
20
22
|
export declare function dialectOption(options: ParserOptions | undefined): SqlDialect | undefined;
|
|
21
23
|
export declare class SqlFileParser implements TemplateParser {
|
|
22
24
|
private readonly dialect?;
|
|
23
|
-
|
|
25
|
+
private readonly options;
|
|
26
|
+
constructor(dialect?: SqlDialect | undefined, options?: {
|
|
27
|
+
skipUndeclarable?: boolean;
|
|
28
|
+
});
|
|
24
29
|
parse(content: string): TemplateIR;
|
|
25
30
|
}
|
|
26
31
|
//# sourceMappingURL=import-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../src/import-parser.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../src/import-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChG,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAqB3D,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAKxD;AAED,4CAA4C;AAC5C,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAKxF;AAED,qBAAa,aAAc,YAAW,cAAc;IAEhD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,OAAO,CAAC,EAAE,UAAU,YAAA,EACpB,OAAO,GAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAO;IAG/D,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;CAUnC"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "b0d3f2af30a4cfda562e80b0d5b293126c5afa89b288fc8078d77db257b1b56c",
|
|
5
5
|
"meta.json": "44b1dcfa5b5e5be2239fdc9614ab6c11d3f1461d64e61d572e3658af6c96aa68",
|
|
6
6
|
"types/index.d.ts": "0286ebb367773e84c76818b0f69cb14cff8328fd2f8c65ed87563c8d5815458c",
|
|
7
7
|
"rules/postgres-templates.ts": "94c2e73ec885a185fcc51c3346c371144eefe1512bc329f752cc7e609a0b4e5d",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"rules/sqlpg125.ts": "cc7411c57c525613dc720ba00e986bbc4e5a84b8b0bcfa7a6ce0d058f039f025",
|
|
80
80
|
"rules/sqlpg126.ts": "d57b82a5842273876510131d67bf4418cbd61eff821e245edebc644dafd502bb"
|
|
81
81
|
},
|
|
82
|
-
"composite": "
|
|
82
|
+
"composite": "573d3e3625a6077e3a9b005581cccb7f98be01143788ac6e4858b967dad9bd37"
|
|
83
83
|
}
|
package/dist/manifest.json
CHANGED
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAwB5E,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAwB5E,eAAO,MAAM,SAAS,EAAE,aA+OvB,CAAC"}
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* declarations: the GRANT and REVOKE statements that take a new object's
|
|
18
18
|
* privileges to what the server holds. Roles are the environment's and are
|
|
19
19
|
* not imported.
|
|
20
|
+
* - An object of a kind no declaration holds (an aggregate, a cast, a
|
|
21
|
+
* composite type, an event trigger, ...; `../live/undeclarable.ts`) fails
|
|
22
|
+
* the import, which names every one of them (#3911). `skipUndeclarable`
|
|
23
|
+
* (`--skip-undeclarable`) imports the rest and lists them as warnings. A
|
|
24
|
+
* selector or `owned` import asks for objects chant can hold, so it does
|
|
25
|
+
* not look.
|
|
20
26
|
*/
|
|
21
27
|
import type { ExportedTemplate, ResourceSelector } from "@intentius/chant/lexicon";
|
|
22
28
|
import { type BindOptions } from "../live/bind.js";
|
|
@@ -27,6 +33,8 @@ export interface ExportOptions extends Omit<BindOptions, "environment"> {
|
|
|
27
33
|
selector?: ResourceSelector;
|
|
28
34
|
owned?: boolean;
|
|
29
35
|
verbatim?: boolean;
|
|
36
|
+
/** Import what can be declared, and list the objects of kinds no declaration holds as warnings instead of failing (#3911). */
|
|
37
|
+
skipUndeclarable?: boolean;
|
|
30
38
|
}
|
|
31
39
|
export declare function exportResources(options: ExportOptions): Promise<ExportedTemplate>;
|
|
32
40
|
//# sourceMappingURL=live-export.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-export.d.ts","sourceRoot":"","sources":["../../../src/postgres/import/live-export.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"live-export.d.ts","sourceRoot":"","sources":["../../../src/postgres/import/live-export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAS9D,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8HAA8H;IAC9H,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyCvF"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - objects an extension owns (`pg_depend.deptype = 'e'`): the extension
|
|
19
19
|
* declares them, so the extension is what is imported;
|
|
20
20
|
* - `plpgsql`, installed in every database;
|
|
21
|
-
* -
|
|
21
|
+
* - a trigger the server makes for itself (a foreign key's)
|
|
22
22
|
* or clones onto a partition from its parent's;
|
|
23
23
|
* - a routine with a SQL-standard body (`RETURN`, `BEGIN ATOMIC`): read, but
|
|
24
24
|
* marked `unsupported`, since the server prints it rewritten and a
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
* progress (`../migrate/names.ts`, `../migrate/carry.ts`), so a table reads
|
|
33
33
|
* as it was before the migration until its switch, and as declared after it.
|
|
34
34
|
*
|
|
35
|
+
* Objects of the kinds no declaration holds (aggregates, casts, collations,
|
|
36
|
+
* composite and range types, event triggers, ...) are not read here: import
|
|
37
|
+
* finds them with `./undeclarable.ts` and refuses them by name (#3911).
|
|
38
|
+
*
|
|
35
39
|
* Objects another tool owns, an ORM's or a migration runner's revision table
|
|
36
40
|
* ({@link FOREIGN_TABLES}), are read and marked with the tool, so import
|
|
37
41
|
* leaves them out with a warning and observation reports them foreign, never
|
|
@@ -82,6 +86,13 @@ export interface SchemaScope {
|
|
|
82
86
|
/** The roles to read, when access is: the declared ones. A role is the cluster's, never read otherwise. */
|
|
83
87
|
roles?: readonly string[];
|
|
84
88
|
}
|
|
89
|
+
/** The scope as a predicate on a schema name column, with its parameter. */
|
|
90
|
+
export declare function scopeSql(column: string, scope: SchemaScope): {
|
|
91
|
+
sql: string;
|
|
92
|
+
params: unknown[];
|
|
93
|
+
};
|
|
94
|
+
/** `NOT <object is owned by an extension>`, for an object of catalog `catalog` with oid column `oid`. */
|
|
95
|
+
export declare const notExtensionOwned: (catalog: string, oid: string) => string;
|
|
85
96
|
interface SequenceParams {
|
|
86
97
|
type: string;
|
|
87
98
|
start: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/catalog.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAyB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAKjF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,iEAAiE;AACjE,eAAO,MAAM,cAAc,UAAmD,CAAC;AAE/E,gHAAgH;AAChH,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,kBAAkB,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wHAAwH;IACxH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qGAAqG;IACrG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,gHAAgH;IAChH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2GAA2G;IAC3G,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAqBD,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAM/F;AAED,yGAAyG;AACzG,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,EAAE,KAAK,MAAM,KAAG,MAC4F,CAAC;AAS9J,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAQD,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,CAKtH;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAa9E;AAcD;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAsf7G;AAED,+HAA+H;AAC/H,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,QAAQ,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,EAAE,YAAY,MAAM,KAAG,MAA6D,CAAC;AAEnK;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GAAG,YAAY,EAAE,CAW1H"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The objects in a live Postgres database of the kinds no declaration holds
|
|
3
|
+
* (#3911), found so import can refuse them by name instead of leaving them
|
|
4
|
+
* out (`../../core/undeclarable.ts`).
|
|
5
|
+
*
|
|
6
|
+
* One catalog query per kind the coverage table marks refused
|
|
7
|
+
* (`../../coverage/postgres.json`), keyed by the row's `kind`. A kind whose
|
|
8
|
+
* row is flipped to declared is no longer asked about: its objects are then
|
|
9
|
+
* the catalog reader's (`./catalog.ts`) to read.
|
|
10
|
+
*
|
|
11
|
+
* Left out, as the reader leaves them out: the server's own schemas, objects
|
|
12
|
+
* an extension owns (`pg_depend.deptype = 'e'`; the extension is declared),
|
|
13
|
+
* the objects `initdb` makes, whose oids are below 16384
|
|
14
|
+
* (`FirstNormalObjectId`), and an object that is part of another
|
|
15
|
+
* (`deptype = 'i'`: the casts a range type makes for itself). An object in a
|
|
16
|
+
* schema is looked for in the schemas in scope; one that belongs to the whole
|
|
17
|
+
* database (a cast, an event trigger, a publication, a foreign data wrapper)
|
|
18
|
+
* is always looked for.
|
|
19
|
+
*/
|
|
20
|
+
import type { PostgresClient } from "./client.js";
|
|
21
|
+
import { type SchemaScope } from "./catalog.js";
|
|
22
|
+
import type { UndeclarableObject } from "../../core/undeclarable.js";
|
|
23
|
+
interface Probe {
|
|
24
|
+
/**
|
|
25
|
+
* A query returning `schema` (NULL for a database-wide object), `name`, and
|
|
26
|
+
* for a database-wide object the role that owns it (so a managed
|
|
27
|
+
* provider's own can be told apart); `scope` is the predicate on `n.nspname`.
|
|
28
|
+
*/
|
|
29
|
+
sql(scope: string): string;
|
|
30
|
+
}
|
|
31
|
+
export declare const POSTGRES_PROBES: Readonly<Record<string, Probe>>;
|
|
32
|
+
/**
|
|
33
|
+
* Every object in scope of a kind the coverage table refuses, kind by kind
|
|
34
|
+
* in the table's order, each by schema then name. A kind the session may not
|
|
35
|
+
* read (a role without the privilege) is a problem in `unread`, not a pass.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readUndeclarable(client: PostgresClient, scope?: SchemaScope): Promise<{
|
|
38
|
+
objects: UndeclarableObject[];
|
|
39
|
+
unread: string[];
|
|
40
|
+
}>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=undeclarable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undeclarable.d.ts","sourceRoot":"","sources":["../../../src/postgres/live/undeclarable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAA+B,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAKlE,UAAU,KAAK;IACb;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AA2BD,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAqD3D,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,GAAE,WAAgB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAkBpJ"}
|
|
@@ -33,6 +33,12 @@ export interface FakeCatalog {
|
|
|
33
33
|
table: string;
|
|
34
34
|
def: string;
|
|
35
35
|
}>;
|
|
36
|
+
/** What the undeclarable-kind probes (`../live/undeclarable.ts`) find, by the coverage table's kind. Default: nothing. */
|
|
37
|
+
undeclarable?: Array<{
|
|
38
|
+
kind: string;
|
|
39
|
+
schema?: string;
|
|
40
|
+
name: string;
|
|
41
|
+
}>;
|
|
36
42
|
}
|
|
37
43
|
export declare function fakeClient(catalog: FakeCatalog, log?: string[]): PostgresClient;
|
|
38
44
|
//# sourceMappingURL=fake-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-client.d.ts","sourceRoot":"","sources":["../../../src/postgres/testing/fake-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACtJ,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvG,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"fake-client.d.ts","sourceRoot":"","sources":["../../../src/postgres/testing/fake-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACtJ,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvG,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,0HAA0H;IAC1H,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,cAAc,CA6BnF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-sql",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.19",
|
|
4
4
|
"description": "SQL lexicon for chant — database schema as typed declarations, one dialect at a time; ClickHouse first, with types read from a pinned server's catalog",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -70,6 +70,6 @@
|
|
|
70
70
|
"typescript": "^5.9.3"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@intentius/chant": "^0.124.
|
|
73
|
+
"@intentius/chant": "^0.124.19"
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
* (#3745), so a use is found by that body (`./inlined.ts`) as well as by
|
|
24
24
|
* the name. A function carries no comment, so `owned` does
|
|
25
25
|
* not apply to it.
|
|
26
|
+
* - An object of a kind no declaration holds (a window view, a quota, a
|
|
27
|
+
* settings profile, a named collection, ...; `../live/undeclarable.ts`)
|
|
28
|
+
* fails the import, which names every one of them (#3911).
|
|
29
|
+
* `skipUndeclarable` (`--skip-undeclarable`) imports the rest and lists
|
|
30
|
+
* them as warnings. A selector or `owned` import asks for objects chant can
|
|
31
|
+
* hold, so it does not look.
|
|
26
32
|
*/
|
|
27
33
|
|
|
28
34
|
import type { ExportedTemplate, ResourceSelector } from "@intentius/chant/lexicon";
|
|
@@ -35,6 +41,8 @@ import { isTrivia, tokenizeText } from "../tokens";
|
|
|
35
41
|
import { unquote } from "../parser";
|
|
36
42
|
import type { LiveObject } from "../live/catalog";
|
|
37
43
|
import { inlinedFunctions } from "./inlined";
|
|
44
|
+
import { readUndeclarable } from "../live/undeclarable";
|
|
45
|
+
import { refuseUndeclarable } from "../../core/undeclarable";
|
|
38
46
|
|
|
39
47
|
/** The functions among `names` a statement calls: a name followed by `(`. */
|
|
40
48
|
export function calledFunctions(ddl: string, names: ReadonlySet<string>): string[] {
|
|
@@ -83,11 +91,19 @@ export interface ExportOptions extends Omit<BindOptions, "environment"> {
|
|
|
83
91
|
selector?: ResourceSelector;
|
|
84
92
|
owned?: boolean;
|
|
85
93
|
verbatim?: boolean;
|
|
94
|
+
/** Import what can be declared, and list the objects of kinds no declaration holds as warnings instead of failing (#3911). */
|
|
95
|
+
skipUndeclarable?: boolean;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
export async function exportResources(options: ExportOptions): Promise<ExportedTemplate> {
|
|
89
99
|
const target = await bindClickHouse(options);
|
|
90
|
-
|
|
100
|
+
// A selector or an owned import asks only for what chant can hold.
|
|
101
|
+
const undeclarable = !options.selector && !options.owned ? await readUndeclarable(target) : { objects: [], unread: [] };
|
|
102
|
+
const warnings: string[] = undeclarable.unread.map((u) => `the server was not checked for objects of one kind chant cannot declare, so some may be left out: ${u}`);
|
|
103
|
+
refuseUndeclarable("clickhouse", undeclarable.objects, options.skipUndeclarable, warnings);
|
|
104
|
+
// What is refused, and with --skip-undeclarable left out, is not also read as a table (a window view is in system.tables).
|
|
105
|
+
const refused = new Set(undeclarable.objects.filter((o) => o.schema !== undefined).map((o) => `${o.schema}.${o.name}`));
|
|
106
|
+
const all = (await readLiveSchema(target)).filter((o) => !(o.database !== undefined && refused.has(`${o.database}.${o.name}`)));
|
|
91
107
|
const isFunction = (o: LiveObject) => o.type === CLICKHOUSE_ENTITY_TYPES.function;
|
|
92
108
|
// A selector picks what it names, functions included; without one, functions go with what calls them (#3718).
|
|
93
109
|
const live = options.selector ? all : all.filter((o) => !isFunction(o));
|
|
@@ -98,7 +114,6 @@ export async function exportResources(options: ExportOptions): Promise<ExportedT
|
|
|
98
114
|
if (name !== undefined && name !== o.name && name !== `${o.database}.${o.name}`) return false;
|
|
99
115
|
return true;
|
|
100
116
|
});
|
|
101
|
-
const warnings: string[] = [];
|
|
102
117
|
if (!options.selector) {
|
|
103
118
|
const { adopted, skipped } = scopedFunctions(selected.filter((o) => !o.foreign), all.filter(isFunction), target.importFunctions);
|
|
104
119
|
selected.push(...adopted);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import against a real ClickHouse server never leaves an object out without
|
|
3
|
+
* a word (#3911): a server holding a quota, a settings profile, a named
|
|
4
|
+
* collection and a workload made with SQL, beside a table, fails the import,
|
|
5
|
+
* which names all of them; with `skipUndeclarable` the table is imported and
|
|
6
|
+
* they are warnings. What `users.xml` defines (the `default` quota and
|
|
7
|
+
* profile) is the server's, and is not refused.
|
|
8
|
+
*
|
|
9
|
+
* Needs Docker; skips cleanly without it. One throwaway
|
|
10
|
+
* `clickhouse/clickhouse-server` container at the pin, removed afterwards
|
|
11
|
+
* even on failure.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
15
|
+
import { dockerAvailable, startScratchServer, type ScratchServer } from "../container";
|
|
16
|
+
import { clickhouseQuery } from "../http";
|
|
17
|
+
import { clickhouseImage } from "../../spec/pin";
|
|
18
|
+
import { exportResources } from "../import/live-export";
|
|
19
|
+
import { UndeclarableObjectsError } from "../../core/undeclarable";
|
|
20
|
+
|
|
21
|
+
const enabled = await dockerAvailable();
|
|
22
|
+
let server: ScratchServer | undefined;
|
|
23
|
+
|
|
24
|
+
const SEED = [
|
|
25
|
+
"CREATE DATABASE analytics",
|
|
26
|
+
"CREATE TABLE analytics.events (id UInt64) ENGINE = MergeTree ORDER BY id",
|
|
27
|
+
"CREATE QUOTA q_reports FOR INTERVAL 1 hour MAX queries = 100",
|
|
28
|
+
"CREATE SETTINGS PROFILE p_reports SETTINGS max_threads = 2",
|
|
29
|
+
"CREATE NAMED COLLECTION nc_s3 AS url = 'https://example.com/bucket'",
|
|
30
|
+
"CREATE WORKLOAD all",
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
beforeAll(async () => {
|
|
34
|
+
if (!enabled) return;
|
|
35
|
+
server = await startScratchServer(clickhouseImage(), { namePrefix: "chant-sql-undeclarable" });
|
|
36
|
+
for (const stmt of SEED) await clickhouseQuery(server.endpoint, stmt);
|
|
37
|
+
}, 600_000);
|
|
38
|
+
|
|
39
|
+
afterAll(async () => {
|
|
40
|
+
await server?.stop();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const run = (extra: Record<string, unknown> = {}) => exportResources({ environment: "x", config: {}, env: { CLICKHOUSE_URL: server!.endpoint.url }, ...extra });
|
|
44
|
+
|
|
45
|
+
const NAMED = ["named collection nc_s3", "quota q_reports", "settings profile p_reports", "workload all"];
|
|
46
|
+
|
|
47
|
+
describe.skipIf(!enabled)("importing a server with objects chant cannot declare", () => {
|
|
48
|
+
test("fails, naming every one of them", async () => {
|
|
49
|
+
const err = await run().then(
|
|
50
|
+
() => undefined,
|
|
51
|
+
(e: unknown) => e,
|
|
52
|
+
);
|
|
53
|
+
expect(err).toBeInstanceOf(UndeclarableObjectsError);
|
|
54
|
+
const objects = (err as UndeclarableObjectsError).objects.map((o) => `${o.kind} ${o.schema ? `${o.schema}.` : ""}${o.name}`);
|
|
55
|
+
expect([...objects].sort()).toEqual(NAMED);
|
|
56
|
+
}, 120_000);
|
|
57
|
+
|
|
58
|
+
test("with skipUndeclarable, imports the rest and lists them as warnings", async () => {
|
|
59
|
+
const ir = await run({ skipUndeclarable: true });
|
|
60
|
+
expect(ir.resources.map((r) => r.properties.name)).toEqual(["analytics", "events"]);
|
|
61
|
+
const left = (ir.warnings ?? []).filter((w) => w.includes("is left out (--skip-undeclarable)"));
|
|
62
|
+
expect(left.map((w) => w.split(" is left out")[0]).sort()).toEqual(NAMED);
|
|
63
|
+
}, 120_000);
|
|
64
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The objects on a live ClickHouse server of the kinds no declaration holds
|
|
3
|
+
* (#3911), found so import can refuse them by name instead of leaving them
|
|
4
|
+
* out (`../../core/undeclarable.ts`).
|
|
5
|
+
*
|
|
6
|
+
* One `system.*` query per kind the coverage table marks refused
|
|
7
|
+
* (`../../coverage/clickhouse.json`), keyed by the row's `kind`. A kind whose
|
|
8
|
+
* row is flipped to declared is no longer asked about.
|
|
9
|
+
*
|
|
10
|
+
* What the server's configuration files define (`storage = 'users_xml'`, a
|
|
11
|
+
* named collection whose source is the config, an executable function) is
|
|
12
|
+
* the server's, not a schema's, and is left out; so is what is in a database
|
|
13
|
+
* out of scope. A server-wide object (a quota, a settings profile, a
|
|
14
|
+
* workload) is always looked for.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { clickhouseQuery } from "../http";
|
|
18
|
+
import type { ClickHouseTarget } from "./bind";
|
|
19
|
+
import { SYSTEM_DATABASES } from "./catalog";
|
|
20
|
+
import { KIND_COVERAGE } from "../../coverage";
|
|
21
|
+
import type { UndeclarableObject } from "../../core/undeclarable";
|
|
22
|
+
|
|
23
|
+
const quote = (s: string) => `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
24
|
+
|
|
25
|
+
/** A query returning `database` (empty for a server-wide object) and `name`; `scope` is the predicate on a database column. */
|
|
26
|
+
type Probe = (scope: (column: string) => string) => string;
|
|
27
|
+
|
|
28
|
+
const serverWide = (table: string, where = "1") => () => `SELECT '' AS database, name FROM system.${table} WHERE ${where}`;
|
|
29
|
+
const SQL_DEFINED = "storage != 'users_xml'";
|
|
30
|
+
|
|
31
|
+
export const CLICKHOUSE_PROBES: Readonly<Record<string, Probe>> = {
|
|
32
|
+
"non-SQL function": serverWide("functions", "origin NOT IN ('System', 'SQLUserDefined', 'ExecutableUserDefined')"),
|
|
33
|
+
"HTTP handler": serverWide("handlers"),
|
|
34
|
+
"masking policy": (scope) => `SELECT database, short_name || ' ON ' || table AS name FROM system.masking_policies WHERE ${SQL_DEFINED} AND ${scope("database")}`,
|
|
35
|
+
"named collection": serverWide("named_collections", "source = 'SQL'"),
|
|
36
|
+
quota: serverWide("quotas", SQL_DEFINED),
|
|
37
|
+
"workload resource": serverWide("resources"),
|
|
38
|
+
"settings profile": serverWide("settings_profiles", SQL_DEFINED),
|
|
39
|
+
"window view": (scope) => `SELECT database, name FROM system.tables WHERE engine = 'WindowView' AND NOT is_temporary AND ${scope("database")}`,
|
|
40
|
+
workload: serverWide("workloads"),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Every object in scope of a kind the coverage table refuses, kind by kind
|
|
45
|
+
* in the table's order, each by database then name. A kind the reader may
|
|
46
|
+
* not read (no grant on its `system` table) is a problem in `unread`, not a
|
|
47
|
+
* pass.
|
|
48
|
+
*/
|
|
49
|
+
export async function readUndeclarable(target: ClickHouseTarget): Promise<{ objects: UndeclarableObject[]; unread: string[] }> {
|
|
50
|
+
const scope = (column: string) =>
|
|
51
|
+
target.databases ? `${column} IN (${target.databases.map(quote).join(", ")})` : `${column} NOT IN (${[...SYSTEM_DATABASES].map(quote).join(", ")})`;
|
|
52
|
+
const objects: UndeclarableObject[] = [];
|
|
53
|
+
const unread: string[] = [];
|
|
54
|
+
for (const row of KIND_COVERAGE.clickhouse) {
|
|
55
|
+
if (row.status !== "refused") continue;
|
|
56
|
+
const probe = CLICKHOUSE_PROBES[row.kind];
|
|
57
|
+
if (!probe) throw new Error(`no system table query finds a ClickHouse ${row.kind} (src/clickhouse/live/undeclarable.ts)`);
|
|
58
|
+
try {
|
|
59
|
+
const rows = await clickhouseQuery<{ database: string; name: string }>(target.endpoint, `/* chant undeclarable: ${row.kind} */ SELECT database, name FROM (${probe(scope)}) ORDER BY database, name`);
|
|
60
|
+
for (const r of rows) objects.push({ kind: row.kind, ...(r.database ? { schema: r.database } : {}), name: r.name });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
unread.push(`${row.kind}: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { objects, unread };
|
|
66
|
+
}
|
|
@@ -26,6 +26,8 @@ export interface FakeOptions {
|
|
|
26
26
|
version?: string;
|
|
27
27
|
/** The SQL user-defined functions `system.functions` lists. Default: none. */
|
|
28
28
|
functions?: Array<{ name: string; statement: string }>;
|
|
29
|
+
/** What the undeclarable-kind probes (`../live/undeclarable.ts`) find, by the coverage table's kind. Default: nothing. */
|
|
30
|
+
undeclarable?: Array<{ kind: string; database?: string; name: string }>;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const row = (o: Record<string, unknown>) => `${JSON.stringify(o)}\n`;
|
|
@@ -38,6 +40,9 @@ export async function fakeClickHouse(objects: FakeObject[], options: FakeOptions
|
|
|
38
40
|
queries.push(body);
|
|
39
41
|
if (options.fail) return { status: options.fail.status, text: options.fail.body };
|
|
40
42
|
if (/version\(\)/.test(body)) return ok(row({ v: options.version ?? "26.8.15.10" }));
|
|
43
|
+
// A probe for one undeclarable kind (`../live/undeclarable.ts`), which its comment names.
|
|
44
|
+
const probe = /^\/\* chant undeclarable: (.+?) \*\//.exec(body);
|
|
45
|
+
if (probe) return ok((options.undeclarable ?? []).filter((u) => u.kind === probe[1]).map((u) => row({ database: u.database ?? "", name: u.name })).join(""));
|
|
41
46
|
if (/FROM system\.databases/.test(body)) {
|
|
42
47
|
return ok(
|
|
43
48
|
objects
|
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { expect, test } from "vitest";
|
|
4
4
|
import { renderClassifierPage } from "./classifier-page";
|
|
5
5
|
import { renderPostgresClassifierPage } from "./postgres-classifier-page";
|
|
6
|
+
import { renderCoveragePage } from "./coverage-page";
|
|
6
7
|
|
|
7
8
|
test("the committed classifier page is what the rules render (run npm run docs)", () => {
|
|
8
9
|
const committed = readFileSync(join(import.meta.dirname, "..", "..", "docs", "pages", "change-classifier.mdx"), "utf-8");
|
|
@@ -14,3 +15,8 @@ test("the committed Postgres classifier page is what the rules render (run npm r
|
|
|
14
15
|
const committed = readFileSync(join(import.meta.dirname, "..", "..", "docs", "pages", "postgres-change-classifier.mdx"), "utf-8");
|
|
15
16
|
expect(committed).toBe(renderPostgresClassifierPage());
|
|
16
17
|
});
|
|
18
|
+
|
|
19
|
+
test("the committed object coverage page is what the coverage tables render (run npm run docs)", () => {
|
|
20
|
+
const committed = readFileSync(join(import.meta.dirname, "..", "..", "docs", "pages", "object-coverage.mdx"), "utf-8");
|
|
21
|
+
expect(committed).toBe(renderCoveragePage());
|
|
22
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `docs/pages/object-coverage.mdx`, written from the coverage tables
|
|
3
|
+
* (`../coverage/<dialect>.json`) by `npm run docs`, so the page cannot
|
|
4
|
+
* disagree with what import refuses.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { KIND_COVERAGE, PINNED_COMMANDS, type KindRow } from "../coverage";
|
|
8
|
+
import type { SqlDialect } from "../dialects";
|
|
9
|
+
|
|
10
|
+
const cell = (s: string) => s.replace(/\|/g, "\\|").replace(/[{}<>]/g, (c) => `\\${c}`);
|
|
11
|
+
|
|
12
|
+
const STATUS: Record<KindRow["status"], string> = { declared: "declared", refused: "refused", "out-of-scope": "out of scope" };
|
|
13
|
+
|
|
14
|
+
function table(dialect: SqlDialect): string {
|
|
15
|
+
const rows = [...KIND_COVERAGE[dialect]].sort((a, b) => (a.command < b.command ? -1 : a.command > b.command ? 1 : 0));
|
|
16
|
+
return [
|
|
17
|
+
"| Command | Kind | Status | Declared by, or why not |",
|
|
18
|
+
"|---|---|---|---|",
|
|
19
|
+
...rows.map((r) => {
|
|
20
|
+
const command = `\`${r.command}\`${r.form ? ` (${cell(r.form)})` : ""}`;
|
|
21
|
+
const why = r.status === "declared" ? `\`${r.entity}\`${r.note ? `: ${cell(r.note)}` : ""}` : cell(r.reason ?? "");
|
|
22
|
+
return `| ${command} | ${cell(r.kind)} | ${STATUS[r.status]} | ${why} |`;
|
|
23
|
+
}),
|
|
24
|
+
].join("\n");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function counts(dialect: SqlDialect): string {
|
|
28
|
+
const rows = KIND_COVERAGE[dialect];
|
|
29
|
+
const n = (s: KindRow["status"]) => rows.filter((r) => r.status === s).length;
|
|
30
|
+
return `${n("declared")} declared, ${n("refused")} refused, ${n("out-of-scope")} out of scope`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function renderCoveragePage(): string {
|
|
34
|
+
const pg = PINNED_COMMANDS.postgres.map((p) => p.version).join(", ");
|
|
35
|
+
const ch = PINNED_COMMANDS.clickhouse.map((p) => p.version).join(", ");
|
|
36
|
+
return `---
|
|
37
|
+
title: "Object Coverage"
|
|
38
|
+
label: "Object Coverage"
|
|
39
|
+
description: "Every kind of object each pinned Postgres and ClickHouse server can create, marked declared, refused with a reason, or out of scope, and what import does with each"
|
|
40
|
+
diataxis: reference
|
|
41
|
+
order: 3
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
{/* Written by src/codegen/coverage-page.ts from src/coverage/postgres.json and src/coverage/clickhouse.json. Edit the tables, then run npm run docs. */}
|
|
45
|
+
|
|
46
|
+
Each row is a \`CREATE\` command a pinned server has, or one form of a command where chant treats the forms apart (\`CREATE TYPE ... AS ENUM\` is declared, \`CREATE TYPE ... AS RANGE\` is not). The commands come from the servers' own sources at each pinned version (\`scripts/sql-create-commands.ts\`), and a test fails when a pinned server has a command with no row here, so a new server release cannot add a kind nobody looked at.
|
|
47
|
+
|
|
48
|
+
- **declared**: a tag declares it, and import writes it as that declaration.
|
|
49
|
+
- **refused**: no declaration holds it yet. \`chant import --from <env>\` fails when the server has one, naming every such object (kind, schema, name) at once, and \`chant import schema.sql\` fails on the statement. Nothing is left out without a word.
|
|
50
|
+
- **out of scope**: the environment's, not a schema's: import never reads it.
|
|
51
|
+
|
|
52
|
+
\`--skip-undeclarable\` (\`--parser-option skipUndeclarable\` for a file) imports everything else and lists the refused objects as warnings, for an adopter who keeps those objects by hand for now:
|
|
53
|
+
|
|
54
|
+
\`\`\`bash
|
|
55
|
+
chant import --from prod --skip-undeclarable
|
|
56
|
+
chant import schema.sql --parser-option skipUndeclarable
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
What a managed provider, an extension or the server's configuration files define is never refused: an extension's aggregates and casts are the extension's, declared with it, and a quota from \`users.xml\` is the server's. An import with \`--type\`, \`--name\` or \`--owned\` asks only for objects chant can hold, and does not look for the rest.
|
|
60
|
+
|
|
61
|
+
## Postgres
|
|
62
|
+
|
|
63
|
+
Pinned majors: ${pg}. ${counts("postgres")}.
|
|
64
|
+
|
|
65
|
+
${table("postgres")}
|
|
66
|
+
|
|
67
|
+
## ClickHouse
|
|
68
|
+
|
|
69
|
+
Pinned release: ${ch}. ${counts("clickhouse")}.
|
|
70
|
+
|
|
71
|
+
${table("clickhouse")}
|
|
72
|
+
|
|
73
|
+
## Declaring a refused kind
|
|
74
|
+
|
|
75
|
+
A kind becomes declarable in three steps: a tag that declares it, the catalog reader reading it (\`src/postgres/live/catalog.ts\`, \`src/clickhouse/live/catalog.ts\`), and its row flipped to \`declared\` with the entity type, its reason taken out. The row's flip is what stops import refusing the kind. After moving a pin, run \`npx tsx scripts/sql-create-commands.ts\` and give each new command a row.
|
|
76
|
+
`;
|
|
77
|
+
}
|
package/src/codegen/docs.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { writeFileSync } from "fs";
|
|
|
9
9
|
import { docsPipeline, writeDocsSite, type DocsConfig } from "@intentius/chant/codegen/docs";
|
|
10
10
|
import { renderClassifierPage } from "./classifier-page";
|
|
11
11
|
import { renderPostgresClassifierPage } from "./postgres-classifier-page";
|
|
12
|
+
import { renderCoveragePage } from "./coverage-page";
|
|
12
13
|
|
|
13
14
|
const overview = `The sql lexicon declares database schema in TypeScript. It has two dialects,
|
|
14
15
|
each a subpath of the one package: ClickHouse at
|
|
@@ -143,6 +144,9 @@ Both dialects:
|
|
|
143
144
|
- [Composites](./composites/): five ClickHouse and five Postgres composites.
|
|
144
145
|
- [Lint Rules and Checks](./lint-rules/): every lint rule and post-synth
|
|
145
146
|
check.
|
|
147
|
+
- [Object Coverage](./object-coverage/): every kind of object the pinned
|
|
148
|
+
servers can create, declared, refused or out of scope, and what import
|
|
149
|
+
does with each.
|
|
146
150
|
|
|
147
151
|
## Schema changes and \`chant migrate\`
|
|
148
152
|
|
|
@@ -201,6 +205,7 @@ export async function generateDocs(opts?: { verbose?: boolean }): Promise<void>
|
|
|
201
205
|
|
|
202
206
|
writeFileSync(join(pkgDir, "docs", "pages", "change-classifier.mdx"), renderClassifierPage());
|
|
203
207
|
writeFileSync(join(pkgDir, "docs", "pages", "postgres-change-classifier.mdx"), renderPostgresClassifierPage());
|
|
208
|
+
writeFileSync(join(pkgDir, "docs", "pages", "object-coverage.mdx"), renderCoveragePage());
|
|
204
209
|
|
|
205
210
|
const config: DocsConfig = {
|
|
206
211
|
name: "sql",
|