@intentius/chant-lexicon-sql 0.116.0 → 0.118.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clickhouse/access.d.ts +132 -0
- package/dist/clickhouse/access.d.ts.map +1 -0
- package/dist/clickhouse/apply/apply.d.ts.map +1 -1
- package/dist/clickhouse/apply/statements.d.ts +2 -0
- package/dist/clickhouse/apply/statements.d.ts.map +1 -1
- package/dist/clickhouse/entities.d.ts +171 -2
- package/dist/clickhouse/entities.d.ts.map +1 -1
- package/dist/clickhouse/http.d.ts +3 -0
- package/dist/clickhouse/http.d.ts.map +1 -1
- package/dist/clickhouse/import/generator.d.ts +28 -0
- package/dist/clickhouse/import/generator.d.ts.map +1 -1
- package/dist/clickhouse/import/ir.d.ts.map +1 -1
- package/dist/clickhouse/import/parser.d.ts +3 -3
- package/dist/clickhouse/import/parser.d.ts.map +1 -1
- package/dist/clickhouse/live/bind.d.ts.map +1 -1
- package/dist/clickhouse/live/catalog.d.ts +41 -6
- package/dist/clickhouse/live/catalog.d.ts.map +1 -1
- package/dist/clickhouse/live/describe-resources.d.ts +3 -2
- package/dist/clickhouse/live/describe-resources.d.ts.map +1 -1
- package/dist/clickhouse/parser.d.ts +54 -3
- package/dist/clickhouse/parser.d.ts.map +1 -1
- package/dist/clickhouse/plan/commands.d.ts +3 -1
- package/dist/clickhouse/plan/commands.d.ts.map +1 -1
- package/dist/clickhouse/plan/deep.d.ts.map +1 -1
- package/dist/clickhouse/plan/diff.d.ts +12 -0
- package/dist/clickhouse/plan/diff.d.ts.map +1 -1
- package/dist/clickhouse/plan/normalize.d.ts +61 -1
- package/dist/clickhouse/plan/normalize.d.ts.map +1 -1
- package/dist/clickhouse/plan/report.d.ts.map +1 -1
- package/dist/clickhouse/plan/rules.d.ts +7 -0
- package/dist/clickhouse/plan/rules.d.ts.map +1 -1
- package/dist/clickhouse/plan/schema.d.ts +8 -4
- package/dist/clickhouse/plan/schema.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/backfill.d.ts +3 -1
- package/dist/clickhouse/rebuild/backfill.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/observe.d.ts +7 -3
- package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/op.d.ts +8 -0
- package/dist/clickhouse/rebuild/op.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/steps.d.ts +48 -0
- package/dist/clickhouse/rebuild/steps.d.ts.map +1 -1
- package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
- package/dist/clickhouse/testing/writable-server.d.ts.map +1 -1
- package/dist/clickhouse/topology.d.ts.map +1 -1
- package/dist/clickhouse.d.ts +1 -1
- package/dist/clickhouse.d.ts.map +1 -1
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/config.d.ts +70 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/core/references.d.ts +4 -2
- package/dist/core/references.d.ts.map +1 -1
- package/dist/core/statements.d.ts +11 -0
- package/dist/core/statements.d.ts.map +1 -1
- package/dist/files/build-root.d.ts +26 -0
- package/dist/files/build-root.d.ts.map +1 -0
- package/dist/files/clickhouse.d.ts +19 -0
- package/dist/files/clickhouse.d.ts.map +1 -0
- package/dist/files/common.d.ts +25 -0
- package/dist/files/common.d.ts.map +1 -0
- package/dist/files/postgres.d.ts +36 -0
- package/dist/files/postgres.d.ts.map +1 -0
- package/dist/import-parser.d.ts +25 -0
- package/dist/import-parser.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +8 -8
- package/dist/lint/rules/sqlch001.d.ts +1 -1
- package/dist/lint/rules/sqlch001.d.ts.map +1 -1
- package/dist/lint/rules/sqlch002.d.ts.map +1 -1
- package/dist/lint/rules/sqlpg001.d.ts.map +1 -1
- package/dist/lint/rules/templates.d.ts +1 -1
- package/dist/lint/rules/templates.d.ts.map +1 -1
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/meta.json +50 -0
- package/dist/okf/index.md +11 -1
- package/dist/okf/rules/SQLCH001.md +2 -2
- package/dist/okf/types/Dictionary.md +9 -0
- package/dist/okf/types/Function.md +9 -0
- package/dist/okf/types/Grant.md +9 -0
- package/dist/okf/types/PostgresDefaultPrivileges.md +9 -0
- package/dist/okf/types/PostgresGrant.md +9 -0
- package/dist/okf/types/PostgresPolicy.md +9 -0
- package/dist/okf/types/PostgresRole.md +9 -0
- package/dist/okf/types/Role.md +9 -0
- package/dist/okf/types/RowPolicy.md +9 -0
- package/dist/okf/types/User.md +9 -0
- package/dist/op/activities/clickhouse-rebuild.d.ts.map +1 -1
- package/dist/op/activities/postgres-migration.d.ts.map +1 -1
- package/dist/op/activity-contracts.d.ts +19 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/postgres/access/acl.d.ts +119 -0
- package/dist/postgres/access/acl.d.ts.map +1 -0
- package/dist/postgres/access/import.d.ts +13 -0
- package/dist/postgres/access/import.d.ts.map +1 -0
- package/dist/postgres/access/live.d.ts +24 -0
- package/dist/postgres/access/live.d.ts.map +1 -0
- package/dist/postgres/apply/apply.d.ts.map +1 -1
- package/dist/postgres/apply/statements.d.ts +71 -0
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/entities.d.ts +106 -4
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entity-types.d.ts +4 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/import/generator.d.ts +37 -1
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/live-export.d.ts +6 -0
- package/dist/postgres/import/live-export.d.ts.map +1 -1
- package/dist/postgres/live/bind.d.ts +2 -0
- package/dist/postgres/live/bind.d.ts.map +1 -1
- package/dist/postgres/live/catalog.d.ts +11 -2
- package/dist/postgres/live/catalog.d.ts.map +1 -1
- package/dist/postgres/live/client.d.ts +3 -0
- package/dist/postgres/live/client.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.d.ts +9 -0
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/migrate/op.d.ts +29 -0
- package/dist/postgres/migrate/op.d.ts.map +1 -1
- package/dist/postgres/parser.d.ts +76 -3
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/plan/commands.d.ts +12 -0
- package/dist/postgres/plan/commands.d.ts.map +1 -1
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/diff.d.ts +10 -1
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.d.ts +8 -1
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/rules.d.ts +8 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/schema.d.ts +2 -1
- package/dist/postgres/plan/schema.d.ts.map +1 -1
- package/dist/postgres/plan/server-normalize.d.ts +4 -1
- package/dist/postgres/plan/server-normalize.d.ts.map +1 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/rules/sqlch001.ts +26 -4
- package/dist/rules/sqlch002.ts +3 -2
- package/dist/rules/sqlpg001.ts +7 -4
- package/dist/rules/templates.ts +2 -2
- package/dist/serializer.d.ts +2 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/sql-files.d.ts +79 -0
- package/dist/sql-files.d.ts.map +1 -0
- package/dist/token-source.d.ts +88 -0
- package/dist/token-source.d.ts.map +1 -0
- package/dist/validate.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clickhouse/access.test.ts +203 -0
- package/src/clickhouse/access.ts +595 -0
- package/src/clickhouse/apply/apply.ts +29 -20
- package/src/clickhouse/apply/statements.ts +105 -8
- package/src/clickhouse/dictionary.test.ts +177 -0
- package/src/clickhouse/entities.ts +356 -6
- package/src/clickhouse/function.test.ts +91 -0
- package/src/clickhouse/http.ts +11 -2
- package/src/clickhouse/import/generator.ts +81 -46
- package/src/clickhouse/import/import.test.ts +9 -3
- package/src/clickhouse/import/ir.ts +6 -2
- package/src/clickhouse/import/parser.ts +10 -18
- package/src/clickhouse/live/access.e2e.test.ts +218 -0
- package/src/clickhouse/live/bind.ts +17 -6
- package/src/clickhouse/live/catalog.ts +106 -10
- package/src/clickhouse/live/describe-resources.ts +23 -10
- package/src/clickhouse/live/dictionaries.e2e.test.ts +186 -0
- package/src/clickhouse/live/functions.e2e.test.ts +155 -0
- package/src/clickhouse/live/live.test.ts +2 -2
- package/src/clickhouse/parser.ts +136 -4
- package/src/clickhouse/plan/commands.ts +18 -7
- package/src/clickhouse/plan/deep.ts +71 -11
- package/src/clickhouse/plan/diff.ts +65 -2
- package/src/clickhouse/plan/normalize.ts +200 -6
- package/src/clickhouse/plan/report.ts +14 -3
- package/src/clickhouse/plan/rules.ts +44 -1
- package/src/clickhouse/plan/schema.ts +40 -17
- package/src/clickhouse/plan/server-format.ts +1 -1
- package/src/clickhouse/rebuild/backfill.ts +5 -25
- package/src/clickhouse/rebuild/observe.ts +7 -3
- package/src/clickhouse/rebuild/op.ts +9 -0
- package/src/clickhouse/rebuild/rebuild.e2e.test.ts +62 -0
- package/src/clickhouse/rebuild/rebuild.test.ts +54 -0
- package/src/clickhouse/rebuild/steps.ts +136 -1
- package/src/clickhouse/testing/fake-server.ts +2 -0
- package/src/clickhouse/testing/writable-server.ts +2 -0
- package/src/clickhouse/topology.ts +17 -2
- package/src/clickhouse.ts +19 -0
- package/src/codegen/classifier-page.ts +2 -0
- package/src/codegen/registry.ts +10 -0
- package/src/config.ts +22 -3
- package/src/core/references.ts +5 -3
- package/src/core/statements.ts +11 -1
- package/src/files/build-root.ts +62 -0
- package/src/files/clickhouse.ts +52 -0
- package/src/files/common.ts +54 -0
- package/src/files/postgres.ts +371 -0
- package/src/generated/lexicon-sql.json +50 -0
- package/src/import-parser.ts +61 -0
- package/src/index.ts +14 -1
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lint/rules/sqlch001.ts +26 -4
- package/src/lint/rules/sqlch002.ts +3 -2
- package/src/lint/rules/sqlpg001.ts +7 -4
- package/src/lint/rules/templates.ts +2 -2
- package/src/lsp/postgres-template.ts +2 -0
- package/src/lsp/template.ts +3 -3
- package/src/mcp/index.ts +6 -6
- package/src/mcp/postgres.ts +1 -1
- package/src/migration-statements.test.ts +25 -0
- package/src/migration-statements.ts +8 -0
- package/src/op/activities/clickhouse-rebuild.ts +1 -0
- package/src/op/activities/postgres-migration.ts +27 -2
- package/src/op/activity-contracts.ts +2 -0
- package/src/plugin.ts +29 -5
- package/src/postgres/access/access.e2e.test.ts +191 -0
- package/src/postgres/access/access.test.ts +147 -0
- package/src/postgres/access/acl.ts +400 -0
- package/src/postgres/access/import.ts +64 -0
- package/src/postgres/access/live.ts +123 -0
- package/src/postgres/apply/apply.ts +117 -4
- package/src/postgres/apply/constraint-sequences.e2e.test.ts +179 -0
- package/src/postgres/apply/safe-sequences.e2e.test.ts +176 -0
- package/src/postgres/apply/statements.test.ts +138 -6
- package/src/postgres/apply/statements.ts +210 -18
- package/src/postgres/entities.ts +287 -11
- package/src/postgres/entity-types.ts +4 -0
- package/src/postgres/import/generator.ts +129 -46
- package/src/postgres/import/live-export.ts +12 -2
- package/src/postgres/live/bind.ts +20 -6
- package/src/postgres/live/catalog.ts +79 -3
- package/src/postgres/live/client.ts +10 -2
- package/src/postgres/live/describe-resources.ts +27 -3
- package/src/postgres/migrate/migrate.test.ts +24 -1
- package/src/postgres/migrate/op.ts +80 -35
- package/src/postgres/migrate/outer.e2e.test.ts +163 -0
- package/src/postgres/parser.ts +247 -8
- package/src/postgres/plan/commands.ts +60 -7
- package/src/postgres/plan/deep.ts +24 -3
- package/src/postgres/plan/diff.ts +71 -6
- package/src/postgres/plan/normalize.ts +115 -2
- package/src/postgres/plan/plan.test.ts +2 -2
- package/src/postgres/plan/rules.ts +49 -5
- package/src/postgres/plan/schema.ts +4 -2
- package/src/postgres/plan/server-normalize.ts +29 -1
- package/src/postgres.ts +11 -0
- package/src/serializer.ts +14 -9
- package/src/skill-defs.ts +1 -1
- package/src/skills/chant-sql-postgres-migration.md +3 -1
- package/src/skills/chant-sql-postgres-plan.md +3 -3
- package/src/skills/chant-sql-postgres.md +9 -2
- package/src/skills/chant-sql-rebuild.md +3 -2
- package/src/skills/chant-sql.md +7 -1
- package/src/sql-files.test.ts +188 -0
- package/src/sql-files.ts +218 -0
- package/src/token-source.e2e.test.ts +142 -0
- package/src/token-source.test.ts +119 -0
- package/src/token-source.ts +189 -0
- package/src/validate.ts +10 -0
package/src/postgres/entities.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
|
|
3
|
-
* `type`, `domain`, `extension`, `func`, `procedure
|
|
4
|
-
* templates that parse their DDL at fold time into
|
|
5
|
-
* (chant #3278, #3279, #3680).
|
|
3
|
+
* `type`, `domain`, `extension`, `func`, `procedure`, `trigger`, `policy`,
|
|
4
|
+
* `role` and `grant` tagged templates that parse their DDL at fold time into
|
|
5
|
+
* one Declarable each (chant #3278, #3279, #3680, #3681).
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
8
|
* import { schema, table, index } from "@intentius/chant-lexicon-sql/postgres";
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
import type { AttrRef } from "@intentius/chant/attrref";
|
|
57
57
|
import { setInterpolationFields } from "@intentius/chant/provenance";
|
|
58
58
|
import { isTrivia, POSTGRES_LEXICAL, SqlSyntaxError, untokenize, type Token } from "./tokens";
|
|
59
|
-
import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type NameNode, type Span, type StatementNode } from "./parser";
|
|
59
|
+
import { identValue, parseStatements, type ColumnNode, type CommentNode, type ConstraintNode, type GrantNode, type NameNode, type RowSecurityNode, type Span, type StatementNode } from "./parser";
|
|
60
60
|
import { keywordCategory, quoteIdent } from "./keywords";
|
|
61
61
|
import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, type TemplateCtx, type TemplateDialect } from "../core/interpolation";
|
|
62
62
|
import { SqlObject, isColumnRefOf, isSqlObjectOf, makeSqlEntity } from "../core/entity";
|
|
@@ -183,6 +183,10 @@ export interface TableProps extends CommonProps {
|
|
|
183
183
|
with?: string;
|
|
184
184
|
onCommit?: string;
|
|
185
185
|
tablespace?: string;
|
|
186
|
+
/** `ALTER TABLE ... ENABLE ROW LEVEL SECURITY` after the CREATE (true), or `DISABLE` (false). */
|
|
187
|
+
rowSecurity?: boolean;
|
|
188
|
+
/** `ALTER TABLE ... FORCE ROW LEVEL SECURITY` after the CREATE (true), or `NO FORCE` (false): the owner is subject to the policies too. */
|
|
189
|
+
forceRowSecurity?: boolean;
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
export interface IndexProps extends CommonProps {
|
|
@@ -338,6 +342,70 @@ export interface TriggerProps extends CommonProps {
|
|
|
338
342
|
args: string[];
|
|
339
343
|
}
|
|
340
344
|
|
|
345
|
+
export interface PolicyProps extends CommonProps {
|
|
346
|
+
/** The table: the entity when interpolated, else its name. */
|
|
347
|
+
table: PostgresObject | string;
|
|
348
|
+
tableName: string;
|
|
349
|
+
/** `AS PERMISSIVE` (the default) or `AS RESTRICTIVE`. */
|
|
350
|
+
permissive: boolean;
|
|
351
|
+
command: "all" | "select" | "insert" | "update" | "delete";
|
|
352
|
+
/** The roles it applies to; `public` when it names none. */
|
|
353
|
+
roles: string[];
|
|
354
|
+
/** `USING (...)`, parentheses excluded. */
|
|
355
|
+
using?: string;
|
|
356
|
+
/** `WITH CHECK (...)`, parentheses excluded. */
|
|
357
|
+
check?: string;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export interface RoleProps extends Omit<CommonProps, "schema"> {
|
|
361
|
+
/** The options written, each by its key word, lower case (`login`, `noinherit`). */
|
|
362
|
+
options: string[];
|
|
363
|
+
/** `CONNECTION LIMIT n`. */
|
|
364
|
+
connectionLimit?: string;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** A privilege as written: `select`, `update` on some columns, `all`. */
|
|
368
|
+
export interface PrivilegeDef {
|
|
369
|
+
privilege: string;
|
|
370
|
+
columns?: string[];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface GrantProps {
|
|
374
|
+
/** A grant's or revoke's name, which no catalog holds: what it grants on what to whom. */
|
|
375
|
+
name: string;
|
|
376
|
+
action: "grant" | "revoke";
|
|
377
|
+
/** `REVOKE GRANT OPTION FOR`: only the right to grant on is revoked. */
|
|
378
|
+
grantOptionFor?: boolean;
|
|
379
|
+
privileges: PrivilegeDef[];
|
|
380
|
+
/** The kind of object. */
|
|
381
|
+
on: "table" | "sequence" | "schema" | "function" | "procedure" | "routine";
|
|
382
|
+
/** The objects: each the entity when interpolated, else its name. */
|
|
383
|
+
objects: Array<PostgresObject | string>;
|
|
384
|
+
/** The objects' SQL names; a routine's with its parameter types when they are written or interpolated. */
|
|
385
|
+
objectNames: string[];
|
|
386
|
+
/** Role names; `public` for PUBLIC. */
|
|
387
|
+
grantees: string[];
|
|
388
|
+
withGrantOption?: boolean;
|
|
389
|
+
ddl: string;
|
|
390
|
+
source: { strings: string[] };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export interface DefaultPrivilegesProps {
|
|
394
|
+
name: string;
|
|
395
|
+
action: "grant" | "revoke";
|
|
396
|
+
grantOptionFor?: boolean;
|
|
397
|
+
/** `FOR ROLE`: the roles whose new objects it applies to; the role that applies it when not written. */
|
|
398
|
+
forRoles?: string[];
|
|
399
|
+
/** `IN SCHEMA`: the schemas; every schema when not written. */
|
|
400
|
+
inSchemas?: string[];
|
|
401
|
+
privileges: string[];
|
|
402
|
+
on: "tables" | "sequences" | "functions" | "types" | "schemas";
|
|
403
|
+
grantees: string[];
|
|
404
|
+
withGrantOption?: boolean;
|
|
405
|
+
ddl: string;
|
|
406
|
+
source: { strings: string[] };
|
|
407
|
+
}
|
|
408
|
+
|
|
341
409
|
/**
|
|
342
410
|
* A string as a quoted Postgres string literal: `'` doubled, a backslash left
|
|
343
411
|
* as written (standard_conforming_strings is on). An interpolated plain string
|
|
@@ -419,6 +487,21 @@ export interface PostgresTrigger extends PostgresObject {
|
|
|
419
487
|
readonly props: TriggerProps;
|
|
420
488
|
}
|
|
421
489
|
|
|
490
|
+
export interface PostgresPolicy extends PostgresObject {
|
|
491
|
+
readonly entityType: "Postgres::Policy";
|
|
492
|
+
readonly props: PolicyProps;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export interface PostgresRole extends PostgresObject {
|
|
496
|
+
readonly entityType: "Postgres::Role";
|
|
497
|
+
readonly props: RoleProps;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export interface PostgresGrant extends PostgresObject {
|
|
501
|
+
readonly entityType: "Postgres::Grant" | "Postgres::DefaultPrivileges";
|
|
502
|
+
readonly props: GrantProps | DefaultPrivilegesProps;
|
|
503
|
+
}
|
|
504
|
+
|
|
422
505
|
export function isPostgresObject(value: unknown): value is PostgresObject {
|
|
423
506
|
return isSqlObjectOf(value, "Postgres::");
|
|
424
507
|
}
|
|
@@ -521,7 +604,7 @@ export const POSTGRES_TEMPLATES: TemplateDialect = {
|
|
|
521
604
|
|
|
522
605
|
type Ctx = TemplateCtx;
|
|
523
606
|
|
|
524
|
-
export type PostgresTag = "schema" | "table" | "index" | "view" | "sequence" | "type" | "domain" | "extension" | "func" | "procedure" | "trigger";
|
|
607
|
+
export type PostgresTag = "schema" | "table" | "index" | "view" | "sequence" | "type" | "domain" | "extension" | "func" | "procedure" | "trigger" | "policy" | "role" | "grant";
|
|
525
608
|
|
|
526
609
|
const STATEMENT_OF: Record<PostgresTag, StatementNode["statement"]> = {
|
|
527
610
|
schema: "schema",
|
|
@@ -535,6 +618,9 @@ const STATEMENT_OF: Record<PostgresTag, StatementNode["statement"]> = {
|
|
|
535
618
|
func: "function",
|
|
536
619
|
procedure: "procedure",
|
|
537
620
|
trigger: "trigger",
|
|
621
|
+
policy: "policy",
|
|
622
|
+
role: "role",
|
|
623
|
+
grant: "grant",
|
|
538
624
|
};
|
|
539
625
|
|
|
540
626
|
const TAG_OF: Record<StatementNode["statement"], PostgresTag | undefined> = {
|
|
@@ -549,6 +635,10 @@ const TAG_OF: Record<StatementNode["statement"], PostgresTag | undefined> = {
|
|
|
549
635
|
function: "func",
|
|
550
636
|
procedure: "procedure",
|
|
551
637
|
trigger: "trigger",
|
|
638
|
+
policy: "policy",
|
|
639
|
+
role: "role",
|
|
640
|
+
grant: "grant",
|
|
641
|
+
rowSecurity: undefined,
|
|
552
642
|
comment: undefined,
|
|
553
643
|
};
|
|
554
644
|
|
|
@@ -565,6 +655,10 @@ export const STATEMENT_NAMES: Record<StatementNode["statement"], string> = {
|
|
|
565
655
|
function: "CREATE FUNCTION",
|
|
566
656
|
procedure: "CREATE PROCEDURE",
|
|
567
657
|
trigger: "CREATE TRIGGER",
|
|
658
|
+
policy: "CREATE POLICY",
|
|
659
|
+
role: "CREATE ROLE",
|
|
660
|
+
grant: "GRANT",
|
|
661
|
+
rowSecurity: "ALTER TABLE",
|
|
568
662
|
comment: "COMMENT ON",
|
|
569
663
|
};
|
|
570
664
|
|
|
@@ -782,9 +876,9 @@ function applyComments(
|
|
|
782
876
|
if (value !== undefined) constraint!.comment = value;
|
|
783
877
|
} else {
|
|
784
878
|
if (!objectTypes.includes(c.objectType)) refuse(c, `does not comment on the ${tag} this template declares`);
|
|
785
|
-
if (c.objectType === "TRIGGER") {
|
|
879
|
+
if (c.objectType === "TRIGGER" || c.objectType === "POLICY") {
|
|
786
880
|
// `COMMENT ON TRIGGER name ON table`: the name is the trigger's, the table its own.
|
|
787
|
-
if (qualified(ctx, c.target).name !== self.name || !c.on || !isSelf({ ...qualified(ctx, c.on), name: self.name })) refuse(c, `names another
|
|
881
|
+
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}`);
|
|
788
882
|
} else if (!isSelf(qualified(ctx, c.target))) refuse(c, `names another object than ${sqlNameOf(self)}`);
|
|
789
883
|
comment = value;
|
|
790
884
|
}
|
|
@@ -801,6 +895,17 @@ function lastDot(ctx: Ctx, span: Span): number {
|
|
|
801
895
|
return span.from;
|
|
802
896
|
}
|
|
803
897
|
|
|
898
|
+
/**
|
|
899
|
+
* The first statement after the CREATE that its template may not hold: a
|
|
900
|
+
* template is one object, then COMMENT ON statements for it; a table's may
|
|
901
|
+
* also turn row-level security on or off (`ALTER TABLE ... ENABLE ROW LEVEL
|
|
902
|
+
* SECURITY`), as `pg_dump` prints it. A grant is one statement.
|
|
903
|
+
*/
|
|
904
|
+
export function followOnStray(tag: PostgresTag, rest: readonly StatementNode[]): StatementNode | undefined {
|
|
905
|
+
if (tag === "grant") return rest[0];
|
|
906
|
+
return rest.find((n) => n.statement !== "comment" && !(tag === "table" && n.statement === "rowSecurity"));
|
|
907
|
+
}
|
|
908
|
+
|
|
804
909
|
/**
|
|
805
910
|
* The interpolations that sit inside a string constant (a routine's
|
|
806
911
|
* `AS $$ ... $$` body), rendered into the text around them: a string
|
|
@@ -897,6 +1002,75 @@ function inlineQuoted(parts: readonly string[], values: readonly unknown[]): { p
|
|
|
897
1002
|
return { parts: out, values: outValues, original, inlined, refs };
|
|
898
1003
|
}
|
|
899
1004
|
|
|
1005
|
+
/** A role named where one is granted to: an interpolated role entity gives its name. */
|
|
1006
|
+
function roleName(ctx: Ctx, r: { name: string; span: Span }): string {
|
|
1007
|
+
if (r.name) return r.name;
|
|
1008
|
+
const t = ctx.tokens[r.span.from];
|
|
1009
|
+
const v = t?.kind === "ref" ? ctx.values[t.part] : undefined;
|
|
1010
|
+
if (isPostgresObject(v)) return (v.props as { name: string }).name;
|
|
1011
|
+
return identValue(req(text(ctx, r.span)));
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/** A GRANT's, REVOKE's or ALTER DEFAULT PRIVILEGES's props. */
|
|
1015
|
+
function grantProps(ctx: Ctx, node: GrantNode, ddl: string, source: { strings: string[] }): GrantProps | DefaultPrivilegesProps {
|
|
1016
|
+
const action = node.revoke ? "revoke" : "grant";
|
|
1017
|
+
const grantees = node.grantees.map((g) => roleName(ctx, g));
|
|
1018
|
+
if (node.defaults) {
|
|
1019
|
+
if (node.privileges.some((p) => p.columns.length > 0)) throw new SqlTemplateError("grant", "default privileges are on whole objects; a column list is for a grant on one table", 0, 0);
|
|
1020
|
+
const on = (node.on === "ROUTINES" ? "functions" : node.on.toLowerCase()) as DefaultPrivilegesProps["on"];
|
|
1021
|
+
const forRoles = node.forRoles?.map((r) => roleName(ctx, r));
|
|
1022
|
+
const inSchemas = node.inSchemas?.map((s) => columnName(ctx, s));
|
|
1023
|
+
if (on === "schemas" && inSchemas) throw new SqlTemplateError("grant", "default privileges on schemas cannot be limited to a schema (IN SCHEMA)", 0, 0);
|
|
1024
|
+
const privileges = node.privileges.map((p) => p.privilege.toLowerCase());
|
|
1025
|
+
return stripUnset({
|
|
1026
|
+
name: `default privileges ${action === "grant" ? "grant" : "revoke"} ${privileges.join(", ")} on ${on}${inSchemas ? ` in ${inSchemas.join(", ")}` : ""}${forRoles ? ` for ${forRoles.join(", ")}` : ""} ${action === "grant" ? "to" : "from"} ${grantees.join(", ")}`,
|
|
1027
|
+
action,
|
|
1028
|
+
grantOptionFor: node.grantOptionFor || undefined,
|
|
1029
|
+
forRoles,
|
|
1030
|
+
inSchemas,
|
|
1031
|
+
privileges,
|
|
1032
|
+
on,
|
|
1033
|
+
grantees,
|
|
1034
|
+
withGrantOption: node.withGrantOption || undefined,
|
|
1035
|
+
ddl,
|
|
1036
|
+
source,
|
|
1037
|
+
}) as DefaultPrivilegesProps;
|
|
1038
|
+
}
|
|
1039
|
+
const on = (node.on === "TABLE" ? "table" : node.on.toLowerCase()) as GrantProps["on"];
|
|
1040
|
+
const routine = on === "function" || on === "procedure" || on === "routine";
|
|
1041
|
+
const objects: Array<PostgresObject | string> = [];
|
|
1042
|
+
const objectNames: string[] = [];
|
|
1043
|
+
for (const o of node.objects) {
|
|
1044
|
+
const t = target(ctx, o.name);
|
|
1045
|
+
objects.push(t);
|
|
1046
|
+
let sqlName = typeof t === "string" ? t : on === "schema" ? (t.props as { name: string }).name : t.sqlName;
|
|
1047
|
+
if (routine) {
|
|
1048
|
+
if (o.args) sqlName += `(${req(text(ctx, o.args, "objects")) ?? ""})`;
|
|
1049
|
+
else if (typeof t !== "string" && (t.entityType === POSTGRES_ENTITY_TYPES.function || t.entityType === POSTGRES_ENTITY_TYPES.procedure)) {
|
|
1050
|
+
// An interpolated routine is named by its own parameter types.
|
|
1051
|
+
sqlName += `(${(t.props as RoutineProps).args.filter((a) => a.mode !== "out").map((a) => a.type).join(", ")})`;
|
|
1052
|
+
} else throw new SqlTemplateError("grant", `${sqlName}: name the parameter types (${sqlName}(...)), or interpolate the routine, since overloads share a name`, 0, 0);
|
|
1053
|
+
}
|
|
1054
|
+
objectNames.push(sqlName);
|
|
1055
|
+
}
|
|
1056
|
+
const privileges = node.privileges.map((p) => stripUnset({ privilege: p.privilege.toLowerCase(), columns: p.columns.length > 0 ? p.columns.map((c) => columnName(ctx, c)) : undefined }) as PrivilegeDef);
|
|
1057
|
+
if (on !== "table" && privileges.some((p) => p.columns)) throw new SqlTemplateError("grant", "a column list is for privileges on a table", 0, 0);
|
|
1058
|
+
const verb = action === "grant" ? "to" : "from";
|
|
1059
|
+
return stripUnset({
|
|
1060
|
+
name: `${action} ${privileges.map((p) => `${p.privilege}${p.columns ? `(${p.columns.join(", ")})` : ""}`).join(", ")} on ${on} ${objectNames.join(", ")} ${verb} ${grantees.join(", ")}`,
|
|
1061
|
+
action,
|
|
1062
|
+
grantOptionFor: node.grantOptionFor || undefined,
|
|
1063
|
+
privileges,
|
|
1064
|
+
on,
|
|
1065
|
+
objects,
|
|
1066
|
+
objectNames,
|
|
1067
|
+
grantees,
|
|
1068
|
+
withGrantOption: node.withGrantOption || undefined,
|
|
1069
|
+
ddl,
|
|
1070
|
+
source,
|
|
1071
|
+
}) as GrantProps;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
900
1074
|
function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string[], rawValues: readonly unknown[]): PostgresObject {
|
|
901
1075
|
const rawParts = templateParts(strings);
|
|
902
1076
|
const routine = tag === "func" || tag === "procedure";
|
|
@@ -915,11 +1089,28 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
915
1089
|
if (node.statement !== STATEMENT_OF[tag]) {
|
|
916
1090
|
const holds = STATEMENT_NAMES[node.statement];
|
|
917
1091
|
const use = TAG_OF[node.statement];
|
|
918
|
-
throw new SqlTemplateError(
|
|
1092
|
+
throw new SqlTemplateError(
|
|
1093
|
+
tag,
|
|
1094
|
+
use
|
|
1095
|
+
? `holds a ${holds}; use the ${use} tag`
|
|
1096
|
+
: node.statement === "rowSecurity"
|
|
1097
|
+
? "holds an ALTER TABLE ... ROW LEVEL SECURITY before any CREATE; it goes after the table's CREATE TABLE, in the table's template"
|
|
1098
|
+
: `holds a ${holds} before any CREATE; a COMMENT ON goes after the CREATE it comments on`,
|
|
1099
|
+
0,
|
|
1100
|
+
0,
|
|
1101
|
+
);
|
|
919
1102
|
}
|
|
920
|
-
const stray =
|
|
921
|
-
if (stray)
|
|
922
|
-
|
|
1103
|
+
const stray = followOnStray(tag, rest);
|
|
1104
|
+
if (stray) {
|
|
1105
|
+
throw new SqlTemplateError(
|
|
1106
|
+
tag,
|
|
1107
|
+
`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" : ""}`,
|
|
1108
|
+
0,
|
|
1109
|
+
0,
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
const comments = rest.filter((n): n is CommentNode => n.statement === "comment");
|
|
1113
|
+
const rowSecurity = rest.filter((n): n is RowSecurityNode => n.statement === "rowSecurity");
|
|
923
1114
|
|
|
924
1115
|
const positions = regclassPositions(tokens, values);
|
|
925
1116
|
const render = (v: unknown, i?: number): string => {
|
|
@@ -1078,6 +1269,16 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1078
1269
|
const named = new Map<string, Commentable>();
|
|
1079
1270
|
for (const c of [primaryKey, ...uniques, ...checks, ...foreignKeys, ...exclusions]) if (c?.name) named.set(c.name, c);
|
|
1080
1271
|
const comment = applyComments(tag, ctx, comments, q, ["TABLE"], { columns: new Map(columns.map((c) => [c.name, c])), constraints: named });
|
|
1272
|
+
let rls: boolean | undefined;
|
|
1273
|
+
let force: boolean | undefined;
|
|
1274
|
+
for (const r of rowSecurity) {
|
|
1275
|
+
const t = qualified(ctx, r.table);
|
|
1276
|
+
if (t.name !== q.name || (t.schema !== undefined && q.schema !== undefined && t.schema !== q.schema)) {
|
|
1277
|
+
throw new SqlTemplateError(tag, `ALTER TABLE ${sqlNameOf(t)} ... ROW LEVEL SECURITY names another table than ${sqlNameOf(q)}`, 0, 0);
|
|
1278
|
+
}
|
|
1279
|
+
if (r.action === "ENABLE" || r.action === "DISABLE") rls = r.action === "ENABLE";
|
|
1280
|
+
else force = r.action === "FORCE";
|
|
1281
|
+
}
|
|
1081
1282
|
const props: TableProps = strip(
|
|
1082
1283
|
{
|
|
1083
1284
|
...q,
|
|
@@ -1099,12 +1300,16 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1099
1300
|
with: text(ctx, node.with, "with"),
|
|
1100
1301
|
onCommit: text(ctx, node.onCommit),
|
|
1101
1302
|
tablespace: text(ctx, node.tablespace, "tablespace"),
|
|
1303
|
+
rowSecurity: rls,
|
|
1304
|
+
forceRowSecurity: force,
|
|
1102
1305
|
comment,
|
|
1103
1306
|
ddl,
|
|
1104
1307
|
source,
|
|
1105
1308
|
},
|
|
1106
1309
|
["columns", "uniques", "checks", "foreignKeys", "exclusions", "like", "inherits"],
|
|
1107
1310
|
);
|
|
1311
|
+
if (rls === false) props.rowSecurity = false;
|
|
1312
|
+
if (force === false) props.forceRowSecurity = false;
|
|
1108
1313
|
return make(POSTGRES_ENTITY_TYPES.table, q, props, columns.map((c) => c.name));
|
|
1109
1314
|
}
|
|
1110
1315
|
case "index": {
|
|
@@ -1274,6 +1479,52 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1274
1479
|
);
|
|
1275
1480
|
return make(POSTGRES_ENTITY_TYPES.trigger, q, props);
|
|
1276
1481
|
}
|
|
1482
|
+
case "policy": {
|
|
1483
|
+
const tq = qualified(ctx, node.table);
|
|
1484
|
+
const name = node.name.pieces[0] || identValue(req(text(ctx, node.name.span)));
|
|
1485
|
+
feed(ctx, node.name.span, "name");
|
|
1486
|
+
// A policy is its table's, named in the table's schema.
|
|
1487
|
+
const q = { schema: tq.schema, name };
|
|
1488
|
+
const comment = applyComments(tag, ctx, comments, q, ["POLICY"]);
|
|
1489
|
+
const props: PolicyProps = stripUnset({
|
|
1490
|
+
...q,
|
|
1491
|
+
table: target(ctx, node.table),
|
|
1492
|
+
tableName: sqlNameOf(tq),
|
|
1493
|
+
permissive: node.permissive !== false,
|
|
1494
|
+
command: (node.command ?? "ALL").toLowerCase() as PolicyProps["command"],
|
|
1495
|
+
roles: node.roles.length > 0 ? node.roles.map((r) => roleName(ctx, r)) : ["public"],
|
|
1496
|
+
using: text(ctx, node.using, "using"),
|
|
1497
|
+
check: text(ctx, node.check, "check"),
|
|
1498
|
+
comment,
|
|
1499
|
+
ddl,
|
|
1500
|
+
source,
|
|
1501
|
+
});
|
|
1502
|
+
// AS RESTRICTIVE is a false that means something.
|
|
1503
|
+
props.permissive = node.permissive !== false;
|
|
1504
|
+
return make(POSTGRES_ENTITY_TYPES.policy, q, props);
|
|
1505
|
+
}
|
|
1506
|
+
case "role": {
|
|
1507
|
+
const name = node.name.pieces[0] || identValue(req(text(ctx, node.name.span)));
|
|
1508
|
+
feed(ctx, node.name.span, "name");
|
|
1509
|
+
const comment = applyComments(tag, ctx, comments, { name }, ["ROLE"]);
|
|
1510
|
+
const value = (o: string) => {
|
|
1511
|
+
const x = node.options.find((y) => y.option === o);
|
|
1512
|
+
return x?.value ? text(ctx, x.value, "options") : undefined;
|
|
1513
|
+
};
|
|
1514
|
+
const props: RoleProps = stripUnset({
|
|
1515
|
+
name,
|
|
1516
|
+
options: node.options.filter((o) => o.value === undefined).map((o) => o.option.toLowerCase()),
|
|
1517
|
+
connectionLimit: value("CONNECTION LIMIT"),
|
|
1518
|
+
comment,
|
|
1519
|
+
ddl,
|
|
1520
|
+
source,
|
|
1521
|
+
});
|
|
1522
|
+
return make(POSTGRES_ENTITY_TYPES.role, { name }, props);
|
|
1523
|
+
}
|
|
1524
|
+
case "grant":
|
|
1525
|
+
return make(node.defaults ? POSTGRES_ENTITY_TYPES.defaultPrivileges : POSTGRES_ENTITY_TYPES.grant, { name: "" }, grantProps(ctx, node, ddl, source));
|
|
1526
|
+
case "rowSecurity":
|
|
1527
|
+
throw new SqlTemplateError(tag, "an ALTER TABLE ... ROW LEVEL SECURITY goes after the table's CREATE TABLE, in the table's template", 0, 0);
|
|
1277
1528
|
case "comment":
|
|
1278
1529
|
throw new SqlTemplateError(tag, "a COMMENT ON goes after the CREATE it comments on", 0, 0);
|
|
1279
1530
|
}
|
|
@@ -1337,3 +1588,28 @@ export function procedure(strings: TemplateStringsArray, ...values: unknown[]):
|
|
|
1337
1588
|
export function trigger(strings: TemplateStringsArray, ...values: unknown[]): PostgresTrigger {
|
|
1338
1589
|
return build("trigger", strings, values) as PostgresTrigger;
|
|
1339
1590
|
}
|
|
1591
|
+
|
|
1592
|
+
/** `` policy`CREATE POLICY name ON ${table} ... USING (...)` ``: one row-level security policy on its table. */
|
|
1593
|
+
export function policy(strings: TemplateStringsArray, ...values: unknown[]): PostgresPolicy {
|
|
1594
|
+
return build("policy", strings, values) as PostgresPolicy;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* `` role`CREATE ROLE name [WITH] options` ``: a role the schema needs, created
|
|
1599
|
+
* when it is missing. A password and memberships are the environment's and are
|
|
1600
|
+
* refused; a role the environment provisions is named as text where it is
|
|
1601
|
+
* granted to, never declared.
|
|
1602
|
+
*/
|
|
1603
|
+
export function role(strings: TemplateStringsArray, ...values: unknown[]): PostgresRole {
|
|
1604
|
+
return build("role", strings, values) as PostgresRole;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
/**
|
|
1608
|
+
* `` grant`GRANT ... ON ... TO ...` ``, `REVOKE ... FROM ...`, or
|
|
1609
|
+
* `ALTER DEFAULT PRIVILEGES ... GRANT|REVOKE ...`: privileges on schemas,
|
|
1610
|
+
* tables, columns, sequences and routines, applied where the profile manages
|
|
1611
|
+
* access (`sql.profiles.<env>.access`).
|
|
1612
|
+
*/
|
|
1613
|
+
export function grant(strings: TemplateStringsArray, ...values: unknown[]): PostgresGrant {
|
|
1614
|
+
return build("grant", strings, values) as PostgresGrant;
|
|
1615
|
+
}
|
|
@@ -16,6 +16,10 @@ export const POSTGRES_ENTITY_TYPES = {
|
|
|
16
16
|
function: "Postgres::Function",
|
|
17
17
|
procedure: "Postgres::Procedure",
|
|
18
18
|
trigger: "Postgres::Trigger",
|
|
19
|
+
policy: "Postgres::Policy",
|
|
20
|
+
role: "Postgres::Role",
|
|
21
|
+
grant: "Postgres::Grant",
|
|
22
|
+
defaultPrivileges: "Postgres::DefaultPrivileges",
|
|
19
23
|
} as const;
|
|
20
24
|
|
|
21
25
|
export type PostgresEntityType = (typeof POSTGRES_ENTITY_TYPES)[keyof typeof POSTGRES_ENTITY_TYPES];
|