@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.
Files changed (60) hide show
  1. package/dist/clickhouse/import/live-export.d.ts +8 -0
  2. package/dist/clickhouse/import/live-export.d.ts.map +1 -1
  3. package/dist/clickhouse/live/undeclarable.d.ts +32 -0
  4. package/dist/clickhouse/live/undeclarable.d.ts.map +1 -0
  5. package/dist/clickhouse/testing/fake-server.d.ts +6 -0
  6. package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
  7. package/dist/codegen/coverage-page.d.ts +7 -0
  8. package/dist/codegen/coverage-page.d.ts.map +1 -0
  9. package/dist/codegen/docs.d.ts.map +1 -1
  10. package/dist/core/undeclarable.d.ts +38 -0
  11. package/dist/core/undeclarable.d.ts.map +1 -0
  12. package/dist/coverage/clickhouse-commands.json +24 -0
  13. package/dist/coverage/clickhouse.json +25 -0
  14. package/dist/coverage/postgres-commands.json +245 -0
  15. package/dist/coverage/postgres.json +51 -0
  16. package/dist/coverage.d.ts +68 -0
  17. package/dist/coverage.d.ts.map +1 -1
  18. package/dist/files/clickhouse.d.ts.map +1 -1
  19. package/dist/files/common.d.ts +12 -0
  20. package/dist/files/common.d.ts.map +1 -1
  21. package/dist/files/postgres.d.ts.map +1 -1
  22. package/dist/import-parser.d.ts +7 -2
  23. package/dist/import-parser.d.ts.map +1 -1
  24. package/dist/integrity.json +2 -2
  25. package/dist/manifest.json +1 -1
  26. package/dist/plugin.d.ts.map +1 -1
  27. package/dist/postgres/import/live-export.d.ts +8 -0
  28. package/dist/postgres/import/live-export.d.ts.map +1 -1
  29. package/dist/postgres/live/catalog.d.ts +12 -1
  30. package/dist/postgres/live/catalog.d.ts.map +1 -1
  31. package/dist/postgres/live/undeclarable.d.ts +42 -0
  32. package/dist/postgres/live/undeclarable.d.ts.map +1 -0
  33. package/dist/postgres/testing/fake-client.d.ts +6 -0
  34. package/dist/postgres/testing/fake-client.d.ts.map +1 -1
  35. package/package.json +2 -2
  36. package/src/clickhouse/import/live-export.ts +17 -2
  37. package/src/clickhouse/live/undeclarable.e2e.test.ts +64 -0
  38. package/src/clickhouse/live/undeclarable.ts +66 -0
  39. package/src/clickhouse/testing/fake-server.ts +5 -0
  40. package/src/codegen/classifier-page.test.ts +6 -0
  41. package/src/codegen/coverage-page.ts +77 -0
  42. package/src/codegen/docs.ts +5 -0
  43. package/src/core/undeclarable.test.ts +138 -0
  44. package/src/core/undeclarable.ts +66 -0
  45. package/src/coverage/clickhouse-commands.json +24 -0
  46. package/src/coverage/clickhouse.json +25 -0
  47. package/src/coverage/postgres-commands.json +245 -0
  48. package/src/coverage/postgres.json +51 -0
  49. package/src/coverage.test.ts +78 -0
  50. package/src/coverage.ts +142 -0
  51. package/src/files/clickhouse.ts +6 -1
  52. package/src/files/common.ts +18 -0
  53. package/src/files/postgres.ts +6 -1
  54. package/src/import-parser.ts +12 -4
  55. package/src/plugin.ts +6 -1
  56. package/src/postgres/import/live-export.ts +25 -2
  57. package/src/postgres/live/catalog.ts +7 -3
  58. package/src/postgres/live/undeclarable.e2e.test.ts +81 -0
  59. package/src/postgres/live/undeclarable.ts +141 -0
  60. 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;AAEpD,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;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
+ {"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,EAAuE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEhI,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,CA2ItH"}
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"}
@@ -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. A clause the parser does not know
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
- constructor(dialect?: SqlDialect | undefined);
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;;;;;;;;;;;;;GAaG;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;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAR,OAAO,CAAC,EAAE,UAAU,YAAA;IAEjD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;CAOnC"}
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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "e48965d23f7b17728998ff0865d9dd4de65dc2c38c7f9dfa0f3cd01ed70ae32e",
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": "fcdfe796a1548ff8cdd0a4b18c35e130c52e4746c2855562a70c2dfe28c9523c"
82
+ "composite": "573d3e3625a6077e3a9b005581cccb7f98be01143788ac6e4858b967dad9bd37"
83
83
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sql",
3
- "version": "0.124.18",
3
+ "version": "0.124.19",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "SQL",
6
6
  "intrinsics": [],
@@ -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,aA0OvB,CAAC"}
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;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAM9D,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;CACpB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6BvF"}
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
- * - aggregates, and a trigger the server makes for itself (a foreign key's)
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;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;AAyCD,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"}
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;CAC/E;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,GAAE,MAAM,EAAO,GAAG,cAAc,CA0BnF"}
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.18",
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.18"
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
- const all = await readLiveSchema(target);
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
+ }
@@ -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",