@technicity/data-service-generator 0.14.1 → 0.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/dist/generation/generate.d.ts +1 -0
  3. package/dist/generation/generate.js +916 -309
  4. package/dist/runtime/Cache.js +6 -3
  5. package/dist/runtime/IRuntime.d.ts +46 -17
  6. package/dist/runtime/RuntimeMSSQL.d.ts +7 -1
  7. package/dist/runtime/RuntimeMSSQL.js +4 -4
  8. package/dist/runtime/RuntimeMySQL.d.ts +3 -1
  9. package/dist/runtime/RuntimeMySQL.js +33 -7
  10. package/dist/runtime/RuntimeSQLite.d.ts +38 -0
  11. package/dist/runtime/RuntimeSQLite.js +135 -0
  12. package/dist/runtime/lib/MSSQL.d.ts +2 -1
  13. package/dist/runtime/lib/MSSQL.js +36 -8
  14. package/dist/runtime/lib/MySQL.d.ts +1 -1
  15. package/dist/runtime/lib/MySQL.js +15 -2
  16. package/dist/runtime/lib/addNullFallbacks.test.d.ts +1 -0
  17. package/dist/runtime/lib/addNullFallbacks.test.js +206 -0
  18. package/dist/runtime/lib/getSqlAst.js +158 -121
  19. package/dist/runtime/lib/shared.d.ts +1 -2
  20. package/dist/runtime/lib/shared.js +180 -71
  21. package/dist/runtime/lib/stringifyWhere.js +39 -12
  22. package/dist/runtime/lib/stringifyWhere.test.d.ts +1 -0
  23. package/dist/runtime/lib/stringifyWhere.test.js +236 -0
  24. package/dist/runtime/lib/typeCastMSSQL.js +24 -1
  25. package/dist/traverseFieldArgs.d.ts +2 -2
  26. package/dist/traverseFieldArgs.js +8 -3
  27. package/dist/traverseFieldArgs.test.d.ts +1 -0
  28. package/dist/traverseFieldArgs.test.js +56 -0
  29. package/package.json +3 -6
  30. package/dist/ksql.d.ts +0 -15
  31. package/dist/ksql.js +0 -55
  32. package/dist/runtime/RuntimeKSQL.d.ts +0 -19
  33. package/dist/runtime/RuntimeKSQL.js +0 -446
  34. package/dist/runtime/lib/runTransforms.d.ts +0 -2
  35. package/dist/runtime/lib/runTransforms.js +0 -36
@@ -1,18 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
2
28
  Object.defineProperty(exports, "__esModule", { value: true });
3
29
  exports.generate = void 0;
4
- const path = require("path");
5
- const fs = require("fs");
6
- const os = require("os");
7
- const child_process = require("child_process");
8
- const prettier = require("prettier");
9
- const changeCase = require("change-case");
10
- const fse = require("fs-extra");
11
- const _ = require("lodash/fp");
12
- const uuid_1 = require("uuid");
13
- const mssql = require("mssql");
30
+ const path = __importStar(require("node:path"));
31
+ const fs = __importStar(require("node:fs"));
32
+ const os = __importStar(require("node:os"));
33
+ const child_process = __importStar(require("node:child_process"));
34
+ const node_crypto_1 = __importDefault(require("node:crypto"));
35
+ const prettier = __importStar(require("prettier"));
36
+ const changeCase = __importStar(require("change-case"));
37
+ const fse = __importStar(require("fs-extra"));
38
+ const _ = __importStar(require("lodash/fp"));
39
+ const mssql = __importStar(require("mssql"));
14
40
  // @ts-ignore
15
- const TSqlString = require("tsqlstring");
41
+ const TSqlString = __importStar(require("tsqlstring"));
16
42
  const json_schema_to_typescript_1 = require("json-schema-to-typescript");
17
43
  const getDuplicates_1 = require("../lib/getDuplicates");
18
44
  const isNotNullOrUndefined_1 = require("../lib/isNotNullOrUndefined");
@@ -25,8 +51,9 @@ const MySQL_1 = require("../runtime/lib/MySQL");
25
51
  let query = undefined;
26
52
  let dialect = "mysql";
27
53
  const json2TsOpts = {
28
- bannerComment: "",
54
+ bannerComment: ""
29
55
  };
56
+ const keyFields = "$fields";
30
57
  async function generate(input) {
31
58
  if (input.tables != null && input.excludeTables != null) {
32
59
  throw new Error("Must specify either `tables` or `excludeTables`, not both");
@@ -57,22 +84,26 @@ async function generate(input) {
57
84
  getPatchOneData(x, specialCaseUuidColumn, includeMappedFields),
58
85
  getPatchListData(x),
59
86
  getDeleteOneData(x),
60
- getDeleteListData(x),
87
+ getDeleteListData(x)
61
88
  ]));
62
- const artifactsSource = await getArtifactsSource(tables, includeMappedFields, specialCaseUuidColumn);
63
- const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts);
89
+ const artifacts = await getArtifacts(tables, includeMappedFields, specialCaseUuidColumn);
90
+ const artifactsSource = getArtifactsSource(artifacts);
91
+ const sdkSource = await getSDKSource(data, specialCaseUuidColumn, supplementClientOpts, artifacts);
64
92
  const sdkFilename = "index.ts";
93
+ const sourceIRuntimeFilePath = fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.ts"))
94
+ ? path.join(__dirname, "../runtime", "IRuntime.ts")
95
+ : path.join(__dirname, "../runtime", "IRuntime.js");
96
+ const IRuntimeFilename = path.basename(sourceIRuntimeFilePath);
65
97
  const artifactsFilename = "artifacts.ts";
66
- const IRuntimeFilename = "IRuntime.d.ts";
67
98
  const tsConfigJSON = {
68
99
  compilerOptions: {
69
100
  module: "commonjs",
70
101
  moduleResolution: "node",
71
102
  target: "es2020",
72
103
  declaration: true,
73
- outDir: "./sdk-ts",
104
+ outDir: "./sdk-ts"
74
105
  },
75
- include: [sdkFilename, artifactsFilename, IRuntimeFilename],
106
+ include: [sdkFilename, artifactsFilename, IRuntimeFilename]
76
107
  };
77
108
  const packageJSON = {
78
109
  name: "temp",
@@ -81,14 +112,15 @@ async function generate(input) {
81
112
  dependencies: require("../../package.json").dependencies,
82
113
  devDependencies: {
83
114
  "@types/node": require("../../package.json").devDependencies["@types/node"],
84
- typescript: require("../../package.json").devDependencies.typescript,
85
- },
115
+ typescript: require("../../package.json").devDependencies.typescript
116
+ }
86
117
  };
87
- const tmpDirPath = path.join(os.tmpdir(), `dsg-${(0, uuid_1.v4)()}`);
118
+ const tmpDirPath = path.join(os.tmpdir(),
119
+ // _ because - in filename is not supported by mysql2sqlite
120
+ `dsg_${node_crypto_1.default.randomUUID()}`.replace(/-/g, "_"));
88
121
  fse.mkdirpSync(tmpDirPath);
89
122
  fs.writeFileSync(path.join(tmpDirPath, sdkFilename), sdkSource);
90
123
  fs.writeFileSync(path.join(tmpDirPath, artifactsFilename), artifactsSource);
91
- const sourceIRuntimeFilePath = path.join(__dirname, "../runtime", IRuntimeFilename);
92
124
  fse.copyFileSync(sourceIRuntimeFilePath, path.join(tmpDirPath, IRuntimeFilename));
93
125
  const typesDirPath = path.join(tmpDirPath, "types");
94
126
  fse.mkdirpSync(typesDirPath);
@@ -128,7 +160,40 @@ async function generate(input) {
128
160
  .join("\n"));
129
161
  // TODO: workaround for IRuntime.d.ts not being included
130
162
  // copyFileSync hangs for some reason, so use writeFileSync + readFileSync instead
131
- fs.writeFileSync(path.join(tmpBuildOutputPath, IRuntimeFilename), fs.readFileSync(sourceIRuntimeFilePath));
163
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "IRuntime.d.ts"), fs.existsSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
164
+ ? fs.readFileSync(path.join(__dirname, "../runtime", "IRuntime.d.ts"))
165
+ : fs.readFileSync(sourceIRuntimeFilePath));
166
+ if (dialect === "mysql" && input.outputSqliteSchema) {
167
+ // Since mysql2sqlite outputs a malformed string if a column
168
+ // has the name `enum`, temporarily change the name to something else,
169
+ // then change it back.
170
+ const enumMarker = "`" + node_crypto_1.default.randomUUID() + "`";
171
+ const schemaMySql = Object.values(artifacts)
172
+ .reduce((acc, x) => {
173
+ let d = x.dump?.schema;
174
+ if (!d) {
175
+ return acc;
176
+ }
177
+ d = d.replace(/`enum`/g, enumMarker);
178
+ d += ";";
179
+ acc.push(d);
180
+ return acc;
181
+ }, [])
182
+ .join("\n\n");
183
+ const mysql2SqliteSrc = getMysql2sqliteSrc();
184
+ const mysql2SqlitePath = path.join(tmpDirPath, "mysql2sqlite");
185
+ fs.writeFileSync(mysql2SqlitePath, mysql2SqliteSrc);
186
+ fs.chmodSync(mysql2SqlitePath, 0o755);
187
+ const tmpMySqlSchemaFilename = "tmp.sql";
188
+ const tmpMySqlSchemaPath = path.join(tmpDirPath, tmpMySqlSchemaFilename);
189
+ fs.writeFileSync(tmpMySqlSchemaPath, schemaMySql);
190
+ let schemaSqlite = child_process
191
+ .execFileSync(mysql2SqlitePath, [tmpMySqlSchemaFilename], { cwd: tmpDirPath })
192
+ .toString();
193
+ schemaSqlite = schemaSqlite.replace(new RegExp(enumMarker, "g"), "`enum`");
194
+ const src = prettier.format(`module.exports = { schema: \`${schemaSqlite.replace(/`/g, "\\`")}\` }`, { parser: "babel" });
195
+ fs.writeFileSync(path.join(tmpBuildOutputPath, "artifacts.sqlite.js"), src);
196
+ }
132
197
  if (!fs.existsSync(outdir)) {
133
198
  fse.mkdirpSync(outdir);
134
199
  }
