@descryy/adapter-sql 0.1.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/adapter.d.ts +33 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +231 -0
- package/dist/adapter.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +39 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +166 -0
- package/dist/parse.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SQL migration adapter.
|
|
3
|
+
*
|
|
4
|
+
* The cheapest capability in the whole plan: it reaches **R3 with no language
|
|
5
|
+
* adapter at all**, because a schema is not inferred from code — it is declared.
|
|
6
|
+
* `discount_code TEXT NULL` states its own name, its own type and its own
|
|
7
|
+
* nullability, and there is nothing left to resolve. That is why golden pattern
|
|
8
|
+
* 10 is `requiredAtResolution: 0` and identical across every ORM: the migration
|
|
9
|
+
* is the same file whatever wrote it.
|
|
10
|
+
*
|
|
11
|
+
* It is a `LanguageAdapter` because it is a per-format reader with a capability
|
|
12
|
+
* matrix, not because SQL is a programming language in the sense the rest of the
|
|
13
|
+
* plan means. `language: "sql"` describes the artefact it read — DEC-026.
|
|
14
|
+
*/
|
|
15
|
+
import { type LanguageAdapter, type ResolutionLevel } from "@descryy/ir";
|
|
16
|
+
export declare const ADAPTER_ID = "adapter-sql";
|
|
17
|
+
export declare const ADAPTER_VERSION = "0.1.0";
|
|
18
|
+
export declare const DEFAULT_SQL_GLOBS: readonly ["**/*.sql"];
|
|
19
|
+
/**
|
|
20
|
+
* The language on every node this adapter emits.
|
|
21
|
+
*
|
|
22
|
+
* Part of the node id since DEC-054, which makes it a shared vocabulary rather
|
|
23
|
+
* than a label: a second SQL reader that wrote `postgres` would emit a different
|
|
24
|
+
* node for the same table and the two would never merge. §11B.4's point stands —
|
|
25
|
+
* this describes the artefact read, not a programming language (DEC-026).
|
|
26
|
+
*/
|
|
27
|
+
export declare const LANGUAGE = "sql";
|
|
28
|
+
export interface SqlAdapterOptions {
|
|
29
|
+
readonly maxResolution?: ResolutionLevel;
|
|
30
|
+
readonly parseGlobs?: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export declare function createSqlAdapter(options?: SqlAdapterOptions): LanguageAdapter;
|
|
33
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAUL,KAAK,eAAe,EAIpB,KAAK,eAAe,EAGrB,MAAM,aAAa,CAAC;AAUrB,eAAO,MAAM,UAAU,gBAAgB,CAAC;AACxC,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,iBAAiB,uBAAwB,CAAC;AAEvD;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAI9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,eAAe,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,eAAe,CA4HjF"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SQL migration adapter.
|
|
3
|
+
*
|
|
4
|
+
* The cheapest capability in the whole plan: it reaches **R3 with no language
|
|
5
|
+
* adapter at all**, because a schema is not inferred from code — it is declared.
|
|
6
|
+
* `discount_code TEXT NULL` states its own name, its own type and its own
|
|
7
|
+
* nullability, and there is nothing left to resolve. That is why golden pattern
|
|
8
|
+
* 10 is `requiredAtResolution: 0` and identical across every ORM: the migration
|
|
9
|
+
* is the same file whatever wrote it.
|
|
10
|
+
*
|
|
11
|
+
* It is a `LanguageAdapter` because it is a per-format reader with a capability
|
|
12
|
+
* matrix, not because SQL is a programming language in the sense the rest of the
|
|
13
|
+
* plan means. `language: "sql"` describes the artefact it read — DEC-026.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { nodeId, tableQsp, } from "@descryy/ir";
|
|
18
|
+
import { discover, isolateFile } from "@descryy/adapter-common";
|
|
19
|
+
import { parseAddColumn, parseCreateTable, statements, } from "./parse.js";
|
|
20
|
+
export const ADAPTER_ID = "adapter-sql";
|
|
21
|
+
export const ADAPTER_VERSION = "0.1.0";
|
|
22
|
+
export const DEFAULT_SQL_GLOBS = ["**/*.sql"];
|
|
23
|
+
/**
|
|
24
|
+
* The language on every node this adapter emits.
|
|
25
|
+
*
|
|
26
|
+
* Part of the node id since DEC-054, which makes it a shared vocabulary rather
|
|
27
|
+
* than a label: a second SQL reader that wrote `postgres` would emit a different
|
|
28
|
+
* node for the same table and the two would never merge. §11B.4's point stands —
|
|
29
|
+
* this describes the artefact read, not a programming language (DEC-026).
|
|
30
|
+
*/
|
|
31
|
+
export const LANGUAGE = "sql";
|
|
32
|
+
const PRODUCED_BY = `${ADAPTER_ID}@${ADAPTER_VERSION}`;
|
|
33
|
+
export function createSqlAdapter(options = {}) {
|
|
34
|
+
const ceiling = options.maxResolution ?? 3;
|
|
35
|
+
const globs = options.parseGlobs ?? DEFAULT_SQL_GLOBS;
|
|
36
|
+
let root;
|
|
37
|
+
const read = (repoRoot, file) => {
|
|
38
|
+
let text = "";
|
|
39
|
+
try {
|
|
40
|
+
text = readFileSync(join(repoRoot.absolutePath, file), "utf8");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
file,
|
|
45
|
+
tables: [],
|
|
46
|
+
skipped: [],
|
|
47
|
+
readError: error instanceof Error ? error.message : String(error),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Isolated for the same reason every other adapter's walk is: the read
|
|
51
|
+
// succeeded, so a throw in here is not `unreadable` and cannot be caught by
|
|
52
|
+
// the guard above, and one hostile migration should not cost the schema of
|
|
53
|
+
// every other file in the repository.
|
|
54
|
+
const walked = isolateFile(file, () => {
|
|
55
|
+
const tables = [];
|
|
56
|
+
const skipped = [];
|
|
57
|
+
for (const statement of statements(text)) {
|
|
58
|
+
const parsed = parseCreateTable(statement) ?? parseAddColumn(statement);
|
|
59
|
+
// A statement this reader does not understand is recorded, never guessed
|
|
60
|
+
// at. That is the denominator DEC-016 exists to keep.
|
|
61
|
+
if (parsed === undefined)
|
|
62
|
+
skipped.push({ text: statement.text.slice(0, 120), line: statement.line });
|
|
63
|
+
else
|
|
64
|
+
tables.push(parsed);
|
|
65
|
+
}
|
|
66
|
+
return { tables, skipped };
|
|
67
|
+
});
|
|
68
|
+
if (walked.failure !== undefined) {
|
|
69
|
+
return { file, tables: [], skipped: [], walkError: walked.failure.detail };
|
|
70
|
+
}
|
|
71
|
+
return { file, tables: walked.value.tables, skipped: walked.value.skipped };
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
id: ADAPTER_ID,
|
|
75
|
+
version: ADAPTER_VERSION,
|
|
76
|
+
async detect(repoRoot) {
|
|
77
|
+
const files = discover(repoRoot.absolutePath, globs);
|
|
78
|
+
if (files.length === 0)
|
|
79
|
+
return { detected: false, roots: [], evidence: [] };
|
|
80
|
+
return {
|
|
81
|
+
detected: true,
|
|
82
|
+
roots: [...new Set(files.map((file) => file.split("/")[0] ?? "."))].sort(),
|
|
83
|
+
evidence: [`${files.length} SQL file(s)`],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
capabilities() {
|
|
87
|
+
return {
|
|
88
|
+
maxResolution: ceiling,
|
|
89
|
+
nodeTypes: ["DATABASE_TABLE", "DATABASE_COLUMN"],
|
|
90
|
+
// A migration declares structure, not behaviour. There is no call graph
|
|
91
|
+
// in a schema, and claiming one would be an invented capability.
|
|
92
|
+
edgeTypes: [],
|
|
93
|
+
hasCallHierarchy: false,
|
|
94
|
+
hasTypeHierarchy: false,
|
|
95
|
+
frameworkExtractors: [],
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
async prepare(ctx) {
|
|
99
|
+
root = ctx.root;
|
|
100
|
+
// Nothing to prepare and nothing that can fail: no dependency resolution,
|
|
101
|
+
// no type checker, no index. The level is whatever the caller capped it to.
|
|
102
|
+
return ceiling;
|
|
103
|
+
},
|
|
104
|
+
async parseFiles(files) {
|
|
105
|
+
const active = root;
|
|
106
|
+
return files.map((file) => ({
|
|
107
|
+
file,
|
|
108
|
+
tree: active === undefined ? { file: file.path, tables: [], skipped: [] } : read(active, file.path),
|
|
109
|
+
}));
|
|
110
|
+
},
|
|
111
|
+
extractNodes(units) {
|
|
112
|
+
return buildNodes(units.map((unit) => unit.tree), root, ceiling).nodes;
|
|
113
|
+
},
|
|
114
|
+
extractEdges() {
|
|
115
|
+
return [];
|
|
116
|
+
},
|
|
117
|
+
async emit(ctx) {
|
|
118
|
+
const reached = await this.prepare(ctx);
|
|
119
|
+
const files = discover(ctx.root.absolutePath, globs);
|
|
120
|
+
const units = files.map((file) => read(ctx.root, file));
|
|
121
|
+
const { nodes, unresolved } = buildNodes(units, ctx.root, reached);
|
|
122
|
+
const skippedFiles = [
|
|
123
|
+
...units
|
|
124
|
+
.filter((unit) => unit.readError !== undefined)
|
|
125
|
+
.map((unit) => ({ file: unit.file, reason: "unreadable", detail: unit.readError })),
|
|
126
|
+
// `other`: the file was read, and what failed is our walk of its
|
|
127
|
+
// statements. The detail carries the real error.
|
|
128
|
+
...units
|
|
129
|
+
.filter((unit) => unit.walkError !== undefined)
|
|
130
|
+
.map((unit) => ({ file: unit.file, reason: "other", detail: unit.walkError })),
|
|
131
|
+
];
|
|
132
|
+
const skippedSet = new Set(skippedFiles.map((f) => f.file));
|
|
133
|
+
return {
|
|
134
|
+
repo: ctx.root.repo,
|
|
135
|
+
...(ctx.root.workspace === undefined ? {} : { workspace: ctx.root.workspace }),
|
|
136
|
+
commitSha: ctx.root.commitSha,
|
|
137
|
+
producedBy: PRODUCED_BY,
|
|
138
|
+
sourceFiles: files.filter((f) => !skippedSet.has(f)),
|
|
139
|
+
skippedFiles,
|
|
140
|
+
reachedResolution: reached,
|
|
141
|
+
nodes,
|
|
142
|
+
edges: [],
|
|
143
|
+
unresolved,
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
async dispose() {
|
|
147
|
+
root = undefined;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function buildNodes(units, repoRoot, reached) {
|
|
152
|
+
const nodes = new Map();
|
|
153
|
+
const unresolved = [];
|
|
154
|
+
if (repoRoot === undefined)
|
|
155
|
+
return { nodes: [], unresolved };
|
|
156
|
+
// DATABASE_TABLE and DATABASE_COLUMN are workspace-scoped (DEC-054), and this
|
|
157
|
+
// is where that pays: the migration lives in one repository and the services
|
|
158
|
+
// that read the column live in others. Repo-scoped, they never meet.
|
|
159
|
+
const scope = { repo: repoRoot.repo, workspace: repoRoot.workspace };
|
|
160
|
+
for (const unit of units) {
|
|
161
|
+
for (const table of unit.tables) {
|
|
162
|
+
// Hashed with `language: null`, not `LANGUAGE` — DEC-240 gave a second
|
|
163
|
+
// producer (`adapter-python`'s own ORM-mapped tables, via
|
|
164
|
+
// `databaseTableNodesFromModel` in `@descryy/ir`) this same
|
|
165
|
+
// `nodeId`/`tableQsp` pair, and a real table minted with two different
|
|
166
|
+
// languages in its hash would be two different ids for one table: the
|
|
167
|
+
// exact silent-join failure DEC-055 already found once. The node's own
|
|
168
|
+
// `language` field below is also `null`, not `LANGUAGE`: the graph
|
|
169
|
+
// builder's collision detector (`identityFingerprint`) compares
|
|
170
|
+
// `[type, language]` for any two nodes sharing an id, so a real
|
|
171
|
+
// per-producer language on this field would flag DEC-240's own intended
|
|
172
|
+
// cross-producer join as a collision and silently discard one side —
|
|
173
|
+
// exactly the corruption this field being language-blind exists to
|
|
174
|
+
// avoid. Per-producer provenance is still recoverable through
|
|
175
|
+
// `corroboratedBy` on the merged node, same as every other type this
|
|
176
|
+
// project already merges across producers.
|
|
177
|
+
const tableId = nodeId(scope, "DATABASE_TABLE", tableQsp(table.schema, table.table), null);
|
|
178
|
+
// Later migrations touch tables earlier ones created; the first
|
|
179
|
+
// definition wins so a table's identity does not move with each ALTER.
|
|
180
|
+
if (!nodes.has(tableId)) {
|
|
181
|
+
nodes.set(tableId, {
|
|
182
|
+
id: tableId,
|
|
183
|
+
type: "DATABASE_TABLE",
|
|
184
|
+
name: table.table,
|
|
185
|
+
file: unit.file,
|
|
186
|
+
range: { startLine: table.line, endLine: table.line },
|
|
187
|
+
language: null,
|
|
188
|
+
producedBy: PRODUCED_BY,
|
|
189
|
+
resolution: reached,
|
|
190
|
+
attrs: { name: table.table, schema: table.schema },
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
for (const column of table.columns) {
|
|
194
|
+
// Same reasoning as `tableId` above — language-blind hash and node field.
|
|
195
|
+
const columnId = nodeId(scope, "DATABASE_COLUMN", tableQsp(table.schema, table.table, column.name), null);
|
|
196
|
+
nodes.set(columnId, {
|
|
197
|
+
id: columnId,
|
|
198
|
+
type: "DATABASE_COLUMN",
|
|
199
|
+
name: `${table.table}.${column.name}`,
|
|
200
|
+
file: unit.file,
|
|
201
|
+
range: { startLine: table.line, endLine: table.line },
|
|
202
|
+
language: null,
|
|
203
|
+
producedBy: PRODUCED_BY,
|
|
204
|
+
resolution: reached,
|
|
205
|
+
attrs: { name: column.name, nullable: column.nullable, dataType: column.type },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Anchored to a table this file actually defined. An `UnresolvedRef` names a
|
|
210
|
+
// node the graph contains, so a synthesised id for a node that was never
|
|
211
|
+
// emitted would be a dangling reference dressed up as a disclosure. A file
|
|
212
|
+
// whose every statement was unrecognised has nothing to anchor to and is
|
|
213
|
+
// therefore silent — a real limit of the ledger, not a bug in it.
|
|
214
|
+
const anchor = unit.tables[0];
|
|
215
|
+
for (const statement of anchor === undefined ? [] : unit.skipped) {
|
|
216
|
+
unresolved.push({
|
|
217
|
+
// `null`, matching `tableId` above — this has to hash to the same id
|
|
218
|
+
// as the table node it anchors to, or the reference never resolves.
|
|
219
|
+
fromNodeId: nodeId(scope, "DATABASE_TABLE", tableQsp(anchor.schema, anchor.table), null),
|
|
220
|
+
edgeType: "WRITES",
|
|
221
|
+
rawTarget: statement.text,
|
|
222
|
+
file: unit.file,
|
|
223
|
+
line: statement.line,
|
|
224
|
+
producedBy: PRODUCED_BY,
|
|
225
|
+
reason: "this reader recognises CREATE TABLE and ALTER TABLE … ADD COLUMN, and nothing else",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { nodes: [...nodes.values()], unresolved };
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,MAAM,EACN,QAAQ,GAeT,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,GAEX,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,UAAU,CAAU,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC;AAE9B,MAAM,WAAW,GAAG,GAAG,UAAU,IAAI,eAAe,EAAE,CAAC;AA0BvD,MAAM,UAAU,gBAAgB,CAAC,UAA6B,EAAE;IAC9D,MAAM,OAAO,GAAoB,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACtD,IAAI,IAA0B,CAAC;IAE/B,MAAM,IAAI,GAAG,CAAC,QAAkB,EAAE,IAAY,EAAQ,EAAE;QACtD,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,IAAI;gBACJ,MAAM,EAAE,EAAE;gBACV,OAAO,EAAE,EAAE;gBACX,SAAS,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAClE,CAAC;QACJ,CAAC;QACD,uEAAuE;QACvE,4EAA4E;QAC5E,2EAA2E;QAC3E,sCAAsC;QACtC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE;YACpC,MAAM,MAAM,GAAsB,EAAE,CAAC;YACrC,MAAM,OAAO,GAAqC,EAAE,CAAC;YACrD,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;gBACxE,yEAAyE;gBACzE,sDAAsD;gBACtD,IAAI,MAAM,KAAK,SAAS;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;;oBAChG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3B,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9E,CAAC,CAAC;IAEF,OAAO;QACL,EAAE,EAAE,UAAU;QACd,OAAO,EAAE,eAAe;QAExB,KAAK,CAAC,MAAM,CAAC,QAAkB;YAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC5E,OAAO;gBACL,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBAC1E,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,cAAc,CAAC;aAC1C,CAAC;QACJ,CAAC;QAED,YAAY;YACV,OAAO;gBACL,aAAa,EAAE,OAAO;gBACtB,SAAS,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;gBAChD,wEAAwE;gBACxE,iEAAiE;gBACjE,SAAS,EAAE,EAAE;gBACb,gBAAgB,EAAE,KAAK;gBACvB,gBAAgB,EAAE,KAAK;gBACvB,mBAAmB,EAAE,EAAE;aACxB,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,GAAoB;YAChC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;YAChB,0EAA0E;YAC1E,4EAA4E;YAC5E,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,KAAyB;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC;YACpB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1B,IAAI;gBACJ,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;aACpG,CAAC,CAAC,CAAC;QACN,CAAC;QAED,YAAY,CAAC,KAA4B;YACvC,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAY,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC;QACjF,CAAC;QAED,YAAY;YACV,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,GAAoB;YAC7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACrD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;YACxD,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACnE,MAAM,YAAY,GAAkB;gBAClC,GAAG,KAAK;qBACL,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC;qBAC9C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAqB,EAAE,MAAM,EAAE,IAAI,CAAC,SAAU,EAAE,CAAC,CAAC;gBAC/F,iEAAiE;gBACjE,iDAAiD;gBACjD,GAAG,KAAK;qBACL,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC;qBAC9C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAgB,EAAE,MAAM,EAAE,IAAI,CAAC,SAAU,EAAE,CAAC,CAAC;aAC3F,CAAC;YACF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAE5D,OAAO;gBACL,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI;gBACnB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC9E,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS;gBAC7B,UAAU,EAAE,WAAW;gBACvB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACpD,YAAY;gBACZ,iBAAiB,EAAE,OAAO;gBAC1B,KAAK;gBACL,KAAK,EAAE,EAAE;gBACT,UAAU;aACX,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,OAAO;YACX,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,KAAsB,EACtB,QAA8B,EAC9B,OAAwB;IAExB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,UAAU,GAAoB,EAAE,CAAC;IACvC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC;IAE7D,8EAA8E;IAC9E,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,KAAK,GAAkB,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;IAEpF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,uEAAuE;YACvE,0DAA0D;YAC1D,4DAA4D;YAC5D,uEAAuE;YACvE,sEAAsE;YACtE,uEAAuE;YACvE,mEAAmE;YACnE,gEAAgE;YAChE,gEAAgE;YAChE,wEAAwE;YACxE,qEAAqE;YACrE,mEAAmE;YACnE,8DAA8D;YAC9D,qEAAqE;YACrE,2CAA2C;YAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;YAC3F,gEAAgE;YAChE,uEAAuE;YACvE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE;oBACjB,EAAE,EAAE,OAAO;oBACX,IAAI,EAAE,gBAAgB;oBACtB,IAAI,EAAE,KAAK,CAAC,KAAK;oBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE;oBACrD,QAAQ,EAAE,IAAI;oBACd,UAAU,EAAE,WAAW;oBACvB,UAAU,EAAE,OAAO;oBACnB,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;iBACnD,CAAC,CAAC;YACL,CAAC;YAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,0EAA0E;gBAC1E,MAAM,QAAQ,GAAG,MAAM,CACrB,KAAK,EACL,iBAAiB,EACjB,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,EAChD,IAAI,CACL,CAAC;gBACF,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;oBAClB,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,iBAAiB;oBACvB,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,EAAE;oBACrC,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE;oBACrD,QAAQ,EAAE,IAAI;oBACd,UAAU,EAAE,WAAW;oBACvB,UAAU,EAAE,OAAO;oBACnB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE;iBAC/E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,6EAA6E;QAC7E,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,KAAK,MAAM,SAAS,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACjE,UAAU,CAAC,IAAI,CAAC;gBACd,qEAAqE;gBACrE,oEAAoE;gBACpE,UAAU,EAAE,MAAM,CAChB,KAAK,EACL,gBAAgB,EAChB,QAAQ,CAAC,MAAO,CAAC,MAAM,EAAE,MAAO,CAAC,KAAK,CAAC,EACvC,IAAI,CACL;gBACD,QAAQ,EAAE,QAAQ;gBAClB,SAAS,EAAE,SAAS,CAAC,IAAI;gBACzB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,UAAU,EAAE,WAAW;gBACvB,MAAM,EAAE,oFAAoF;aAC7F,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;AACpD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@descryy/adapter-sql` — schema migrations.
|
|
3
|
+
*
|
|
4
|
+
* The cheapest capability in the plan and the one that proves the format track:
|
|
5
|
+
* a DDL file states its own shape, so this reaches R3 with no language adapter
|
|
6
|
+
* involved (§11B.4). Golden pattern 10 is identical across every ORM for the
|
|
7
|
+
* same reason.
|
|
8
|
+
*/
|
|
9
|
+
export { ADAPTER_ID, ADAPTER_VERSION, DEFAULT_SQL_GLOBS, createSqlAdapter } from "./adapter.ts";
|
|
10
|
+
export type { SqlAdapterOptions } from "./adapter.ts";
|
|
11
|
+
export { DEFAULT_SCHEMA, parseAddColumn, parseCreateTable, statements, } from "./parse.ts";
|
|
12
|
+
export type { ColumnDefinition, ParsedStatement, TableDefinition } from "./parse.ts";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChG,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@descryy/adapter-sql` — schema migrations.
|
|
3
|
+
*
|
|
4
|
+
* The cheapest capability in the plan and the one that proves the format track:
|
|
5
|
+
* a DDL file states its own shape, so this reaches R3 with no language adapter
|
|
6
|
+
* involved (§11B.4). Golden pattern 10 is identical across every ORM for the
|
|
7
|
+
* same reason.
|
|
8
|
+
*/
|
|
9
|
+
export { ADAPTER_ID, ADAPTER_VERSION, DEFAULT_SQL_GLOBS, createSqlAdapter } from "./adapter.js";
|
|
10
|
+
export { DEFAULT_SCHEMA, parseAddColumn, parseCreateTable, statements, } from "./parse.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhG,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,UAAU,GACX,MAAM,YAAY,CAAC"}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A deliberately small DDL reader: `CREATE TABLE` and `ALTER TABLE … ADD COLUMN`.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is not a SQL parser
|
|
5
|
+
*
|
|
6
|
+
* It does not need to be. §11B.4's point is that a schema **states its own
|
|
7
|
+
* shape** — there is no inference here, no type resolution, no call graph. The
|
|
8
|
+
* two statements below are the ones that define tables and columns, and a full
|
|
9
|
+
* dialect parser would add thousands of lines to read the same two facts.
|
|
10
|
+
*
|
|
11
|
+
* The honest limit: anything it does not recognise is skipped silently at the
|
|
12
|
+
* statement level and reported as an unresolved reference, never guessed at. A
|
|
13
|
+
* `CREATE TABLE … AS SELECT` yields no columns rather than invented ones.
|
|
14
|
+
*/
|
|
15
|
+
export interface ColumnDefinition {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly type: string;
|
|
18
|
+
readonly nullable: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface TableDefinition {
|
|
21
|
+
readonly schema: string;
|
|
22
|
+
readonly table: string;
|
|
23
|
+
readonly columns: readonly ColumnDefinition[];
|
|
24
|
+
readonly line: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ParsedStatement {
|
|
27
|
+
readonly text: string;
|
|
28
|
+
readonly line: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_SCHEMA = "public";
|
|
31
|
+
/**
|
|
32
|
+
* Split on `;`, ignoring separators inside strings, quoted identifiers and
|
|
33
|
+
* comments. A regex split would cut a statement in half at the first semicolon
|
|
34
|
+
* in a default value, which is a real thing to find in a migration.
|
|
35
|
+
*/
|
|
36
|
+
export declare function statements(sql: string): ParsedStatement[];
|
|
37
|
+
export declare function parseCreateTable(statement: ParsedStatement): TableDefinition | undefined;
|
|
38
|
+
export declare function parseAddColumn(statement: ParsedStatement): TableDefinition | undefined;
|
|
39
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CA8DzD;AA0CD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,eAAe,GAAG,eAAe,GAAG,SAAS,CAoBxF;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,eAAe,GAAG,eAAe,GAAG,SAAS,CAoBtF"}
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A deliberately small DDL reader: `CREATE TABLE` and `ALTER TABLE … ADD COLUMN`.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is not a SQL parser
|
|
5
|
+
*
|
|
6
|
+
* It does not need to be. §11B.4's point is that a schema **states its own
|
|
7
|
+
* shape** — there is no inference here, no type resolution, no call graph. The
|
|
8
|
+
* two statements below are the ones that define tables and columns, and a full
|
|
9
|
+
* dialect parser would add thousands of lines to read the same two facts.
|
|
10
|
+
*
|
|
11
|
+
* The honest limit: anything it does not recognise is skipped silently at the
|
|
12
|
+
* statement level and reported as an unresolved reference, never guessed at. A
|
|
13
|
+
* `CREATE TABLE … AS SELECT` yields no columns rather than invented ones.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_SCHEMA = "public";
|
|
16
|
+
/**
|
|
17
|
+
* Split on `;`, ignoring separators inside strings, quoted identifiers and
|
|
18
|
+
* comments. A regex split would cut a statement in half at the first semicolon
|
|
19
|
+
* in a default value, which is a real thing to find in a migration.
|
|
20
|
+
*/
|
|
21
|
+
export function statements(sql) {
|
|
22
|
+
const found = [];
|
|
23
|
+
let buffer = "";
|
|
24
|
+
let line = 1;
|
|
25
|
+
let startLine = 1;
|
|
26
|
+
let quote = null;
|
|
27
|
+
let comment = null;
|
|
28
|
+
const flush = () => {
|
|
29
|
+
if (buffer.trim() !== "")
|
|
30
|
+
found.push({ text: buffer.trim(), line: startLine });
|
|
31
|
+
buffer = "";
|
|
32
|
+
startLine = line;
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
35
|
+
const char = sql[i];
|
|
36
|
+
const next = sql[i + 1];
|
|
37
|
+
if (char === "\n")
|
|
38
|
+
line += 1;
|
|
39
|
+
if (comment === "line") {
|
|
40
|
+
if (char === "\n")
|
|
41
|
+
comment = null;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (comment === "block") {
|
|
45
|
+
if (char === "*" && next === "/") {
|
|
46
|
+
comment = null;
|
|
47
|
+
i += 1;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (quote !== null) {
|
|
52
|
+
buffer += char;
|
|
53
|
+
if (char === quote)
|
|
54
|
+
quote = null;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (char === "-" && next === "-") {
|
|
58
|
+
comment = "line";
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (char === "/" && next === "*") {
|
|
62
|
+
comment = "block";
|
|
63
|
+
i += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
67
|
+
quote = char;
|
|
68
|
+
buffer += char;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (char === ";") {
|
|
72
|
+
flush();
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (buffer.trim() === "" && char.trim() === "") {
|
|
76
|
+
// Leading whitespace belongs to the next statement's line number.
|
|
77
|
+
startLine = line;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
buffer += char;
|
|
81
|
+
}
|
|
82
|
+
flush();
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
function unquote(identifier) {
|
|
86
|
+
return identifier.replace(/^["`[]|["`\]]$/g, "");
|
|
87
|
+
}
|
|
88
|
+
function splitQualified(raw) {
|
|
89
|
+
const parts = raw.split(".").map(unquote);
|
|
90
|
+
const table = parts.at(-1) ?? raw;
|
|
91
|
+
const schema = parts.length > 1 ? (parts.at(-2) ?? DEFAULT_SCHEMA) : DEFAULT_SCHEMA;
|
|
92
|
+
return { schema, table };
|
|
93
|
+
}
|
|
94
|
+
/** SQL columns are nullable unless the definition says otherwise. */
|
|
95
|
+
function nullabilityOf(rest) {
|
|
96
|
+
const upper = rest.toUpperCase();
|
|
97
|
+
if (/\bNOT\s+NULL\b/.test(upper))
|
|
98
|
+
return false;
|
|
99
|
+
if (/\bPRIMARY\s+KEY\b/.test(upper))
|
|
100
|
+
return false;
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/** Split a `CREATE TABLE` body on commas at nesting depth zero. */
|
|
104
|
+
function topLevelParts(body) {
|
|
105
|
+
const parts = [];
|
|
106
|
+
let depth = 0;
|
|
107
|
+
let current = "";
|
|
108
|
+
for (const char of body) {
|
|
109
|
+
if (char === "(")
|
|
110
|
+
depth += 1;
|
|
111
|
+
if (char === ")")
|
|
112
|
+
depth -= 1;
|
|
113
|
+
if (char === "," && depth === 0) {
|
|
114
|
+
parts.push(current);
|
|
115
|
+
current = "";
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
current += char;
|
|
119
|
+
}
|
|
120
|
+
if (current.trim() !== "")
|
|
121
|
+
parts.push(current);
|
|
122
|
+
return parts;
|
|
123
|
+
}
|
|
124
|
+
const TABLE_CONSTRAINTS = /^(CONSTRAINT|PRIMARY|FOREIGN|UNIQUE|CHECK|EXCLUDE|INDEX|KEY)\b/i;
|
|
125
|
+
export function parseCreateTable(statement) {
|
|
126
|
+
const match = /^CREATE\s+(?:TEMP(?:ORARY)?\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([\w."`[\]]+)\s*\(([\s\S]*)\)\s*$/i.exec(statement.text);
|
|
127
|
+
if (match === null)
|
|
128
|
+
return undefined;
|
|
129
|
+
const { schema, table } = splitQualified(match[1]);
|
|
130
|
+
const columns = [];
|
|
131
|
+
for (const part of topLevelParts(match[2])) {
|
|
132
|
+
const trimmed = part.trim();
|
|
133
|
+
if (trimmed === "" || TABLE_CONSTRAINTS.test(trimmed))
|
|
134
|
+
continue;
|
|
135
|
+
const column = /^([\w."`[\]]+)\s+([\w\s()]+?)(\s+.*)?$/.exec(trimmed);
|
|
136
|
+
if (column === null)
|
|
137
|
+
continue;
|
|
138
|
+
columns.push({
|
|
139
|
+
name: unquote(column[1]),
|
|
140
|
+
type: column[2].trim(),
|
|
141
|
+
nullable: nullabilityOf(trimmed),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return { schema, table, columns, line: statement.line };
|
|
145
|
+
}
|
|
146
|
+
export function parseAddColumn(statement) {
|
|
147
|
+
const match = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?([\w."`[\]]+)\s+ADD\s+(?:COLUMN\s+)?(?:IF\s+NOT\s+EXISTS\s+)?([\w."`[\]]+)\s+([\s\S]+)$/i.exec(statement.text);
|
|
148
|
+
if (match === null)
|
|
149
|
+
return undefined;
|
|
150
|
+
const { schema, table } = splitQualified(match[1]);
|
|
151
|
+
const rest = match[3].trim();
|
|
152
|
+
const type = /^([\w\s()]+?)(\s+(?:NOT\s+NULL|NULL|DEFAULT|PRIMARY|REFERENCES|CHECK|UNIQUE)\b[\s\S]*)?$/i.exec(rest);
|
|
153
|
+
return {
|
|
154
|
+
schema,
|
|
155
|
+
table,
|
|
156
|
+
columns: [
|
|
157
|
+
{
|
|
158
|
+
name: unquote(match[2]),
|
|
159
|
+
type: (type?.[1] ?? rest).trim(),
|
|
160
|
+
nullable: nullabilityOf(rest),
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
line: statement.line,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@descryy/adapter-sql",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "SQL migration adapter. Reaches R3 with no language adapter at all \u2014 a schema states its own shape.",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.5"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"registry": "https://registry.npmjs.org",
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -b"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@descryy/adapter-common": "0.1.0",
|
|
28
|
+
"@descryy/ir": "0.3.0"
|
|
29
|
+
}
|
|
30
|
+
}
|