@intentius/chant-lexicon-sql 0.124.12 → 0.124.13
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/apply/statements.d.ts.map +1 -1
- package/dist/clickhouse/entities.d.ts +7 -1
- package/dist/clickhouse/entities.d.ts.map +1 -1
- package/dist/clickhouse/expressions.d.ts +27 -0
- package/dist/clickhouse/expressions.d.ts.map +1 -0
- package/dist/clickhouse/parser.d.ts +12 -2
- package/dist/clickhouse/parser.d.ts.map +1 -1
- package/dist/clickhouse/plan/diff.d.ts.map +1 -1
- package/dist/clickhouse/plan/normalize.d.ts +11 -1
- package/dist/clickhouse/plan/normalize.d.ts.map +1 -1
- package/dist/clickhouse/plan/rebuild-handoff.d.ts.map +1 -1
- package/dist/clickhouse/plan/rules.d.ts +1 -0
- package/dist/clickhouse/plan/rules.d.ts.map +1 -1
- package/dist/clickhouse.d.ts +1 -0
- package/dist/clickhouse.d.ts.map +1 -1
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/postgres-classifier-page.d.ts.map +1 -1
- package/dist/core/cursor.d.ts +41 -0
- package/dist/core/cursor.d.ts.map +1 -1
- package/dist/core/interpolation.d.ts +18 -1
- package/dist/core/interpolation.d.ts.map +1 -1
- package/dist/files/clickhouse.d.ts +2 -2
- package/dist/files/clickhouse.d.ts.map +1 -1
- package/dist/files/common.d.ts +16 -0
- package/dist/files/common.d.ts.map +1 -1
- package/dist/files/postgres.d.ts +2 -2
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/import-parser.d.ts +2 -1
- package/dist/import-parser.d.ts.map +1 -1
- package/dist/integrity.json +19 -19
- package/dist/lint/post-synth/clickhouse-helpers.d.ts +6 -19
- package/dist/lint/post-synth/clickhouse-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/postgres-helpers.d.ts +15 -55
- package/dist/lint/post-synth/postgres-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/postgres-names.d.ts +2 -2
- package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
- package/dist/lint/post-synth/sql-helpers.d.ts +5 -6
- package/dist/lint/post-synth/sql-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch102.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch103.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch104.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch105.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch106.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch107.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch111.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch112.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch113.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch114.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch115.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch120.d.ts.map +1 -1
- package/dist/lint/post-synth/sqlch125.d.ts.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/entities.d.ts +7 -1
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/parser.d.ts +10 -2
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/rules.d.ts +1 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres.d.ts +1 -0
- package/dist/postgres.d.ts.map +1 -1
- package/dist/read/clickhouse.d.ts +66 -0
- package/dist/read/clickhouse.d.ts.map +1 -0
- package/dist/read/objects.d.ts +99 -0
- package/dist/read/objects.d.ts.map +1 -0
- package/dist/read/postgres.d.ts +83 -0
- package/dist/read/postgres.d.ts.map +1 -0
- package/dist/read.d.ts +49 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/rules/clickhouse-helpers.ts +20 -97
- package/dist/rules/postgres-helpers.ts +18 -53
- package/dist/rules/postgres-names.ts +6 -6
- package/dist/rules/sql-helpers.ts +8 -28
- package/dist/rules/sqlch102.ts +2 -3
- package/dist/rules/sqlch103.ts +2 -3
- package/dist/rules/sqlch104.ts +2 -3
- package/dist/rules/sqlch105.ts +2 -3
- package/dist/rules/sqlch106.ts +2 -3
- package/dist/rules/sqlch107.ts +2 -3
- package/dist/rules/sqlch111.ts +2 -3
- package/dist/rules/sqlch112.ts +2 -3
- package/dist/rules/sqlch113.ts +2 -3
- package/dist/rules/sqlch114.ts +2 -3
- package/dist/rules/sqlch115.ts +2 -3
- package/dist/rules/sqlch120.ts +2 -3
- package/dist/rules/sqlch125.ts +2 -3
- package/dist/serializer.d.ts +11 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/sql-files.d.ts +17 -1
- package/dist/sql-files.d.ts.map +1 -1
- package/dist/sql-types.d.ts +3 -0
- package/dist/sql-types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clickhouse/apply/statements.ts +2 -0
- package/src/clickhouse/entities.ts +8 -1
- package/src/clickhouse/expressions.ts +108 -0
- package/src/clickhouse/parser.ts +113 -31
- package/src/clickhouse/plan/diff.ts +19 -0
- package/src/clickhouse/plan/normalize.ts +27 -2
- package/src/clickhouse/plan/rebuild-handoff.ts +2 -1
- package/src/clickhouse/plan/rules.ts +7 -0
- package/src/clickhouse/testdata/server-ddl-baseline.json +67 -0
- package/src/clickhouse.ts +10 -0
- package/src/codegen/classifier-page.ts +2 -0
- package/src/codegen/postgres-classifier-page.ts +2 -0
- package/src/core/cursor.ts +96 -0
- package/src/core/interpolation.ts +32 -1
- package/src/files/clickhouse.ts +7 -5
- package/src/files/common.ts +23 -0
- package/src/files/postgres.ts +12 -8
- package/src/import-parser.ts +2 -1
- package/src/lint/post-synth/clickhouse-helpers.ts +20 -97
- package/src/lint/post-synth/postgres-helpers.ts +18 -53
- package/src/lint/post-synth/postgres-names.ts +6 -6
- package/src/lint/post-synth/sql-helpers.ts +8 -28
- package/src/lint/post-synth/sqlch102.ts +2 -3
- package/src/lint/post-synth/sqlch103.ts +2 -3
- package/src/lint/post-synth/sqlch104.ts +2 -3
- package/src/lint/post-synth/sqlch105.ts +2 -3
- package/src/lint/post-synth/sqlch106.ts +2 -3
- package/src/lint/post-synth/sqlch107.ts +2 -3
- package/src/lint/post-synth/sqlch111.ts +2 -3
- package/src/lint/post-synth/sqlch112.ts +2 -3
- package/src/lint/post-synth/sqlch113.ts +2 -3
- package/src/lint/post-synth/sqlch114.ts +2 -3
- package/src/lint/post-synth/sqlch115.ts +2 -3
- package/src/lint/post-synth/sqlch120.ts +2 -3
- package/src/lint/post-synth/sqlch125.ts +2 -3
- package/src/postgres/apply/statements.ts +2 -0
- package/src/postgres/entities.ts +9 -1
- package/src/postgres/parser.ts +31 -8
- package/src/postgres/plan/diff.ts +18 -0
- package/src/postgres/plan/normalize.ts +12 -0
- package/src/postgres/plan/rules.ts +7 -0
- package/src/postgres/testdata/server-ddl-baseline.json +38 -0
- package/src/postgres.ts +12 -0
- package/src/read/clickhouse.ts +137 -0
- package/src/read/objects.ts +137 -0
- package/src/read/postgres.ts +180 -0
- package/src/read-policies.e2e.test.ts +144 -0
- package/src/read.test.ts +143 -0
- package/src/read.ts +69 -0
- package/src/serializer.ts +37 -1
- package/src/sql-files.ts +27 -6
- package/src/sql-types.ts +19 -2
- package/src/unknown-clauses.test.ts +227 -0
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reading the sql lexicon's build output in a post-synth check: the JSON
|
|
3
|
-
* document the serializer writes (`dialect`, `applyOrder`, `objects`).
|
|
3
|
+
* document the serializer writes (`dialect`, `applyOrder`, `objects`). The
|
|
4
|
+
* documents are found by `../../read/objects.ts`, the same path the public
|
|
5
|
+
* read API (`@intentius/chant-lexicon-sql/read`) takes.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
9
|
+
import { dialectObjects, recordedPostgresMajor, type SqlObjectJson } from "../../read/objects";
|
|
7
10
|
|
|
8
|
-
export interface OutputObject {
|
|
9
|
-
export: string;
|
|
10
|
-
type: string;
|
|
11
|
-
name: string;
|
|
11
|
+
export interface OutputObject extends SqlObjectJson {
|
|
12
12
|
database?: string;
|
|
13
13
|
engine?: { name: string; args?: string[] };
|
|
14
|
-
[key: string]: unknown;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
/** Every schema object in the build's sql output, ClickHouse dialect. */
|
|
18
17
|
export function clickhouseObjects(ctx: PostSynthContext): OutputObject[] {
|
|
19
|
-
|
|
20
|
-
for (const doc of ctx.docs ?? []) {
|
|
21
|
-
if (doc.error || doc.lexicon !== "sql" || doc.format !== "json") continue;
|
|
22
|
-
const value = doc.value as { dialect?: unknown; objects?: unknown };
|
|
23
|
-
if (value?.dialect !== "clickhouse" || !Array.isArray(value.objects)) continue;
|
|
24
|
-
out.push(...(value.objects as OutputObject[]));
|
|
25
|
-
}
|
|
26
|
-
return out;
|
|
18
|
+
return dialectObjects(ctx, "clickhouse") as OutputObject[];
|
|
27
19
|
}
|
|
28
20
|
|
|
29
21
|
/** Every schema object in the build's sql output, Postgres dialect. */
|
|
30
22
|
export function postgresObjects(ctx: PostSynthContext): OutputObject[] {
|
|
31
|
-
|
|
32
|
-
for (const doc of ctx.docs ?? []) {
|
|
33
|
-
if (doc.error || doc.lexicon !== "sql" || doc.format !== "json") continue;
|
|
34
|
-
const value = doc.value as { dialect?: unknown; objects?: unknown };
|
|
35
|
-
if (value?.dialect !== "postgres" || !Array.isArray(value.objects)) continue;
|
|
36
|
-
out.push(...(value.objects as OutputObject[]));
|
|
37
|
-
}
|
|
38
|
-
return out;
|
|
23
|
+
return dialectObjects(ctx, "postgres") as OutputObject[];
|
|
39
24
|
}
|
|
40
25
|
|
|
41
26
|
/**
|
|
@@ -44,10 +29,5 @@ export function postgresObjects(ctx: PostSynthContext): OutputObject[] {
|
|
|
44
29
|
* output that carries none.
|
|
45
30
|
*/
|
|
46
31
|
export function postgresMajorOf(ctx: PostSynthContext, fallback: number): number {
|
|
47
|
-
|
|
48
|
-
if (doc.error || doc.lexicon !== "sql" || doc.format !== "json") continue;
|
|
49
|
-
const value = doc.value as { dialect?: unknown; postgresMajor?: unknown };
|
|
50
|
-
if (value?.dialect === "postgres" && typeof value.postgresMajor === "number") return value.postgresMajor;
|
|
51
|
-
}
|
|
52
|
-
return fallback;
|
|
32
|
+
return recordedPostgresMajor(ctx, fallback);
|
|
53
33
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, keyElements, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* SQLCH102: a PRIMARY KEY that is not a prefix of ORDER BY.
|
|
@@ -9,7 +8,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
9
8
|
* the table at CREATE.
|
|
10
9
|
*/
|
|
11
10
|
export const sqlch102 = checkOf({ id: "SQLCH102", description: "A PRIMARY KEY is not a prefix of ORDER BY" }, (ctx, report) => {
|
|
12
|
-
for (const t of
|
|
11
|
+
for (const t of tablesOf(ctx)) {
|
|
13
12
|
if (!t.primaryKey || !t.orderBy) continue;
|
|
14
13
|
const pk = keyElements(t.primaryKey).map((e) => e.replace(/\s+/g, ""));
|
|
15
14
|
const ob = keyElements(t.orderBy).map((e) => e.replace(/\s+/g, ""));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { baseEngineName, checkOf, columnByName, engineArguments, isNullableType,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { baseEngineName, checkOf, columnByName, engineArguments, isNullableType, tablesOf, typeFamily } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
const VERSION_TYPES = new Set(["UInt8", "UInt16", "UInt32", "UInt64", "UInt128", "UInt256", "Date", "DateTime", "DateTime64"]);
|
|
5
4
|
|
|
@@ -25,7 +24,7 @@ const SPECIAL: Record<string, Array<{ role: string; index: number; accepts: (fam
|
|
|
25
24
|
* .../collapsingmergetree, .../versionedcollapsingmergetree (sign: Int8).
|
|
26
25
|
*/
|
|
27
26
|
export const sqlch103 = checkOf({ id: "SQLCH103", description: "An engine's version, sign or is_deleted column has an unsupported type" }, (ctx, report) => {
|
|
28
|
-
for (const t of
|
|
27
|
+
for (const t of tablesOf(ctx)) {
|
|
29
28
|
if (!t.engine) continue;
|
|
30
29
|
const roles = SPECIAL[baseEngineName(t.engine)];
|
|
31
30
|
if (!roles) continue;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { baseEngineName, checkOf, engineArguments,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { baseEngineName, checkOf, engineArguments, splitTop, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/** The engine arguments that name columns, by base engine. */
|
|
5
4
|
function columnArguments(engine: string, args: string[]): string[] {
|
|
@@ -26,7 +25,7 @@ function columnArguments(engine: string, args: string[]): string[] {
|
|
|
26
25
|
* sign, version, is_deleted and summed columns are columns of the table).
|
|
27
26
|
*/
|
|
28
27
|
export const sqlch104 = checkOf({ id: "SQLCH104", description: "A MergeTree engine argument names a column the table does not declare" }, (ctx, report) => {
|
|
29
|
-
for (const t of
|
|
28
|
+
for (const t of tablesOf(ctx)) {
|
|
30
29
|
if (!t.engine || t.columns.length === 0) continue;
|
|
31
30
|
const declared = new Set(t.columns.map((c) => c.name));
|
|
32
31
|
for (const arg of columnArguments(baseEngineName(t.engine), engineArguments(t.engine))) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { bareName, checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { bareName, checkOf, keyElements, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* SQLCH105: ORDER BY, PRIMARY KEY, PARTITION BY or SAMPLE BY lists a bare
|
|
@@ -10,7 +9,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
10
9
|
* judged; an expression is left to the server.
|
|
11
10
|
*/
|
|
12
11
|
export const sqlch105 = checkOf({ id: "SQLCH105", description: "A table key clause names a column the table does not declare" }, (ctx, report) => {
|
|
13
|
-
for (const t of
|
|
12
|
+
for (const t of tablesOf(ctx)) {
|
|
14
13
|
if (t.columns.length === 0) continue;
|
|
15
14
|
const declared = new Set(t.columns.map((c) => c.name));
|
|
16
15
|
const clauses: Array<[string, string | undefined]> = [
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, mentionedNames, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* SQLCH106: a data skipping index expression mentions a column the table does
|
|
@@ -11,7 +10,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
11
10
|
* SQL parser would guess.
|
|
12
11
|
*/
|
|
13
12
|
export const sqlch106 = checkOf({ id: "SQLCH106", description: "A skip index expression names a column the table does not declare" }, (ctx, report) => {
|
|
14
|
-
for (const t of
|
|
13
|
+
for (const t of tablesOf(ctx)) {
|
|
15
14
|
if (t.columns.length === 0) continue;
|
|
16
15
|
const declared = new Set(t.columns.map((c) => c.name));
|
|
17
16
|
for (const index of t.indexes ?? []) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { bareName, checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { bareName, checkOf, splitTop, tablesOf, typeFamily } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
const DATE_TYPES = new Set(["Date", "Date32", "DateTime", "DateTime64"]);
|
|
5
4
|
const TTL_ACTION = /\s+(DELETE|TO\s+DISK|TO\s+VOLUME|RECOMPRESS|GROUP\s+BY|WHERE)\b[\s\S]*$/i;
|
|
@@ -13,7 +12,7 @@ const TTL_ACTION = /\s+(DELETE|TO\s+DISK|TO\s+VOLUME|RECOMPRESS|GROUP\s+BY|WHERE
|
|
|
13
12
|
* a function call is left to the server.
|
|
14
13
|
*/
|
|
15
14
|
export const sqlch107 = checkOf({ id: "SQLCH107", description: "A TTL expression is built on a column that is not a Date or DateTime" }, (ctx, report) => {
|
|
16
|
-
for (const t of
|
|
15
|
+
for (const t of tablesOf(ctx)) {
|
|
17
16
|
const ttls: Array<{ where: string; expr: string }> = [];
|
|
18
17
|
if (t.ttl) for (const clause of splitTop(t.ttl)) ttls.push({ where: "table TTL", expr: clause.replace(TTL_ACTION, "") });
|
|
19
18
|
for (const c of t.columns) if (c.ttl) ttls.push({ where: `column ${c.name} TTL`, expr: c.ttl.replace(TTL_ACTION, "") });
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, splitTop, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
const MAX_VALUES = 100;
|
|
5
4
|
|
|
@@ -23,7 +22,7 @@ function allowedValues(expr: string): { column: string; count: number } | undefi
|
|
|
23
22
|
* https://clickhouse.com/docs/best-practices/select-data-types.
|
|
24
23
|
*/
|
|
25
24
|
export const sqlch111 = checkOf({ id: "SQLCH111", description: "A String column limited to a few values is not LowCardinality" }, (ctx, report) => {
|
|
26
|
-
for (const t of
|
|
25
|
+
for (const t of tablesOf(ctx)) {
|
|
27
26
|
for (const c of t.constraints ?? []) {
|
|
28
27
|
if (c.kind !== "CHECK") continue;
|
|
29
28
|
const found = allowedValues(c.expr);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, tablesOf, typeFamily } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
const FINE_FUNCTIONS =
|
|
5
4
|
/\b(toStartOfSecond|toStartOfMinute|toStartOfFiveMinutes?|toStartOfTenMinutes|toStartOfFifteenMinutes|toStartOfHour|toRelativeSecondNum|toRelativeMinuteNum|toRelativeHourNum|toUnixTimestamp|toYYYYMMDDhhmmss|toStartOfInterval)\s*\(/;
|
|
@@ -15,7 +14,7 @@ const FINE_FUNCTIONS =
|
|
|
15
14
|
* the sub-day truncation functions.
|
|
16
15
|
*/
|
|
17
16
|
export const sqlch112 = checkOf({ id: "SQLCH112", description: "A PARTITION BY expression is finer than a day" }, (ctx, report) => {
|
|
18
|
-
for (const t of
|
|
17
|
+
for (const t of tablesOf(ctx)) {
|
|
19
18
|
const expr = t.partitionBy?.trim();
|
|
20
19
|
if (!expr) continue;
|
|
21
20
|
const col = t.columns.find((c) => c.name === expr.replace(/^\(|\)$/g, "").trim());
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf, isMergeTree,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, isMergeTree, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* SQLCH113: a MergeTree table with neither ORDER BY nor PRIMARY KEY.
|
|
@@ -9,7 +8,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
9
8
|
* `ORDER BY tuple()` to have none). The server refuses the CREATE.
|
|
10
9
|
*/
|
|
11
10
|
export const sqlch113 = checkOf({ id: "SQLCH113", description: "A MergeTree table declares no sort key" }, (ctx, report) => {
|
|
12
|
-
for (const t of
|
|
11
|
+
for (const t of tablesOf(ctx)) {
|
|
13
12
|
if (!isMergeTree(t.engine) || t.orderBy || t.primaryKey) continue;
|
|
14
13
|
report({
|
|
15
14
|
severity: "error",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CODECS } from "../../generated/clickhouse";
|
|
2
|
-
import { checkOf, codecNames,
|
|
3
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
2
|
+
import { checkOf, codecNames, tablesOf } from "./clickhouse-helpers";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* SQLCH114: a column CODEC names a codec the pinned server does not have.
|
|
@@ -10,7 +9,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
10
9
|
*/
|
|
11
10
|
export const sqlch114 = checkOf({ id: "SQLCH114", description: "A column codec is not one the pinned server has" }, (ctx, report) => {
|
|
12
11
|
const known = new Set(Object.keys(CODECS).map((n) => n.toLowerCase()));
|
|
13
|
-
for (const t of
|
|
12
|
+
for (const t of tablesOf(ctx)) {
|
|
14
13
|
for (const c of t.columns) {
|
|
15
14
|
if (!c.codec) continue;
|
|
16
15
|
for (const name of codecNames(c.codec)) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CODECS } from "../../generated/clickhouse";
|
|
2
|
-
import { checkOf, codecNames,
|
|
3
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
2
|
+
import { checkOf, codecNames, tablesOf } from "./clickhouse-helpers";
|
|
4
3
|
|
|
5
4
|
const SENSITIVE = /(^|_)(password|passwd|pwd|secret|api_?key|access_?key|private_?key|token|ssn|social_?security|credit_?card|card_?number|iban)(_|$)/i;
|
|
6
5
|
|
|
@@ -17,7 +16,7 @@ const SENSITIVE = /(^|_)(password|passwd|pwd|secret|api_?key|access_?key|private
|
|
|
17
16
|
*/
|
|
18
17
|
export const sqlch115 = checkOf({ id: "SQLCH115", description: "A secret- or PII-named column carries no comment, TTL or encryption" }, (ctx, report) => {
|
|
19
18
|
const encryption = new Set(Object.entries(CODECS).filter(([, s]) => s.encryption).map(([n]) => n.toLowerCase()));
|
|
20
|
-
for (const t of
|
|
19
|
+
for (const t of tablesOf(ctx)) {
|
|
21
20
|
for (const c of t.columns) {
|
|
22
21
|
if (!SENSITIVE.test(c.name) || c.comment || c.ttl || t.ttl) continue;
|
|
23
22
|
if (c.codec && codecNames(c.codec).some((n) => encryption.has(n.toLowerCase()))) continue;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { checkOf,
|
|
2
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
1
|
+
import { checkOf, tablesOf } from "./clickhouse-helpers";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* SQLCH120: a MergeTree engine written with the legacy positional arguments.
|
|
@@ -10,7 +9,7 @@ import { clickhouseObjects } from "./sql-helpers";
|
|
|
10
9
|
* syntax is a bare `MergeTree` with ORDER BY, PARTITION BY and SETTINGS.
|
|
11
10
|
*/
|
|
12
11
|
export const sqlch120 = checkOf({ id: "SQLCH120", description: "A MergeTree engine uses the deprecated positional arguments" }, (ctx, report) => {
|
|
13
|
-
for (const t of
|
|
12
|
+
for (const t of tablesOf(ctx)) {
|
|
14
13
|
if (t.engine?.name !== "MergeTree" || (t.engine.args ?? []).length === 0) continue;
|
|
15
14
|
report({
|
|
16
15
|
severity: "warning",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CODECS, CLICKHOUSE_VERSION } from "../../generated/clickhouse";
|
|
2
|
-
import { checkOf,
|
|
3
|
-
import { clickhouseObjects } from "./sql-helpers";
|
|
2
|
+
import { checkOf, splitTop, tablesOf } from "./clickhouse-helpers";
|
|
4
3
|
|
|
5
4
|
type Param = { name: string; kind: string; optional?: boolean; range?: readonly [number, number]; values?: readonly string[] };
|
|
6
5
|
|
|
@@ -17,7 +16,7 @@ const NUMBER = /^-?\d+(\.\d+)?$/;
|
|
|
17
16
|
*/
|
|
18
17
|
export const sqlch125 = checkOf({ id: "SQLCH125", description: "A codec parameter is outside what the codec takes" }, (ctx, report) => {
|
|
19
18
|
const codecs = CODECS as Record<string, { parameters: readonly Param[] | "unknown" } | undefined>;
|
|
20
|
-
for (const t of
|
|
19
|
+
for (const t of tablesOf(ctx)) {
|
|
21
20
|
for (const c of t.columns) {
|
|
22
21
|
if (!c.codec) continue;
|
|
23
22
|
for (const part of splitTop(c.codec)) {
|
|
@@ -789,6 +789,8 @@ export function refusalDetail(changes: readonly PgChange[], name: string, declar
|
|
|
789
789
|
? `Run it as the expand-and-contract migration Op: ${ops.join(", ")} from @intentius/chant-lexicon-sql/postgres (add the new column, write both, backfill, switch readers, then drop the old).` +
|
|
790
790
|
(ops.length < changes.length ? " The other changes have no migration Op yet: make them by hand as expand and contract." : "")
|
|
791
791
|
: "No migration Op makes this change yet: make it by hand as expand and contract (add the new, write both, backfill, move readers, then drop the old).";
|
|
792
|
+
// A clause chant does not read (#3872) is refused for review: what the change takes is not known.
|
|
793
|
+
if (changes.every((c) => c.rule === "SQLPG299")) return `${name} needs review, since chant does not read the clause, so nothing was sent for it. ${lines.join(" ")}`;
|
|
792
794
|
return `${name} needs expand and contract, which no in-place statement makes, so nothing was sent for it. ${lines.join(" ")} ${instead}`;
|
|
793
795
|
}
|
|
794
796
|
|
package/src/postgres/entities.ts
CHANGED
|
@@ -58,7 +58,7 @@ import { setInterpolationFields } from "@intentius/chant/provenance";
|
|
|
58
58
|
import { isTrivia, POSTGRES_LEXICAL, SqlSyntaxError, untokenize, type Token } from "./tokens";
|
|
59
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
|
-
import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, type TemplateCtx, type TemplateDialect } from "../core/interpolation";
|
|
61
|
+
import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, unparsedClauses, type TemplateCtx, type TemplateDialect, type UnparsedClause } from "../core/interpolation";
|
|
62
62
|
import { SqlObject, isColumnRefOf, isSqlObjectOf, makeSqlEntity } from "../core/entity";
|
|
63
63
|
import type { LineageEdge } from "../core/references";
|
|
64
64
|
import { SqlLiteral, SqlTemplateError, describeValue as describe, stripUnset, templateParts } from "../core/template";
|
|
@@ -66,6 +66,7 @@ import { setTypedSlots, type ColumnsIn, type Named, type RowIn, type SlotsIn, ty
|
|
|
66
66
|
|
|
67
67
|
export { SQL_LEXICON } from "../core/entity";
|
|
68
68
|
export type { LineageEdge } from "../core/references";
|
|
69
|
+
export type { UnparsedClause } from "../core/interpolation";
|
|
69
70
|
export { SqlLiteral, SqlTemplateError } from "../core/template";
|
|
70
71
|
export { quoteIdent } from "./keywords";
|
|
71
72
|
export type { AnyColumn, Column, ColumnTraits, KeyColumn, Named, NotNullColumn, UncheckedColumn, Unnamed } from "../core/typed";
|
|
@@ -155,6 +156,11 @@ interface CommonProps {
|
|
|
155
156
|
ddl: string;
|
|
156
157
|
/** The template's raw parts, as written, for a source round trip. */
|
|
157
158
|
source: { strings: string[] };
|
|
159
|
+
/**
|
|
160
|
+
* Clauses chant's parser does not know (#3872), kept in `ddl` as written
|
|
161
|
+
* and left to the catalog checks and the server. Absent when there are none.
|
|
162
|
+
*/
|
|
163
|
+
unparsed?: UnparsedClause[];
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
export interface SchemaProps extends Omit<CommonProps, "schema"> {
|
|
@@ -1182,7 +1188,9 @@ function build(tag: PostgresTag, strings: TemplateStringsArray | readonly string
|
|
|
1182
1188
|
const source = { strings: rawParts };
|
|
1183
1189
|
const dependsOn = [...new Set([...values, ...(joined?.refs ?? [])].filter((v) => isPostgresObject(v) || isColumnRef(v)))];
|
|
1184
1190
|
const slots: TypedSlot[] = [];
|
|
1191
|
+
const unparsed = unparsedClauses(ctx, nodes.flatMap((n) => ("unknown" in n && n.unknown ? n.unknown : [])));
|
|
1185
1192
|
const make = <T extends PostgresObject>(type: PostgresEntityType, q: { schema?: string; name: string }, props: object, columnNames?: readonly string[]): T => {
|
|
1193
|
+
if (unparsed) (props as { unparsed?: UnparsedClause[] }).unparsed = unparsed;
|
|
1186
1194
|
const entity = makeSqlEntity(PostgresObject.prototype as PostgresObject, type, sqlNameOf(q), props, columnNames, dependsOn) as T;
|
|
1187
1195
|
const fed = ctx.fed.map((paths) => [...paths].sort());
|
|
1188
1196
|
if (!joined) setInterpolationFields(entity, fed);
|
package/src/postgres/parser.ts
CHANGED
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
* `src/backend/parser/gram.y` at `REL_18_6` where a page is silent.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import { kw, SqlCursor, type Span } from "../core/cursor";
|
|
38
|
+
import { kw, SqlCursor, type Span, type UnknownClause } from "../core/cursor";
|
|
39
39
|
import { SqlSyntaxError, type Token } from "../core/tokens";
|
|
40
40
|
import { keywordCategory } from "./keywords";
|
|
41
41
|
|
|
42
|
-
export type { Span } from "../core/cursor";
|
|
42
|
+
export type { Span, UnknownClause } from "../core/cursor";
|
|
43
43
|
export { SqlSyntaxError };
|
|
44
44
|
|
|
45
45
|
/** An identifier as Postgres reads it: an unquoted name folds to lower case, a quoted one is exact. */
|
|
@@ -116,6 +116,8 @@ export interface ColumnNode {
|
|
|
116
116
|
|
|
117
117
|
export interface TableNode {
|
|
118
118
|
statement: "table";
|
|
119
|
+
/** Clauses the parser does not know (#3872), kept in the DDL as written; absent when there are none. */
|
|
120
|
+
unknown?: UnknownClause[];
|
|
119
121
|
persistence?: "temporary" | "unlogged";
|
|
120
122
|
ifNotExists: boolean;
|
|
121
123
|
name: NameNode;
|
|
@@ -141,6 +143,8 @@ export interface IndexElement {
|
|
|
141
143
|
|
|
142
144
|
export interface IndexNode {
|
|
143
145
|
statement: "index";
|
|
146
|
+
/** Clauses the parser does not know (#3872), kept in the DDL as written; absent when there are none. */
|
|
147
|
+
unknown?: UnknownClause[];
|
|
144
148
|
unique: boolean;
|
|
145
149
|
concurrently: boolean;
|
|
146
150
|
ifNotExists: boolean;
|
|
@@ -158,6 +162,8 @@ export interface IndexNode {
|
|
|
158
162
|
|
|
159
163
|
export interface ViewNode {
|
|
160
164
|
statement: "view";
|
|
165
|
+
/** Clauses the parser does not know (#3872), kept in the DDL as written; absent when there are none. */
|
|
166
|
+
unknown?: UnknownClause[];
|
|
161
167
|
materialized: boolean;
|
|
162
168
|
orReplace: boolean;
|
|
163
169
|
temporary: boolean;
|
|
@@ -176,6 +182,8 @@ export interface ViewNode {
|
|
|
176
182
|
|
|
177
183
|
export interface SequenceNode {
|
|
178
184
|
statement: "sequence";
|
|
185
|
+
/** Clauses the parser does not know (#3872), kept in the DDL as written; absent when there are none. */
|
|
186
|
+
unknown?: UnknownClause[];
|
|
179
187
|
persistence?: "temporary" | "unlogged";
|
|
180
188
|
ifNotExists: boolean;
|
|
181
189
|
name: NameNode;
|
|
@@ -366,6 +374,18 @@ const COLUMN_CLAUSE = [
|
|
|
366
374
|
"ENFORCED",
|
|
367
375
|
];
|
|
368
376
|
|
|
377
|
+
/** A column's clause words, and its constraints' attributes: an unknown clause stops at the next (#3872). */
|
|
378
|
+
const COLUMN_WORDS = [...COLUMN_CLAUSE, "REFERENCES", "ON", "MATCH", "NO", "WITH", "INCLUDE", "USING"];
|
|
379
|
+
|
|
380
|
+
/** The clauses after a table's column list. */
|
|
381
|
+
const TABLE_WORDS = ["INHERITS", "PARTITION", "USING", "WITH", "WITHOUT", "ON", "TABLESPACE", "FOR", "DEFAULT"];
|
|
382
|
+
|
|
383
|
+
/** The clauses after an index's elements. */
|
|
384
|
+
const INDEX_WORDS = ["INCLUDE", "NULLS", "WITH", "TABLESPACE", "WHERE"];
|
|
385
|
+
|
|
386
|
+
/** The clauses before a view's AS. */
|
|
387
|
+
const VIEW_WORDS = ["USING", "WITH", "TABLESPACE", "AS"];
|
|
388
|
+
|
|
369
389
|
/** `LIKE source_table [ like_option ... ]`'s option words. */
|
|
370
390
|
const LIKE_OPTIONS = ["COMMENTS", "COMPRESSION", "CONSTRAINTS", "DEFAULTS", "GENERATED", "IDENTITY", "INDEXES", "STATISTICS", "STORAGE", "ALL"];
|
|
371
391
|
|
|
@@ -653,7 +673,7 @@ class PgParser extends SqlCursor {
|
|
|
653
673
|
if (this.accept("COMPRESSION")) col.compression = this.expr(COLUMN_CLAUSE);
|
|
654
674
|
else if (this.accept("STORAGE")) col.storage = this.expr(COLUMN_CLAUSE);
|
|
655
675
|
else if (this.accept("COLLATE")) col.collate = this.expr(COLUMN_CLAUSE);
|
|
656
|
-
else if (!this.columnConstraint(col)) break;
|
|
676
|
+
else if (!this.columnConstraint(col) && !this.unknownClause(COLUMN_WORDS, { inList: true, column: text || undefined })) break;
|
|
657
677
|
}
|
|
658
678
|
const after = this.peek();
|
|
659
679
|
if (after && !(after.kind === "punct" && (after.text === "," || after.text === ")"))) this.fail("expected a column constraint, ',' or ')'");
|
|
@@ -991,7 +1011,7 @@ class PgParser extends SqlCursor {
|
|
|
991
1011
|
if (!(this.acceptSeq("PRESERVE", "ROWS") || this.acceptSeq("DELETE", "ROWS") || this.accept("DROP"))) this.fail("expected PRESERVE ROWS, DELETE ROWS or DROP");
|
|
992
1012
|
node.onCommit = this.span(from, []);
|
|
993
1013
|
} else if (this.accept("TABLESPACE")) node.tablespace = this.nameTok("a tablespace").span;
|
|
994
|
-
else break;
|
|
1014
|
+
else if (!this.unknownClause(TABLE_WORDS)) break;
|
|
995
1015
|
}
|
|
996
1016
|
return node;
|
|
997
1017
|
}
|
|
@@ -1061,7 +1081,7 @@ class PgParser extends SqlCursor {
|
|
|
1061
1081
|
node.with = this.reloptions();
|
|
1062
1082
|
} else if (this.accept("TABLESPACE")) node.tablespace = this.nameTok("a tablespace").span;
|
|
1063
1083
|
else if (this.accept("WHERE")) node.where = this.checkedExpr([], false);
|
|
1064
|
-
else return node;
|
|
1084
|
+
else if (!this.unknownClause(INDEX_WORDS)) return node;
|
|
1065
1085
|
}
|
|
1066
1086
|
}
|
|
1067
1087
|
|
|
@@ -1085,7 +1105,7 @@ class PgParser extends SqlCursor {
|
|
|
1085
1105
|
this.p++;
|
|
1086
1106
|
node.with = this.reloptions();
|
|
1087
1107
|
} else if (materialized && this.accept("TABLESPACE")) node.tablespace = this.nameTok("a tablespace").span;
|
|
1088
|
-
else break;
|
|
1108
|
+
else if (!this.unknownClause(VIEW_WORDS)) break;
|
|
1089
1109
|
}
|
|
1090
1110
|
this.expect("AS");
|
|
1091
1111
|
const tail = materialized
|
|
@@ -1131,7 +1151,7 @@ class PgParser extends SqlCursor {
|
|
|
1131
1151
|
else if (this.acceptSeq("OWNED", "BY")) {
|
|
1132
1152
|
if (this.accept("NONE")) node.options.push({ option: "OWNED BY", value: this.span(this.idx(-1), []) });
|
|
1133
1153
|
else node.options.push({ option: "OWNED BY", value: this.qualifiedName("a column").span });
|
|
1134
|
-
} else return node;
|
|
1154
|
+
} else if (!this.unknownClause(STOP)) return node;
|
|
1135
1155
|
}
|
|
1136
1156
|
}
|
|
1137
1157
|
|
|
@@ -1575,7 +1595,10 @@ class PgParser extends SqlCursor {
|
|
|
1575
1595
|
const out: StatementNode[] = [];
|
|
1576
1596
|
while (this.peek() !== undefined) {
|
|
1577
1597
|
if (this.acceptPunct(";")) continue;
|
|
1578
|
-
|
|
1598
|
+
const before = this.unknownClauses.length;
|
|
1599
|
+
const node = this.parse();
|
|
1600
|
+
if (this.unknownClauses.length > before) (node as { unknown?: UnknownClause[] }).unknown = this.unknownClauses.slice(before);
|
|
1601
|
+
out.push(node);
|
|
1579
1602
|
if (this.peek() !== undefined && !this.isPunct(";")) this.fail(`unexpected token after the ${describe(out[out.length - 1]!)}`);
|
|
1580
1603
|
}
|
|
1581
1604
|
if (out.length === 0) this.fail("expected a statement");
|
|
@@ -197,7 +197,25 @@ function diffConstraints(key: string, before: PgDiffObject, after: PgDiffObject,
|
|
|
197
197
|
const same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);
|
|
198
198
|
const show = (v: unknown): string | undefined => (v === undefined ? undefined : typeof v === "string" ? v : JSON.stringify(v));
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The clauses chant does not read (#3872), compared as text: one the
|
|
202
|
+
* declaration has and the server does not, or has differently, is a change
|
|
203
|
+
* to review (SQLPG299); one only the server prints is a hint, since a
|
|
204
|
+
* default a newer server prints cannot be told from a clause removed.
|
|
205
|
+
*/
|
|
206
|
+
function diffUnparsed(key: string, before: PgDiffObject, after: PgDiffObject, out: PgChange[], hints: string[]): void {
|
|
207
|
+
const b = (before.fields.unparsed as Record<string, string> | undefined) ?? {};
|
|
208
|
+
const a = (after.fields.unparsed as Record<string, string> | undefined) ?? {};
|
|
209
|
+
for (const clause of Object.keys(a)) {
|
|
210
|
+
if (b[clause] !== a[clause]) out.push(change(key, `unparsed.${clause}`, "SQLPG299", b[clause], a[clause], { note: "chant does not read this clause; review the change" }));
|
|
211
|
+
}
|
|
212
|
+
for (const clause of Object.keys(b)) {
|
|
213
|
+
if (!(clause in a)) hints.push(`${key}: the current definition has ${clause} (${b[clause]}), a clause chant does not read and the declaration does not have; it is not compared`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
200
217
|
function diffObject(key: string, before: PgDiffObject, after: PgDiffObject, out: PgChange[], hints: string[], major: number): void {
|
|
218
|
+
diffUnparsed(key, before, after, out, hints);
|
|
201
219
|
if (before.kind !== after.kind) {
|
|
202
220
|
out.push(change(key, "kind", "SQLPG268", before.kind, after.kind));
|
|
203
221
|
return;
|
|
@@ -797,6 +797,18 @@ export function canonicalPgObject(entityType: string, props: Record<string, unkn
|
|
|
797
797
|
break;
|
|
798
798
|
}
|
|
799
799
|
}
|
|
800
|
+
// Clauses the parser does not know (#3872): compared as text, by clause.
|
|
801
|
+
const unparsed = props.unparsed as Array<{ clause: string; text: string; column?: string }> | undefined;
|
|
802
|
+
if (unparsed && unparsed.length > 0) {
|
|
803
|
+
const out: Record<string, string> = {};
|
|
804
|
+
for (const u of unparsed) {
|
|
805
|
+
const base = u.column !== undefined ? `columns.${u.column}.${u.clause}` : u.clause;
|
|
806
|
+
let key = base;
|
|
807
|
+
for (let n = 2; key in out; n++) key = `${base}#${n}`;
|
|
808
|
+
out[key] = canonicalExpr(u.text) ?? u.text;
|
|
809
|
+
}
|
|
810
|
+
fields.unparsed = out;
|
|
811
|
+
}
|
|
800
812
|
return { ...base, fields: Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined && v !== false)), columns, constraints };
|
|
801
813
|
}
|
|
802
814
|
|
|
@@ -350,6 +350,13 @@ export const PG_CLASSIFIER_RULES = {
|
|
|
350
350
|
"ALTER DEFAULT PRIVILEGES changes what objects created afterwards are given; objects that exist keep their privileges.",
|
|
351
351
|
`${DOCS}/sql-alterdefaultprivileges.html`,
|
|
352
352
|
),
|
|
353
|
+
SQLPG299: rule(
|
|
354
|
+
"SQLPG299",
|
|
355
|
+
"expand",
|
|
356
|
+
"Change a clause chant does not read",
|
|
357
|
+
"chant's parser does not know this clause, so it cannot tell which lock the change takes or whether an in-place statement makes it; the plan refuses it rather than guess. Review it: make it by hand with the ALTER the server documents for the clause, or as expand and contract.",
|
|
358
|
+
`${DOCS}/sql-altertable.html`,
|
|
359
|
+
),
|
|
353
360
|
SQLPG270: rule("SQLPG270", "drop", "Drop an object", "The object and, for a table, a materialized view or a sequence, its data are gone.", `${DOCS}/sql-droptable.html`),
|
|
354
361
|
} as const satisfies Record<string, PgClassifierRule>;
|
|
355
362
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"release": "REL_18_6",
|
|
3
|
+
"unknownClauses": [
|
|
4
|
+
"USING"
|
|
5
|
+
],
|
|
6
|
+
"failures": [
|
|
7
|
+
"expected AS at 'USING'",
|
|
8
|
+
"expected IDENTITY (a generated column is GENERATED ALWAYS AS) at '('",
|
|
9
|
+
"expected NOT NULL, NULL or CHECK at 'REFERENCES'",
|
|
10
|
+
"expected NOT NULL, NULL or CHECK at 'generated'",
|
|
11
|
+
"expected RANGE or LIST or HASH at 'MAGIC'",
|
|
12
|
+
"expected a column constraint, ',' or ')' at 'NOT'",
|
|
13
|
+
"expected a column name ('with' is a reserved key word; quote it to use it as a name) at 'with'",
|
|
14
|
+
"expected a column type at ')'",
|
|
15
|
+
"expected a column type at ','",
|
|
16
|
+
"expected a partition bound value at ')'",
|
|
17
|
+
"expected a table name at '\\'",
|
|
18
|
+
"expected an expression at ')'",
|
|
19
|
+
"expected an index name ('ON' is a reserved key word; quote it to use it as a name) at 'ON'",
|
|
20
|
+
"expected the column list (CREATE TABLE ... AS is not declared here) at 'AS'",
|
|
21
|
+
"expected the column list (CREATE TABLE ... AS is not declared here) at 'TABLESPACE'",
|
|
22
|
+
"expected the column list (CREATE TABLE ... AS is not declared here) at 'USING'",
|
|
23
|
+
"expected the column list (CREATE TABLE ... AS is not declared here) at 'WITH'",
|
|
24
|
+
"expected the column list (CREATE TABLE ... AS is not declared here) at 'as'",
|
|
25
|
+
"expected the end of the default (a default cannot hold this operator unparenthesized) at 'IN'",
|
|
26
|
+
"schema elements inside CREATE SCHEMA are not declared here; declare each object on its own at 'CREATE'",
|
|
27
|
+
"schema elements inside CREATE SCHEMA are not declared here; declare each object on its own at 'create'",
|
|
28
|
+
"unexpected end of statement",
|
|
29
|
+
"unexpected token after the domain definition at 'ENFORCED'",
|
|
30
|
+
"unexpected token after the domain definition at 'NOT'",
|
|
31
|
+
"unexpected token after the domain definition at 'PRIMARY'",
|
|
32
|
+
"unexpected token after the domain definition at 'deferrable'",
|
|
33
|
+
"unexpected token after the domain definition at 'no'",
|
|
34
|
+
"unexpected token after the domain definition at 'unique'",
|
|
35
|
+
"unexpected token after the table definition at 'WITH'",
|
|
36
|
+
"unexpected token after the table definition at '\\'"
|
|
37
|
+
]
|
|
38
|
+
}
|
package/src/postgres.ts
CHANGED
|
@@ -105,3 +105,15 @@ export {
|
|
|
105
105
|
} from "./postgres/migrate/op";
|
|
106
106
|
export { POSTGRES_RECEIPTS_TABLE } from "./postgres/migrate/receipts";
|
|
107
107
|
export * from "./composites/postgres";
|
|
108
|
+
|
|
109
|
+
// The build's Postgres objects as typed values, for a project's own
|
|
110
|
+
// `lint.policies` (#3870); also at `@intentius/chant-lexicon-sql/read`.
|
|
111
|
+
export {
|
|
112
|
+
readPostgres,
|
|
113
|
+
canonicalPostgresType,
|
|
114
|
+
type PostgresRead,
|
|
115
|
+
type PostgresTableInfo,
|
|
116
|
+
type PostgresColumnInfo,
|
|
117
|
+
type PostgresIndexInfo,
|
|
118
|
+
type PostgresViewInfo,
|
|
119
|
+
} from "./read/postgres";
|