@@ -145,17 +210,17 @@ function init(input) {
145
210
  password,
146
211
  host,
147
212
  port,
148
- database,
213
+ database
149
214
  });
150
215
  query = mysql.query.bind(mysql);
151
216
  }
152
- if (dialect === "mssql" || dialect === "ksql") {
217
+ if (dialect === "mssql") {
153
218
  const pool = new mssql.ConnectionPool({
154
219
  server: server ?? "localhost",
155
220
  user,
156
221
  password,
157
222
  port,
158
- database,
223
+ database
159
224
  });
160
225
  const poolConnect = pool.connect();
161
226
  async function runMSSQLQuery(...input) {
@@ -170,7 +235,7 @@ function init(input) {
170
235
  }
171
236
  // It's a bit awkward to put __whereNeedsProcessing, __prepareWhere on the class,
172
237
  // but it allows us to share the same database pool, clientOpts, etc.
173
- async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts) {
238
+ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts, artifacts) {
174
239
  function getTypeImports() {
175
240
  let set = new Set();
176
241
  for (let d of input) {
@@ -179,7 +244,7 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts)
179
244
  "typeReturnBaseName",
180
245
  "typeWhereName",
181
246
  "typeOrderByName",
182
- "typeDataName",
247
+ "typeDataName"
183
248
  ]) {
184
249
  const str = d[k];
185
250
  if (str) {
@@ -204,10 +269,13 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts)
204
269
  clientOpts: { [k: string]: any; },
205
270
  otherOpts?: { [k: string]: any; }
206
271
  }) {
207
- opts.otherOpts = opts.otherOpts ?? {}
272
+ let otherOpts = opts.otherOpts ?? {};
273
+ if (opts.clientOpts.filename === ":memory:") {
274
+ otherOpts = { ...otherOpts, createTablesString: require("./artifacts.sqlite").schema }
275
+ }
208
276
  this.runtime = new opts.runtime(opts.clientOpts, ${supplementClientOpts === true
209
- ? "{ supplementClientOpts: true, ...opts.otherOpts }"
210
- : "opts.otherOpts"}, artifacts);
277
+ ? "{ supplementClientOpts: true, ...otherOpts }"
278
+ : "otherOpts"}, artifacts);
211
279
  }
212
280
 
213
281
  $use(middleware: TMiddleware) {
@@ -233,159 +301,362 @@ async function getSDKSource(input, specialCaseUuidColumn, supplementClientOpts)
233
301
  return this.runtime.$shutdown();
234
302
  }
235
303
 
304
+ async $startTransaction(input?: {
305
+ isolationLevel?:
306
+ | "READ UNCOMMITTED"
307
+ | "READ COMMITTED"
308
+ | "REPEATABLE READ"
309
+ | "SERIALIZABLE"
310
+ }) {
311
+ const { dbCall, commit, rollback } = await this.runtime.$startTransaction(input);
312
+ const runtime = this.runtime;
313
+ return {
314
+ $commit: commit,
315
+ $rollback: rollback,
316
+ ${(await Promise.all(input.flatMap(async (x) => {
317
+ if (x.kind === "getOne") {
318
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
319
+ return getMethodSourceGetOne(x, findOnes, true);
320
+ }
321
+ if (x.kind === "getList") {
322
+ return getMethodSourceGetList(x, true);
323
+ }
324
+ if (x.kind === "getListPaginated") {
325
+ return getMethodSourceGetListPaginated(x, true);
326
+ }
327
+ if (x.kind === "postOne") {
328
+ return getMethodSourcePostOne(x, specialCaseUuidColumn, true);
329
+ }
330
+ if (x.kind === "patchOne") {
331
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
332
+ return getMethodSourcePatchOne(x, findOnes, true);
333
+ }
334
+ if (x.kind === "patchList") {
335
+ return getMethodSourcePatchList(x, true);
336
+ }
337
+ if (x.kind === "deleteOne") {
338
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
339
+ return getMethodSourceDeleteOne(x, findOnes, true);
340
+ }
341
+ if (x.kind === "deleteList") {
342
+ return getMethodSourceDeleteList(x, true);
343
+ }
344
+ }))).join(",\n")}
345
+ }
346
+ }
347
+
236
348
  ${(await Promise.all(input.flatMap(async (x) => {
237
- let findOnes = [];
238
- const primaryColumn = await getPrimaryColumn(x.table);
239
- const uniqueColumns = await getUniqueColumns(x.table, specialCaseUuidColumn);
240
- findOnes = findOnes
241
- .concat([primaryColumn])
242
- .concat(uniqueColumns)
243
- .map((x) => ({
244
- ...x,
245
- type: x.type === "integer" ? "number" : x.type,
246
- }));
247
349
  if (x.kind === "getOne") {
248
- return `async ${x.methodName}(
249
- param1: ${findOnes
250
- .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
251
- .join(" | ")},
252
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }
253
- ): Promise<${x.typeReturnBaseName}> {
254
- return this.runtime.resolve(
255
- {
256
- resource: "${x.table}",
257
- action: "findUnique",
258
- args: { $where: param1 },
259
- fields: param2?.fields,
260
- artifacts,
261
- context: param2?.context,
262
- skipCache: param2?.skipCache
263
- }
264
- );
265
- }`;
350
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
351
+ return getMethodSourceGetOne(x, findOnes, false);
266
352
  }
267
353
  if (x.kind === "getList") {
268
- return `async ${x.methodName}(
269
- param1: { $where?: ${x.typeWhereName}, $orderBy?: ${x.typeOrderByName}, $limit?: number },
270
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }
271
- ): Promise<Array<${x.typeReturnBaseName}>> {
272
- return this.runtime.resolve(
273
- {
274
- resource: "${x.table}",
275
- action: "findMany",
276
- args: param1,
277
- fields: param2?.fields,
278
- artifacts,
279
- context: param2?.context,
280
- skipCache: param2?.skipCache
281
- }
282
- );
283
- }`;
354
+ return getMethodSourceGetList(x, false);
284
355
  }
285
356
  if (x.kind === "getListPaginated") {
286
- return `async ${x.methodName}(
287
- param1: { $where?: ${x.typeWhereName}, $orderBy?: ${x.typeOrderByName}, $paginate: Paginate },
288
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }
289
- ): Promise<ListPaginated<${x.typeReturnBaseName}>> {
290
- return this.runtime.resolve(
291
- {
292
- resource: "${x.table}",
293
- action: "findManyPaginated",
294
- args: param1,
295
- fields: param2?.fields,
296
- artifacts,
297
- context: param2?.context,
298
- skipCache: param2?.skipCache
299
- }
300
- );
301
- }`;
357
+ return getMethodSourceGetListPaginated(x, false);
302
358
  }
303
359
  if (x.kind === "postOne") {
304
- return `async ${x.methodName}(
305
- data: ${x.typeDataName},
306
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }
307
- ): Promise<${x.typeReturnBaseName}> {
308
- return this.runtime.resolve({
309
- resource: "${x.table}",
310
- action: "create",
311
- data,
312
- artifacts,
313
- fields: param2?.fields,
314
- context: {...param2?.context, specialCaseUuidColumn: ${JSON.stringify(specialCaseUuidColumn)}}
315
- });
316
- }`;
360
+ return getMethodSourcePostOne(x, specialCaseUuidColumn, false);
317
361
  }
318
362
  if (x.kind === "patchOne") {
319
- return `async ${x.methodName}(
320
- param1: ${findOnes
321
- .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
322
- .join(" | ")},
323
- data: ${x.typeDataName},
324
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }
325
- ): Promise<${x.typeReturnBaseName}> {
326
- return this.runtime.resolve({
327
- resource: "${x.table}",
328
- action: "update",
329
- args: { $where: param1 },
330
- data,
331
- artifacts,
332
- fields: param2?.fields,
333
- context: param2?.context
334
- });
335
- }`;
363
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
364
+ return getMethodSourcePatchOne(x, findOnes, false);
336
365
  }
337
366
  if (x.kind === "patchList") {
338
- return `async ${x.methodName}(
339
- param1: { $where?: ${x.typeWhereName} },
340
- data: ${x.typeDataName},
341
- param2?: { fields?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }
342
- ): Promise<Array<${x.typeReturnBaseName}>> {
343
- return this.runtime.resolve({
344
- resource: "${x.table}",
345
- action: "updateMany",
346
- args: param1,
347
- data,
348
- artifacts,
349
- fields: param2?.fields,
350
- context: param2?.context
351
- });
352
- }`;
367
+ return getMethodSourcePatchList(x, false);
353
368
  }
354
369
  if (x.kind === "deleteOne") {
355
- return `async ${x.methodName}(
356
- param1: ${findOnes
357
- .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
358
- .join(" | ")},
359
- param2?: { correlationId?: string, context?: TContext }
360
- ): Promise<void> {
361
- await this.runtime.resolve({
362
- resource: "${x.table}",
363
- action: "delete",
364
- args: { $where: param1 },
365
- artifacts,
366
- context: param2?.context
367
- });
368
- }`;
370
+ const findOnes = await getFindOnes(x, specialCaseUuidColumn);
371
+ return getMethodSourceDeleteOne(x, findOnes, false);
369
372
  }
370
373
  if (x.kind === "deleteList") {
371
- return `async ${x.methodName}(
372
- param1: { $where?: ${x.typeWhereName} },
373
- param2?: { correlationId?: string, context?: TContext }
374
- ): Promise<void> {
375
- await this.runtime.resolve({
376
- resource: "${x.table}",
377
- action: "deleteMany",
378
- args: param1,
379
- artifacts,
380
- context: param2?.context
381
- });
382
- }`;
374
+ return getMethodSourceDeleteList(x, false);
383
375
  }
384
376
  }))).join("\n\n")}
385
377
  }
