@intentius/chant-lexicon-sql 0.124.48 → 0.124.49
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/plan/statements.d.ts +29 -0
- package/dist/clickhouse/plan/statements.d.ts.map +1 -0
- package/dist/clickhouse/plan/statements.js +334 -0
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/classifier-page.js +23 -0
- package/dist/codegen/postgres-classifier-page.js +2 -2
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/codegen/registry.js +5 -0
- package/dist/core/statement-class.d.ts +55 -0
- package/dist/core/statement-class.d.ts.map +1 -0
- package/dist/core/statement-class.js +43 -0
- package/dist/coverage/postgres.json +6 -6
- package/dist/drizzle.d.ts.map +1 -1
- package/dist/drizzle.js +5 -0
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/files/postgres.js +23 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/integrity.json +5 -5
- package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
- package/dist/lint/post-synth/postgres-names.js +3 -0
- package/dist/lint/rules/postgres-templates.js +2 -2
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/lsp/postgres-template.js +10 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/postgres.js +1 -1
- package/dist/meta.json +25 -0
- package/dist/migration-classify.d.ts +23 -0
- package/dist/migration-classify.d.ts.map +1 -0
- package/dist/migration-classify.js +44 -0
- package/dist/okf/index.md +5 -0
- package/dist/okf/types/PostgresForeignTable.md +9 -0
- package/dist/okf/types/PostgresOperatorClass.md +9 -0
- package/dist/okf/types/PostgresOperatorFamily.md +9 -0
- package/dist/okf/types/PostgresTextSearchConfiguration.md +9 -0
- package/dist/okf/types/PostgresTextSearchDictionary.md +9 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +13 -0
- package/dist/postgres/access/acl.js +1 -1
- package/dist/postgres/access/import.js +1 -1
- package/dist/postgres/apply/apply.js +5 -0
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/apply/statements.js +156 -2
- package/dist/postgres/entities.d.ts +120 -2
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entities.js +180 -5
- package/dist/postgres/entity-types.d.ts +5 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/entity-types.js +5 -0
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/generator.js +16 -1
- package/dist/postgres/live/catalog-kinds.d.ts +6 -0
- package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
- package/dist/postgres/live/catalog-kinds.js +246 -0
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.js +2 -0
- package/dist/postgres/migrate/observe.d.ts.map +1 -1
- package/dist/postgres/migrate/observe.js +4 -1
- package/dist/postgres/migrate/replication.d.ts +42 -0
- package/dist/postgres/migrate/replication.d.ts.map +1 -1
- package/dist/postgres/migrate/replication.js +53 -0
- package/dist/postgres/parser.d.ts +73 -1
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/parser.js +181 -7
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/deep.js +15 -4
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/diff.js +120 -5
- package/dist/postgres/plan/normalize.d.ts +12 -1
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.js +151 -0
- package/dist/postgres/plan/rules.d.ts +3 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/rules.js +5 -2
- package/dist/postgres/plan/schema.js +1 -1
- package/dist/postgres/plan/statements.d.ts +26 -0
- package/dist/postgres/plan/statements.d.ts.map +1 -0
- package/dist/postgres/plan/statements.js +364 -0
- package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
- package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
- package/dist/postgres/testing/roundtrip-schema.js +12 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/postgres.js +1 -1
- package/dist/rules/postgres-names.ts +2 -0
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +5 -0
- package/package.json +2 -2
- package/src/clickhouse/plan/statements.test.ts +61 -0
- package/src/clickhouse/plan/statements.ts +306 -0
- package/src/codegen/classifier-page.ts +23 -0
- package/src/codegen/postgres-classifier-page.ts +2 -2
- package/src/codegen/registry.ts +5 -0
- package/src/core/statement-class.ts +88 -0
- package/src/coverage/postgres.json +6 -6
- package/src/drizzle.ts +5 -0
- package/src/files/postgres.ts +22 -1
- package/src/generated/lexicon-sql.json +25 -0
- package/src/index.ts +1 -1
- package/src/lint/post-synth/postgres-checks.test.ts +8 -1
- package/src/lint/post-synth/postgres-names.ts +2 -0
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lsp/postgres-template.ts +9 -0
- package/src/mcp/postgres.ts +1 -1
- package/src/migration-classify.test.ts +150 -0
- package/src/migration-classify.ts +45 -0
- package/src/op/watch-pipeline.test.ts +8 -1
- package/src/plugin.ts +14 -0
- package/src/postgres/access/acl.ts +1 -1
- package/src/postgres/access/import.ts +1 -1
- package/src/postgres/apply/apply.ts +5 -0
- package/src/postgres/apply/statements.ts +151 -3
- package/src/postgres/database-kinds.test.ts +498 -17
- package/src/postgres/entities.ts +276 -5
- package/src/postgres/entity-types.ts +5 -0
- package/src/postgres/import/generator.ts +13 -1
- package/src/postgres/live/catalog-kinds.ts +271 -0
- package/src/postgres/live/describe-resources.ts +2 -0
- package/src/postgres/migrate/observe.ts +3 -1
- package/src/postgres/migrate/replication.ts +66 -0
- package/src/postgres/migrate/subscriber.e2e.test.ts +141 -0
- package/src/postgres/migrate/subscriber.test.ts +59 -0
- package/src/postgres/parser.ts +225 -6
- package/src/postgres/plan/deep.ts +15 -4
- package/src/postgres/plan/diff.ts +107 -6
- package/src/postgres/plan/normalize.ts +155 -0
- package/src/postgres/plan/rules.ts +25 -4
- package/src/postgres/plan/schema.ts +1 -1
- package/src/postgres/plan/statements.test.ts +78 -0
- package/src/postgres/plan/statements.ts +299 -0
- package/src/postgres/testing/roundtrip-schema.ts +12 -1
- package/src/postgres.ts +17 -0
- package/src/skills/chant-sql-postgres-migration.md +1 -0
- package/src/skills/chant-sql-postgres.md +5 -0
- package/src/sql-files.test.ts +33 -0
- package/src/validate.ts +5 -0
package/src/postgres/entities.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
|
|
3
3
|
* `type` (enum, composite and range), `domain`, `extension`, `func`,
|
|
4
4
|
* `procedure`, `aggregate`, `trigger`, `eventTrigger`, `collation`, `cast`,
|
|
5
|
-
* `
|
|
5
|
+
* `operatorClass`, `operatorFamily`, `foreignTable`, `textSearchConfiguration`,
|
|
6
|
+
* `textSearchDictionary`, `policy`, `role` and `grant` tagged templates that parse their DDL at fold
|
|
6
7
|
* time into one Declarable each (chant #3278, #3279, #3680, #3681, #3911).
|
|
7
8
|
*
|
|
8
9
|
* ```ts
|
|
@@ -57,7 +58,7 @@
|
|
|
57
58
|
import type { AttrRef } from "@intentius/chant/attrref";
|
|
58
59
|
import { setInterpolationFields } from "@intentius/chant/provenance";
|
|
59
60
|
import { isTrivia, POSTGRES_LEXICAL, SqlSyntaxError, untokenize, type Token } from "./tokens";
|
|
60
|
-
import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type DefElem, type GenericOption, type GrantNode, type NameNode, type RowSecurityNode, type Span, type StatementNode } from "./parser";
|
|
61
|
+
import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type DefElem, type GenericOption, type GrantNode, type NameNode, type RowSecurityNode, type Span, type StatementNode, type TextSearchMappingNode } from "./parser";
|
|
61
62
|
import { keywordCategory, quoteIdent } from "./keywords";
|
|
62
63
|
import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, unparsedClauses, type TemplateCtx, type TemplateDialect, type UnparsedClause } from "../core/interpolation";
|
|
63
64
|
import { SqlObject, isColumnRefOf, isSqlObjectOf, makeSqlEntity } from "../core/entity";
|
|
@@ -93,6 +94,8 @@ export interface ColumnDef {
|
|
|
93
94
|
collate?: string;
|
|
94
95
|
compression?: string;
|
|
95
96
|
storage?: string;
|
|
97
|
+
/** A foreign table column's `OPTIONS (...)`: each option by its name to its string value. */
|
|
98
|
+
options?: Record<string, string>;
|
|
96
99
|
comment?: string;
|
|
97
100
|
}
|
|
98
101
|
|
|
@@ -355,6 +358,73 @@ export interface OperatorProps extends CommonProps {
|
|
|
355
358
|
options: Record<string, string>;
|
|
356
359
|
}
|
|
357
360
|
|
|
361
|
+
/** One member of an operator class, as written. */
|
|
362
|
+
export type OperatorClassItemDef =
|
|
363
|
+
| {
|
|
364
|
+
kind: "operator";
|
|
365
|
+
number: string;
|
|
366
|
+
/** The operator, `schema.symbol` or a bare symbol. */
|
|
367
|
+
operator: string;
|
|
368
|
+
/** `(left, right)` as written, parentheses excluded; absent when both are the class's type. */
|
|
369
|
+
types?: string;
|
|
370
|
+
/** `FOR ORDER BY`'s btree operator family; absent for `FOR SEARCH`, the default. */
|
|
371
|
+
orderBy?: string;
|
|
372
|
+
}
|
|
373
|
+
| { kind: "function"; number: string; types?: string; function: string; args: string }
|
|
374
|
+
| { kind: "storage"; type: string };
|
|
375
|
+
|
|
376
|
+
/** `CREATE OPERATOR CLASS`: the operators and support functions an index access method uses for a data type. */
|
|
377
|
+
export interface OperatorClassProps extends CommonProps {
|
|
378
|
+
default?: boolean;
|
|
379
|
+
/** `FOR TYPE`, as written. Not `type`: a serialized object has that key for its entity type. */
|
|
380
|
+
dataType: string;
|
|
381
|
+
/** `USING`: the index access method, folded. */
|
|
382
|
+
method: string;
|
|
383
|
+
/** `FAMILY`, as written; absent when the class is in a family of its own name. */
|
|
384
|
+
family?: string;
|
|
385
|
+
items: OperatorClassItemDef[];
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** `CREATE OPERATOR FAMILY`: a set of operator classes that work together; members arrive with the classes. */
|
|
389
|
+
export interface OperatorFamilyProps extends CommonProps {
|
|
390
|
+
/** `USING`: the index access method, folded. */
|
|
391
|
+
method: string;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** `CREATE FOREIGN TABLE`: a table whose rows are on a foreign server. */
|
|
395
|
+
export interface ForeignTableProps extends CommonProps {
|
|
396
|
+
ifNotExists?: boolean;
|
|
397
|
+
columns: ColumnDef[];
|
|
398
|
+
checks: CheckDef[];
|
|
399
|
+
/** `SERVER`: the foreign server's SQL name. */
|
|
400
|
+
serverName: string;
|
|
401
|
+
/** `OPTIONS (...)`: each option by its name to its string value. */
|
|
402
|
+
options: Record<string, string>;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** `CREATE TEXT SEARCH DICTIONARY`: a template with its options. */
|
|
406
|
+
export interface TextSearchDictionaryProps extends CommonProps {
|
|
407
|
+
/** `TEMPLATE`, as written. */
|
|
408
|
+
template: string;
|
|
409
|
+
/** The template's options by name, folded, to their value as written. */
|
|
410
|
+
options: Record<string, string>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** One `ADD MAPPING FOR tokens WITH dictionaries` of a text search configuration. */
|
|
414
|
+
export interface TextSearchMappingDef {
|
|
415
|
+
/** The token types, folded. */
|
|
416
|
+
tokens: string[];
|
|
417
|
+
/** The dictionaries in the order they are consulted, as written. */
|
|
418
|
+
dictionaries: string[];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** `CREATE TEXT SEARCH CONFIGURATION`: a parser and what each token type it finds is mapped to. */
|
|
422
|
+
export interface TextSearchConfigurationProps extends CommonProps {
|
|
423
|
+
/** `PARSER`, as written. */
|
|
424
|
+
parser: string;
|
|
425
|
+
mappings: TextSearchMappingDef[];
|
|
426
|
+
}
|
|
427
|
+
|
|
358
428
|
/** `CREATE STATISTICS`: extended statistics on columns or expressions of one table. */
|
|
359
429
|
export interface StatisticsProps extends CommonProps {
|
|
360
430
|
ifNotExists?: boolean;
|
|
@@ -681,6 +751,31 @@ export interface PostgresOperator extends PostgresObject {
|
|
|
681
751
|
readonly props: OperatorProps;
|
|
682
752
|
}
|
|
683
753
|
|
|
754
|
+
export interface PostgresOperatorClass extends PostgresObject {
|
|
755
|
+
readonly entityType: "Postgres::OperatorClass";
|
|
756
|
+
readonly props: OperatorClassProps;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export interface PostgresOperatorFamily extends PostgresObject {
|
|
760
|
+
readonly entityType: "Postgres::OperatorFamily";
|
|
761
|
+
readonly props: OperatorFamilyProps;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
export interface PostgresForeignTable extends PostgresObject {
|
|
765
|
+
readonly entityType: "Postgres::ForeignTable";
|
|
766
|
+
readonly props: ForeignTableProps;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
export interface PostgresTextSearchDictionary extends PostgresObject {
|
|
770
|
+
readonly entityType: "Postgres::TextSearchDictionary";
|
|
771
|
+
readonly props: TextSearchDictionaryProps;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export interface PostgresTextSearchConfiguration extends PostgresObject {
|
|
775
|
+
readonly entityType: "Postgres::TextSearchConfiguration";
|
|
776
|
+
readonly props: TextSearchConfigurationProps;
|
|
777
|
+
}
|
|
778
|
+
|
|
684
779
|
export interface PostgresStatistics extends PostgresObject {
|
|
685
780
|
readonly entityType: "Postgres::Statistics";
|
|
686
781
|
readonly props: StatisticsProps;
|
|
@@ -860,6 +955,11 @@ export type PostgresTag =
|
|
|
860
955
|
| "trigger"
|
|
861
956
|
| "eventTrigger"
|
|
862
957
|
| "operator"
|
|
958
|
+
| "operatorClass"
|
|
959
|
+
| "operatorFamily"
|
|
960
|
+
| "foreignTable"
|
|
961
|
+
| "textSearchConfiguration"
|
|
962
|
+
| "textSearchDictionary"
|
|
863
963
|
| "statistics"
|
|
864
964
|
| "foreignDataWrapper"
|
|
865
965
|
| "foreignServer"
|
|
@@ -886,6 +986,11 @@ export const STATEMENTS_OF: Record<PostgresTag, readonly StatementNode["statemen
|
|
|
886
986
|
trigger: ["trigger"],
|
|
887
987
|
eventTrigger: ["eventTrigger"],
|
|
888
988
|
operator: ["operator"],
|
|
989
|
+
operatorClass: ["operatorClass"],
|
|
990
|
+
operatorFamily: ["operatorFamily"],
|
|
991
|
+
foreignTable: ["foreignTable"],
|
|
992
|
+
textSearchConfiguration: ["textSearchConfiguration"],
|
|
993
|
+
textSearchDictionary: ["textSearchDictionary"],
|
|
889
994
|
statistics: ["statistics"],
|
|
890
995
|
foreignDataWrapper: ["foreignDataWrapper"],
|
|
891
996
|
foreignServer: ["foreignServer"],
|
|
@@ -914,6 +1019,12 @@ const TAG_OF: Record<StatementNode["statement"], PostgresTag | undefined> = {
|
|
|
914
1019
|
trigger: "trigger",
|
|
915
1020
|
eventTrigger: "eventTrigger",
|
|
916
1021
|
operator: "operator",
|
|
1022
|
+
operatorClass: "operatorClass",
|
|
1023
|
+
operatorFamily: "operatorFamily",
|
|
1024
|
+
foreignTable: "foreignTable",
|
|
1025
|
+
textSearchConfiguration: "textSearchConfiguration",
|
|
1026
|
+
textSearchDictionary: "textSearchDictionary",
|
|
1027
|
+
textSearchMapping: undefined,
|
|
917
1028
|
statistics: "statistics",
|
|
918
1029
|
foreignDataWrapper: "foreignDataWrapper",
|
|
919
1030
|
foreignServer: "foreignServer",
|
|
@@ -945,6 +1056,12 @@ export const STATEMENT_NAMES: Record<StatementNode["statement"], string> = {
|
|
|
945
1056
|
trigger: "CREATE TRIGGER",
|
|
946
1057
|
eventTrigger: "CREATE EVENT TRIGGER",
|
|
947
1058
|
operator: "CREATE OPERATOR",
|
|
1059
|
+
operatorClass: "CREATE OPERATOR CLASS",
|
|
1060
|
+
operatorFamily: "CREATE OPERATOR FAMILY",
|
|
1061
|
+
foreignTable: "CREATE FOREIGN TABLE",
|
|
1062
|
+
textSearchConfiguration: "CREATE TEXT SEARCH CONFIGURATION",
|
|
1063
|
+
textSearchDictionary: "CREATE TEXT SEARCH DICTIONARY",
|
|
1064
|
+
textSearchMapping: "ALTER TEXT SEARCH CONFIGURATION",
|
|
948
1065
|
statistics: "CREATE STATISTICS",
|
|
949
1066
|
foreignDataWrapper: "CREATE FOREIGN DATA WRAPPER",
|
|
950
1067
|
foreignServer: "CREATE SERVER",
|
|
@@ -1061,7 +1178,7 @@ function columnName(ctx: Ctx, c: { name: string; span: Span }): string {
|
|
|
1061
1178
|
return isColumnRef(v) ? v.attribute : req(text(ctx, c.span));
|
|
1062
1179
|
}
|
|
1063
1180
|
|
|
1064
|
-
function columnDef(ctx: Ctx, c: ColumnNode): ColumnDef {
|
|
1181
|
+
function columnDef(ctx: Ctx, c: ColumnNode, tag: PostgresTag = "table"): ColumnDef {
|
|
1065
1182
|
for (const span of [c.nameSpan, c.type, c.default, c.collate, c.compression, c.storage, c.generated?.expr, c.generated?.options]) feed(ctx, span, "columns");
|
|
1066
1183
|
const def: ColumnDef = { name: columnName(ctx, { name: c.name, span: c.nameSpan }) };
|
|
1067
1184
|
const set = <K extends keyof ColumnDef>(k: K, v: ColumnDef[K] | undefined) => {
|
|
@@ -1084,6 +1201,7 @@ function columnDef(ctx: Ctx, c: ColumnNode): ColumnDef {
|
|
|
1084
1201
|
set("collate", text(ctx, c.collate));
|
|
1085
1202
|
set("compression", text(ctx, c.compression));
|
|
1086
1203
|
set("storage", text(ctx, c.storage));
|
|
1204
|
+
if (c.options && c.options.length > 0) def.options = genericOptionValues(tag, ctx, c.options);
|
|
1087
1205
|
return def;
|
|
1088
1206
|
}
|
|
1089
1207
|
|
|
@@ -1182,6 +1300,7 @@ function applyComments(
|
|
|
1182
1300
|
self: { schema?: string; name: string },
|
|
1183
1301
|
objectTypes: readonly string[],
|
|
1184
1302
|
parts: { columns?: Map<string, Commentable>; constraints?: Map<string, Commentable>; columnComments?: Record<string, string> } = {},
|
|
1303
|
+
using?: string,
|
|
1185
1304
|
): string | undefined {
|
|
1186
1305
|
let comment: string | undefined;
|
|
1187
1306
|
const isSelf = (q: { schema?: string; name: string }) => q.name === self.name && (q.schema === undefined || self.schema === undefined || q.schema === self.schema);
|
|
@@ -1223,6 +1342,8 @@ function applyComments(
|
|
|
1223
1342
|
// `COMMENT ON TRIGGER name ON table`: the name is the trigger's, the table its own.
|
|
1224
1343
|
if (qualified(ctx, c.target).name !== self.name || !c.on || !isSelf({ ...qualified(ctx, c.on), name: self.name })) refuse(c, `names another ${c.objectType.toLowerCase()} than ${self.name}`);
|
|
1225
1344
|
} else if (!isSelf(qualified(ctx, c.target))) refuse(c, `names another object than ${sqlNameOf(self)}`);
|
|
1345
|
+
// An operator class or family is named by its index method too.
|
|
1346
|
+
if (using !== undefined && c.using !== using) refuse(c, `names another index method than ${using}`);
|
|
1226
1347
|
comment = value;
|
|
1227
1348
|
}
|
|
1228
1349
|
}
|
|
@@ -1246,7 +1367,7 @@ function lastDot(ctx: Ctx, span: Span): number {
|
|
|
1246
1367
|
*/
|
|
1247
1368
|
export function followOnStray(tag: PostgresTag, rest: readonly StatementNode[]): StatementNode | undefined {
|
|
1248
1369
|
if (tag === "grant") return rest[0];
|
|
1249
|
-
return rest.find((n) => n.statement !== "comment" && !(tag === "table" && n.statement === "rowSecurity"));
|
|
1370
|
+
return rest.find((n) => n.statement !== "comment" && !(tag === "table" && n.statement === "rowSecurity") && !(tag === "textSearchConfiguration" && n.statement === "textSearchMapping"));
|
|
1250
1371
|
}
|
|
1251
1372
|
|
|
1252
1373
|
/**
|
|
@@ -1463,7 +1584,7 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1463
1584
|
if (stray) {
|
|
1464
1585
|
throw new SqlTemplateError(
|
|
1465
1586
|
tag,
|
|
1466
|
-
`holds a second statement (${STATEMENT_NAMES[stray.statement]}); a template declares one object, followed only by COMMENT ON statements for it${tag === "table" ? " and ALTER TABLE ... ROW LEVEL SECURITY" : ""}`,
|
|
1587
|
+
`holds a second statement (${STATEMENT_NAMES[stray.statement]}); a template declares one object, followed only by COMMENT ON statements for it${tag === "table" ? " and ALTER TABLE ... ROW LEVEL SECURITY" : tag === "textSearchConfiguration" ? " and ALTER TEXT SEARCH CONFIGURATION ... ADD MAPPING" : ""}`,
|
|
1467
1588
|
0,
|
|
1468
1589
|
0,
|
|
1469
1590
|
);
|
|
@@ -1634,6 +1755,112 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1634
1755
|
// The symbol is unquoted wherever the operator is named: `app.===`.
|
|
1635
1756
|
return make(POSTGRES_ENTITY_TYPES.operator, q, props, undefined, q.schema ? `${quoteIdent(q.schema)}.${q.name}` : q.name);
|
|
1636
1757
|
}
|
|
1758
|
+
case "operatorClass": {
|
|
1759
|
+
const q = qualified(ctx, node.name);
|
|
1760
|
+
if (node.method === "") throw new SqlTemplateError(tag, "the index method is written out, not interpolated", 0, 0);
|
|
1761
|
+
const comment = applyComments(tag, ctx, comments, q, ["OPERATOR CLASS"], {}, node.method);
|
|
1762
|
+
const items = node.items.map((i): OperatorClassItemDef => {
|
|
1763
|
+
if (i.kind === "storage") return { kind: "storage", type: req(text(ctx, i.type, "items")) };
|
|
1764
|
+
if (i.kind === "function") {
|
|
1765
|
+
feed(ctx, i.function.span, "items");
|
|
1766
|
+
return stripUnset({ kind: "function", number: req(text(ctx, i.number, "items")), types: text(ctx, i.types, "items"), function: targetName(ctx, i.function), args: text(ctx, i.args, "items") ?? "" }) as OperatorClassItemDef;
|
|
1767
|
+
}
|
|
1768
|
+
return stripUnset({
|
|
1769
|
+
kind: "operator",
|
|
1770
|
+
number: req(text(ctx, i.number, "items")),
|
|
1771
|
+
operator: req(text(ctx, i.operator.span, "items")),
|
|
1772
|
+
types: text(ctx, i.types, "items"),
|
|
1773
|
+
orderBy: i.orderBy ? targetName(ctx, i.orderBy) : undefined,
|
|
1774
|
+
}) as OperatorClassItemDef;
|
|
1775
|
+
});
|
|
1776
|
+
const props: OperatorClassProps = stripUnset({
|
|
1777
|
+
...q,
|
|
1778
|
+
default: node.default || undefined,
|
|
1779
|
+
dataType: req(text(ctx, node.type, "dataType")),
|
|
1780
|
+
method: node.method,
|
|
1781
|
+
family: node.family ? targetName(ctx, node.family) : undefined,
|
|
1782
|
+
items,
|
|
1783
|
+
comment,
|
|
1784
|
+
ddl,
|
|
1785
|
+
source,
|
|
1786
|
+
});
|
|
1787
|
+
return make(POSTGRES_ENTITY_TYPES.operatorClass, q, props);
|
|
1788
|
+
}
|
|
1789
|
+
case "operatorFamily": {
|
|
1790
|
+
const q = qualified(ctx, node.name);
|
|
1791
|
+
if (node.method === "") throw new SqlTemplateError(tag, "the index method is written out, not interpolated", 0, 0);
|
|
1792
|
+
const comment = applyComments(tag, ctx, comments, q, ["OPERATOR FAMILY"], {}, node.method);
|
|
1793
|
+
const props: OperatorFamilyProps = stripUnset({ ...q, method: node.method, comment, ddl, source });
|
|
1794
|
+
return make(POSTGRES_ENTITY_TYPES.operatorFamily, q, props);
|
|
1795
|
+
}
|
|
1796
|
+
case "foreignTable": {
|
|
1797
|
+
const q = qualified(ctx, node.name);
|
|
1798
|
+
const columns = node.columns.map((c) => columnDef(ctx, c, tag));
|
|
1799
|
+
const checks: CheckDef[] = [];
|
|
1800
|
+
const file = (c: ConstraintNode, cols: string[]) => {
|
|
1801
|
+
if (c.kind === "CHECK") checks.push(checkDef(ctx, c));
|
|
1802
|
+
else if (c.kind === "NOT NULL") {
|
|
1803
|
+
const col = columns.find((x) => x.name === cols[0]);
|
|
1804
|
+
if (!col) throw new SqlTemplateError(tag, `NOT NULL ${cols[0]} names no column of ${sqlNameOf(q)}`, 0, 0);
|
|
1805
|
+
col.notNull = true;
|
|
1806
|
+
if (c.name) col.notNullName = c.name;
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
node.columns.forEach((c, i) => c.constraints.forEach((k) => file(k, [columns[i]!.name])));
|
|
1810
|
+
node.constraints.forEach((k) => file(k, k.columns.map((x) => columnName(ctx, x))));
|
|
1811
|
+
const named = new Map<string, Commentable>();
|
|
1812
|
+
for (const c of checks) if (c.name) named.set(c.name, c);
|
|
1813
|
+
const comment = applyComments(tag, ctx, comments, q, ["FOREIGN TABLE"], { columns: new Map(columns.map((c) => [c.name, c])), constraints: named });
|
|
1814
|
+
const props: ForeignTableProps = strip(
|
|
1815
|
+
{
|
|
1816
|
+
...q,
|
|
1817
|
+
ifNotExists: node.ifNotExists || undefined,
|
|
1818
|
+
columns,
|
|
1819
|
+
checks,
|
|
1820
|
+
serverName: databaseObjectName(ctx, node.server),
|
|
1821
|
+
options: genericOptionValues(tag, ctx, node.options),
|
|
1822
|
+
comment,
|
|
1823
|
+
ddl,
|
|
1824
|
+
source,
|
|
1825
|
+
},
|
|
1826
|
+
["columns", "checks", "options"],
|
|
1827
|
+
);
|
|
1828
|
+
return make(POSTGRES_ENTITY_TYPES.foreignTable, q, props, columns.map((c) => c.name));
|
|
1829
|
+
}
|
|
1830
|
+
case "textSearchDictionary": {
|
|
1831
|
+
const q = qualified(ctx, node.name);
|
|
1832
|
+
const comment = applyComments(tag, ctx, comments, q, ["TEXT SEARCH DICTIONARY"]);
|
|
1833
|
+
const { template, ...options } = definitionValues(ctx, node.options, "options");
|
|
1834
|
+
const props: TextSearchDictionaryProps = strip({ ...q, template: req(template), options, comment, ddl, source }, ["options"]);
|
|
1835
|
+
return make(POSTGRES_ENTITY_TYPES.textSearchDictionary, q, props);
|
|
1836
|
+
}
|
|
1837
|
+
case "textSearchConfiguration": {
|
|
1838
|
+
const q = qualified(ctx, node.name);
|
|
1839
|
+
const comment = applyComments(tag, ctx, comments, q, ["TEXT SEARCH CONFIGURATION"]);
|
|
1840
|
+
const opt = definitionValues(ctx, node.options, "options");
|
|
1841
|
+
const seen = new Set<string>();
|
|
1842
|
+
const mappings = rest
|
|
1843
|
+
.filter((n): n is TextSearchMappingNode => n.statement === "textSearchMapping")
|
|
1844
|
+
.map((m): TextSearchMappingDef => {
|
|
1845
|
+
feed(ctx, m.name.span, "mappings");
|
|
1846
|
+
const t = qualified(ctx, m.name);
|
|
1847
|
+
if (t.name !== q.name || (t.schema !== undefined && q.schema !== undefined && t.schema !== q.schema)) {
|
|
1848
|
+
throw new SqlTemplateError(tag, `ADD MAPPING names another configuration (${sqlNameOf(t)}) than ${sqlNameOf(q)}`, 0, 0);
|
|
1849
|
+
}
|
|
1850
|
+
const tokens = m.tokens.map((x) => x.name || identValue(req(text(ctx, x.span, "mappings"))));
|
|
1851
|
+
for (const tok of tokens) {
|
|
1852
|
+
if (seen.has(tok)) throw new SqlTemplateError(tag, `the token type ${tok} is mapped twice in ${sqlNameOf(q)}`, 0, 0);
|
|
1853
|
+
seen.add(tok);
|
|
1854
|
+
}
|
|
1855
|
+
for (const d of m.dictionaries) feed(ctx, d.span, "mappings");
|
|
1856
|
+
return { tokens, dictionaries: m.dictionaries.map((d) => sqlNameOf(qualified(ctx, d))) };
|
|
1857
|
+
});
|
|
1858
|
+
const props: TextSearchConfigurationProps = strip({ ...q, parser: req(opt.parser), mappings, comment, ddl, source }, ["mappings"]);
|
|
1859
|
+
return make(POSTGRES_ENTITY_TYPES.textSearchConfiguration, q, props);
|
|
1860
|
+
}
|
|
1861
|
+
case "textSearchMapping":
|
|
1862
|
+
// Only a configuration's follow-on statement (`followOnStray`); first in a template it is refused above.
|
|
1863
|
+
throw new SqlTemplateError(tag, "holds an ALTER TEXT SEARCH CONFIGURATION before any CREATE", 0, 0);
|
|
1637
1864
|
case "statistics": {
|
|
1638
1865
|
const q = qualified(ctx, node.name);
|
|
1639
1866
|
const comment = applyComments(tag, ctx, comments, q, ["STATISTICS"]);
|
|
@@ -2193,6 +2420,50 @@ export function operator(strings: TemplateStringsArray, ...values: unknown[]): P
|
|
|
2193
2420
|
return build("operator", strings, values) as PostgresOperator;
|
|
2194
2421
|
}
|
|
2195
2422
|
|
|
2423
|
+
/**
|
|
2424
|
+
* `` operatorClass`CREATE OPERATOR CLASS app.ops DEFAULT FOR TYPE int USING btree AS OPERATOR 1 <, FUNCTION 1 ${cmp}(int, int)` ``:
|
|
2425
|
+
* one operator class, identified by its name and index method. Creating one takes a superuser.
|
|
2426
|
+
*/
|
|
2427
|
+
export function operatorClass(strings: TemplateStringsArray, ...values: unknown[]): PostgresOperatorClass {
|
|
2428
|
+
return build("operatorClass", strings, values) as PostgresOperatorClass;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
/**
|
|
2432
|
+
* `` operatorFamily`CREATE OPERATOR FAMILY app.ops USING btree` ``: one operator
|
|
2433
|
+
* family, identified by its name and index method. Its members come with the
|
|
2434
|
+
* operator classes that name it. Creating one takes a superuser.
|
|
2435
|
+
*/
|
|
2436
|
+
export function operatorFamily(strings: TemplateStringsArray, ...values: unknown[]): PostgresOperatorFamily {
|
|
2437
|
+
return build("operatorFamily", strings, values) as PostgresOperatorFamily;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
/**
|
|
2441
|
+
* `` foreignTable`CREATE FOREIGN TABLE app.remote_orders (id int, total numeric) SERVER ${remote} OPTIONS (table_name 'orders')` ``:
|
|
2442
|
+
* one foreign table. It has its own tag, not `table`, because it is another
|
|
2443
|
+
* kind of relation: another CREATE and ALTER, no key, index, partitioning or
|
|
2444
|
+
* storage clauses, and a server it cannot move off.
|
|
2445
|
+
*/
|
|
2446
|
+
export function foreignTable(strings: TemplateStringsArray, ...values: unknown[]): PostgresForeignTable {
|
|
2447
|
+
return build("foreignTable", strings, values) as PostgresForeignTable;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
/**
|
|
2451
|
+
* `` textSearchDictionary`CREATE TEXT SEARCH DICTIONARY app.english_stop (TEMPLATE = snowball, Language = english, StopWords = english)` ``:
|
|
2452
|
+
* one text search dictionary: a template and its options.
|
|
2453
|
+
*/
|
|
2454
|
+
export function textSearchDictionary(strings: TemplateStringsArray, ...values: unknown[]): PostgresTextSearchDictionary {
|
|
2455
|
+
return build("textSearchDictionary", strings, values) as PostgresTextSearchDictionary;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* `` textSearchConfiguration`CREATE TEXT SEARCH CONFIGURATION app.english2 (PARSER = default); ALTER TEXT SEARCH CONFIGURATION app.english2 ADD MAPPING FOR asciiword WITH ${dict}` ``:
|
|
2460
|
+
* one text search configuration: a parser, and the dictionaries each token
|
|
2461
|
+
* type it finds is looked up in.
|
|
2462
|
+
*/
|
|
2463
|
+
export function textSearchConfiguration(strings: TemplateStringsArray, ...values: unknown[]): PostgresTextSearchConfiguration {
|
|
2464
|
+
return build("textSearchConfiguration", strings, values) as PostgresTextSearchConfiguration;
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2196
2467
|
/**
|
|
2197
2468
|
* `` statistics`CREATE STATISTICS name (ndistinct, dependencies) ON a, b FROM ${table}` ``:
|
|
2198
2469
|
* extended statistics on columns or expressions of one table.
|
|
@@ -19,6 +19,11 @@ export const POSTGRES_ENTITY_TYPES = {
|
|
|
19
19
|
cast: "Postgres::Cast",
|
|
20
20
|
eventTrigger: "Postgres::EventTrigger",
|
|
21
21
|
operator: "Postgres::Operator",
|
|
22
|
+
operatorClass: "Postgres::OperatorClass",
|
|
23
|
+
operatorFamily: "Postgres::OperatorFamily",
|
|
24
|
+
foreignTable: "Postgres::ForeignTable",
|
|
25
|
+
textSearchConfiguration: "Postgres::TextSearchConfiguration",
|
|
26
|
+
textSearchDictionary: "Postgres::TextSearchDictionary",
|
|
22
27
|
statistics: "Postgres::Statistics",
|
|
23
28
|
foreignDataWrapper: "Postgres::ForeignDataWrapper",
|
|
24
29
|
foreignServer: "Postgres::ForeignServer",
|
|
@@ -61,6 +61,11 @@ const TAG: Record<string, string> = {
|
|
|
61
61
|
[POSTGRES_ENTITY_TYPES.cast]: "cast",
|
|
62
62
|
[POSTGRES_ENTITY_TYPES.eventTrigger]: "eventTrigger",
|
|
63
63
|
[POSTGRES_ENTITY_TYPES.operator]: "operator",
|
|
64
|
+
[POSTGRES_ENTITY_TYPES.operatorClass]: "operatorClass",
|
|
65
|
+
[POSTGRES_ENTITY_TYPES.operatorFamily]: "operatorFamily",
|
|
66
|
+
[POSTGRES_ENTITY_TYPES.foreignTable]: "foreignTable",
|
|
67
|
+
[POSTGRES_ENTITY_TYPES.textSearchConfiguration]: "textSearchConfiguration",
|
|
68
|
+
[POSTGRES_ENTITY_TYPES.textSearchDictionary]: "textSearchDictionary",
|
|
64
69
|
[POSTGRES_ENTITY_TYPES.statistics]: "statistics",
|
|
65
70
|
[POSTGRES_ENTITY_TYPES.foreignDataWrapper]: "foreignDataWrapper",
|
|
66
71
|
[POSTGRES_ENTITY_TYPES.foreignServer]: "foreignServer",
|
|
@@ -87,8 +92,10 @@ const UNREFERENCED: ReadonlySet<string> = new Set([
|
|
|
87
92
|
POSTGRES_ENTITY_TYPES.cast,
|
|
88
93
|
POSTGRES_ENTITY_TYPES.eventTrigger,
|
|
89
94
|
POSTGRES_ENTITY_TYPES.operator,
|
|
95
|
+
// An index names a class, but a class and its own family share a name; a configuration shares one with a dictionary, which a mapping names.
|
|
96
|
+
POSTGRES_ENTITY_TYPES.operatorClass,
|
|
97
|
+
POSTGRES_ENTITY_TYPES.textSearchConfiguration,
|
|
90
98
|
POSTGRES_ENTITY_TYPES.statistics,
|
|
91
|
-
POSTGRES_ENTITY_TYPES.foreignServer,
|
|
92
99
|
POSTGRES_ENTITY_TYPES.publication,
|
|
93
100
|
]);
|
|
94
101
|
|
|
@@ -141,6 +148,11 @@ function bareSpots(tokens: Token[]): Set<number> {
|
|
|
141
148
|
}
|
|
142
149
|
if (node.statement === "eventTrigger") name(node.function);
|
|
143
150
|
if (node.statement === "foreignServer") name(node.wrapper);
|
|
151
|
+
if (node.statement === "foreignTable") {
|
|
152
|
+
for (const c of node.columns) if (c.type) first(c.type.from, c.type.to);
|
|
153
|
+
name(node.server);
|
|
154
|
+
}
|
|
155
|
+
if (node.statement === "operatorClass") first(node.type.from, node.type.to);
|
|
144
156
|
if (node.statement === "foreignDataWrapper") {
|
|
145
157
|
name(node.handler ?? undefined);
|
|
146
158
|
name(node.validator ?? undefined);
|