@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.
Files changed (259) hide show
  1. package/dist/clickhouse/access.d.ts +132 -0
  2. package/dist/clickhouse/access.d.ts.map +1 -0
  3. package/dist/clickhouse/apply/apply.d.ts.map +1 -1
  4. package/dist/clickhouse/apply/statements.d.ts +2 -0
  5. package/dist/clickhouse/apply/statements.d.ts.map +1 -1
  6. package/dist/clickhouse/entities.d.ts +171 -2
  7. package/dist/clickhouse/entities.d.ts.map +1 -1
  8. package/dist/clickhouse/http.d.ts +3 -0
  9. package/dist/clickhouse/http.d.ts.map +1 -1
  10. package/dist/clickhouse/import/generator.d.ts +28 -0
  11. package/dist/clickhouse/import/generator.d.ts.map +1 -1
  12. package/dist/clickhouse/import/ir.d.ts.map +1 -1
  13. package/dist/clickhouse/import/parser.d.ts +3 -3
  14. package/dist/clickhouse/import/parser.d.ts.map +1 -1
  15. package/dist/clickhouse/live/bind.d.ts.map +1 -1
  16. package/dist/clickhouse/live/catalog.d.ts +41 -6
  17. package/dist/clickhouse/live/catalog.d.ts.map +1 -1
  18. package/dist/clickhouse/live/describe-resources.d.ts +3 -2
  19. package/dist/clickhouse/live/describe-resources.d.ts.map +1 -1
  20. package/dist/clickhouse/parser.d.ts +54 -3
  21. package/dist/clickhouse/parser.d.ts.map +1 -1
  22. package/dist/clickhouse/plan/commands.d.ts +3 -1
  23. package/dist/clickhouse/plan/commands.d.ts.map +1 -1
  24. package/dist/clickhouse/plan/deep.d.ts.map +1 -1
  25. package/dist/clickhouse/plan/diff.d.ts +12 -0
  26. package/dist/clickhouse/plan/diff.d.ts.map +1 -1
  27. package/dist/clickhouse/plan/normalize.d.ts +61 -1
  28. package/dist/clickhouse/plan/normalize.d.ts.map +1 -1
  29. package/dist/clickhouse/plan/report.d.ts.map +1 -1
  30. package/dist/clickhouse/plan/rules.d.ts +7 -0
  31. package/dist/clickhouse/plan/rules.d.ts.map +1 -1
  32. package/dist/clickhouse/plan/schema.d.ts +8 -4
  33. package/dist/clickhouse/plan/schema.d.ts.map +1 -1
  34. package/dist/clickhouse/rebuild/backfill.d.ts +3 -1
  35. package/dist/clickhouse/rebuild/backfill.d.ts.map +1 -1
  36. package/dist/clickhouse/rebuild/observe.d.ts +7 -3
  37. package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
  38. package/dist/clickhouse/rebuild/op.d.ts +8 -0
  39. package/dist/clickhouse/rebuild/op.d.ts.map +1 -1
  40. package/dist/clickhouse/rebuild/steps.d.ts +48 -0
  41. package/dist/clickhouse/rebuild/steps.d.ts.map +1 -1
  42. package/dist/clickhouse/testing/fake-server.d.ts.map +1 -1
  43. package/dist/clickhouse/testing/writable-server.d.ts.map +1 -1
  44. package/dist/clickhouse/topology.d.ts.map +1 -1
  45. package/dist/clickhouse.d.ts +1 -1
  46. package/dist/clickhouse.d.ts.map +1 -1
  47. package/dist/codegen/classifier-page.d.ts.map +1 -1
  48. package/dist/codegen/registry.d.ts.map +1 -1
  49. package/dist/config.d.ts +70 -7
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/core/references.d.ts +4 -2
  52. package/dist/core/references.d.ts.map +1 -1
  53. package/dist/core/statements.d.ts +11 -0
  54. package/dist/core/statements.d.ts.map +1 -1
  55. package/dist/files/build-root.d.ts +26 -0
  56. package/dist/files/build-root.d.ts.map +1 -0
  57. package/dist/files/clickhouse.d.ts +19 -0
  58. package/dist/files/clickhouse.d.ts.map +1 -0
  59. package/dist/files/common.d.ts +25 -0
  60. package/dist/files/common.d.ts.map +1 -0
  61. package/dist/files/postgres.d.ts +36 -0
  62. package/dist/files/postgres.d.ts.map +1 -0
  63. package/dist/import-parser.d.ts +25 -0
  64. package/dist/import-parser.d.ts.map +1 -0
  65. package/dist/index.d.ts +2 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/integrity.json +8 -8
  68. package/dist/lint/rules/sqlch001.d.ts +1 -1
  69. package/dist/lint/rules/sqlch001.d.ts.map +1 -1
  70. package/dist/lint/rules/sqlch002.d.ts.map +1 -1
  71. package/dist/lint/rules/sqlpg001.d.ts.map +1 -1
  72. package/dist/lint/rules/templates.d.ts +1 -1
  73. package/dist/lint/rules/templates.d.ts.map +1 -1
  74. package/dist/lsp/postgres-template.d.ts.map +1 -1
  75. package/dist/manifest.json +1 -1
  76. package/dist/meta.json +50 -0
  77. package/dist/okf/index.md +11 -1
  78. package/dist/okf/rules/SQLCH001.md +2 -2
  79. package/dist/okf/types/Dictionary.md +9 -0
  80. package/dist/okf/types/Function.md +9 -0
  81. package/dist/okf/types/Grant.md +9 -0
  82. package/dist/okf/types/PostgresDefaultPrivileges.md +9 -0
  83. package/dist/okf/types/PostgresGrant.md +9 -0
  84. package/dist/okf/types/PostgresPolicy.md +9 -0
  85. package/dist/okf/types/PostgresRole.md +9 -0
  86. package/dist/okf/types/Role.md +9 -0
  87. package/dist/okf/types/RowPolicy.md +9 -0
  88. package/dist/okf/types/User.md +9 -0
  89. package/dist/op/activities/clickhouse-rebuild.d.ts.map +1 -1
  90. package/dist/op/activities/postgres-migration.d.ts.map +1 -1
  91. package/dist/op/activity-contracts.d.ts +19 -0
  92. package/dist/op/activity-contracts.d.ts.map +1 -1
  93. package/dist/plugin.d.ts.map +1 -1
  94. package/dist/postgres/access/acl.d.ts +119 -0
  95. package/dist/postgres/access/acl.d.ts.map +1 -0
  96. package/dist/postgres/access/import.d.ts +13 -0
  97. package/dist/postgres/access/import.d.ts.map +1 -0
  98. package/dist/postgres/access/live.d.ts +24 -0
  99. package/dist/postgres/access/live.d.ts.map +1 -0
  100. package/dist/postgres/apply/apply.d.ts.map +1 -1
  101. package/dist/postgres/apply/statements.d.ts +71 -0
  102. package/dist/postgres/apply/statements.d.ts.map +1 -1
  103. package/dist/postgres/entities.d.ts +106 -4
  104. package/dist/postgres/entities.d.ts.map +1 -1
  105. package/dist/postgres/entity-types.d.ts +4 -0
  106. package/dist/postgres/entity-types.d.ts.map +1 -1
  107. package/dist/postgres/import/generator.d.ts +37 -1
  108. package/dist/postgres/import/generator.d.ts.map +1 -1
  109. package/dist/postgres/import/live-export.d.ts +6 -0
  110. package/dist/postgres/import/live-export.d.ts.map +1 -1
  111. package/dist/postgres/live/bind.d.ts +2 -0
  112. package/dist/postgres/live/bind.d.ts.map +1 -1
  113. package/dist/postgres/live/catalog.d.ts +11 -2
  114. package/dist/postgres/live/catalog.d.ts.map +1 -1
  115. package/dist/postgres/live/client.d.ts +3 -0
  116. package/dist/postgres/live/client.d.ts.map +1 -1
  117. package/dist/postgres/live/describe-resources.d.ts +9 -0
  118. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  119. package/dist/postgres/migrate/op.d.ts +29 -0
  120. package/dist/postgres/migrate/op.d.ts.map +1 -1
  121. package/dist/postgres/parser.d.ts +76 -3
  122. package/dist/postgres/parser.d.ts.map +1 -1
  123. package/dist/postgres/plan/commands.d.ts +12 -0
  124. package/dist/postgres/plan/commands.d.ts.map +1 -1
  125. package/dist/postgres/plan/deep.d.ts.map +1 -1
  126. package/dist/postgres/plan/diff.d.ts +10 -1
  127. package/dist/postgres/plan/diff.d.ts.map +1 -1
  128. package/dist/postgres/plan/normalize.d.ts +8 -1
  129. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  130. package/dist/postgres/plan/rules.d.ts +8 -0
  131. package/dist/postgres/plan/rules.d.ts.map +1 -1
  132. package/dist/postgres/plan/schema.d.ts +2 -1
  133. package/dist/postgres/plan/schema.d.ts.map +1 -1
  134. package/dist/postgres/plan/server-normalize.d.ts +4 -1
  135. package/dist/postgres/plan/server-normalize.d.ts.map +1 -1
  136. package/dist/postgres.d.ts +1 -1
  137. package/dist/postgres.d.ts.map +1 -1
  138. package/dist/rules/postgres-templates.ts +2 -2
  139. package/dist/rules/sqlch001.ts +26 -4
  140. package/dist/rules/sqlch002.ts +3 -2
  141. package/dist/rules/sqlpg001.ts +7 -4
  142. package/dist/rules/templates.ts +2 -2
  143. package/dist/serializer.d.ts +2 -1
  144. package/dist/serializer.d.ts.map +1 -1
  145. package/dist/sql-files.d.ts +79 -0
  146. package/dist/sql-files.d.ts.map +1 -0
  147. package/dist/token-source.d.ts +88 -0
  148. package/dist/token-source.d.ts.map +1 -0
  149. package/dist/validate.d.ts.map +1 -1
  150. package/package.json +2 -2
  151. package/src/clickhouse/access.test.ts +203 -0
  152. package/src/clickhouse/access.ts +595 -0
  153. package/src/clickhouse/apply/apply.ts +29 -20
  154. package/src/clickhouse/apply/statements.ts +105 -8
  155. package/src/clickhouse/dictionary.test.ts +177 -0
  156. package/src/clickhouse/entities.ts +356 -6
  157. package/src/clickhouse/function.test.ts +91 -0
  158. package/src/clickhouse/http.ts +11 -2
  159. package/src/clickhouse/import/generator.ts +81 -46
  160. package/src/clickhouse/import/import.test.ts +9 -3
  161. package/src/clickhouse/import/ir.ts +6 -2
  162. package/src/clickhouse/import/parser.ts +10 -18
  163. package/src/clickhouse/live/access.e2e.test.ts +218 -0
  164. package/src/clickhouse/live/bind.ts +17 -6
  165. package/src/clickhouse/live/catalog.ts +106 -10
  166. package/src/clickhouse/live/describe-resources.ts +23 -10
  167. package/src/clickhouse/live/dictionaries.e2e.test.ts +186 -0
  168. package/src/clickhouse/live/functions.e2e.test.ts +155 -0
  169. package/src/clickhouse/live/live.test.ts +2 -2
  170. package/src/clickhouse/parser.ts +136 -4
  171. package/src/clickhouse/plan/commands.ts +18 -7
  172. package/src/clickhouse/plan/deep.ts +71 -11
  173. package/src/clickhouse/plan/diff.ts +65 -2
  174. package/src/clickhouse/plan/normalize.ts +200 -6
  175. package/src/clickhouse/plan/report.ts +14 -3
  176. package/src/clickhouse/plan/rules.ts +44 -1
  177. package/src/clickhouse/plan/schema.ts +40 -17
  178. package/src/clickhouse/plan/server-format.ts +1 -1
  179. package/src/clickhouse/rebuild/backfill.ts +5 -25
  180. package/src/clickhouse/rebuild/observe.ts +7 -3
  181. package/src/clickhouse/rebuild/op.ts +9 -0
  182. package/src/clickhouse/rebuild/rebuild.e2e.test.ts +62 -0
  183. package/src/clickhouse/rebuild/rebuild.test.ts +54 -0
  184. package/src/clickhouse/rebuild/steps.ts +136 -1
  185. package/src/clickhouse/testing/fake-server.ts +2 -0
  186. package/src/clickhouse/testing/writable-server.ts +2 -0
  187. package/src/clickhouse/topology.ts +17 -2
  188. package/src/clickhouse.ts +19 -0
  189. package/src/codegen/classifier-page.ts +2 -0
  190. package/src/codegen/registry.ts +10 -0
  191. package/src/config.ts +22 -3
  192. package/src/core/references.ts +5 -3
  193. package/src/core/statements.ts +11 -1
  194. package/src/files/build-root.ts +62 -0
  195. package/src/files/clickhouse.ts +52 -0
  196. package/src/files/common.ts +54 -0
  197. package/src/files/postgres.ts +371 -0
  198. package/src/generated/lexicon-sql.json +50 -0
  199. package/src/import-parser.ts +61 -0
  200. package/src/index.ts +14 -1
  201. package/src/lint/rules/postgres-templates.ts +2 -2
  202. package/src/lint/rules/sqlch001.ts +26 -4
  203. package/src/lint/rules/sqlch002.ts +3 -2
  204. package/src/lint/rules/sqlpg001.ts +7 -4
  205. package/src/lint/rules/templates.ts +2 -2
  206. package/src/lsp/postgres-template.ts +2 -0
  207. package/src/lsp/template.ts +3 -3
  208. package/src/mcp/index.ts +6 -6
  209. package/src/mcp/postgres.ts +1 -1
  210. package/src/migration-statements.test.ts +25 -0
  211. package/src/migration-statements.ts +8 -0
  212. package/src/op/activities/clickhouse-rebuild.ts +1 -0
  213. package/src/op/activities/postgres-migration.ts +27 -2
  214. package/src/op/activity-contracts.ts +2 -0
  215. package/src/plugin.ts +29 -5
  216. package/src/postgres/access/access.e2e.test.ts +191 -0
  217. package/src/postgres/access/access.test.ts +147 -0
  218. package/src/postgres/access/acl.ts +400 -0
  219. package/src/postgres/access/import.ts +64 -0
  220. package/src/postgres/access/live.ts +123 -0
  221. package/src/postgres/apply/apply.ts +117 -4
  222. package/src/postgres/apply/constraint-sequences.e2e.test.ts +179 -0
  223. package/src/postgres/apply/safe-sequences.e2e.test.ts +176 -0
  224. package/src/postgres/apply/statements.test.ts +138 -6
  225. package/src/postgres/apply/statements.ts +210 -18
  226. package/src/postgres/entities.ts +287 -11
  227. package/src/postgres/entity-types.ts +4 -0
  228. package/src/postgres/import/generator.ts +129 -46
  229. package/src/postgres/import/live-export.ts +12 -2
  230. package/src/postgres/live/bind.ts +20 -6
  231. package/src/postgres/live/catalog.ts +79 -3
  232. package/src/postgres/live/client.ts +10 -2
  233. package/src/postgres/live/describe-resources.ts +27 -3
  234. package/src/postgres/migrate/migrate.test.ts +24 -1
  235. package/src/postgres/migrate/op.ts +80 -35
  236. package/src/postgres/migrate/outer.e2e.test.ts +163 -0
  237. package/src/postgres/parser.ts +247 -8
  238. package/src/postgres/plan/commands.ts +60 -7
  239. package/src/postgres/plan/deep.ts +24 -3
  240. package/src/postgres/plan/diff.ts +71 -6
  241. package/src/postgres/plan/normalize.ts +115 -2
  242. package/src/postgres/plan/plan.test.ts +2 -2
  243. package/src/postgres/plan/rules.ts +49 -5
  244. package/src/postgres/plan/schema.ts +4 -2
  245. package/src/postgres/plan/server-normalize.ts +29 -1
  246. package/src/postgres.ts +11 -0
  247. package/src/serializer.ts +14 -9
  248. package/src/skill-defs.ts +1 -1
  249. package/src/skills/chant-sql-postgres-migration.md +3 -1
  250. package/src/skills/chant-sql-postgres-plan.md +3 -3
  251. package/src/skills/chant-sql-postgres.md +9 -2
  252. package/src/skills/chant-sql-rebuild.md +3 -2
  253. package/src/skills/chant-sql.md +7 -1
  254. package/src/sql-files.test.ts +188 -0
  255. package/src/sql-files.ts +218 -0
  256. package/src/token-source.e2e.test.ts +142 -0
  257. package/src/token-source.test.ts +119 -0
  258. package/src/token-source.ts +189 -0
  259. package/src/validate.ts +10 -0
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * The Postgres entities: `schema`, `table`, `index`, `view`, `sequence`,
3
- * `type`, `domain`, `extension`, `func`, `procedure` and `trigger` tagged
4
- * templates that parse their DDL at fold time into one Declarable each
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 trigger than ${self.name}`);
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(tag, use ? `holds a ${holds}; use the ${use} tag` : `holds a ${holds} before any CREATE; a COMMENT ON goes after the CREATE it comments on`, 0, 0);
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 = rest.find((n) => n.statement !== "comment");
921
- if (stray) throw new SqlTemplateError(tag, `holds a second statement (${STATEMENT_NAMES[stray.statement]}); a template declares one object, followed only by COMMENT ON statements for it`, 0, 0);
922
- const comments = rest as CommentNode[];
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];