378
+
379
+ ${await Promise.all(Object.entries(artifacts).map(async ([table, tableArtifacts]) => {
380
+ const jsonSchema = {
381
+ type: "object",
382
+ properties: tableArtifacts.fields.reduce((acc, x) => {
383
+ if (x.kind === "object") {
384
+ return acc;
385
+ }
386
+ if (x.kind === "scalar" && x.mapped) {
387
+ return acc;
388
+ }
389
+ acc[x.name] =
390
+ x.kind === "enum"
391
+ ? { enum: x.values }
392
+ : { type: getJSONTypes(x.type, x.nullable) };
393
+ return acc;
394
+ }, {}),
395
+ required: tableArtifacts.fields
396
+ .filter((x) => x.kind === "scalar" || x.kind === "enum")
397
+ .map((x) => x.name),
398
+ additionalProperties: false
399
+ };
400
+ return (0, json_schema_to_typescript_1.compile)(jsonSchema, getTypeReturnName(table), json2TsOpts);
401
+ })).then((xs) => xs.join(";\n\n"))}
402
+
403
+ ${Object.entries(artifacts)
404
+ .map(([table, tableArtifacts]) => {
405
+ // https://pkerschbaum.com/blog/how-prisma-adapts-result-types-based-on-the-actual-arguments-given
406
+ // https://github.com/pkerschbaum/how-prisma-adapts-result-types-based-on-the-actual-arguments-given_example/blob/main/prisma/lib/prisma-client/index.d.ts
407
+ return `export type ${getTypeGetPayloadName(table)}<S extends { ${keyFields}?: ${getTypeFieldsName(table)} } | boolean, U = keyof S> = S extends true ? ${getTypeReturnName(table)} : S extends { ${keyFields}?: ${getTypeFieldsName(table)}, [k: string]: any } ? "${keyFields}" extends U ? S["${keyFields}"] extends undefined ? ${getTypeReturnName(table)} : { [P in TrueKeys<S["${keyFields}"]>]: ${tableArtifacts.fields
408
+ .reduce((acc, x) => {
409
+ if (x.kind !== "object") {
410
+ return acc;
411
+ }
412
+ let str = `P extends "${x.name}" ? ${getTypeGetPayloadName(x.type)}<S["${keyFields}"][P]>`;
413
+ if (x.isList) {
414
+ str += "[]";
415
+ }
416
+ if (x.nullable) {
417
+ str += " | null";
418
+ }
419
+ str += " : ";
420
+ acc.push(str);
421
+ return acc;
422
+ }, [])
423
+ .join("")} ${tableArtifacts.fields
424
+ .reduce((acc, x) => {
425
+ if (x.kind === "scalar" && x.mapped) {
426
+ let type = x.type;
427
+ if (x.nullable) {
428
+ type += " | null";
429
+ }
430
+ const str = `P extends "${x.name}" ? ${type} : `;
431
+ acc.push(str);
432
+ }
433
+ return acc;
434
+ }, [])
435
+ .join("")} P extends keyof ${getTypeReturnName(table)} ? ${getTypeReturnName(table)}[P] : never } : ${getTypeReturnName(table)} : ${getTypeReturnName(table)}`;
436
+ })
437
+ .join(";\n\n")}
438
+
439
+ // Utils
440
+
441
+ type Pick2<T, K extends keyof T> = { [P in K]: T[P]; };
442
+
443
+ type RequiredKeys<T> = {
444
+ [K in keyof T]-?: {} extends Pick2<T, K> ? never : K;
445
+ }[keyof T];
446
+
447
+ type TruthyKeys<T> = {
448
+ [key in keyof T]: T[key] extends false | undefined | null ? never : key;
449
+ }[keyof T];
450
+
451
+ type TrueKeys<T> = TruthyKeys<Pick2<T, RequiredKeys<T>>>;
452
+
453
+ type SelectSubset<T, U> = {
454
+ [key in keyof T]: key extends keyof U ? T[key] : never;
455
+ }
456
+
457
+ type HasSelect = { ${keyFields}: any; };
458
+
459
+ type CheckSelect<T, S, U> = T extends HasSelect ? U : S;
386
460
  `;
387
461
  return prettier.format(src, { parser: "typescript" });
388
462
  }
463
+ async function getFindOnes(x, specialCaseUuidColumn) {
464
+ let findOnes = [];
465
+ const primaryColumn = await getPrimaryColumn(x.table);
466
+ const uniqueColumns = await getUniqueColumns(x.table, specialCaseUuidColumn);
467
+ findOnes = findOnes
468
+ .concat([primaryColumn])
469
+ .concat(uniqueColumns)
470
+ .map((x) => ({
471
+ ...x,
472
+ type: x.type === "integer" ? "number" : x.type
473
+ }));
474
+ return findOnes;
475
+ }
476
+ function getMethodSourceGetOne(x, findOnes, isTransaction) {
477
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }`;
478
+ return `async ${x.methodName}<T extends ${param2}>(
479
+ param1: ${findOnes
480
+ .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
481
+ .join(" | ")},
482
+ param2?: SelectSubset<T, ${param2}>
483
+ ): Promise<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>> {
484
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve(
485
+ {
486
+ resource: "${x.table}",
487
+ action: "${mapKindToAction(x.kind)}",
488
+ args: { $where: param1 },
489
+ fields: param2?.$fields as any,
490
+ artifacts,
491
+ context: param2?.context,
492
+ skipCache: param2?.skipCache,
493
+ ${isTransaction ? "dbCall" : ""}
494
+ }
495
+ );
496
+ }`;
497
+ }
498
+ function getMethodSourceGetList(x, isTransaction) {
499
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }`;
500
+ return `async ${x.methodName}<T extends ${param2}>(
501
+ param1: { $where?: ${x.typeWhereName}, $orderBy?: ${x.typeOrderByName}, $limit?: number },
502
+ param2?: SelectSubset<T, ${param2}>
503
+ ): Promise<Array<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>>> {
504
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve(
505
+ {
506
+ resource: "${x.table}",
507
+ action: "${mapKindToAction(x.kind)}",
508
+ args: param1,
509
+ fields: param2?.$fields as any,
510
+ artifacts,
511
+ context: param2?.context,
512
+ skipCache: param2?.skipCache,
513
+ ${isTransaction ? "dbCall" : ""}
514
+ }
515
+ );
516
+ }`;
517
+ }
518
+ function getMethodSourceGetListPaginated(x, isTransaction) {
519
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, skipCache?: boolean, context?: TContext }`;
520
+ return `async ${x.methodName}<T extends ${param2}>(
521
+ param1: { $where?: ${x.typeWhereName}, $orderBy?: ${x.typeOrderByName}, $paginate: Paginate },
522
+ param2?: SelectSubset<T, ${param2}>
523
+ ): Promise<ListPaginated<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>>> {
524
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve(
525
+ {
526
+ resource: "${x.table}",
527
+ action: "${mapKindToAction(x.kind)}",
528
+ args: param1,
529
+ fields: param2?.$fields as any,
530
+ artifacts,
531
+ context: param2?.context,
532
+ skipCache: param2?.skipCache,
533
+ ${isTransaction ? "dbCall" : ""}
534
+ }
535
+ );
536
+ }`;
537
+ }
538
+ function getMethodSourcePostOne(x, specialCaseUuidColumn, isTransaction) {
539
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }`;
540
+ return `async ${x.methodName}<T extends ${param2}>(
541
+ data: ${x.typeDataName},
542
+ param2?: SelectSubset<T, ${param2}>
543
+ ): Promise<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>> {
544
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve({
545
+ resource: "${x.table}",
546
+ action: "${mapKindToAction(x.kind)}",
547
+ data,
548
+ artifacts,
549
+ fields: param2?.$fields as any,
550
+ context: {...param2?.context, specialCaseUuidColumn: ${JSON.stringify(specialCaseUuidColumn)}},
551
+ ${isTransaction ? "dbCall" : ""}
552
+ });
553
+ }`;
554
+ }
555
+ function getMethodSourcePatchOne(x, findOnes, isTransaction) {
556
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }`;
557
+ return `async ${x.methodName}<T extends ${param2}>(
558
+ param1: ${findOnes
559
+ .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
560
+ .join(" | ")},
561
+ data: ${x.typeDataName},
562
+ param2?: SelectSubset<T, ${param2}>
563
+ ): Promise<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>> {
564
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve({
565
+ resource: "${x.table}",
566
+ action: "${mapKindToAction(x.kind)}",
567
+ args: { $where: param1 },
568
+ data,
569
+ artifacts,
570
+ fields: param2?.$fields as any,
571
+ context: param2?.context,
572
+ ${isTransaction ? "dbCall" : ""}
573
+ });
574
+ }`;
575
+ }
576
+ function getMethodSourcePatchList(x, isTransaction) {
577
+ const param2 = `{ ${keyFields}?: ${x.typeFieldsName}, correlationId?: string, context?: TContext }`;
578
+ return `async ${x.methodName}<T extends ${param2}>(
579
+ param1: { $where?: ${x.typeWhereName}, $orderBy?: ${x.typeOrderByName} },
580
+ data: ${x.typeDataName},
581
+ param2?: SelectSubset<T, ${param2}>
582
+ ): Promise<Array<CheckSelect<T, ${getTypeReturnName(x.table)}, ${getTypeGetPayloadName(x.table)}<T>>>> {
583
+ return ${isTransaction ? "runtime" : "this.runtime"}.resolve({
584
+ resource: "${x.table}",
585
+ action: "${mapKindToAction(x.kind)}",
586
+ args: param1,
587
+ data,
588
+ artifacts,
589
+ fields: param2?.$fields as any,
590
+ context: param2?.context,
591
+ ${isTransaction ? "dbCall" : ""}
592
+ });
593
+ }`;
594
+ }
595
+ function getMethodSourceDeleteOne(x, findOnes, isTransaction) {
596
+ return `async ${x.methodName}(
597
+ param1: ${findOnes
598
+ .map((findOne) => `{ ${findOne.name}: ${findOne.type}${findOne.nullable ? " | null" : ""} }`)
599
+ .join(" | ")},
600
+ param2?: { correlationId?: string, context?: TContext }
601
+ ): Promise<void> {
602
+ await ${isTransaction ? "runtime" : "this.runtime"}.resolve({
603
+ resource: "${x.table}",
604
+ action: "${mapKindToAction(x.kind)}",
605
+ args: { $where: param1 },
606
+ artifacts,
607
+ context: param2?.context,
608
+ ${isTransaction ? "dbCall" : ""}
609
+ });
610
+ }`;
611
+ }
612
+ function getMethodSourceDeleteList(x, isTransaction) {
613
+ return `async ${x.methodName}(
614
+ param1: { $where?: ${x.typeWhereName} },
615
+ param2?: { correlationId?: string, context?: TContext }
616
+ ): Promise<void> {
617
+ await ${isTransaction ? "runtime" : "this.runtime"}.resolve({
618
+ resource: "${x.table}",
619
+ action: "${mapKindToAction(x.kind)}",
620
+ args: param1,
621
+ artifacts,
622
+ context: param2?.context,
623
+ ${isTransaction ? "dbCall" : ""}
624
+ });
625
+ }`;
626
+ }
627
+ function mapKindToAction(kind) {
628
+ if (kind === "getOne") {
629
+ return "findUnique";
630
+ }
631
+ if (kind === "getList") {
632
+ return "findMany";
633
+ }
634
+ if (kind === "getListPaginated") {
635
+ return "findManyPaginated";
636
+ }
637
+ if (kind === "postOne") {
638
+ return "create";
639
+ }
640
+ if (kind === "patchOne") {
641
+ return "update";
642
+ }
643
+ if (kind === "patchList") {
644
+ return "updateMany";
645
+ }
646
+ if (kind === "deleteOne") {
647
+ return "delete";
648
+ }
649
+ if (kind === "deleteList") {
650
+ return "deleteMany";
651
+ }
652
+ throw new Error(`Unhandled kind: ${kind}`);
653
+ }
654
+ function getTypeGetPayloadName(table) {
655
+ return "GetPayload" + changeCase.pascalCase(table);
656
+ }
657
+ function getTypeReturnName(table) {
658
+ return changeCase.pascalCase(table);
659
+ }
389
660
  function getTypeReturnBaseName(table) {
390
661
  return "ReturnBase" + changeCase.pascalCase(table);
391
662
  }
