sasat 0.22.0 → 0.22.1
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/cli/index.cjs +604 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +604 -0
- package/dist/index.cjs +32 -3981
- package/dist/index.mjs +4 -3928
- package/dist/migrate-Cc47Mufl.cjs +4165 -0
- package/dist/migrate-DfAkhVPb.mjs +3947 -0
- package/package.json +3 -3
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_migrate = require("../migrate-Cc47Mufl.cjs");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
node_fs = require_migrate.__toESM(node_fs, 1);
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
node_path = require_migrate.__toESM(node_path, 1);
|
|
7
|
+
let cac = require("cac");
|
|
8
|
+
//#region src/cli/commands/createMigration.ts
|
|
9
|
+
const getMigrationFile = (className) => `import { SasatMigration, MigrationStore } from "sasat";
|
|
10
|
+
|
|
11
|
+
export default class ${require_migrate.capitalizeFirstLetter(className)} implements SasatMigration {
|
|
12
|
+
|
|
13
|
+
up: (store: MigrationStore) => void = store => {
|
|
14
|
+
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
down: (store: MigrationStore) => void = store => {
|
|
18
|
+
throw new Error('Down is not implemented on ${className}');
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
const createMigrationFile = (migrationName) => {
|
|
23
|
+
const date = /* @__PURE__ */ new Date();
|
|
24
|
+
const pad = (val) => val.toString().padStart(2, "0");
|
|
25
|
+
const fileName = date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) + `_` + pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds()) + migrationName;
|
|
26
|
+
const outDir = (0, node_path.join)(require_migrate.config().migration.dir);
|
|
27
|
+
require_migrate.mkDirIfNotExist(outDir);
|
|
28
|
+
node_fs.writeFileSync((0, node_path.join)(outDir, fileName) + ".ts", getMigrationFile(migrationName));
|
|
29
|
+
return fileName;
|
|
30
|
+
};
|
|
31
|
+
const createMigration = (args) => {
|
|
32
|
+
if (!args) {
|
|
33
|
+
require_migrate.Console.error("missing argument migration name");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!/^[$A-Za-z_][0-9A-Za-z_]+$/.test(args)) {
|
|
37
|
+
require_migrate.Console.error("migration name should be match /^[$A-Za-z_][0-9A-Za-z_]+$/");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
require_migrate.Console.success(createMigrationFile(args) + " Successfully created");
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/db/sql/createTable/createTableParser.ts
|
|
44
|
+
const splitArray = (array, callback) => {
|
|
45
|
+
const indexes = [];
|
|
46
|
+
array.forEach((it, i) => {
|
|
47
|
+
if (callback(it)) indexes.push(i);
|
|
48
|
+
});
|
|
49
|
+
let prev = 0;
|
|
50
|
+
const result = [];
|
|
51
|
+
indexes.forEach((it) => {
|
|
52
|
+
result.push(array.slice(prev, it));
|
|
53
|
+
prev = it + 1;
|
|
54
|
+
});
|
|
55
|
+
result.push(array.slice(prev, array.length));
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
const isParenToken = (token) => {
|
|
59
|
+
return token.kind === "Paren";
|
|
60
|
+
};
|
|
61
|
+
var CreateTableParser = class {
|
|
62
|
+
constructor(tokens) {
|
|
63
|
+
this.tokens = tokens;
|
|
64
|
+
this.parse = () => {
|
|
65
|
+
const parenStart = this.findIndex("separator", "(", this.tokens);
|
|
66
|
+
this.result.tableName = this.tokens[parenStart - 1].value;
|
|
67
|
+
splitArray(this.resolveParen().find((it) => it.kind === "Paren").tokens, (token) => token.kind === "separator" && token.value === ",").forEach((it) => {
|
|
68
|
+
const kindIncludes = (kind) => !!it.find((it) => it.kind === kind);
|
|
69
|
+
if (kindIncludes("PRIMARY")) return this.parsePrimary(it);
|
|
70
|
+
if (kindIncludes("UNIQUE")) return this.parseUnique(it);
|
|
71
|
+
if (kindIncludes("FOREIGN")) return this.parseFkey(it);
|
|
72
|
+
if (kindIncludes("KEY") || kindIncludes("INDEX")) return this.parseIndex(it);
|
|
73
|
+
const kind = it[0].kind;
|
|
74
|
+
if (kind === "identifier" || kind === "string") return this.parseColumn(it);
|
|
75
|
+
throw new Error("fail to parse sql");
|
|
76
|
+
});
|
|
77
|
+
return this.result;
|
|
78
|
+
};
|
|
79
|
+
this.normalizeFieldName = (fieldName) => {
|
|
80
|
+
return /^[0-9].*/.test(fieldName) ? "_" + fieldName : fieldName;
|
|
81
|
+
};
|
|
82
|
+
this.result = {
|
|
83
|
+
tableName: "",
|
|
84
|
+
columns: [],
|
|
85
|
+
primaryKey: [],
|
|
86
|
+
uniqueKeys: [],
|
|
87
|
+
indexes: [],
|
|
88
|
+
gqlOption: require_migrate.defaultGQLOption(),
|
|
89
|
+
virtualRelations: []
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
resolveParen() {
|
|
93
|
+
const resolveParen = (tokens) => {
|
|
94
|
+
const result = [];
|
|
95
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
96
|
+
const token = tokens[i];
|
|
97
|
+
if (token.kind === "separator" && token.value === "(") {
|
|
98
|
+
const endParen = this.findEndOfParenIndex(tokens, i);
|
|
99
|
+
result.push({
|
|
100
|
+
kind: "Paren",
|
|
101
|
+
tokens: resolveParen(tokens.slice(i + 1, endParen)),
|
|
102
|
+
value: ""
|
|
103
|
+
});
|
|
104
|
+
i = endParen;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
result.push(token);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
return resolveParen(this.tokens);
|
|
112
|
+
}
|
|
113
|
+
parseColumn(tokens) {
|
|
114
|
+
const columnName = tokens[0].value;
|
|
115
|
+
const type = tokens[1].value.toLowerCase();
|
|
116
|
+
const gqlType = require_migrate.columnTypeToGqlPrimitive(type);
|
|
117
|
+
const defaultTokenIndex = tokens.findIndex((it) => it.kind === "DEFAULT");
|
|
118
|
+
const getDefaultValue = () => {
|
|
119
|
+
if (defaultTokenIndex === -1) return void 0;
|
|
120
|
+
const next = tokens[defaultTokenIndex + 1];
|
|
121
|
+
if (next.kind === "NULL") return void 0;
|
|
122
|
+
if (next.kind === "number" || gqlType === "Float" || gqlType === "Int") return +next.value;
|
|
123
|
+
if (next.kind === "string") return next.value;
|
|
124
|
+
};
|
|
125
|
+
const defaultCurrentTimeStamp = defaultTokenIndex !== -1 && tokens[defaultTokenIndex + 1].kind === "identifier" && tokens[defaultTokenIndex + 1].value.toUpperCase() === "CURRENT_TIMESTAMP";
|
|
126
|
+
const isOnUpdate = () => {
|
|
127
|
+
return tokens[tokens.findIndex((it) => it.kind === "UPDATE") + 1].value.toUpperCase() === "CURRENT_TIMESTAMP";
|
|
128
|
+
};
|
|
129
|
+
const isNotNull = () => {
|
|
130
|
+
let prevIsNot = false;
|
|
131
|
+
return tokens.some((it) => {
|
|
132
|
+
if (prevIsNot && it.kind === "NULL") return true;
|
|
133
|
+
prevIsNot = it.kind === "NOT";
|
|
134
|
+
return false;
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const identifiers = tokens.filter((it) => it.kind === "identifier");
|
|
138
|
+
const length = tokens[2] && isParenToken(tokens[2]) ? tokens[2].tokens[0]?.value : void 0;
|
|
139
|
+
const scale = tokens[2] && isParenToken(tokens[2]) ? tokens[2].tokens[1]?.value : void 0;
|
|
140
|
+
const column = {
|
|
141
|
+
hasReference: false,
|
|
142
|
+
columnName,
|
|
143
|
+
fieldName: this.normalizeFieldName(require_migrate.camelize(tokens[0].value)),
|
|
144
|
+
type,
|
|
145
|
+
notNull: isNotNull(),
|
|
146
|
+
default: getDefaultValue(),
|
|
147
|
+
zerofill: identifiers.some((it) => it.value.toLowerCase() === "zerofill"),
|
|
148
|
+
signed: identifiers.some((it) => it.value.toLowerCase() === "unsigned") ? false : identifiers.some((it) => it.value.toLowerCase() === "signed") ? true : void 0,
|
|
149
|
+
autoIncrement: tokens.some((it) => it.kind === "AUTO_INCREMENT"),
|
|
150
|
+
length: length !== void 0 ? +length : void 0,
|
|
151
|
+
scale: scale !== void 0 ? +scale : void 0,
|
|
152
|
+
defaultCurrentTimeStamp,
|
|
153
|
+
onUpdateCurrentTimeStamp: isOnUpdate(),
|
|
154
|
+
option: require_migrate.defaultColumnOption
|
|
155
|
+
};
|
|
156
|
+
if (column.default === ".nan" || Number.isNaN(column.default)) console.error(column);
|
|
157
|
+
this.result.columns.push(column);
|
|
158
|
+
if (tokens.some((it) => it.kind === "PRIMARY")) this.result.primaryKey = [columnName];
|
|
159
|
+
if (tokens.some((it) => it.kind === "UNIQUE")) this.result.uniqueKeys.push([columnName]);
|
|
160
|
+
}
|
|
161
|
+
parsePrimary(tokens) {
|
|
162
|
+
const paren = tokens[tokens.findIndex((it) => it.kind === "KEY") + 1];
|
|
163
|
+
this.result.primaryKey = paren.tokens.filter((it) => it.kind !== "separator").map((it) => it.value);
|
|
164
|
+
}
|
|
165
|
+
parseUnique(tokens) {
|
|
166
|
+
const paren = tokens.find((it) => it.kind === "Paren");
|
|
167
|
+
this.result.uniqueKeys.push(paren.tokens.filter((it) => it.kind !== "separator").map((it) => it.value));
|
|
168
|
+
}
|
|
169
|
+
parseIndex(tokens) {
|
|
170
|
+
const paren = tokens.find((it) => it.kind === "Paren");
|
|
171
|
+
this.result.indexes.push({
|
|
172
|
+
constraintName: tokens[1].value,
|
|
173
|
+
columns: paren.tokens.filter((it) => it.kind !== "separator" && it.kind !== "Paren").map((it) => it.value)
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
parseFkey(tokens) {
|
|
177
|
+
const columnName = tokens[tokens.findIndex((it) => it.kind === "FOREIGN") + 2].tokens[0].value;
|
|
178
|
+
const refIndex = tokens.findIndex((it) => it.kind === "REFERENCES" && it.value === "REFERENCES");
|
|
179
|
+
const targetTable = tokens[refIndex + 1].value;
|
|
180
|
+
const targetColumn = tokens[refIndex + 2].tokens[0].value;
|
|
181
|
+
let onDelete;
|
|
182
|
+
let onUpdate;
|
|
183
|
+
tokens.map((it, i) => it.kind === "ON" ? i : 0).filter((it) => it !== 0).forEach((it) => {
|
|
184
|
+
const action = () => {
|
|
185
|
+
let name = tokens[it + 2].value.toUpperCase();
|
|
186
|
+
if (name === "SET" || name === "NO") name += tokens[it + 3].value.toUpperCase();
|
|
187
|
+
return name;
|
|
188
|
+
};
|
|
189
|
+
if (tokens[it + 1].kind === "DELETE") onDelete = action();
|
|
190
|
+
if (tokens[it + 1].kind === "UPDATE") onUpdate = action();
|
|
191
|
+
});
|
|
192
|
+
const isColumnUnique = this.result.uniqueKeys.filter((it) => it.length === 1).find((it) => it[0] === columnName);
|
|
193
|
+
const sameTableRefs = this.result.columns.filter((it) => it.hasReference && it.reference.parentTable === targetTable);
|
|
194
|
+
const reference = {
|
|
195
|
+
parentTable: targetTable,
|
|
196
|
+
parentColumn: targetColumn,
|
|
197
|
+
columnName,
|
|
198
|
+
relation: isColumnUnique ? "OneOrZero" : "Many",
|
|
199
|
+
relationName: sameTableRefs.length !== 0 ? targetTable + sameTableRefs.length : void 0,
|
|
200
|
+
onUpdate,
|
|
201
|
+
onDelete
|
|
202
|
+
};
|
|
203
|
+
this.result.columns = this.result.columns.map((it) => it.columnName === columnName ? {
|
|
204
|
+
...it,
|
|
205
|
+
hasReference: true,
|
|
206
|
+
reference
|
|
207
|
+
} : it);
|
|
208
|
+
}
|
|
209
|
+
findIndex(kind, value, tokens) {
|
|
210
|
+
return tokens.findIndex((it) => it.kind === kind && value === it.value);
|
|
211
|
+
}
|
|
212
|
+
findEndOfParenIndex(tokens, startParenIndex) {
|
|
213
|
+
let opened = 1;
|
|
214
|
+
for (let i = startParenIndex + 1; i < tokens.length; i++) {
|
|
215
|
+
const token = tokens[i];
|
|
216
|
+
if (token.kind === "separator") {
|
|
217
|
+
if (token.value === ")") {
|
|
218
|
+
opened -= 1;
|
|
219
|
+
if (opened === 0) return i;
|
|
220
|
+
} else if (token.value === "(") opened += 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
throw new Error("Closing Parenthesis Not Found");
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/db/sql/createTable/lexer/lexer2.ts
|
|
228
|
+
var Lexer2 = class {
|
|
229
|
+
constructor(str, rules, separators, operators, whiteSpaces = /([ \t\n])/) {
|
|
230
|
+
this.rules = rules;
|
|
231
|
+
this.separators = separators;
|
|
232
|
+
this.operators = operators;
|
|
233
|
+
this.whiteSpaces = whiteSpaces;
|
|
234
|
+
this.index = 0;
|
|
235
|
+
this.tokens = [];
|
|
236
|
+
this.chars = str.split("");
|
|
237
|
+
}
|
|
238
|
+
isTerminated(value, terminators) {
|
|
239
|
+
const separator = (value) => this.separators.includes(value);
|
|
240
|
+
const operator = (value) => this.operators.some((it) => it.startsWith(value));
|
|
241
|
+
const whiteSpace = (value) => this.whiteSpaces.test(value);
|
|
242
|
+
return terminators.some((it) => {
|
|
243
|
+
switch (it) {
|
|
244
|
+
case "operator": return operator(value);
|
|
245
|
+
case "separator": return separator(value);
|
|
246
|
+
case "whitespace": return whiteSpace(value);
|
|
247
|
+
default: return false;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
lex() {
|
|
252
|
+
let value = this.read();
|
|
253
|
+
do
|
|
254
|
+
value = this.exec(value);
|
|
255
|
+
while (value.hasNext);
|
|
256
|
+
return this.tokens;
|
|
257
|
+
}
|
|
258
|
+
findOperator(current) {
|
|
259
|
+
const operator = (value) => ({
|
|
260
|
+
kind: "operator",
|
|
261
|
+
value
|
|
262
|
+
});
|
|
263
|
+
if (!current.hasNext) return this.operators.includes(current.value) ? operator(current.value) : void 0;
|
|
264
|
+
let operators = this.operators.filter((it) => it.startsWith(current.value));
|
|
265
|
+
let state = current.value;
|
|
266
|
+
if (operators.length === 1) return operator(state);
|
|
267
|
+
while (operators.length !== 0) {
|
|
268
|
+
if (!current.hasNext) return operator(state);
|
|
269
|
+
current = this.read();
|
|
270
|
+
state += current.value;
|
|
271
|
+
operators = operators.filter((it) => it.startsWith(state));
|
|
272
|
+
if (operators.length === 1) return operator(state);
|
|
273
|
+
}
|
|
274
|
+
if (state.length > 1) return operator(state.slice(0, -1));
|
|
275
|
+
}
|
|
276
|
+
exec(current) {
|
|
277
|
+
const currentIndex = this.index;
|
|
278
|
+
if (this.whiteSpaces.test(current.value)) return this.read();
|
|
279
|
+
if (this.separators.includes(current.value)) {
|
|
280
|
+
this.tokens.push({
|
|
281
|
+
kind: "separator",
|
|
282
|
+
value: current.value
|
|
283
|
+
});
|
|
284
|
+
return this.read();
|
|
285
|
+
}
|
|
286
|
+
const op = this.findOperator(current);
|
|
287
|
+
if (op) {
|
|
288
|
+
this.tokens.push(op);
|
|
289
|
+
this.index = currentIndex + op.value.length - 1;
|
|
290
|
+
return this.read();
|
|
291
|
+
}
|
|
292
|
+
this.index = currentIndex;
|
|
293
|
+
for (let i = 0; i < this.rules.length; i++) {
|
|
294
|
+
const rule = this.rules[i];
|
|
295
|
+
if (!rule.start.test(current.value)) continue;
|
|
296
|
+
let terminated = false;
|
|
297
|
+
let value;
|
|
298
|
+
const next = () => {
|
|
299
|
+
value = this.read();
|
|
300
|
+
terminated = this.isTerminated(value.value, rule.terminator);
|
|
301
|
+
return {
|
|
302
|
+
terminated,
|
|
303
|
+
...value
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
const result = rule.fn(current.value, next);
|
|
307
|
+
if (!result) {
|
|
308
|
+
this.index = currentIndex;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
this.tokens.push(result);
|
|
312
|
+
if (terminated && value) return value;
|
|
313
|
+
return this.read();
|
|
314
|
+
}
|
|
315
|
+
throw new Error("No Rule Matched");
|
|
316
|
+
}
|
|
317
|
+
read() {
|
|
318
|
+
const char = this.chars[this.index] || "";
|
|
319
|
+
this.index += 1;
|
|
320
|
+
return {
|
|
321
|
+
hasNext: this.chars.length > this.index,
|
|
322
|
+
value: char
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/db/sql/createTable/lexer/rules.ts
|
|
328
|
+
const createStringLiteralRule = (literalInitializer) => {
|
|
329
|
+
return {
|
|
330
|
+
start: literalInitializer,
|
|
331
|
+
terminator: [],
|
|
332
|
+
fn: (start, next) => {
|
|
333
|
+
let literal = "";
|
|
334
|
+
let isEscape = false;
|
|
335
|
+
let v;
|
|
336
|
+
do {
|
|
337
|
+
v = next();
|
|
338
|
+
if (!isEscape && v.value === start) return {
|
|
339
|
+
kind: "string",
|
|
340
|
+
value: literal
|
|
341
|
+
};
|
|
342
|
+
literal += v.value;
|
|
343
|
+
isEscape = !isEscape && v.value === "\\";
|
|
344
|
+
} while (v.hasNext);
|
|
345
|
+
throw new Error("Non Terminated String");
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
const numberLiteral = {
|
|
350
|
+
start: /[0-9]/,
|
|
351
|
+
terminator: [
|
|
352
|
+
"separator",
|
|
353
|
+
"whitespace",
|
|
354
|
+
"operator"
|
|
355
|
+
],
|
|
356
|
+
fn: (start, next) => {
|
|
357
|
+
let literal = start;
|
|
358
|
+
let v;
|
|
359
|
+
do {
|
|
360
|
+
v = next();
|
|
361
|
+
if (v.terminated) break;
|
|
362
|
+
literal += v.value;
|
|
363
|
+
} while (v.hasNext);
|
|
364
|
+
if (!Number.isFinite(+literal)) throw new Error("fail to read number");
|
|
365
|
+
return {
|
|
366
|
+
kind: "number",
|
|
367
|
+
value: literal
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
const createKeywordRule = (keywords, ignoreCase = false) => {
|
|
372
|
+
return {
|
|
373
|
+
start: /./,
|
|
374
|
+
terminator: ["whitespace", "separator"],
|
|
375
|
+
fn: (start, next) => {
|
|
376
|
+
let str = ignoreCase ? start.toUpperCase() : start;
|
|
377
|
+
let words = keywords.map((it) => typeof it === "string" ? {
|
|
378
|
+
kind: "keyword",
|
|
379
|
+
value: it
|
|
380
|
+
} : {
|
|
381
|
+
...it,
|
|
382
|
+
value: it.value
|
|
383
|
+
});
|
|
384
|
+
if (ignoreCase) words = words.map((it) => ({
|
|
385
|
+
...it,
|
|
386
|
+
value: it.value.toUpperCase()
|
|
387
|
+
}));
|
|
388
|
+
let v;
|
|
389
|
+
while (true) {
|
|
390
|
+
v = next();
|
|
391
|
+
if (v.terminated && words.find((it) => it.value === str)) return {
|
|
392
|
+
kind: words.find((it) => it.value === str).kind,
|
|
393
|
+
value: str
|
|
394
|
+
};
|
|
395
|
+
str += ignoreCase ? v.value.toUpperCase() : v.value;
|
|
396
|
+
words = words.filter((it) => it.value.startsWith(str));
|
|
397
|
+
if (words.length === 0) return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
const Rules = {
|
|
403
|
+
createStringLiteralRule,
|
|
404
|
+
numberLiteral,
|
|
405
|
+
createKeywordRule,
|
|
406
|
+
identifier: {
|
|
407
|
+
start: /[^0-9]/,
|
|
408
|
+
terminator: [
|
|
409
|
+
"separator",
|
|
410
|
+
"whitespace",
|
|
411
|
+
"operator"
|
|
412
|
+
],
|
|
413
|
+
fn: (start, next) => {
|
|
414
|
+
let value = start;
|
|
415
|
+
let v;
|
|
416
|
+
do {
|
|
417
|
+
v = next();
|
|
418
|
+
if (v.terminated) break;
|
|
419
|
+
value += v.value;
|
|
420
|
+
} while (v.hasNext);
|
|
421
|
+
return {
|
|
422
|
+
kind: "identifier",
|
|
423
|
+
value
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/db/sql/createTable/lexer/createSimpleLexer.ts
|
|
430
|
+
const createSimpleLexer = (str, keywords, ignoreKeywordCase, separators, operators = []) => new Lexer2(str, [
|
|
431
|
+
Rules.createStringLiteralRule(/['"`]/),
|
|
432
|
+
Rules.numberLiteral,
|
|
433
|
+
Rules.createKeywordRule(keywords, ignoreKeywordCase),
|
|
434
|
+
Rules.identifier
|
|
435
|
+
], separators, operators);
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/db/sql/createTable/lexer/createTableLexer.ts
|
|
438
|
+
const separators = [
|
|
439
|
+
"(",
|
|
440
|
+
")",
|
|
441
|
+
","
|
|
442
|
+
];
|
|
443
|
+
const keywords = [
|
|
444
|
+
"CREATE",
|
|
445
|
+
"TABLE",
|
|
446
|
+
"PRIMARY",
|
|
447
|
+
"KEY",
|
|
448
|
+
"UNIQUE",
|
|
449
|
+
"FOREIGN",
|
|
450
|
+
"NOT",
|
|
451
|
+
"NULL",
|
|
452
|
+
"DEFAULT",
|
|
453
|
+
"AUTO_INCREMENT",
|
|
454
|
+
"ON",
|
|
455
|
+
"UPDATE",
|
|
456
|
+
"DELETE",
|
|
457
|
+
"REFERENCES",
|
|
458
|
+
"CONSTRAINT"
|
|
459
|
+
].map((it) => ({
|
|
460
|
+
kind: it,
|
|
461
|
+
value: it
|
|
462
|
+
}));
|
|
463
|
+
const createTableLexer = (str) => {
|
|
464
|
+
return createSimpleLexer(str, keywords, true, separators, ["="]);
|
|
465
|
+
};
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/db/sql/createTable/createTableSerializer.ts
|
|
468
|
+
const serializeCreateTable = (str) => {
|
|
469
|
+
return new CreateTableParser(createTableLexer(str).lex()).parse();
|
|
470
|
+
};
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/cli/commands/dumpDb.ts
|
|
473
|
+
const dumpDB = async () => {
|
|
474
|
+
const con = require_migrate.getDbClient();
|
|
475
|
+
try {
|
|
476
|
+
const tables = await con.rawQuery("show tables").then((it) => it.flatMap((it) => Object.values(it)));
|
|
477
|
+
const serialized = await Promise.all(tables.map((table) => {
|
|
478
|
+
return con.rawQuery("show create table " + require_migrate.SqlString.escapeId(table)).then((it) => it[0]["Create Table"]).then(serializeCreateTable);
|
|
479
|
+
}));
|
|
480
|
+
const supportedTypes = Object.values(require_migrate.DBColumnTypes);
|
|
481
|
+
const store = { tables: serialized.filter((it) => {
|
|
482
|
+
if (it.primaryKey.length === 0) {
|
|
483
|
+
require_migrate.Console.error(`table ${it.tableName} skipped, reason: missing primary key`);
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
const notSupported = it.columns.find((it) => !supportedTypes.includes(it.type));
|
|
487
|
+
if (notSupported) {
|
|
488
|
+
require_migrate.Console.error(`table ${it.tableName} skipped, reason: ${it.tableName}.${notSupported.columnName} column type "${notSupported.type}" is Not Supported`);
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
return true;
|
|
492
|
+
}) };
|
|
493
|
+
require_migrate.writeYmlFile(require_migrate.config().migration.dir, "initialSchema.yml", store);
|
|
494
|
+
} catch (e) {
|
|
495
|
+
require_migrate.Console.error(e.message);
|
|
496
|
+
throw e;
|
|
497
|
+
} finally {
|
|
498
|
+
await con.release();
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region src/cli/commands/getCurrentStore.ts
|
|
503
|
+
async function getCurrentStore() {
|
|
504
|
+
await require_migrate.compileMigrationFiles();
|
|
505
|
+
const files = require_migrate.getMigrationFileNames();
|
|
506
|
+
return (await require_migrate.createCurrentMigrationDataStore(files.find((it) => it === require_migrate.config().migration.target) || files[files.length - 1])).serialize();
|
|
507
|
+
}
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/cli/commands/erDiagram.ts
|
|
510
|
+
const writeDiagram = async () => {
|
|
511
|
+
try {
|
|
512
|
+
const store = new require_migrate.DataStoreHandler(await getCurrentStore());
|
|
513
|
+
const result = `erDiagram
|
|
514
|
+
${store.tables.map((it) => processTable(store, it)).join("\n")}
|
|
515
|
+
`;
|
|
516
|
+
node_fs.default.writeFileSync(node_path.default.join(require_migrate.config().migration.out, require_migrate.Directory.paths.GENERATED, "er-diagram.mermaid"), result);
|
|
517
|
+
} catch (e) {
|
|
518
|
+
require_migrate.Console.error(e.message);
|
|
519
|
+
throw e;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
function getRefType(parent, rel) {
|
|
523
|
+
const left = parent.isNullable() ? "|o" : "||";
|
|
524
|
+
const getRight = () => {
|
|
525
|
+
switch (rel) {
|
|
526
|
+
case "One": return "||";
|
|
527
|
+
case "Many": return "o{";
|
|
528
|
+
case "OneOrZero": return "|o";
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
return `${left} -- ${getRight()}`;
|
|
532
|
+
}
|
|
533
|
+
function processTable(store, table) {
|
|
534
|
+
const rel = table.columns.filter((it) => it.isReference()).map((it) => {
|
|
535
|
+
const rel = it;
|
|
536
|
+
const ref = rel.data.reference;
|
|
537
|
+
const parent = store.table(ref.parentTable).column(ref.parentColumn);
|
|
538
|
+
return `${ref.parentTable} ${getRefType(parent, ref.relation)} ${table.tableName} : ${ref.relationName ?? `"${rel.data.reference.parentTable}__${rel.table.tableName}"`}`;
|
|
539
|
+
});
|
|
540
|
+
return `${table.tableName} {
|
|
541
|
+
${table.columns.map((it) => ` ${it.columnName()} ${it.dataType()}`).join("\n")}
|
|
542
|
+
}
|
|
543
|
+
${rel.join("\n")}
|
|
544
|
+
`;
|
|
545
|
+
}
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/cli/commands/generate.ts
|
|
548
|
+
const generate = async () => {
|
|
549
|
+
try {
|
|
550
|
+
await require_migrate.compileMigrationFiles();
|
|
551
|
+
const files = require_migrate.getMigrationFileNames();
|
|
552
|
+
const targetFile = files.find((it) => it === require_migrate.config().migration.target) || files[files.length - 1];
|
|
553
|
+
const store = await getCurrentStore();
|
|
554
|
+
const storeHandler = new require_migrate.DataStoreHandler(store);
|
|
555
|
+
require_migrate.writeCurrentSchema(store);
|
|
556
|
+
await new require_migrate.CodeGen_v2(storeHandler).generate();
|
|
557
|
+
require_migrate.Console.success(`code generated. DIR: ${require_migrate.config().migration.out}\nmigration target: ${targetFile}`);
|
|
558
|
+
} catch (e) {
|
|
559
|
+
require_migrate.Console.error(e.message);
|
|
560
|
+
throw e;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/cli/commands/init.ts
|
|
565
|
+
const init = () => {
|
|
566
|
+
if (node_fs.existsSync("./sasat.yml")) {
|
|
567
|
+
require_migrate.Console.error("sasat.yml already exist");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
require_migrate.writeYmlFile("./", "sasat.yml", require_migrate.defaultConf);
|
|
571
|
+
require_migrate.Console.success("sasat.yml created");
|
|
572
|
+
};
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/cli/commands/migrationBuild.ts
|
|
575
|
+
const migrationBuild = async () => {
|
|
576
|
+
require_migrate.Console.log("--migration build started--");
|
|
577
|
+
await require_migrate.compileMigrationFiles();
|
|
578
|
+
require_migrate.Console.success("Done!");
|
|
579
|
+
};
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/cli/index.ts
|
|
582
|
+
const index = (0, cac.cac)();
|
|
583
|
+
try {
|
|
584
|
+
index.usage("yarn sasat <command> [options]\n").command("migrate", "execute migration").option("-g, --generateFiles", "migrate with generate files").option("-d, --dry", "dry run").option("-s, --silent", "do not print logs").option("-b, --skipBuild", "skip compile migration files").action(async (options) => {
|
|
585
|
+
const client = require_migrate.getDbClient();
|
|
586
|
+
await require_migrate.migrate(client, options).catch((e) => {
|
|
587
|
+
console.error(e);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
});
|
|
590
|
+
await client.release();
|
|
591
|
+
});
|
|
592
|
+
index.command("migration:build", "compile migration files").action(migrationBuild);
|
|
593
|
+
index.command("generate", "generate files").action(generate);
|
|
594
|
+
index.command("generate:er", "generate mermaid er diagram").action(writeDiagram);
|
|
595
|
+
index.command("migration:create [name]", "generate new migration file").action(createMigration);
|
|
596
|
+
index.command("dump-db", "dump database schema").action(dumpDB);
|
|
597
|
+
index.command("init").action(init);
|
|
598
|
+
index.parse();
|
|
599
|
+
if (!index.matchedCommand) index.outputHelp();
|
|
600
|
+
} catch (e) {
|
|
601
|
+
console.error(e);
|
|
602
|
+
process.exit(1);
|
|
603
|
+
}
|
|
604
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|