@technicity/data-service-generator 0.23.0-next.1 → 0.23.0-next.11
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.
|
@@ -9,6 +9,9 @@ type IGenerateInput = {
|
|
|
9
9
|
host?: string;
|
|
10
10
|
port?: number;
|
|
11
11
|
server?: string;
|
|
12
|
+
clientOpts?: {
|
|
13
|
+
[k: string]: any;
|
|
14
|
+
};
|
|
12
15
|
outdir: string;
|
|
13
16
|
tables?: Array<string>;
|
|
14
17
|
excludeTables?: Array<string>;
|
|
@@ -16,6 +19,7 @@ type IGenerateInput = {
|
|
|
16
19
|
includeMappedFields?: IIncludeMappedFields;
|
|
17
20
|
supplementClientOpts?: ISupplementClientOpts;
|
|
18
21
|
outputSqliteSchema?: boolean;
|
|
22
|
+
logLevel?: string;
|
|
19
23
|
};
|
|
20
24
|
export declare function generate(input: IGenerateInput): Promise<void>;
|
|
21
25
|
export {};
|
|
@@ -33,6 +33,7 @@ const os = __importStar(require("node:os"));
|
|
|
33
33
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
34
34
|
const child_process = __importStar(require("node:child_process"));
|
|
35
35
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
36
|
+
const pino_1 = require("pino");
|
|
36
37
|
const prettier = __importStar(require("prettier"));
|
|
37
38
|
const changeCase = __importStar(require("change-case"));
|
|
38
39
|
const fse = __importStar(require("fs-extra"));
|
|
@@ -44,6 +45,7 @@ const isNotNullOrUndefined_1 = require("../lib/isNotNullOrUndefined");
|
|
|
44
45
|
const pg_1 = require("pg");
|
|
45
46
|
const MySQL_1 = require("../runtime/lib/MySQL");
|
|
46
47
|
const capitalizeFirstLetter_1 = require("../lib/capitalizeFirstLetter");
|
|
48
|
+
const pg2sqliteSchema_1 = require("./pg2sqliteSchema");
|
|
47
49
|
const ctxStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
48
50
|
function getCtx() {
|
|
49
51
|
const c = ctxStorage.getStore();
|
|
@@ -62,17 +64,38 @@ async function generate(input) {
|
|
|
62
64
|
if (input.dialect == null) {
|
|
63
65
|
throw new Error("Must specify `dialect`");
|
|
64
66
|
}
|
|
67
|
+
const log = (0, pino_1.pino)({
|
|
68
|
+
name: "generate",
|
|
69
|
+
level: input.logLevel ?? process.env.LOG_LEVEL ?? "info",
|
|
70
|
+
transport: {
|
|
71
|
+
target: "pino-pretty",
|
|
72
|
+
options: {
|
|
73
|
+
colorize: true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
65
77
|
const specialCaseUuidColumn = input.specialCaseUuidColumn ?? true;
|
|
66
78
|
const includeMappedFields = input.includeMappedFields ?? true;
|
|
67
79
|
const supplementClientOpts = input.supplementClientOpts ?? true;
|
|
80
|
+
const runId = node_crypto_1.default.randomUUID();
|
|
68
81
|
const ctx = {
|
|
69
|
-
runId
|
|
82
|
+
runId,
|
|
83
|
+
log: log.child({}),
|
|
70
84
|
dialect: input.dialect,
|
|
71
|
-
query: undefined
|
|
85
|
+
query: undefined,
|
|
86
|
+
pool: undefined
|
|
72
87
|
};
|
|
88
|
+
log.debug({
|
|
89
|
+
runId: ctx.runId,
|
|
90
|
+
dialect: input.dialect,
|
|
91
|
+
database: input.database,
|
|
92
|
+
outdir: input.outdir
|
|
93
|
+
}, "generate() started");
|
|
73
94
|
return ctxStorage.run(ctx, async () => {
|
|
74
95
|
init(input);
|
|
96
|
+
ctx.log.debug("init() completed");
|
|
75
97
|
let tables = await getTableNames();
|
|
98
|
+
ctx.log.debug({ tableCount: tables.length, tables }, "getTableNames() completed");
|
|
76
99
|
if (tables.length === 0) {
|
|
77
100
|
throw new Error("No tables found");
|
|
78
101
|
}
|
|
@@ -82,19 +105,23 @@ async function generate(input) {
|
|
|
82
105
|
if (input.excludeTables != null) {
|
|
83
106
|
tables = tables.filter((x) => !input.excludeTables?.includes(x));
|
|
84
107
|
}
|
|
108
|
+
ctx.log.debug({ tableCount: tables.length, tables }, "tables after filter");
|
|
85
109
|
const data = await Promise.all(tables.flatMap((x) => [
|
|
86
|
-
getGetOneData(x, includeMappedFields),
|
|
110
|
+
getGetOneData(x, includeMappedFields, tables),
|
|
87
111
|
getGetListData(x),
|
|
88
112
|
getGetListPaginatedData(x),
|
|
89
|
-
getPostOneData(x, specialCaseUuidColumn, includeMappedFields),
|
|
113
|
+
getPostOneData(x, specialCaseUuidColumn, includeMappedFields, tables),
|
|
90
114
|
getPatchOneData(x, specialCaseUuidColumn, includeMappedFields),
|
|
91
115
|
getPatchListData(x),
|
|
92
116
|
getDeleteOneData(x),
|
|
93
117
|
getDeleteListData(x)
|
|
94
118
|
]));
|
|
119
|
+
ctx.log.debug({ inputLength: data.length }, "SDK input data collected");
|
|
95
120
|
const artifacts = await getArtifacts(tables, includeMappedFields, specialCaseUuidColumn);
|
|
121
|
+
ctx.log.debug("getArtifacts() completed");
|
|
96
122
|
const artifactsSource = getArtifactsSource(artifacts);
|
|
97
123
|
const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts, artifacts, input.outputSqliteSchema);
|
|
124
|
+
ctx.log.debug("getSDKSource() completed");
|
|
98
125
|
const sdkFilename = "index.ts";
|
|
99
126
|
const sourceIRuntimeFilePath = fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.ts"))
|
|
100
127
|
? path.join(__dirname, "../runtime", "IRuntime.ts")
|
|
@@ -129,6 +156,7 @@ async function generate(input) {
|
|
|
129
156
|
const tmpDirPath = path.join(os.tmpdir(),
|
|
130
157
|
// _ because - in filename is not supported by mysql2sqlite
|
|
131
158
|
`dsg_${node_crypto_1.default.randomUUID()}`.replace(/-/g, "_"));
|
|
159
|
+
ctx.log.debug({ tmpDirPath }, "writing SDK and artifacts to tmp dir");
|
|
132
160
|
fse.mkdirpSync(tmpDirPath);
|
|
133
161
|
fs.writeFileSync(path.join(tmpDirPath, sdkFilename), sdkSource);
|
|
134
162
|
fs.writeFileSync(path.join(tmpDirPath, artifactsFilename), artifactsSource);
|
|
@@ -174,67 +202,99 @@ async function generate(input) {
|
|
|
174
202
|
fs.writeFileSync(path.join(tmpBuildOutputPath, "IRuntime.d.ts"), fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
|
|
175
203
|
? fs.readFileSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"), "utf-8")
|
|
176
204
|
: fs.readFileSync(sourceIRuntimeFilePath, "utf-8"));
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.
|
|
184
|
-
|
|
185
|
-
|
|
205
|
+
if (input.outputSqliteSchema) {
|
|
206
|
+
let schemaSqlite = null;
|
|
207
|
+
if (ctx.dialect === "mysql") {
|
|
208
|
+
// Since mysql2sqlite outputs a malformed string if a column
|
|
209
|
+
// has the name `enum`, temporarily change the name to something else,
|
|
210
|
+
// then change it back.
|
|
211
|
+
const enumMarker = "`" + node_crypto_1.default.randomUUID() + "`";
|
|
212
|
+
const schemaMySql = Object.values(artifacts)
|
|
213
|
+
.reduce((acc, x) => {
|
|
214
|
+
let d = x.dump?.schema;
|
|
215
|
+
if (!d) {
|
|
216
|
+
return acc;
|
|
217
|
+
}
|
|
218
|
+
d = d.replace(/`enum`/g, enumMarker);
|
|
219
|
+
d += ";";
|
|
220
|
+
acc.push(d);
|
|
186
221
|
return acc;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
222
|
+
}, [])
|
|
223
|
+
.join("\n\n");
|
|
224
|
+
const mysql2SqliteSrc = getMysql2sqliteSrc();
|
|
225
|
+
const mysql2SqlitePath = path.join(tmpDirPath, "mysql2sqlite");
|
|
226
|
+
fs.writeFileSync(mysql2SqlitePath, mysql2SqliteSrc);
|
|
227
|
+
fs.chmodSync(mysql2SqlitePath, 0o755);
|
|
228
|
+
const tmpMySqlSchemaFilename = "tmp.sql";
|
|
229
|
+
const tmpMySqlSchemaPath = path.join(tmpDirPath, tmpMySqlSchemaFilename);
|
|
230
|
+
fs.writeFileSync(tmpMySqlSchemaPath, schemaMySql);
|
|
231
|
+
schemaSqlite = child_process
|
|
232
|
+
.execFileSync(mysql2SqlitePath, [tmpMySqlSchemaFilename], {
|
|
233
|
+
cwd: tmpDirPath
|
|
234
|
+
})
|
|
235
|
+
.toString();
|
|
236
|
+
schemaSqlite = schemaSqlite.replace(new RegExp(enumMarker, "g"), "`enum`");
|
|
237
|
+
}
|
|
238
|
+
else if (ctx.dialect === "postgresql") {
|
|
239
|
+
schemaSqlite = await (0, pg2sqliteSchema_1.convertPgSchemaToSqlite)(ctx.pool);
|
|
240
|
+
}
|
|
241
|
+
if (schemaSqlite) {
|
|
242
|
+
const src = prettier.format(`module.exports = { schema: \`${schemaSqlite.replace(/`/g, "\\`")}\` }`, { parser: "babel" });
|
|
243
|
+
fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.sqlite.js"), src);
|
|
244
|
+
}
|
|
207
245
|
}
|
|
208
246
|
if (!fs.existsSync(outdir)) {
|
|
209
247
|
fse.mkdirpSync(outdir);
|
|
210
248
|
}
|
|
211
249
|
fse.emptyDirSync(sdkOutputPath);
|
|
212
250
|
fse.copySync(tmpBuildOutputPath, sdkOutputPath);
|
|
251
|
+
ctx.log.debug({ outdir, sdkOutputPath }, "copy to output dir completed");
|
|
213
252
|
fse.removeSync(tmpDirPath);
|
|
253
|
+
ctx.log.debug("generate() completed");
|
|
214
254
|
});
|
|
215
255
|
}
|
|
216
256
|
function init(input) {
|
|
217
257
|
const ctx = getCtx();
|
|
218
258
|
const { database, user, password, host, port, server } = input;
|
|
219
259
|
if (ctx.dialect === "mysql") {
|
|
220
|
-
const
|
|
260
|
+
const connectionOpts = {
|
|
221
261
|
user,
|
|
222
262
|
password,
|
|
223
|
-
host,
|
|
224
|
-
port,
|
|
225
|
-
database
|
|
226
|
-
|
|
263
|
+
host: host ?? "localhost",
|
|
264
|
+
port: port ?? 3306,
|
|
265
|
+
database,
|
|
266
|
+
...input.clientOpts
|
|
267
|
+
};
|
|
268
|
+
ctx.log.debug({
|
|
269
|
+
dialect: "mysql",
|
|
270
|
+
host: connectionOpts.host,
|
|
271
|
+
port: connectionOpts.port,
|
|
272
|
+
database,
|
|
273
|
+
user
|
|
274
|
+
}, "connecting to MySQL");
|
|
275
|
+
const mysql = new MySQL_1.MySQL(connectionOpts);
|
|
227
276
|
ctx.query = mysql.query.bind(mysql);
|
|
277
|
+
ctx.pool = mysql;
|
|
228
278
|
}
|
|
229
279
|
if (ctx.dialect === "postgresql") {
|
|
230
|
-
const
|
|
280
|
+
const connectionOpts = {
|
|
231
281
|
host: host ?? "localhost",
|
|
232
282
|
port: port ?? 5432,
|
|
233
283
|
user,
|
|
234
284
|
password,
|
|
235
|
-
database
|
|
236
|
-
|
|
285
|
+
database,
|
|
286
|
+
...input.clientOpts
|
|
287
|
+
};
|
|
288
|
+
ctx.log.debug({
|
|
289
|
+
dialect: "postgresql",
|
|
290
|
+
host: connectionOpts.host,
|
|
291
|
+
port: connectionOpts.port,
|
|
292
|
+
database,
|
|
293
|
+
user
|
|
294
|
+
}, "connecting to PostgreSQL");
|
|
295
|
+
const pool = new pg_1.Pool(connectionOpts);
|
|
237
296
|
ctx.query = (q, values) => pool.query(q, values ?? []).then((r) => r.rows);
|
|
297
|
+
ctx.pool = pool;
|
|
238
298
|
}
|
|
239
299
|
}
|
|
240
300
|
// It's a bit awkward to put __whereNeedsProcessing, __prepareWhere on the class,
|
|
@@ -278,7 +338,7 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts,
|
|
|
278
338
|
runtime: any;
|
|
279
339
|
clientOpts: { [k: string]: any; },
|
|
280
340
|
otherOpts?: { [k: string]: any; },
|
|
281
|
-
passBeforeValueToAfterCallback
|
|
341
|
+
passBeforeValueToAfterCallback?: boolean,
|
|
282
342
|
}) {
|
|
283
343
|
let otherOpts = opts.otherOpts ?? {};
|
|
284
344
|
if (opts.clientOpts.filename === ":memory:") {
|
|
@@ -291,7 +351,7 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts,
|
|
|
291
351
|
: "otherOpts"}, artifacts);
|
|
292
352
|
this.onHandlerMap = new Map();
|
|
293
353
|
this.eventTarget = new EventTarget();
|
|
294
|
-
this.passBeforeValueToAfterCallback = opts.passBeforeValueToAfterCallback;
|
|
354
|
+
this.passBeforeValueToAfterCallback = opts.passBeforeValueToAfterCallback ?? false;
|
|
295
355
|
}
|
|
296
356
|
|
|
297
357
|
$use(middleware: TMiddleware) {
|
|
@@ -621,6 +681,7 @@ function getMethodSourceOnHandlerPostOne(x) {
|
|
|
621
681
|
return `on${(0, capitalizeFirstLetter_1.capitalizeFirstLetter)(x.methodName)}(handler:
|
|
622
682
|
(sdk: InstanceType<typeof SDK>, input: { data: ${x.typeDataName} },
|
|
623
683
|
output: Partial<${getTypeReturnName(x.table)}>,
|
|
684
|
+
before: null,
|
|
624
685
|
context: TContext,
|
|
625
686
|
) => Promise<void>
|
|
626
687
|
): void {
|
|
@@ -735,6 +796,7 @@ function getMethodSourceOnHandlerDeleteOne(x, findOnes) {
|
|
|
735
796
|
.map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
|
|
736
797
|
.join(" | ")}, },
|
|
737
798
|
output: void,
|
|
799
|
+
before: ${getTypeReturnName(x.table)} | null,
|
|
738
800
|
context: TContext,
|
|
739
801
|
) => Promise<void>
|
|
740
802
|
): void {
|
|
@@ -822,16 +884,16 @@ function getTypeOrderByName(table) {
|
|
|
822
884
|
function getTypeDataPostName(table) {
|
|
823
885
|
return "DataPost" + changeCase.pascalCase(table);
|
|
824
886
|
}
|
|
825
|
-
async function getGetOneData(table, includeMappedFields) {
|
|
887
|
+
async function getGetOneData(table, includeMappedFields, tables) {
|
|
826
888
|
const typeFieldsName = getTypeFieldsName(table);
|
|
827
889
|
const typeReturnBaseName = getTypeReturnBaseName(table);
|
|
828
890
|
return {
|
|
829
891
|
kind: "getOne",
|
|
830
892
|
table,
|
|
831
893
|
methodName: "get" + changeCase.pascalCase(table),
|
|
832
|
-
typeFields: await getTypeFields(table, typeFieldsName, includeMappedFields),
|
|
894
|
+
typeFields: await getTypeFields(table, typeFieldsName, includeMappedFields, tables),
|
|
833
895
|
typeFieldsName,
|
|
834
|
-
typeReturnBase: await getTypeReturnBase(table, typeReturnBaseName, includeMappedFields),
|
|
896
|
+
typeReturnBase: await getTypeReturnBase(table, typeReturnBaseName, includeMappedFields, tables),
|
|
835
897
|
typeReturnBaseName
|
|
836
898
|
};
|
|
837
899
|
}
|
|
@@ -869,7 +931,7 @@ async function getGetListPaginatedData(table) {
|
|
|
869
931
|
typeOrderByName
|
|
870
932
|
};
|
|
871
933
|
}
|
|
872
|
-
async function getPostOneData(table, specialCaseUuidColumn, includeMappedFields) {
|
|
934
|
+
async function getPostOneData(table, specialCaseUuidColumn, includeMappedFields, tables) {
|
|
873
935
|
const typeFieldsName = getTypeFieldsName(table);
|
|
874
936
|
const typeReturnBaseName = getTypeReturnBaseName(table);
|
|
875
937
|
const typeDataName = getTypeDataPostName(table);
|
|
@@ -879,7 +941,7 @@ async function getPostOneData(table, specialCaseUuidColumn, includeMappedFields)
|
|
|
879
941
|
methodName: "post" + changeCase.pascalCase(table),
|
|
880
942
|
typeFieldsName,
|
|
881
943
|
typeReturnBaseName,
|
|
882
|
-
typeData: await getTypeDataPost(table, typeDataName, specialCaseUuidColumn, includeMappedFields),
|
|
944
|
+
typeData: await getTypeDataPost(table, typeDataName, specialCaseUuidColumn, includeMappedFields, tables),
|
|
883
945
|
typeDataName
|
|
884
946
|
};
|
|
885
947
|
}
|
|
@@ -934,7 +996,7 @@ async function getTypeWhere(table, name) {
|
|
|
934
996
|
const jsonSchemaWhere = await getJSONSchemaWhere(table);
|
|
935
997
|
return (0, json_schema_to_typescript_1.compile)(jsonSchemaWhere, name, json2TsOpts);
|
|
936
998
|
}
|
|
937
|
-
async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMappedFields) {
|
|
999
|
+
async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMappedFields, tables) {
|
|
938
1000
|
const primaryColumn = await getPrimaryColumn(table);
|
|
939
1001
|
const tableMeta = (await getTableMeta(table)).filter((x) => x.Field !== primaryColumn.name);
|
|
940
1002
|
const nullable = tableMeta.reduce((acc, m) => ({
|
|
@@ -947,7 +1009,7 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
|
|
|
947
1009
|
}), {});
|
|
948
1010
|
let properties = getJSONSchemaObjProperties(tableMeta);
|
|
949
1011
|
let notRequiredList = [];
|
|
950
|
-
const oneToManyRelations = (await getRelationInfo(table)).filter((x) => x.type === "one-to-many__many-to-one" && x.kind === "one-to-many");
|
|
1012
|
+
const oneToManyRelations = (await getRelationInfo(table, tables)).filter((x) => x.type === "one-to-many__many-to-one" && x.kind === "one-to-many");
|
|
951
1013
|
const mappedFields = includeMappedFields ? await getMappedFields(table) : [];
|
|
952
1014
|
let mappedFieldsMap = new Map();
|
|
953
1015
|
if (includeMappedFields) {
|
|
@@ -1328,9 +1390,9 @@ export type TUpdateOperationsNumber = {$increment: number} | {$decrement: number
|
|
|
1328
1390
|
`;
|
|
1329
1391
|
return prettier.format(src, { parser: "typescript" });
|
|
1330
1392
|
}
|
|
1331
|
-
async function getTypeFields(table, name, includeMappedFields) {
|
|
1393
|
+
async function getTypeFields(table, name, includeMappedFields, tables) {
|
|
1332
1394
|
const scalarKeys = Object.keys(getJSONSchemaObjProperties(await getTableMeta(table)));
|
|
1333
|
-
const relations = await getRelationInfo(table);
|
|
1395
|
+
const relations = await getRelationInfo(table, tables);
|
|
1334
1396
|
const mappedFields = includeMappedFields ? await getMappedFields(table) : [];
|
|
1335
1397
|
const keyWhere = "$where";
|
|
1336
1398
|
const keyOrderBy = "$orderBy";
|
|
@@ -1390,10 +1452,10 @@ async function getTypeFields(table, name, includeMappedFields) {
|
|
|
1390
1452
|
type = imports + "\n\n" + type;
|
|
1391
1453
|
return type;
|
|
1392
1454
|
}
|
|
1393
|
-
async function getTypeReturnBase(table, name, includeMappedFields) {
|
|
1455
|
+
async function getTypeReturnBase(table, name, includeMappedFields, tables) {
|
|
1394
1456
|
const tableMeta = await getTableMeta(table);
|
|
1395
1457
|
const scalarProperties = getJSONSchemaObjProperties(tableMeta);
|
|
1396
|
-
const relations = await getRelationInfo(table);
|
|
1458
|
+
const relations = await getRelationInfo(table, tables);
|
|
1397
1459
|
const mappedFields = includeMappedFields ? await getMappedFields(table) : [];
|
|
1398
1460
|
const jsonSchemaReturn = {
|
|
1399
1461
|
type: "object",
|
|
@@ -1444,6 +1506,7 @@ function getArtifactsSource(artifacts) {
|
|
|
1444
1506
|
return prettier.format(src, { parser: "typescript" });
|
|
1445
1507
|
}
|
|
1446
1508
|
async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn) {
|
|
1509
|
+
const ctx = getCtx();
|
|
1447
1510
|
const tableMetaList = await Promise.all(tables.map(async (table) => {
|
|
1448
1511
|
const [tableMeta, primaryKey, dumpSchema] = await Promise.all([
|
|
1449
1512
|
getTableMeta(table),
|
|
@@ -1451,7 +1514,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
|
|
|
1451
1514
|
getShowCreateTable(table)
|
|
1452
1515
|
]);
|
|
1453
1516
|
const scalarFields = tableMeta.map((x) => x.Field);
|
|
1454
|
-
const relationInfo = await getRelationInfo(table);
|
|
1517
|
+
const relationInfo = await getRelationInfo(table, tables);
|
|
1455
1518
|
const relationFields = relationInfo.reduce((acc, x) => {
|
|
1456
1519
|
if (x.type === "one-to-many__many-to-one") {
|
|
1457
1520
|
acc[x.name] = {
|
|
@@ -1509,7 +1572,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
|
|
|
1509
1572
|
}
|
|
1510
1573
|
return {
|
|
1511
1574
|
kind: "scalar",
|
|
1512
|
-
type: getBaseJSONType(t.Type,
|
|
1575
|
+
type: getBaseJSONType(t.Type, ctx.dialect),
|
|
1513
1576
|
name: t.Field,
|
|
1514
1577
|
nullable,
|
|
1515
1578
|
hasDefaultValue: !!t.Default
|
|
@@ -1554,7 +1617,7 @@ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn)
|
|
|
1554
1617
|
}, {});
|
|
1555
1618
|
return artifacts;
|
|
1556
1619
|
}
|
|
1557
|
-
const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(table) {
|
|
1620
|
+
const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(table, tables) {
|
|
1558
1621
|
const relationsManyToOne = await getRelationsManyToOne(table);
|
|
1559
1622
|
const relationsOneToMany = await getRelationsOneToMany(table);
|
|
1560
1623
|
let out = [];
|
|
@@ -1601,7 +1664,7 @@ const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(ta
|
|
|
1601
1664
|
});
|
|
1602
1665
|
return acc;
|
|
1603
1666
|
}, []));
|
|
1604
|
-
const relationsManyToMany = (await getJunctionTables()).reduce((acc, x) => {
|
|
1667
|
+
const relationsManyToMany = (await getJunctionTables(tables)).reduce((acc, x) => {
|
|
1605
1668
|
const dataForParentTable = x.relations.find((r) => r.referencedTable === table);
|
|
1606
1669
|
if (dataForParentTable == null) {
|
|
1607
1670
|
return acc;
|
|
@@ -1622,15 +1685,14 @@ const getRelationInfo = (0, memoize_1.default)(async function getRelationInfo(ta
|
|
|
1622
1685
|
return acc;
|
|
1623
1686
|
}, []);
|
|
1624
1687
|
out = out.concat(relationsManyToMany);
|
|
1625
|
-
out = _.sortBy((x) => x.table, out);
|
|
1688
|
+
out = _.sortBy([(x) => x.table, (x) => x.name], out);
|
|
1626
1689
|
return out;
|
|
1627
1690
|
}, (table) => getCtx().runId + ":" + table);
|
|
1628
1691
|
function getRelationManyToOneFieldName(x) {
|
|
1629
1692
|
return changeCase.camelCase(x.foreignKey.replace(new RegExp(x.referencedKey + "$", "i"), ""));
|
|
1630
1693
|
}
|
|
1631
1694
|
// TODO: not sure if this logic is correct
|
|
1632
|
-
async function getJunctionTables() {
|
|
1633
|
-
const tables = await getTableNames();
|
|
1695
|
+
const getJunctionTables = (0, memoize_1.default)(async function getJunctionTables(tables) {
|
|
1634
1696
|
return (await Promise.all(tables.map(async (table) => {
|
|
1635
1697
|
const relations = await getRelationsManyToOne(table);
|
|
1636
1698
|
if (relations.length === 2 &&
|
|
@@ -1644,7 +1706,7 @@ async function getJunctionTables() {
|
|
|
1644
1706
|
}
|
|
1645
1707
|
return null;
|
|
1646
1708
|
}))).filter(isNotNullOrUndefined_1.isNotNullOrUndefined);
|
|
1647
|
-
}
|
|
1709
|
+
}, () => getCtx().runId);
|
|
1648
1710
|
// `from` relations
|
|
1649
1711
|
// https://stackoverflow.com/a/54732547
|
|
1650
1712
|
const getRelationsManyToOne = (0, memoize_1.default)(async function getRelationsManyToOne(table) {
|
|
@@ -1674,7 +1736,8 @@ const getRelationsManyToOne = (0, memoize_1.default)(async function getRelations
|
|
|
1674
1736
|
FROM information_schema.key_column_usage kcu
|
|
1675
1737
|
JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
|
|
1676
1738
|
JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
|
|
1677
|
-
WHERE kcu.table_schema = 'public' AND kcu.table_name = $1
|
|
1739
|
+
WHERE kcu.table_schema = 'public' AND kcu.table_name = $1
|
|
1740
|
+
ORDER BY ccu.table_name, ccu.column_name`, [table]);
|
|
1678
1741
|
}
|
|
1679
1742
|
else {
|
|
1680
1743
|
throw new Error("Unsupported dialect: " + dialect);
|
|
@@ -1688,7 +1751,7 @@ const getRelationsManyToOne = (0, memoize_1.default)(async function getRelations
|
|
|
1688
1751
|
nullable: tableMeta.find((m) => m.Field === v.t1Field)?.Null === "YES"
|
|
1689
1752
|
};
|
|
1690
1753
|
})));
|
|
1691
|
-
return _.sortBy((x) => x.referencedTable, xs);
|
|
1754
|
+
return _.sortBy([(x) => x.referencedTable, (x) => x.referencedKey, (x) => x.foreignKey], xs);
|
|
1692
1755
|
}, (table) => getCtx().runId + ":" + table);
|
|
1693
1756
|
// `to` relations
|
|
1694
1757
|
const getRelationsOneToMany = (0, memoize_1.default)(async function getRelationsOneToMany(table) {
|
|
@@ -1717,7 +1780,8 @@ const getRelationsOneToMany = (0, memoize_1.default)(async function getRelations
|
|
|
1717
1780
|
FROM information_schema.key_column_usage kcu
|
|
1718
1781
|
JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name AND kcu.table_schema = rc.constraint_schema
|
|
1719
1782
|
JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name AND rc.unique_constraint_schema = ccu.table_schema
|
|
1720
|
-
WHERE kcu.table_schema = 'public' AND ccu.table_name = $1
|
|
1783
|
+
WHERE kcu.table_schema = 'public' AND ccu.table_name = $1
|
|
1784
|
+
ORDER BY kcu.table_name, kcu.column_name`, [table]);
|
|
1721
1785
|
}
|
|
1722
1786
|
else {
|
|
1723
1787
|
throw new Error("Unsupported dialect: " + dialect);
|
|
@@ -1731,7 +1795,7 @@ const getRelationsOneToMany = (0, memoize_1.default)(async function getRelations
|
|
|
1731
1795
|
nullable: false
|
|
1732
1796
|
};
|
|
1733
1797
|
})));
|
|
1734
|
-
return _.sortBy((x) => x.referencedKey,
|
|
1798
|
+
return _.sortBy([(x) => x.referencedTable, (x) => x.referencedKey, (x) => x.foreignKey], xs);
|
|
1735
1799
|
}, (table) => getCtx().runId + ":" + table);
|
|
1736
1800
|
async function getPrimaryColumn(table) {
|
|
1737
1801
|
const tableMeta = await getTableMeta(table);
|
|
@@ -1770,27 +1834,29 @@ async function getUuidColumn(table) {
|
|
|
1770
1834
|
nullable: column.Null === "YES"
|
|
1771
1835
|
};
|
|
1772
1836
|
}
|
|
1773
|
-
const getPgEnumDefinition = (0, memoize_1.default)(async function getPgEnumDefinition(udtName) {
|
|
1837
|
+
const getPgEnumDefinition = (0, memoize_1.default)(async function getPgEnumDefinition(udtSchema, udtName) {
|
|
1774
1838
|
const { dialect, query } = getCtx();
|
|
1775
1839
|
if (dialect !== "postgresql")
|
|
1776
1840
|
return null;
|
|
1777
1841
|
const rows = await query(`SELECT e.enumlabel FROM pg_enum e
|
|
1778
1842
|
JOIN pg_type t ON e.enumtypid = t.oid
|
|
1779
1843
|
JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid
|
|
1780
|
-
WHERE t.typname = $
|
|
1781
|
-
ORDER BY e.enumsortorder`, [udtName]);
|
|
1844
|
+
WHERE t.typname = $2 AND n.nspname = $1
|
|
1845
|
+
ORDER BY e.enumsortorder`, [udtSchema, udtName]);
|
|
1782
1846
|
if (rows.length === 0)
|
|
1783
1847
|
return null;
|
|
1784
1848
|
const labels = rows.map((r) => String(r.enumlabel).replace(/'/g, "''"));
|
|
1785
1849
|
return "enum('" + labels.join("', '") + "')";
|
|
1786
|
-
}, (udtName) => getCtx().runId + ":" + udtName);
|
|
1850
|
+
}, (udtSchema, udtName) => getCtx().runId + ":" + udtSchema + ":" + udtName);
|
|
1787
1851
|
const getTableMeta = (0, memoize_1.default)(async function getTableMeta(table) {
|
|
1788
|
-
const
|
|
1852
|
+
const ctx = getCtx();
|
|
1853
|
+
const { dialect, query } = ctx;
|
|
1854
|
+
ctx.log.debug({ table }, "getTableMeta() fetching");
|
|
1789
1855
|
if (dialect === "mysql") {
|
|
1790
1856
|
return query("DESCRIBE ??", [table]).then((xs) => _.sortBy((x) => x.Field, xs));
|
|
1791
1857
|
}
|
|
1792
1858
|
if (dialect === "postgresql") {
|
|
1793
|
-
const columns = await query(`SELECT column_name AS "Field", data_type, udt_name, character_maximum_length AS char_max, is_nullable, column_default AS "Default"
|
|
1859
|
+
const columns = await query(`SELECT column_name AS "Field", data_type, udt_schema, udt_name, character_maximum_length AS char_max, is_nullable, column_default AS "Default"
|
|
1794
1860
|
FROM information_schema.columns
|
|
1795
1861
|
WHERE table_schema = 'public' AND table_name = $1
|
|
1796
1862
|
ORDER BY ordinal_position`, [table]);
|
|
@@ -1815,17 +1881,26 @@ const getTableMeta = (0, memoize_1.default)(async function getTableMeta(table) {
|
|
|
1815
1881
|
if (!keyMap.has(k.col) || k.key_type === "PRI")
|
|
1816
1882
|
keyMap.set(k.col, k.key_type);
|
|
1817
1883
|
}
|
|
1818
|
-
const
|
|
1819
|
-
...new
|
|
1820
|
-
.filter((c) => c.data_type === "USER-DEFINED" &&
|
|
1821
|
-
.
|
|
1884
|
+
const udtKeys = [
|
|
1885
|
+
...new Map(columns
|
|
1886
|
+
.filter((c) => c.data_type === "USER-DEFINED" &&
|
|
1887
|
+
c.udt_schema != null &&
|
|
1888
|
+
c.udt_name != null)
|
|
1889
|
+
.map((c) => [`${c.udt_schema}.${c.udt_name}`, c])).keys()
|
|
1822
1890
|
];
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1891
|
+
const udtPairs = udtKeys.map((k) => {
|
|
1892
|
+
const [s, n] = k.split(".", 2);
|
|
1893
|
+
return [s, n];
|
|
1894
|
+
});
|
|
1895
|
+
const enumDefs = await Promise.all(udtPairs.map(([schema, name]) => getPgEnumDefinition(schema, name)));
|
|
1896
|
+
const enumMap = new Map(udtKeys.map((k, i) => [k, enumDefs[i] ?? "varchar(255)"]));
|
|
1897
|
+
const cols = columns.map((c) => {
|
|
1826
1898
|
let type;
|
|
1827
|
-
if (c.data_type === "USER-DEFINED" &&
|
|
1828
|
-
|
|
1899
|
+
if (c.data_type === "USER-DEFINED" &&
|
|
1900
|
+
c.udt_schema != null &&
|
|
1901
|
+
c.udt_name != null) {
|
|
1902
|
+
const enumKey = `${c.udt_schema}.${c.udt_name}`;
|
|
1903
|
+
type = enumMap.get(enumKey) ?? "character varying(255)";
|
|
1829
1904
|
}
|
|
1830
1905
|
else {
|
|
1831
1906
|
type = c.data_type;
|
|
@@ -1839,9 +1914,18 @@ const getTableMeta = (0, memoize_1.default)(async function getTableMeta(table) {
|
|
|
1839
1914
|
Type: type,
|
|
1840
1915
|
Null: c.is_nullable === "YES" ? "YES" : "NO",
|
|
1841
1916
|
Key: keyMap.get(c.Field) ?? "",
|
|
1842
|
-
|
|
1917
|
+
// Preserve `null` when there is no default so that
|
|
1918
|
+
// required-field detection (via `hasDefault`) works
|
|
1919
|
+
// consistently with the MySQL `DESCRIBE` output.
|
|
1920
|
+
Default: c.Default,
|
|
1921
|
+
...(c.data_type === "USER-DEFINED" &&
|
|
1922
|
+
c.udt_schema != null &&
|
|
1923
|
+
c.udt_name != null
|
|
1924
|
+
? { PgType: c.udt_name }
|
|
1925
|
+
: {})
|
|
1843
1926
|
};
|
|
1844
1927
|
});
|
|
1928
|
+
return _.sortBy((c) => c.Field, cols);
|
|
1845
1929
|
}
|
|
1846
1930
|
throw new Error("Unsupported dialect: " + dialect);
|
|
1847
1931
|
}, (table) => getCtx().runId + ":" + table);
|
|
@@ -1859,7 +1943,21 @@ async function getShowCreateTable(table) {
|
|
|
1859
1943
|
]);
|
|
1860
1944
|
const refByFk = new Map(relations.map((r) => [r.foreignKey, r]));
|
|
1861
1945
|
const columnDefs = tableMeta.map((c) => {
|
|
1862
|
-
|
|
1946
|
+
const isSerialPk = c.Key === "PRI" &&
|
|
1947
|
+
c.Default != null &&
|
|
1948
|
+
c.Default !== "" &&
|
|
1949
|
+
/nextval\s*\(/i.test(c.Default);
|
|
1950
|
+
if (isSerialPk) {
|
|
1951
|
+
const baseType = (c.PgType ?? c.Type).toLowerCase();
|
|
1952
|
+
const serialType = baseType === "bigint" || baseType === "int8"
|
|
1953
|
+
? "BIGSERIAL"
|
|
1954
|
+
: baseType === "smallint" || baseType === "int2"
|
|
1955
|
+
? "SMALLSERIAL"
|
|
1956
|
+
: "SERIAL";
|
|
1957
|
+
return `"${c.Field.replace(/"/g, '""')}" ${serialType} PRIMARY KEY`;
|
|
1958
|
+
}
|
|
1959
|
+
const pgType = c.PgType ?? c.Type;
|
|
1960
|
+
let def = `"${c.Field.replace(/"/g, '""')}" ${pgType} ${c.Null === "YES" ? "NULL" : "NOT NULL"}`;
|
|
1863
1961
|
if (c.Default != null && c.Default !== "") {
|
|
1864
1962
|
def += ` DEFAULT ${c.Default}`;
|
|
1865
1963
|
}
|
|
@@ -1873,7 +1971,7 @@ async function getShowCreateTable(table) {
|
|
|
1873
1971
|
}
|
|
1874
1972
|
return def;
|
|
1875
1973
|
});
|
|
1876
|
-
return `CREATE TABLE "${table.replace(/"/g, '""')}" (\n ${columnDefs.join(",\n ")}\n)
|
|
1974
|
+
return `CREATE TABLE "${table.replace(/"/g, '""')}" (\n ${columnDefs.join(",\n ")}\n);`;
|
|
1877
1975
|
}
|
|
1878
1976
|
return Promise.resolve(null);
|
|
1879
1977
|
}
|
|
@@ -2043,18 +2141,18 @@ function getPropertyFormat(sqlType) {
|
|
|
2043
2141
|
}
|
|
2044
2142
|
return undefined;
|
|
2045
2143
|
}
|
|
2046
|
-
async function getTableNames() {
|
|
2144
|
+
const getTableNames = (0, memoize_1.default)(async function getTableNames() {
|
|
2047
2145
|
const { dialect, query } = getCtx();
|
|
2048
2146
|
if (dialect === "mysql") {
|
|
2049
2147
|
return query("SHOW TABLES").then((xs) => xs.flatMap((x) => Object.values(x)).sort());
|
|
2050
2148
|
}
|
|
2051
2149
|
if (dialect === "postgresql") {
|
|
2052
2150
|
return query(`SELECT table_name FROM information_schema.tables
|
|
2053
|
-
|
|
2054
|
-
|
|
2151
|
+
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
|
2152
|
+
ORDER BY table_name`).then((rows) => rows.map((r) => r.table_name));
|
|
2055
2153
|
}
|
|
2056
2154
|
throw new Error("Unsupported dialect: " + dialect);
|
|
2057
|
-
}
|
|
2155
|
+
}, () => getCtx().runId);
|
|
2058
2156
|
function getMysql2sqliteSrc() {
|
|
2059
2157
|
return `#!/usr/bin/awk -f
|
|
2060
2158
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertPgSchemaToSqlite = convertPgSchemaToSqlite;
|
|
4
|
+
/**
|
|
5
|
+
* Reflects the PostgreSQL schema from the given connection and returns
|
|
6
|
+
* SQLite-compatible CREATE TABLE statements as a string.
|
|
7
|
+
*/
|
|
8
|
+
async function convertPgSchemaToSqlite(pool) {
|
|
9
|
+
const columnsResult = await pool.query(`
|
|
10
|
+
SELECT table_schema, table_name, column_name, ordinal_position,
|
|
11
|
+
data_type, character_maximum_length, is_nullable, column_default
|
|
12
|
+
FROM information_schema.columns
|
|
13
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
14
|
+
ORDER BY table_schema, table_name, ordinal_position
|
|
15
|
+
`);
|
|
16
|
+
const pkResult = await pool.query(`
|
|
17
|
+
SELECT tc.table_schema, tc.table_name, kcu.column_name
|
|
18
|
+
FROM information_schema.table_constraints tc
|
|
19
|
+
JOIN information_schema.key_column_usage kcu
|
|
20
|
+
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
|
21
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
22
|
+
`);
|
|
23
|
+
const primaryKeys = new Set(pkResult.rows.map((r) => `${r.table_schema}.${r.table_name}.${r.column_name}`));
|
|
24
|
+
const byTable = {};
|
|
25
|
+
for (const c of columnsResult.rows) {
|
|
26
|
+
const key = `${c.table_schema}.${c.table_name}`;
|
|
27
|
+
if (!byTable[key])
|
|
28
|
+
byTable[key] = [];
|
|
29
|
+
byTable[key].push(c);
|
|
30
|
+
}
|
|
31
|
+
const statements = [];
|
|
32
|
+
for (const [key, cols] of Object.entries(byTable)) {
|
|
33
|
+
const tableName = key.split(".").slice(-1)[0];
|
|
34
|
+
const parts = cols.map((c) => {
|
|
35
|
+
const sqliteType = toSqliteType(c.data_type);
|
|
36
|
+
const pk = primaryKeys.has(`${c.table_schema}.${c.table_name}.${c.column_name}`);
|
|
37
|
+
const notNull = c.is_nullable === "NO" && !(c.column_default?.includes("nextval") ?? false);
|
|
38
|
+
const defaultVal = pgDefaultToSqlite(c.column_default);
|
|
39
|
+
const pkClause = pk ? " PRIMARY KEY" : "";
|
|
40
|
+
const nullClause = notNull && !pk ? " NOT NULL" : "";
|
|
41
|
+
const defaultClause = defaultVal != null ? ` DEFAULT ${defaultVal}` : "";
|
|
42
|
+
return ` "${c.column_name}" ${sqliteType}${pkClause}${nullClause}${defaultClause}`;
|
|
43
|
+
});
|
|
44
|
+
statements.push(`CREATE TABLE "${tableName}" (\n${parts.join(",\n")}\n);`);
|
|
45
|
+
}
|
|
46
|
+
return statements.join("\n\n") + "\n";
|
|
47
|
+
}
|
|
48
|
+
const PG_TO_SQLITE_TYPE = {
|
|
49
|
+
smallint: "INTEGER",
|
|
50
|
+
integer: "INTEGER",
|
|
51
|
+
bigint: "INTEGER",
|
|
52
|
+
serial: "INTEGER",
|
|
53
|
+
bigserial: "INTEGER",
|
|
54
|
+
smallserial: "INTEGER",
|
|
55
|
+
real: "REAL",
|
|
56
|
+
"double precision": "REAL",
|
|
57
|
+
numeric: "REAL",
|
|
58
|
+
decimal: "REAL",
|
|
59
|
+
boolean: "INTEGER",
|
|
60
|
+
character: "TEXT",
|
|
61
|
+
"character varying": "TEXT",
|
|
62
|
+
varchar: "TEXT",
|
|
63
|
+
char: "TEXT",
|
|
64
|
+
text: "TEXT",
|
|
65
|
+
timestamp: "TEXT",
|
|
66
|
+
"timestamp with time zone": "TEXT",
|
|
67
|
+
"timestamp without time zone": "TEXT",
|
|
68
|
+
date: "TEXT",
|
|
69
|
+
time: "TEXT",
|
|
70
|
+
json: "TEXT",
|
|
71
|
+
jsonb: "TEXT",
|
|
72
|
+
uuid: "TEXT",
|
|
73
|
+
bytea: "BLOB"
|
|
74
|
+
};
|
|
75
|
+
function toSqliteType(pgType) {
|
|
76
|
+
const base = (pgType ?? "")
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replace(/^(.*?)(\s+with.*|\s+without.*)$/, "$1")
|
|
79
|
+
.trim();
|
|
80
|
+
return PG_TO_SQLITE_TYPE[base] ?? "TEXT";
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Converts a PostgreSQL column_default expression to a SQLite DEFAULT clause
|
|
84
|
+
* value (without the "DEFAULT" keyword). Returns null if we should not emit a default.
|
|
85
|
+
*/
|
|
86
|
+
function pgDefaultToSqlite(columnDefault) {
|
|
87
|
+
if (columnDefault == null || columnDefault.trim() === "")
|
|
88
|
+
return null;
|
|
89
|
+
const raw = columnDefault.trim();
|
|
90
|
+
// Skip sequence-based defaults (serial/identity); SQLite uses INTEGER PRIMARY KEY
|
|
91
|
+
if (raw.toLowerCase().includes("nextval"))
|
|
92
|
+
return null;
|
|
93
|
+
// Timestamp / time
|
|
94
|
+
if (/^(now\s*\(\s*\)|current_timestamp)$/i.test(raw))
|
|
95
|
+
return "CURRENT_TIMESTAMP";
|
|
96
|
+
if (/^current_date$/i.test(raw))
|
|
97
|
+
return "CURRENT_DATE";
|
|
98
|
+
if (/^current_time$/i.test(raw))
|
|
99
|
+
return "CURRENT_TIME";
|
|
100
|
+
// Booleans
|
|
101
|
+
if (/^\s*true\s*$/i.test(raw))
|
|
102
|
+
return "1";
|
|
103
|
+
if (/^\s*false\s*$/i.test(raw))
|
|
104
|
+
return "0";
|
|
105
|
+
// Numeric literal (optionally with ::type)
|
|
106
|
+
const numericMatch = raw.match(/^(-?\d+(?:\.\d+)?)\s*(?:::\s*\w+(?:\s+\w+)*)?\s*$/);
|
|
107
|
+
if (numericMatch)
|
|
108
|
+
return numericMatch[1];
|
|
109
|
+
// String literal: '...' with optional ::type
|
|
110
|
+
const stringMatch = raw.match(/^'(.*)'\s*(?:::\s*\w+(?:\s+\w+)*)?\s*$/s);
|
|
111
|
+
if (stringMatch) {
|
|
112
|
+
const inner = stringMatch[1].replace(/'/g, "''");
|
|
113
|
+
return `'${inner}'`;
|
|
114
|
+
}
|
|
115
|
+
// Unknown expression: strip ::type and emit in parens if it looks safe
|
|
116
|
+
const withoutCast = raw.replace(/\s*::\s*[\w\s]+$/, "").trim();
|
|
117
|
+
if (withoutCast && /^[\w().\s+-]+$/i.test(withoutCast)) {
|
|
118
|
+
return `(${withoutCast})`;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
@@ -70,7 +70,9 @@ async function resolve(input, dbCall, formatQuery, beginTransaction, dialect, mi
|
|
|
70
70
|
const shouldRunOnHandler = typeof onHandler === "function" &&
|
|
71
71
|
asyncLocalStorage != null &&
|
|
72
72
|
asyncLocalStorage?.getStore()?.isInOnHandler !== true;
|
|
73
|
-
const beforeValue = shouldRunOnHandler &&
|
|
73
|
+
const beforeValue = shouldRunOnHandler &&
|
|
74
|
+
input.passBeforeValueToAfterCallback &&
|
|
75
|
+
input.action !== "create"
|
|
74
76
|
? await _resolve({
|
|
75
77
|
...paramsChanged,
|
|
76
78
|
action: paramsChanged.action === "updateMany" ||
|
|
@@ -103,7 +105,9 @@ async function resolve(input, dbCall, formatQuery, beginTransaction, dialect, mi
|
|
|
103
105
|
const shouldRunOnHandler = typeof onHandler === "function" &&
|
|
104
106
|
asyncLocalStorage != null &&
|
|
105
107
|
asyncLocalStorage?.getStore()?.isInOnHandler !== true;
|
|
106
|
-
const beforeValue = shouldRunOnHandler &&
|
|
108
|
+
const beforeValue = shouldRunOnHandler &&
|
|
109
|
+
input.passBeforeValueToAfterCallback &&
|
|
110
|
+
input.action !== "create"
|
|
107
111
|
? await _resolve({
|
|
108
112
|
...input,
|
|
109
113
|
action: input.action === "updateMany" || input.action === "deleteMany"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@technicity/data-service-generator",
|
|
3
|
-
"version": "0.23.0-next.
|
|
3
|
+
"version": "0.23.0-next.11",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"mysql2": "^3.10.1",
|
|
28
28
|
"pg": "^8.13.1",
|
|
29
29
|
"pg-format": "^1.0.4",
|
|
30
|
+
"pino": "^9.5.0",
|
|
31
|
+
"pino-pretty": "^13.1.3",
|
|
30
32
|
"prettier": "^2.1.2",
|
|
31
33
|
"sqlstring": "^2.3.2",
|
|
32
34
|
"sqlstring-sqlite": "^0.1.1",
|