@@ -411,7 +682,7 @@ async function getGetOneData(table, includeMappedFields) {
411
682
  typeFields: await getTypeFields(table, typeFieldsName, includeMappedFields),
412
683
  typeFieldsName,
413
684
  typeReturnBase: await getTypeReturnBase(table, typeReturnBaseName, includeMappedFields),
414
- typeReturnBaseName,
685
+ typeReturnBaseName
415
686
  };
416
687
  }
417
688
  async function getGetListData(table) {
@@ -430,7 +701,7 @@ async function getGetListData(table) {
430
701
  typeWhere,
431
702
  typeWhereName,
432
703
  typeOrderBy,
433
- typeOrderByName,
704
+ typeOrderByName
434
705
  };
435
706
  }
436
707
  async function getGetListPaginatedData(table) {
@@ -445,7 +716,7 @@ async function getGetListPaginatedData(table) {
445
716
  typeFieldsName,
446
717
  typeReturnBaseName,
447
718
  typeWhereName,
448
- typeOrderByName,
719
+ typeOrderByName
449
720
  };
450
721
  }
451
722
  async function getPostOneData(table, specialCaseUuidColumn, includeMappedFields) {
@@ -459,7 +730,7 @@ async function getPostOneData(table, specialCaseUuidColumn, includeMappedFields)
459
730
  typeFieldsName,
460
731
  typeReturnBaseName,
461
732
  typeData: await getTypeDataPost(table, typeDataName, specialCaseUuidColumn, includeMappedFields),
462
- typeDataName,
733
+ typeDataName
463
734
  };
464
735
  }
465
736
  async function getPatchOneData(table, specialCaseUuidColumn, includeMappedFields) {
@@ -473,7 +744,7 @@ async function getPatchOneData(table, specialCaseUuidColumn, includeMappedFields
473
744
  typeFieldsName,
474
745
  typeReturnBaseName,
475
746
  typeData: await getTypeDataPatch(table, typeDataName, specialCaseUuidColumn, includeMappedFields),
476
- typeDataName,
747
+ typeDataName
477
748
  };
478
749
  }
479
750
  async function getPatchListData(table) {
@@ -481,6 +752,7 @@ async function getPatchListData(table) {
481
752
  const typeReturnBaseName = getTypeReturnBaseName(table);
482
753
  const typeWhereName = getTypeWhereName(table);
483
754
  const typeDataName = "DataPatch" + changeCase.pascalCase(table);
755
+ const typeOrderByName = getTypeOrderByName(table);
484
756
  return {
485
757
  kind: "patchList",
486
758
  table,
@@ -489,13 +761,14 @@ async function getPatchListData(table) {
489
761
  typeReturnBaseName,
490
762
  typeWhereName,
491
763
  typeDataName,
764
+ typeOrderByName
492
765
  };
493
766
  }
494
767
  function getDeleteOneData(table) {
495
768
  return {
496
769
  kind: "deleteOne",
497
770
  table,
498
- methodName: "delete" + changeCase.pascalCase(table),
771
+ methodName: "delete" + changeCase.pascalCase(table)
499
772
  };
500
773
  }
501
774
  function getDeleteListData(table) {
@@ -504,7 +777,7 @@ function getDeleteListData(table) {
504
777
  kind: "deleteList",
505
778
  table,
506
779
  methodName: "delete" + changeCase.pascalCase(table) + "List",
507
- typeWhereName,
780
+ typeWhereName
508
781
  };
509
782
  }
510
783
  async function getTypeWhere(table, name) {
@@ -516,11 +789,11 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
516
789
  const tableMeta = (await getTableMeta(table)).filter((x) => x.Field !== primaryColumn.name);
517
790
  const nullable = tableMeta.reduce((acc, m) => ({
518
791
  ...acc,
519
- [m.Field]: m.Null === "YES" ? true : false,
792
+ [m.Field]: m.Null === "YES" ? true : false
520
793
  }), {});
521
794
  const hasDefault = tableMeta.reduce((acc, m) => ({
522
795
  ...acc,
523
- [m.Field]: m.Default == null ? false : true,
796
+ [m.Field]: m.Default == null ? false : true
524
797
  }), {});
525
798
  let properties = getJSONSchemaObjProperties(tableMeta);
526
799
  let notRequiredList = [];
@@ -533,7 +806,7 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
533
806
  ...mappedFields.reduce((acc, v) => {
534
807
  acc[v.as] = { type: getJSONTypes(v.type, v.nullable) };
535
808
  return acc;
536
- }, {}),
809
+ }, {})
537
810
  };
538
811
  notRequiredList = mappedFields.flatMap((x) => [x.as, x.foreignKey]);
539
812
  for (let r of oneToManyRelations) {
@@ -548,9 +821,7 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
548
821
  ...oneToManyRelations.reduce((acc, v) => {
549
822
  let tsType = getTypeDataPostName(v.table);
550
823
  const mappedFields = mappedFieldsMap.get(v.table);
551
- if (includeMappedFields &&
552
- mappedFields != null &&
553
- mappedFields.length > 0) {
824
+ if (includeMappedFields && mappedFields != null && mappedFields.length > 0) {
554
825
  tsType = `Omit<${tsType}, ${mappedFields
555
826
  .map((x) => JSON.stringify(x.as))
556
827
  .join(" | ")}>`;
@@ -558,21 +829,19 @@ async function getTypeDataPost(table, name, specialCaseUuidColumn, includeMapped
558
829
  tsType = `{$create: ${tsType}[]}`;
559
830
  acc[v.name] = { tsType };
560
831
  return acc;
561
- }, {}),
832
+ }, {})
562
833
  },
563
834
  additionalProperties: false,
564
835
  required: Object.keys(properties)
565
836
  .filter(
566
837
  // `uuid` should not be required
567
- (x) => !specialCaseUuidColumn || uuidColumn == null
568
- ? true
569
- : x !== uuidColumn.name)
838
+ (x) => !specialCaseUuidColumn || uuidColumn == null ? true : x !== uuidColumn.name)
570
839
  .filter(
571
840
  // Required if column is non-nullable and has no default.
572
841
  (x) => !nullable[x] && !hasDefault[x])
573
842
  // Instead of doing a union with all possible permutations of UUID and IDs,
574
843
  // for simplicity, just make both not required for now.
575
- .filter((x) => !notRequiredList.includes(x)),
844
+ .filter((x) => !notRequiredList.includes(x))
576
845
  };
577
846
  let type = await (0, json_schema_to_typescript_1.compile)(jsonSchema, name, json2TsOpts);
578
847
  const imports = _.uniq(oneToManyRelations
@@ -601,13 +870,13 @@ async function getTypeDataPatch(table, name, specialCaseUuidColumn, includeMappe
601
870
  const type = unwrapJSONType(properties[key].type);
602
871
  if (type === "string") {
603
872
  properties[key] = {
604
- oneOf: [properties[key], { tsType: "TUpdateOperationsString" }],
873
+ oneOf: [properties[key], { tsType: "TUpdateOperationsString" }]
605
874
  };
606
875
  mustImportTUpdateOperationsString = true;
607
876
  }
608
877
  else if (type === "number" || type === "integer") {
609
878
  properties[key] = {
610
- oneOf: [properties[key], { tsType: "TUpdateOperationsNumber" }],
879
+ oneOf: [properties[key], { tsType: "TUpdateOperationsNumber" }]
611
880
  };
612
881
  mustImportTUpdateOperationsNumber = true;
613
882
  }
@@ -619,14 +888,14 @@ async function getTypeDataPatch(table, name, specialCaseUuidColumn, includeMappe
619
888
  ...mappedFields.reduce((acc, v) => {
620
889
  acc[v.as] = { type: getJSONTypes(v.type, v.nullable) };
621
890
  return acc;
622
- }, {}),
891
+ }, {})
623
892
  };
624
893
  }
625
894
  const jsonSchema = {
626
895
  type: "object",
627
896
  properties,
628
897
  additionalProperties: false,
629
- required: [],
898
+ required: []
630
899
  };
631
900
  let type = await (0, json_schema_to_typescript_1.compile)(jsonSchema, name, json2TsOpts);
632
901
  if (mustImportTUpdateOperationsString || mustImportTUpdateOperationsNumber) {
@@ -669,7 +938,7 @@ async function getMappedFields(table) {
669
938
  name: "uuid",
670
939
  // Replace `Id` with `Uuid`
671
940
  as: x.foreignKey.slice(0, -2) + "Uuid",
672
- type: getBaseJSONType(uuidColumn.Type),
941
+ type: getBaseJSONType(uuidColumn.Type)
673
942
  });
674
943
  }
675
944
  return out;
@@ -688,52 +957,52 @@ async function getJSONSchemaWhere(table) {
688
957
  {
689
958
  type: "object",
690
959
  properties: { $eq: v },
691
- additionalProperties: false,
960
+ additionalProperties: false
692
961
  },
693
962
  {
694
963
  type: "object",
695
964
  properties: { $neq: v },
696
- additionalProperties: false,
965
+ additionalProperties: false
697
966
  },
698
967
  {
699
968
  type: "object",
700
969
  properties: { $gt: v },
701
- additionalProperties: false,
970
+ additionalProperties: false
702
971
  },
703
972
  {
704
973
  type: "object",
705
974
  properties: { $gte: v },
706
- additionalProperties: false,
975
+ additionalProperties: false
707
976
  },
708
977
  {
709
978
  type: "object",
710
979
  properties: { $lt: v },
711
- additionalProperties: false,
980
+ additionalProperties: false
712
981
  },
713
982
  {
714
983
  type: "object",
715
984
  properties: { $lte: v },
716
- additionalProperties: false,
985
+ additionalProperties: false
717
986
  },
718
987
  {
719
988
  type: "object",
720
989
  properties: { $like: { type: "string", minLength: 1 } },
721
- additionalProperties: false,
990
+ additionalProperties: false
722
991
  },
723
992
  {
724
993
  type: "object",
725
994
  properties: { $nlike: { type: "string", minLength: 1 } },
726
- additionalProperties: false,
995
+ additionalProperties: false
727
996
  },
728
997
  {
729
998
  type: "object",
730
999
  properties: { $in: { type: "array", items: v } },
731
- additionalProperties: false,
1000
+ additionalProperties: false
732
1001
  },
733
1002
  {
734
1003
  type: "object",
735
1004
  properties: { $nin: { type: "array", items: v } },
736
- additionalProperties: false,
1005
+ additionalProperties: false
737
1006
  },
738
1007
  {
739
1008
  type: "object",
@@ -743,10 +1012,10 @@ async function getJSONSchemaWhere(table) {
743
1012
  items: v,
744
1013
  minItems: 2,
745
1014
  maxItems: 2,
746
- uniqueItems: true,
747
- },
1015
+ uniqueItems: true
1016
+ }
748
1017
  },
749
- additionalProperties: false,
1018
+ additionalProperties: false
750
1019
  },
751
1020
  {
752
1021
  type: "object",
@@ -756,15 +1025,15 @@ async function getJSONSchemaWhere(table) {
756
1025
  items: v,
757
1026
  minItems: 2,
758
1027
  maxItems: 2,
759
- uniqueItems: true,
760
- },
1028
+ uniqueItems: true
1029
+ }
761
1030
  },
762
- additionalProperties: false,
763
- },
764
- ],
765
- },
1031
+ additionalProperties: false
1032
+ }
1033
+ ]
1034
+ }
766
1035
  }), {}),
767
- additionalProperties: false,
1036
+ additionalProperties: false
768
1037
  },
769
1038
  {
770
1039
  type: "object",
@@ -772,17 +1041,17 @@ async function getJSONSchemaWhere(table) {
772
1041
  $and: {
773
1042
  type: "array",
774
1043
  items: {
775
- $ref: `#/definitions/${whereSchemaName}`,
1044
+ $ref: `#/definitions/${whereSchemaName}`
776
1045
  },
777
1046
  // While it makes sense conceptually for $and to have
778
1047
  // at least 2 items, in practice, $and could be
779
1048
  // generated dynamically and could end up having
780
1049
  // less than 2 items, so don't enforce minItems.
781
1050
  // minItems: 2,
782
- additionalProperties: false,
783
- },
1051
+ additionalProperties: false
1052
+ }
784
1053
  },
785
- additionalProperties: false,
1054
+ additionalProperties: false
786
1055
  },
787
1056
  {
788
1057
  type: "object",
@@ -790,19 +1059,19 @@ async function getJSONSchemaWhere(table) {
790
1059
  $or: {
791
1060
  type: "array",
792
1061
  items: {
793
- $ref: `#/definitions/${whereSchemaName}`,
1062
+ $ref: `#/definitions/${whereSchemaName}`
794
1063
  },
795
1064
  // While it makes sense conceptually for $and to have
796
1065
  // at least 2 items, in practice, $and could be
797
1066
  // generated dynamically and could end up having
798
1067
  // less than 2 items, so don't enforce minItems.
799
1068
  // minItems: 2,
800
- additionalProperties: false,
801
- },
1069
+ additionalProperties: false
1070
+ }
802
1071
  },
803
- additionalProperties: false,
804
- },
805
- ],
1072
+ additionalProperties: false
1073
+ }
1074
+ ]
806
1075
  };
807
1076
  return {
808
1077
  definitions: { [whereSchemaName]: defWhere },
@@ -819,12 +1088,12 @@ async function getJSONSchemaWhere(table) {
819
1088
  // generated dynamically and could end up having
820
1089
  // less than 2 items, so don't enforce minItems.
821
1090
  // minItems: 2,
822
- additionalProperties: false,
823
- },
1091
+ additionalProperties: false
1092
+ }
824
1093
  },
825
- additionalProperties: false,
826
- })),
827
- ],
1094
+ additionalProperties: false
1095
+ }))
1096
+ ]
828
1097
  };
829
1098
  }
830
1099
  async function getTypeOrderBy(table, name) {
@@ -837,14 +1106,14 @@ async function getJSONSchemaOrderBy(table, name) {
837
1106
  type: "object",
838
1107
  properties: { [k]: { enum: ["asc", "desc"] } },
839
1108
  required: [k],
840
- additionalProperties: false,
841
- })),
1109
+ additionalProperties: false
1110
+ }))
842
1111
  };
843
1112
  const defName = `_${name}`;
844
1113
  const _schema = { $ref: `#/definitions/${defName}` };
845
1114
  return {
846
1115
  definitions: { [defName]: def },
847
- oneOf: [_schema, { type: "array", items: _schema }],
1116
+ oneOf: [_schema, { type: "array", items: _schema }]
848
1117
  };
849
1118
  }
850
1119
  function getTypeTypesIndex(data) {
@@ -858,7 +1127,7 @@ function getTypeTypesIndex(data) {
858
1127
  "typeReturnBaseName",
859
1128
  "typeWhereName",
860
1129
  "typeOrderByName",
861
- "typeDataName",
1130
+ "typeDataName"
862
1131
  ]) {
863
1132
  const str = d[k];
864
1133
  if (str) {
@@ -870,7 +1139,7 @@ function getTypeTypesIndex(data) {
870
1139
  "Paginate",
871
1140
  "ListPaginated",
872
1141
  "TUpdateOperationsString",
873
- "TUpdateOperationsNumber",
1142
+ "TUpdateOperationsNumber"
874
1143
  ].join(",")} } from "./_shared";\n\n`;
875
1144
  let arr = Array.from(set).sort();
876
1145
  for (let x of arr) {
@@ -896,8 +1165,8 @@ export type ListPaginated<T> = {
896
1165
  paginationInfo: {
897
1166
  hasPreviousPage?: boolean,
898
1167
  hasNextPage?: boolean,
899
- startCursor?: string | number,
900
- endCursor?: string | number,
1168
+ startCursor?: string,
1169
+ endCursor?: string,
901
1170
  totalCount: number,
902
1171
  },
903
1172
  results: Array<T>,
@@ -915,58 +1184,49 @@ async function getTypeFields(table, name, includeMappedFields) {
915
1184
  const mappedFields = includeMappedFields ? await getMappedFields(table) : [];
916
1185
  const keyWhere = "$where";
917
1186
  const keyOrderBy = "$orderBy";
918
- const jsonSchemaFields = {
919
- type: "array",
920
- items: {
921
- anyOf: [
922
- {
923
- enum: scalarKeys.concat(mappedFields.map((x) => x.as)),
924
- },
925
- ...relations.map((x) => {
926
- const argsProperties = x.type === "many-to-many"
927
- ? {
928
- [keyWhere]: {
929
- type: "object",
930
- properties: {
931
- [x.table]: { tsType: getTypeWhereName(x.table) },
932
- [x.junctionTable]: {
933
- tsType: getTypeWhereName(x.junctionTable),
934
- },
935
- },
936
- additionalProperties: false,
937
- },
1187
+ let properties = {};
1188
+ for (let x of scalarKeys) {
1189
+ properties[x] = { type: "boolean" };
1190
+ }
1191
+ for (let x of mappedFields) {
1192
+ properties[x.as] = { type: "boolean" };
1193
+ }
1194
+ for (let x of relations) {
1195
+ const argsProperties = x.type === "many-to-many"
1196
+ ? {
1197
+ [keyWhere]: {
1198
+ type: "object",
1199
+ properties: {
1200
+ [x.table]: { tsType: getTypeWhereName(x.table) },
1201
+ [x.junctionTable]: {
1202
+ tsType: getTypeWhereName(x.junctionTable)
938
1203
  }
939
- : { [keyWhere]: { tsType: getTypeWhereName(x.table) } };
940
- // $orderBy only makes sense for a list
941
- if (x.grabMany) {
942
- argsProperties[keyOrderBy] = {
943
- tsType: getTypeOrderByName(x.table),
944
- };
945
- }
946
- return {
947
- type: "object",
948
- properties: {
949
- name: { enum: [x.name] },
950
- as: { type: "string" },
951
- fields: { tsType: getTypeFieldsName(x.table) },
952
- args: {
953
- type: "object",
954
- properties: argsProperties,
955
- additionalProperties: false,
956
- },
957
- transform: { tsType: `(x: any) => any` },
958
- },
959
- additionalProperties: false,
960
- required: ["name", "fields"],
961
- };
962
- }),
963
- ],
964
- },
1204
+ },
1205
+ additionalProperties: false
1206
+ }
1207
+ }
1208
+ : { [keyWhere]: { tsType: getTypeWhereName(x.table) } };
1209
+ // $orderBy only makes sense for a list
1210
+ if (x.grabMany) {
1211
+ argsProperties[keyOrderBy] = {
1212
+ tsType: getTypeOrderByName(x.table)
1213
+ };
1214
+ }
1215
+ argsProperties[keyFields] = { tsType: getTypeFieldsName(x.table) };
1216
+ const jsonSchemaArgs = {
1217
+ type: "object",
1218
+ properties: argsProperties,
1219
+ additionalProperties: false
1220
+ };
1221
+ properties[x.name] = { enum: [{ type: "boolean" }, jsonSchemaArgs] };
1222
+ }
1223
+ const jsonSchemaFields = {
1224
+ type: "object",
1225
+ additionalProperties: false,
1226
+ properties
965
1227
  };
966
1228
  let type = await (0, json_schema_to_typescript_1.compile)(jsonSchemaFields, name, json2TsOpts);
967
- const fieldImports = _.uniq(relations
968
- .filter((x) => x.table !== table)
969
- .map((x) => getTypeFieldsName(x.table)));
1229
+ const fieldImports = _.uniq(relations.filter((x) => x.table !== table).map((x) => getTypeFieldsName(x.table)));
970
1230
  const whereImports = _.uniq(relations.flatMap((x) => x.type === "many-to-many"
971
1231
  ? [getTypeWhereName(x.table), getTypeWhereName(x.junctionTable)]
972
1232
  : getTypeWhereName(x.table)));
@@ -1006,10 +1266,10 @@ async function getTypeReturnBase(table, name, includeMappedFields) {
1006
1266
  acc[key] = { tsType };
1007
1267
  }
1008
1268
  return acc;
1009
- }, {}),
1269
+ }, {})
1010
1270
  },
1011
1271
  // Because of aliases
1012
- additionalProperties: true,
1272
+ additionalProperties: true
1013
1273
  };
1014
1274
  let type = await (0, json_schema_to_typescript_1.compile)(jsonSchemaReturn, name, json2TsOpts);
1015
1275
  const imports = _.uniq(relations.map((x) => getTypeReturnBaseName(x.table)))
@@ -1025,10 +1285,21 @@ function getMaybeNullableTsType(type, nullable) {
1025
1285
  }
1026
1286
  return type;
1027
1287
  }
1028
- async function getArtifactsSource(tables, includeMappedFields, specialCaseUuidColumn) {
1288
+ function getArtifactsSource(artifacts) {
1289
+ const src = `
1290
+ import type {IArtifacts} from "./IRuntime";
1291
+
1292
+ export const artifacts: IArtifacts = ${JSON.stringify(artifacts)};
1293
+ `;
1294
+ return prettier.format(src, { parser: "typescript" });
1295
+ }
1296
+ async function getArtifacts(tables, includeMappedFields, specialCaseUuidColumn) {
1029
1297
  const tableMetaList = await Promise.all(tables.map(async (table) => {
1030
- const tableMeta = await getTableMeta(table);
1031
- const primaryKey = await getPrimaryColumn(table).then((x) => x.name);
1298
+ const [tableMeta, primaryKey, dumpSchema] = await Promise.all([
1299
+ getTableMeta(table),
1300
+ getPrimaryColumn(table).then((x) => x.name),
1301
+ getShowCreateTable(table)
1302
+ ]);
1032
1303
  const scalarFields = tableMeta.map((x) => x.Field);
1033
1304
  const relationInfo = await getRelationInfo(table);
1034
1305
  const relationFields = relationInfo.reduce((acc, x) => {
@@ -1040,7 +1311,7 @@ async function getArtifactsSource(tables, includeMappedFields, specialCaseUuidCo
1040
1311
  table: x.table,
1041
1312
  grabMany: x.grabMany,
1042
1313
  nullable: x.nullable,
1043
- relation: x.relation,
1314
+ relation: x.relation
1044
1315
  };
1045
1316
  }
1046
1317
  else {
@@ -1050,14 +1321,12 @@ async function getArtifactsSource(tables, includeMappedFields, specialCaseUuidCo
1050
1321
  table: x.table,
1051
1322
  junctionTable: x.junctionTable,
1052
1323
  grabMany: x.grabMany,
1053
- relations: x.relations,
1324
+ relations: x.relations
1054
1325
  };
1055
1326
  }
1056
1327
  return acc;
1057
1328
  }, {});
1058
- const _mappedFields = includeMappedFields
1059
- ? await getMappedFields(table)
1060
- : [];
1329
+ const _mappedFields = includeMappedFields ? await getMappedFields(table) : [];
1061
1330
  const mappedFields = _mappedFields.length === 0
1062
1331
  ? null
1063
1332
  : _mappedFields.reduce((acc, v) => {
@@ -1073,6 +1342,49 @@ async function getArtifactsSource(tables, includeMappedFields, specialCaseUuidCo
1073
1342
  return acc;
1074
1343
  }, {});
1075
1344
  const uniqueFields = await getUniqueColumns(table, specialCaseUuidColumn);
1345
+ let fields = tableMeta.map((t) => {
1346
+ const nullable = t.Null === "YES";
1347
+ const isEnum = t.Type.startsWith("enum");
1348
+ if (isEnum) {
1349
+ const values = getPropertyEnum(t.Type);
1350
+ if (values && nullable) {
1351
+ values.push(null);
1352
+ }
1353
+ return {
1354
+ kind: "enum",
1355
+ values,
1356
+ name: t.Field,
1357
+ nullable
1358
+ };
1359
+ }
1360
+ return {
1361
+ kind: "scalar",
1362
+ type: getBaseJSONType(t.Type),
1363
+ name: t.Field,
1364
+ nullable,
1365
+ hasDefaultValue: !!t.Default
1366
+ };
1367
+ });
1368
+ for (let x of _mappedFields) {
1369
+ fields.push({
1370
+ kind: "scalar",
1371
+ type: x.type,
1372
+ name: x.as,
1373
+ nullable: x.nullable,
1374
+ // TODO
1375
+ hasDefaultValue: false,
1376
+ mapped: true
1377
+ });
1378
+ }
1379
+ for (let x of relationInfo) {
1380
+ fields.push({
1381
+ kind: "object",
1382
+ type: x.table,
1383
+ name: x.name,
1384
+ isList: x.grabMany,
1385
+ nullable: x.type === "one-to-many__many-to-one" ? x.nullable : false
1386
+ });
1387
+ }
1076
1388
  return {
1077
1389
  table,
1078
1390
  primaryKey,
@@ -1082,18 +1394,15 @@ async function getArtifactsSource(tables, includeMappedFields, specialCaseUuidCo
1082
1394
  uniqueFields,
1083
1395
  dateTimeFields,
1084
1396
  dateTimeFieldsCount: Object.keys(dateTimeFields).length,
1397
+ fields,
1398
+ dump: dumpSchema == null ? null : { schema: dumpSchema }
1085
1399
  };
1086
1400
  }));
1087
1401
  const artifacts = tableMetaList.reduce((acc, x) => {
1088
1402
  acc[x.table] = x;
1089
1403
  return acc;
1090
1404
  }, {});
1091
- const src = `
1092
- import type {IArtifacts} from "./IRuntime";
1093
-
1094
- export const artifacts: IArtifacts = ${JSON.stringify(artifacts)};
1095
- `;
1096
- return prettier.format(src, { parser: "typescript" });
1405
+ return artifacts;
1097
1406
  }
1098
1407
  const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1099
1408
  const relationsManyToOne = await getRelationsManyToOne(table);
@@ -1108,7 +1417,7 @@ const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1108
1417
  table: x.referencedTable,
1109
1418
  name,
1110
1419
  relation: x,
1111
- nullable: x.nullable,
1420
+ nullable: x.nullable
1112
1421
  };
1113
1422
  }));
1114
1423
  const relationsOneToManyDuplicates = (0, getDuplicates_1.getDuplicates)(relationsOneToMany.map((x) => x.referencedTable));
@@ -1131,7 +1440,7 @@ const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1131
1440
  table: x.referencedTable,
1132
1441
  name,
1133
1442
  relation: x,
1134
- nullable: x.nullable,
1443
+ nullable: x.nullable
1135
1444
  };
1136
1445
  }));
1137
1446
  const relationsManyToMany = (await getJunctionTables()).reduce((acc, x) => {
@@ -1150,7 +1459,7 @@ const getRelationInfo = _.memoize(async function getRelationInfo(table) {
1150
1459
  name: changeCase.camelCase(dataForChildTable.referencedTable) + "List",
1151
1460
  // Ensure parent comes before child
1152
1461
  relations: [dataForParentTable, dataForChildTable],
1153
- grabMany: true,
1462
+ grabMany: true
1154
1463
  });
1155
1464
  return acc;
1156
1465
  }, []);
@@ -1171,7 +1480,7 @@ async function getJunctionTables() {
1171
1480
  // e.g. junction of Foo, Bar must be FooBar or BarFoo
1172
1481
  [
1173
1482
  relations[0].referencedTable + relations[1].referencedTable,
1174
- relations[1].referencedTable + relations[0].referencedTable,
1483
+ relations[1].referencedTable + relations[0].referencedTable
1175
1484
  ].includes(table)) {
1176
1485
  return { table, relations };
1177
1486
  }
@@ -1227,7 +1536,7 @@ const getRelationsManyToOne = _.memoize(async function getRelationsManyToOne(tab
1227
1536
  foreignKey: v.t1Field,
1228
1537
  referencedTable: v.t2,
1229
1538
  referencedKey: v.t2Field,
1230
- nullable: tableMeta.find((m) => m.Field === v.t1Field)?.Null === "YES",
1539
+ nullable: tableMeta.find((m) => m.Field === v.t1Field)?.Null === "YES"
1231
1540
  };
1232
1541
  })));
1233
1542
  return _.sortBy((x) => x.referencedTable, xs);
@@ -1276,7 +1585,7 @@ const getRelationsOneToMany = _.memoize(async function getRelationsOneToMany(tab
1276
1585
  referencedTable: v.t1,
1277
1586
  referencedKey: v.t1Field,
1278
1587
  // TODO? I think this is right, since it's one-to-many, so a list
1279
- nullable: false,
1588
+ nullable: false
1280
1589
  };
1281
1590
  })));
1282
1591
  return _.sortBy((x) => x.referencedKey, _.sortBy((x) => x.referencedTable, xs));
@@ -1291,7 +1600,7 @@ async function getPrimaryColumn(table) {
1291
1600
  return {
1292
1601
  name: column.Field,
1293
1602
  type: getBaseJSONType(column.Type),
1294
- nullable: column.Null === "YES",
1603
+ nullable: column.Null === "YES"
1295
1604
  };
1296
1605
  }
1297
1606
  async function getUniqueColumns(table, specialCaseUuidColumn) {
@@ -1303,7 +1612,7 @@ async function getUniqueColumns(table, specialCaseUuidColumn) {
1303
1612
  .map((x) => ({
1304
1613
  name: x.Field,
1305
1614
  type: getBaseJSONType(x.Type),
1306
- nullable: x.Null === "YES",
1615
+ nullable: x.Null === "YES"
1307
1616
  }));
1308
1617
  }
1309
1618
  async function getUuidColumn(table) {
@@ -1315,14 +1624,14 @@ async function getUuidColumn(table) {
1315
1624
  return {
1316
1625
  name: column.Field,
1317
1626
  type: column.Type,
1318
- nullable: column.Null === "YES",
1627
+ nullable: column.Null === "YES"
1319
1628
  };
1320
1629
  }
1321
1630
  const getTableMeta = _.memoize(async function getTableMeta(table) {
1322
1631
  if (dialect === "mysql") {
1323
1632
  return query("DESCRIBE ??", [table]).then((xs) => _.sortBy((x) => x.Field, xs));
1324
1633
  }
1325
- if (dialect === "mssql" || dialect === "ksql") {
1634
+ if (dialect === "mssql") {
1326
1635
  const primaryColumn = await query(`SELECT columns.name as COLUMN_NAME
1327
1636
  FROM sys.tables tables
1328
1637
  JOIN sys.columns columns
@@ -1359,12 +1668,20 @@ const getTableMeta = _.memoize(async function getTableMeta(table) {
1359
1668
  ? "UNI"
1360
1669
  : "",
1361
1670
  Null: x["IS_NULLABLE"],
1362
- Default: x["COLUMN_DEFAULT"],
1671
+ Default: x["COLUMN_DEFAULT"]
1363
1672
  };
1364
1673
  })));
1365
1674
  }
1366
1675
  throw new Error("Unsupported dialect: " + dialect);
1367
1676
  });
1677
+ function getShowCreateTable(table) {
1678
+ if (dialect === "mysql") {
1679
+ return query("SHOW CREATE TABLE ??", [table]).then((xs) => xs[0]["Create Table"]
1680
+ // https://github.com/bradzacher/mysqldump/blob/66839a57e572a07c046b0ba98753f30a7026cbd8/src/getSchemaDump.ts#L65
1681
+ .replace(/AUTO_INCREMENT\s*=\s*\d+ /g, ""));
1682
+ }
1683
+ return Promise.resolve(null);
1684
+ }
1368
1685
  function getJSONSchemaObjProperties(tableMeta) {
1369
1686
  return tableMeta.reduce((acc, m) => {
1370
1687
  const baseType = getBaseJSONType(m.Type);
@@ -1378,7 +1695,7 @@ function getJSONSchemaObjProperties(tableMeta) {
1378
1695
  _enum.push(null);
1379
1696
  }
1380
1697
  acc[m.Field] = {
1381
- type: getJSONTypes(baseType, nullable),
1698
+ type: getJSONTypes(baseType, nullable)
1382
1699
  // maxLength:
1383
1700
  // baseType === "string" && format == null && isEnum == null
1384
1701
  // ? getPropertyMaxLength(m.Type)
@@ -1488,9 +1805,7 @@ function getPropertyEnum(sqlType) {
1488
1805
  return c;
1489
1806
  }
1490
1807
  function getPropertyFormat(sqlType) {
1491
- if (sqlType === "datetime" ||
1492
- sqlType === "datetime2" ||
1493
- sqlType === "timestamp") {
1808
+ if (sqlType === "datetime" || sqlType === "datetime2" || sqlType === "timestamp") {
1494
1809
  // TODO: not sure this is correct for `timestamp`
1495
1810
  return "date-time";
1496
1811
  }
@@ -1514,13 +1829,13 @@ const mssqlTableExcludes = new Set([
1514
1829
  "ddl_history",
1515
1830
  "index_columns",
1516
1831
  "lsn_time_mapping",
1517
- "systranschemas",
1832
+ "systranschemas"
1518
1833
  ]);
1519
1834
  async function getTableNames() {
1520
1835
  if (dialect === "mysql") {
1521
1836
  return query("SHOW TABLES").then((xs) => xs.flatMap((x) => Object.values(x)).sort());
1522
1837
  }
1523
- if (dialect === "mssql" || dialect === "ksql") {
1838
+ if (dialect === "mssql") {
1524
1839
  return query("SELECT * FROM INFORMATION_SCHEMA.TABLES").then((xs) => xs
1525
1840
  .map((x) => x["TABLE_NAME"])
1526
1841
  .filter((x) => !x.startsWith("dbo_") && !mssqlTableExcludes.has(x))
@@ -1528,3 +1843,295 @@ async function getTableNames() {
1528
1843
  }
1529
1844
  throw new Error("Unsupported dialect: " + dialect);
1530
1845
  }
1846
+ function getMysql2sqliteSrc() {
1847
+ return `#!/usr/bin/awk -f
1848
+
1849
+ # Authors: @esperlu, @artemyk, @gkuenning, @dumblob
1850
+
1851
+ # FIXME detect empty input file and issue a warning
1852
+
1853
+ function printerr( s ){ print s | "cat >&2" }
1854
+
1855
+ BEGIN {
1856
+ if( ARGC != 2 ){
1857
+ printerr( \\
1858
+ "USAGE:\\n"\\
1859
+ " mysql2sqlite dump_mysql.sql > dump_sqlite3.sql\\n" \\
1860
+ " OR\\n" \\
1861
+ " mysql2sqlite dump_mysql.sql | sqlite3 sqlite.db\\n" \\
1862
+ "\\n" \\
1863
+ "NOTES:\\n" \\
1864
+ " Dash in filename is not supported, because dash (-) means stdin." )
1865
+ no_END = 1
1866
+ exit 1
1867
+ }
1868
+
1869
+ # Find INT_MAX supported by both this AWK (usually an ISO C signed int)
1870
+ # and SQlite.
1871
+ # On non-8bit-based architectures, the additional bits are safely ignored.
1872
+
1873
+ # 8bit (lower precision should not exist)
1874
+ s="127"
1875
+ # "63" + 0 avoids potential parser misbehavior
1876
+ if( (s + 0) "" == s ){ INT_MAX_HALF = "63" + 0 }
1877
+ # 16bit
1878
+ s="32767"
1879
+ if( (s + 0) "" == s ){ INT_MAX_HALF = "16383" + 0 }
1880
+ # 32bit
1881
+ s="2147483647"
1882
+ if( (s + 0) "" == s ){ INT_MAX_HALF = "1073741823" + 0 }
1883
+ # 64bit (as INTEGER in SQlite3)
1884
+ s="9223372036854775807"
1885
+ if( (s + 0) "" == s ){ INT_MAX_HALF = "4611686018427387904" + 0 }
1886
+ # # 128bit
1887
+ # s="170141183460469231731687303715884105728"
1888
+ # if( (s + 0) "" == s ){ INT_MAX_HALF = "85070591730234615865843651857942052864" + 0 }
1889
+ # # 256bit
1890
+ # s="57896044618658097711785492504343953926634992332820282019728792003956564819968"
1891
+ # if( (s + 0) "" == s ){ INT_MAX_HALF = "28948022309329048855892746252171976963317496166410141009864396001978282409984" + 0 }
1892
+ # # 512bit
1893
+ # s="6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042048"
1894
+ # if( (s + 0) "" == s ){ INT_MAX_HALF = "3351951982485649274893506249551461531869841455148098344430890360930441007518386744200468574541725856922507964546621512713438470702986642486608412251521024" + 0 }
1895
+ # # 1024bit
1896
+ # s="89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119973622969239858152417678164812112068608"
1897
+ # if( (s + 0) "" == s ){ INT_MAX_HALF = "44942328371557897693232629769725618340449424473557664318357520289433168951375240783177119330601884005280028469967848339414697442203604155623211857659868531094441973356216371319075554900311523529863270738021251442209537670585615720368478277635206809290837627671146574559986811484619929076208839082406056034304" + 0 }
1898
+ # # higher precision probably not needed
1899
+
1900
+ FS=",$"
1901
+ print "PRAGMA synchronous = OFF;"
1902
+ print "PRAGMA journal_mode = MEMORY;"
1903
+ print "BEGIN TRANSACTION;"
1904
+ }
1905
+
1906
+ # historically 3 spaces separate non-argument local variables
1907
+ function bit_to_int( str_bit, powtwo, i, res, bit, overflow ){
1908
+ powtwo = 1
1909
+ overflow = 0
1910
+ # 011101 = 1*2^0 + 0*2^1 + 1*2^2 ...
1911
+ for( i = length( str_bit ); i > 0; --i ){
1912
+ bit = substr( str_bit, i, 1 )
1913
+ if( overflow || ( bit == 1 && res > INT_MAX_HALF ) ){
1914
+ printerr( \\
1915
+ NR ": WARN Bit field overflow, number truncated (LSBs saved, MSBs ignored)." )
1916
+ break
1917
+ }
1918
+ res = res + bit * powtwo
1919
+ # no warning here as it might be the last iteration
1920
+ if( powtwo > INT_MAX_HALF ){ overflow = 1; continue }
1921
+ powtwo = powtwo * 2
1922
+ }
1923
+ return res
1924
+ }
1925
+
1926
+ # CREATE TRIGGER statements have funny commenting. Remember we are in trigger.
1927
+ /^\\/\\*.*(CREATE.*TRIGGER|create.*trigger)/ {
1928
+ gsub( /^.*(TRIGGER|trigger)/, "CREATE TRIGGER" )
1929
+ print
1930
+ inTrigger = 1
1931
+ next
1932
+ }
1933
+ # The end of CREATE TRIGGER has a stray comment terminator
1934
+ /(END|end) \\*\\/;;/ { gsub( /\\*\\//, "" ); print; inTrigger = 0; next }
1935
+ # The rest of triggers just get passed through
1936
+ inTrigger != 0 { print; next }
1937
+
1938
+ # CREATE VIEW looks like a TABLE in comments
1939
+ /^\\/\\*.*(CREATE.*TABLE|create.*table)/ {
1940
+ inView = 1
1941
+ next
1942
+ }
1943
+ # end of CREATE VIEW
1944
+ /^(\\).*(ENGINE|engine).*\\*\\/;)/ {
1945
+ inView = 0
1946
+ next
1947
+ }
1948
+ # content of CREATE VIEW
1949
+ inView != 0 { next }
1950
+
1951
+ # skip comments
1952
+ /^\\/\\*/ { next }
1953
+
1954
+ # skip PARTITION statements
1955
+ /^ *[(]?(PARTITION|partition) +[^ ]+/ { next }
1956
+
1957
+ # print all INSERT lines
1958
+ ( /^ *\\(/ && /\\) *[,;] *$/ ) || /^(INSERT|insert|REPLACE|replace)/ {
1959
+ prev = ""
1960
+
1961
+ # first replace \\\\ by \\_ that mysqldump never generates to deal with
1962
+ # sequnces like \\\\n that should be translated into \\n, not \\<LF>.
1963
+ # After we convert all escapes we replace \\_ by backslashes.
1964
+ gsub( /\\\\\\\\/, "\\\\_" )
1965
+
1966
+ # single quotes are escaped by another single quote
1967
+ gsub( /\\\\'/, "''" )
1968
+ gsub( /\\\\n/, "\\n" )
1969
+ gsub( /\\\\r/, "\\r" )
1970
+ gsub( /\\\\"/, "\\"" )
1971
+ gsub( /\\\\\\032/, "\\032" ) # substitute char
1972
+
1973
+ gsub( /\\\\_/, "\\\\" )
1974
+
1975
+ # sqlite3 is limited to 16 significant digits of precision
1976
+ while( match( $0, /0x[0-9a-fA-F]{17}/ ) ){
1977
+ hexIssue = 1
1978
+ sub( /0x[0-9a-fA-F]+/, substr( $0, RSTART, RLENGTH-1 ), $0 )
1979
+ }
1980
+ if( hexIssue ){
1981
+ printerr( \\
1982
+ NR ": WARN Hex number trimmed (length longer than 16 chars)." )
1983
+ hexIssue = 0
1984
+ }
1985
+ print
1986
+ next
1987
+ }
1988
+
1989
+ # CREATE DATABASE is not supported
1990
+ /^(CREATE DATABASE|create database)/ { next }
1991
+
1992
+ # print the CREATE line as is and capture the table name
1993
+ /^(CREATE|create)/ {
1994
+ if( $0 ~ /IF NOT EXISTS|if not exists/ || $0 ~ /TEMPORARY|temporary/ ){
1995
+ caseIssue = 1
1996
+ printerr( \\
1997
+ NR ": WARN Potential case sensitivity issues with table/column naming\\n" \\
1998
+ " (see INFO at the end)." )
1999
+ }
2000
+ if( match( $0, /\`[^\`]+/ ) ){
2001
+ tableName = substr( $0, RSTART+1, RLENGTH-1 )
2002
+ }
2003
+ aInc = 0
2004
+ prev = ""
2005
+ firstInTable = 1
2006
+ print
2007
+ next
2008
+ }
2009
+
2010
+ # Replace \`FULLTEXT KEY\` (probably other \`XXXXX KEY\`)
2011
+ /^ (FULLTEXT KEY|fulltext key)/ { gsub( /[A-Za-z ]+(KEY|key)/, " KEY" ) }
2012
+
2013
+ # Get rid of field lengths in KEY lines
2014
+ / (PRIMARY |primary )?(KEY|key)/ { gsub( /\\([0-9]+\\)/, "" ) }
2015
+
2016
+ aInc == 1 && /PRIMARY KEY|primary key/ { next }
2017
+
2018
+ # Replace COLLATE xxx_xxxx_xx statements with COLLATE BINARY
2019
+ / (COLLATE|collate) [a-z0-9_]*/ { gsub( /(COLLATE|collate) [a-z0-9_]*/, "COLLATE BINARY" ) }
2020
+
2021
+ # Print all fields definition lines except the \`KEY\` lines.
2022
+ /^ / && !/^( (KEY|key)|\\);)/ {
2023
+ if( match( $0, /[^"\`]AUTO_INCREMENT|auto_increment[^"\`]/) ){
2024
+ aInc = 1
2025
+ gsub( /AUTO_INCREMENT|auto_increment/, "PRIMARY KEY AUTOINCREMENT" )
2026
+ }
2027
+ gsub( /(UNIQUE KEY|unique key) (\`.*\`|".*") /, "UNIQUE " )
2028
+ gsub( /(CHARACTER SET|character set) [^ ]+[ ,]/, "" )
2029
+ # FIXME
2030
+ # CREATE TRIGGER [UpdateLastTime]
2031
+ # AFTER UPDATE
2032
+ # ON Package
2033
+ # FOR EACH ROW
2034
+ # BEGIN
2035
+ # UPDATE Package SET LastUpdate = CURRENT_TIMESTAMP WHERE ActionId = old.ActionId;
2036
+ # END
2037
+ gsub( /(ON|on) (UPDATE|update) (CURRENT_TIMESTAMP|current_timestamp)(\\(\\))?/, "" )
2038
+ gsub( /(DEFAULT|default) (CURRENT_TIMESTAMP|current_timestamp)(\\(\\))?/, "DEFAULT current_timestamp")
2039
+ gsub( /(COLLATE|collate) [^ ]+ /, "" )
2040
+ gsub( /(ENUM|enum)[^)]+\\)/, "text " )
2041
+ gsub( /(SET|set)\\([^)]+\\)/, "text " )
2042
+ gsub( /UNSIGNED|unsigned/, "" )
2043
+ gsub( /_utf8mb3/, "" )
2044
+ gsub( /\` [^ ]*(INT|int|BIT|bit)[^ ]*/, "\` integer" )
2045
+ gsub( /" [^ ]*(INT|int|BIT|bit)[^ ]*/, "\\" integer" )
2046
+ ere_bit_field = "[bB]'[10]+'"
2047
+ if( match($0, ere_bit_field) ){
2048
+ sub( ere_bit_field, bit_to_int( substr( $0, RSTART +2, RLENGTH -2 -1 ) ) )
2049
+ }
2050
+
2051
+ # remove USING BTREE and other suffixes for USING, for example: "UNIQUE KEY
2052
+ # \`hostname_domain\` (\`hostname\`,\`domain\`) USING BTREE,"
2053
+ gsub( / USING [^, ]+/, "" )
2054
+
2055
+ # field comments are not supported
2056
+ gsub( / (COMMENT|comment).+$/, "" )
2057
+ # Get commas off end of line
2058
+ gsub( /,.?$/, "" )
2059
+ if( prev ){
2060
+ if( firstInTable ){
2061
+ print prev
2062
+ firstInTable = 0
2063
+ }
2064
+ else {
2065
+ print "," prev
2066
+ }
2067
+ }
2068
+ else {
2069
+ # FIXME check if this is correct in all cases
2070
+ if( match( $1,
2071
+ /(CONSTRAINT|constraint) ["].*["] (FOREIGN KEY|foreign key)/ ) ){
2072
+ print ","
2073
+ }
2074
+ }
2075
+ prev = $1
2076
+ }
2077
+
2078
+ / ENGINE| engine/ {
2079
+ if( prev ){
2080
+ if( firstInTable ){
2081
+ print prev
2082
+ firstInTable = 0
2083
+ }
2084
+ else {
2085
+ print "," prev
2086
+ }
2087
+ }
2088
+ prev=""
2089
+ print ");"
2090
+ next
2091
+ }
2092
+ # \`KEY\` lines are extracted from the \`CREATE\` block and stored in array for later print
2093
+ # in a separate \`CREATE KEY\` command. The index name is prefixed by the table name to
2094
+ # avoid a sqlite error for duplicate index name.
2095
+ /^( (KEY|key)|\\);)/ {
2096
+ if( prev ){
2097
+ if( firstInTable ){
2098
+ print prev
2099
+ firstInTable = 0
2100
+ }
2101
+ else {
2102
+ print "," prev
2103
+ }
2104
+ }
2105
+ prev = ""
2106
+ if( $0 == ");" ){
2107
+ print
2108
+ }
2109
+ else {
2110
+ if( match( $0, /\`[^\`]+/ ) ){
2111
+ indexName = substr( $0, RSTART+1, RLENGTH-1 )
2112
+ }
2113
+ if( match( $0, /\\([^()]+/ ) ){
2114
+ indexKey = substr( $0, RSTART+1, RLENGTH-1 )
2115
+ }
2116
+ # idx_ prefix to avoid name clashes (they really happen!)
2117
+ key[tableName] = key[tableName] "CREATE INDEX \\"idx_" \\
2118
+ tableName "_" indexName "\\" ON \\"" tableName "\\" (" indexKey ");\\n"
2119
+ }
2120
+ }
2121
+
2122
+ END {
2123
+ if( no_END ){ exit 1}
2124
+ # print all KEY creation lines.
2125
+ for( table in key ){ printf key[table] }
2126
+
2127
+ print "END TRANSACTION;"
2128
+
2129
+ if( caseIssue ){
2130
+ printerr( \\
2131
+ "INFO Pure sqlite identifiers are case insensitive (even if quoted\\n" \\
2132
+ " or if ASCII) and doesnt cross-check TABLE and TEMPORARY TABLE\\n" \\
2133
+ " identifiers. Thus expect errors like \\"table T has no column named F\\".")
2134
+ }
2135
+ }
2136
+ `;
2137
+ }