sonamu 0.2.55 → 0.2.56

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 (45) hide show
  1. package/dist/bin/cli.js +89 -118
  2. package/dist/bin/cli.js.map +1 -1
  3. package/dist/bin/cli.mjs +45 -74
  4. package/dist/bin/cli.mjs.map +1 -1
  5. package/dist/{chunk-ZFLQLW37.mjs → chunk-RHSTOLSM.mjs} +5568 -5243
  6. package/dist/chunk-RHSTOLSM.mjs.map +1 -0
  7. package/dist/{chunk-WJGRXAXE.js → chunk-V6CWU5VD.js} +5577 -5252
  8. package/dist/chunk-V6CWU5VD.js.map +1 -0
  9. package/dist/index.d.mts +931 -226
  10. package/dist/index.d.ts +931 -226
  11. package/dist/index.js +26 -13
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +31 -18
  14. package/dist/index.mjs.map +1 -1
  15. package/package.json +1 -1
  16. package/dist/base-model-BzMJ2E_I.d.mts +0 -43
  17. package/dist/base-model-CWRKUX49.d.ts +0 -43
  18. package/dist/chunk-4K2F3SOM.mjs +0 -231
  19. package/dist/chunk-4K2F3SOM.mjs.map +0 -1
  20. package/dist/chunk-6SP5N5ND.mjs +0 -1579
  21. package/dist/chunk-6SP5N5ND.mjs.map +0 -1
  22. package/dist/chunk-EUP6N7EK.js +0 -1579
  23. package/dist/chunk-EUP6N7EK.js.map +0 -1
  24. package/dist/chunk-HVVCQLAU.mjs +0 -280
  25. package/dist/chunk-HVVCQLAU.mjs.map +0 -1
  26. package/dist/chunk-N6N3LENC.js +0 -231
  27. package/dist/chunk-N6N3LENC.js.map +0 -1
  28. package/dist/chunk-UAG3SKFM.js +0 -280
  29. package/dist/chunk-UAG3SKFM.js.map +0 -1
  30. package/dist/chunk-WJGRXAXE.js.map +0 -1
  31. package/dist/chunk-ZFLQLW37.mjs.map +0 -1
  32. package/dist/database/drivers/knex/base-model.d.mts +0 -16
  33. package/dist/database/drivers/knex/base-model.d.ts +0 -16
  34. package/dist/database/drivers/knex/base-model.js +0 -55
  35. package/dist/database/drivers/knex/base-model.js.map +0 -1
  36. package/dist/database/drivers/knex/base-model.mjs +0 -56
  37. package/dist/database/drivers/knex/base-model.mjs.map +0 -1
  38. package/dist/database/drivers/kysely/base-model.d.mts +0 -22
  39. package/dist/database/drivers/kysely/base-model.d.ts +0 -22
  40. package/dist/database/drivers/kysely/base-model.js +0 -64
  41. package/dist/database/drivers/kysely/base-model.js.map +0 -1
  42. package/dist/database/drivers/kysely/base-model.mjs +0 -65
  43. package/dist/database/drivers/kysely/base-model.mjs.map +0 -1
  44. package/dist/model-CAH_4oQh.d.mts +0 -1042
  45. package/dist/model-CAH_4oQh.d.ts +0 -1042
@@ -1,231 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;
2
-
3
- var _chunkWJGRXAXEjs = require('./chunk-WJGRXAXE.js');
4
-
5
- // src/database/base-model.ts
6
- var _luxon = require('luxon');
7
- var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash);
8
- var _nodesqlparser = require('node-sql-parser'); var _nodesqlparser2 = _interopRequireDefault(_nodesqlparser);
9
- var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
10
- var _inflection = require('inflection'); var _inflection2 = _interopRequireDefault(_inflection);
11
-
12
- // src/utils/sql-parser.ts
13
-
14
- function getTableName(expr) {
15
- if ("table" in expr && expr.table !== null) {
16
- return typeof expr.table === "string" ? expr.table : expr.table.value;
17
- }
18
- return null;
19
- }
20
- function getTableNamesFromWhere(ast) {
21
- const extractTableNames = (where) => {
22
- if (where === null || !(where.type === "binary_expr" && "left" in where)) {
23
- return [];
24
- }
25
- const extractTableName = (expr) => {
26
- if (expr.type === "column_ref") {
27
- const table = getTableName(expr);
28
- return table ? [table] : [];
29
- } else if (expr.type === "binary_expr" && "left" in expr) {
30
- return extractTableNames(expr);
31
- }
32
- return [];
33
- };
34
- return [...extractTableName(where.left), ...extractTableName(where.right)];
35
- };
36
- return _lodash2.default.uniq(
37
- (Array.isArray(ast) ? ast : [ast]).flatMap(
38
- (a) => a.type === "select" || a.type === "update" || a.type === "delete" ? extractTableNames(a.where) : []
39
- )
40
- );
41
- }
42
-
43
- // src/database/base-model.ts
44
- var BaseModelClassAbstract = (_class = class {constructor() { _class.prototype.__init.call(this); }
45
- __init() {this.modelName = "Unknown"}
46
- async runSubsetQuery({
47
- params,
48
- baseTable,
49
- subset,
50
- subsetQuery,
51
- build,
52
- debug,
53
- db: _db,
54
- optimizeCountQuery
55
- }) {
56
- const db = _nullishCoalesce(_db, () => ( _chunkWJGRXAXEjs.DB.getDB(subset.startsWith("A") ? "w" : "r")));
57
- const dbClient = _chunkWJGRXAXEjs.DB.toClient(db);
58
- baseTable = _nullishCoalesce(baseTable, () => ( _inflection2.default.pluralize(_inflection2.default.underscore(this.modelName))));
59
- const queryMode = _nullishCoalesce(params.queryMode, () => ( (params.id !== void 0 ? "list" : "both")));
60
- const { select, virtual, joins, loaders } = subsetQuery;
61
- const _qb = build({
62
- qb: dbClient.from(baseTable).qb,
63
- db,
64
- select,
65
- joins,
66
- virtual
67
- });
68
- dbClient.qb = _qb;
69
- const qb = dbClient;
70
- const total = await (async () => {
71
- if (queryMode === "list") return void 0;
72
- const clonedQb = qb.clone().clearQueryParts(["order", "offset", "limit"]).clearSelect().select(`${baseTable}.id`);
73
- const parser = new _nodesqlparser2.default.Parser();
74
- if (optimizeCountQuery) {
75
- const parsedQuery2 = parser.astify(clonedQb.sql);
76
- const tables = getTableNamesFromWhere(parsedQuery2);
77
- const needToJoin = _lodash2.default.uniq(
78
- tables.flatMap(
79
- (table) => table.split("__").map((t) => _inflection2.default.pluralize(t))
80
- )
81
- );
82
- this.applyJoins(
83
- clonedQb,
84
- joins.filter((j) => needToJoin.includes(j.table))
85
- );
86
- } else {
87
- this.applyJoins(clonedQb, joins);
88
- }
89
- const parsedQuery = parser.astify(clonedQb.sql);
90
- const q = Array.isArray(parsedQuery) ? parsedQuery[0] : parsedQuery;
91
- if (q.type !== "select") {
92
- throw new Error("Invalid query");
93
- }
94
- const countColumn = `${getTableName(q.columns[0].expr)}.${q.columns[0].expr.column}`;
95
- clonedQb.clearSelect().count(countColumn, "total").first();
96
- if (q.distinct) {
97
- clonedQb.distinct(countColumn);
98
- }
99
- if (debug === true || debug === "count") {
100
- console.debug("DEBUG: count query", _chalk2.default.blue(clonedQb.sql));
101
- }
102
- const [{ total: total2 }] = await clonedQb.execute();
103
- return total2;
104
- })();
105
- const rows = await (async () => {
106
- if (queryMode === "count") return [];
107
- let listQb = qb;
108
- if (params.num !== 0) {
109
- listQb = listQb.limit(params.num).offset(params.num * (params.page - 1));
110
- }
111
- listQb.select(select);
112
- listQb = this.applyJoins(listQb, joins);
113
- if (debug === true || debug === "list") {
114
- console.debug("DEBUG: list query", _chalk2.default.blue(listQb.sql));
115
- }
116
- let rows2 = await listQb.execute();
117
- rows2 = await this.useLoaders(dbClient, rows2, loaders);
118
- return this.hydrate(rows2);
119
- })();
120
- return {
121
- rows,
122
- total,
123
- subsetQuery,
124
- qb: dbClient.qb
125
- };
126
- }
127
- async useLoaders(db, rows, loaders) {
128
- if (loaders.length === 0) return rows;
129
- for (const loader of loaders) {
130
- const fromIds = rows.map((row) => row[loader.manyJoin.idField]);
131
- if (!fromIds.length) continue;
132
- let subRows;
133
- let toCol;
134
- if (loader.manyJoin.through === void 0) {
135
- const { subQ, col } = await this.buildHasManyQuery(db, loader, fromIds);
136
- subRows = await subQ.execute();
137
- toCol = col;
138
- } else {
139
- const { subQ, col } = await this.buildManyToManyQuery(
140
- db,
141
- loader,
142
- fromIds
143
- );
144
- subRows = await subQ.execute();
145
- toCol = col;
146
- }
147
- if (loader.loaders) {
148
- subRows = await this.useLoaders(db, subRows, loader.loaders);
149
- }
150
- const subRowGroups = _lodash2.default.groupBy(subRows, toCol);
151
- rows = rows.map((row) => {
152
- row[loader.as] = (_nullishCoalesce(subRowGroups[row[loader.manyJoin.idField]], () => ( []))).map(
153
- (r) => _lodash2.default.omit(r, toCol)
154
- );
155
- return row;
156
- });
157
- }
158
- return rows;
159
- }
160
- async buildHasManyQuery(db, loader, fromIds) {
161
- const idColumn = `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`;
162
- let qb = db.from(loader.manyJoin.toTable);
163
- db.where([idColumn, "in", fromIds]).select([...loader.select, idColumn]);
164
- qb = this.applyJoins(qb, loader.oneJoins);
165
- return {
166
- subQ: qb,
167
- col: loader.manyJoin.toCol
168
- };
169
- }
170
- async buildManyToManyQuery(db, loader, fromIds) {
171
- if (!loader.manyJoin.through)
172
- throw new Error("Through table info missing for many-to-many relation");
173
- const idColumn = `${loader.manyJoin.through.table}.${loader.manyJoin.through.fromCol}`;
174
- let qb = db.from(loader.manyJoin.through.table);
175
- const throughTable = loader.manyJoin.through.table;
176
- const targetTable = loader.manyJoin.toTable;
177
- qb = this.applyJoins(qb, [
178
- {
179
- join: "inner",
180
- table: targetTable,
181
- as: targetTable,
182
- from: `${throughTable}.${loader.manyJoin.through.toCol}`,
183
- to: `${targetTable}.${loader.manyJoin.toCol}`
184
- }
185
- ]);
186
- qb.where([idColumn, "in", fromIds]).select([...loader.select, idColumn]);
187
- qb = this.applyJoins(qb, loader.oneJoins);
188
- return {
189
- subQ: qb,
190
- col: loader.manyJoin.through.fromCol
191
- };
192
- }
193
- myNow(timestamp) {
194
- const dt = timestamp === void 0 ? _luxon.DateTime.local() : _luxon.DateTime.fromSeconds(timestamp);
195
- return dt.toFormat("yyyy-MM-dd HH:mm:ss");
196
- }
197
- hydrate(rows) {
198
- return rows.map((row) => {
199
- const nestedKeys = Object.keys(row).filter((key) => key.includes("__"));
200
- const groups = _lodash2.default.groupBy(nestedKeys, (key) => key.split("__")[0]);
201
- const nullKeys = Object.keys(groups).filter(
202
- (key) => groups[key].length > 1 && groups[key].every((field) => row[field] === null)
203
- );
204
- const hydrated = Object.keys(row).reduce((r, field) => {
205
- if (!field.includes("__")) {
206
- if (Array.isArray(row[field]) && _lodash2.default.isObject(row[field][0])) {
207
- r[field] = this.hydrate(row[field]);
208
- } else {
209
- r[field] = row[field];
210
- }
211
- return r;
212
- }
213
- const parts = field.split("__");
214
- const objPath = parts[0] + parts.slice(1).map((part) => `[${part}]`).join("");
215
- _lodash2.default.set(
216
- r,
217
- objPath,
218
- row[field] && Array.isArray(row[field]) && _lodash2.default.isObject(row[field][0]) ? this.hydrate(row[field]) : row[field]
219
- );
220
- return r;
221
- }, {});
222
- nullKeys.forEach((nullKey) => hydrated[nullKey] = null);
223
- return hydrated;
224
- });
225
- }
226
- }, _class);
227
-
228
-
229
-
230
- exports.BaseModelClassAbstract = BaseModelClassAbstract;
231
- //# sourceMappingURL=chunk-N6N3LENC.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/database/base-model.ts","../src/utils/sql-parser.ts"],"names":["_","parsedQuery","total","rows"],"mappings":"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","sourcesContent":["// base-model.ts\nimport { DateTime } from \"luxon\";\nimport _ from \"lodash\";\nimport SqlParser from \"node-sql-parser\";\nimport chalk from \"chalk\";\nimport inflection from \"inflection\";\nimport { BaseListParams } from \"../utils/model\";\nimport { DBPreset, DriverSpec, DatabaseDriver } from \"./types\";\nimport { SubsetQuery } from \"../types/types\";\nimport { getTableName, getTableNamesFromWhere } from \"../utils/sql-parser\";\nimport { DB } from \"./db\";\n\nexport abstract class BaseModelClassAbstract<D extends DatabaseDriver> {\n public modelName: string = \"Unknown\";\n\n protected abstract applyJoins(\n qb: DriverSpec[D][\"adapter\"],\n joins: SubsetQuery[\"joins\"]\n ): DriverSpec[D][\"adapter\"];\n protected abstract executeCountQuery(\n qb: DriverSpec[D][\"queryBuilder\"]\n ): Promise<number>;\n\n abstract getDB(which: DBPreset): DriverSpec[D][\"core\"];\n abstract destroy(): Promise<void>;\n\n async runSubsetQuery<T extends BaseListParams, U extends string>({\n params,\n baseTable,\n subset,\n subsetQuery,\n build,\n debug,\n db: _db,\n optimizeCountQuery,\n }: {\n subset: U;\n params: T;\n subsetQuery: SubsetQuery;\n build: (buildParams: {\n qb: DriverSpec[D][\"queryBuilder\"];\n db: DriverSpec[D][\"core\"];\n select: SubsetQuery[\"select\"];\n joins: SubsetQuery[\"joins\"];\n virtual: string[];\n }) => DriverSpec[D][\"queryBuilder\"];\n baseTable?: DriverSpec[D][\"table\"];\n debug?: boolean | \"list\" | \"count\";\n db?: DriverSpec[D][\"core\"];\n optimizeCountQuery?: boolean;\n }): Promise<{\n rows: any[];\n total?: number;\n subsetQuery: SubsetQuery;\n qb: DriverSpec[D][\"queryBuilder\"];\n }> {\n const db = _db ?? DB.getDB(subset.startsWith(\"A\") ? \"w\" : \"r\");\n const dbClient = DB.toClient(db);\n baseTable =\n baseTable ?? inflection.pluralize(inflection.underscore(this.modelName));\n const queryMode =\n params.queryMode ?? (params.id !== undefined ? \"list\" : \"both\");\n\n const { select, virtual, joins, loaders } = subsetQuery;\n const _qb = build({\n qb: dbClient.from(baseTable).qb,\n db,\n select,\n joins,\n virtual,\n });\n dbClient.qb = _qb;\n const qb = dbClient;\n\n const total = await (async () => {\n if (queryMode === \"list\") return undefined;\n\n const clonedQb = qb\n .clone()\n .clearQueryParts([\"order\", \"offset\", \"limit\"])\n .clearSelect()\n .select(`${baseTable}.id`);\n const parser = new SqlParser.Parser();\n\n if (optimizeCountQuery) {\n const parsedQuery = parser.astify(clonedQb.sql);\n const tables = getTableNamesFromWhere(parsedQuery);\n const needToJoin = _.uniq(\n tables.flatMap((table) =>\n table.split(\"__\").map((t) => inflection.pluralize(t))\n )\n );\n\n this.applyJoins(\n clonedQb,\n joins.filter((j) => needToJoin.includes(j.table))\n );\n } else {\n this.applyJoins(clonedQb, joins);\n }\n\n const parsedQuery = parser.astify(clonedQb.sql);\n const q = Array.isArray(parsedQuery) ? parsedQuery[0] : parsedQuery;\n\n if (q.type !== \"select\") {\n throw new Error(\"Invalid query\");\n }\n\n const countColumn = `${getTableName(q.columns[0].expr)}.${q.columns[0].expr.column}`;\n clonedQb.clearSelect().count(countColumn, \"total\").first();\n if (q.distinct) {\n clonedQb.distinct(countColumn);\n }\n\n if (debug === true || debug === \"count\") {\n console.debug(\"DEBUG: count query\", chalk.blue(clonedQb.sql));\n }\n\n const [{ total }] = await clonedQb.execute();\n return total;\n })();\n\n const rows = await (async () => {\n if (queryMode === \"count\") return [];\n\n let listQb = qb;\n if (params.num !== 0) {\n listQb = listQb\n .limit(params.num!)\n .offset(params.num! * (params.page! - 1));\n }\n\n listQb.select(select);\n listQb = this.applyJoins(listQb, joins);\n\n if (debug === true || debug === \"list\") {\n console.debug(\"DEBUG: list query\", chalk.blue(listQb.sql));\n }\n\n let rows = await listQb.execute();\n rows = await this.useLoaders(dbClient, rows, loaders);\n return this.hydrate(rows);\n })();\n\n return {\n rows,\n total,\n subsetQuery,\n qb: dbClient.qb,\n };\n }\n\n async useLoaders(\n db: DriverSpec[D][\"adapter\"],\n rows: any[],\n loaders: SubsetQuery[\"loaders\"]\n ): Promise<any[]> {\n if (loaders.length === 0) return rows;\n\n for (const loader of loaders) {\n const fromIds = rows.map((row) => row[loader.manyJoin.idField]);\n\n if (!fromIds.length) continue;\n\n let subRows: any[];\n let toCol: string;\n\n if (loader.manyJoin.through === undefined) {\n // HasMany\n const { subQ, col } = await this.buildHasManyQuery(db, loader, fromIds);\n subRows = await subQ.execute();\n toCol = col;\n } else {\n // ManyToMany\n const { subQ, col } = await this.buildManyToManyQuery(\n db,\n loader,\n fromIds\n );\n subRows = await subQ.execute();\n toCol = col;\n }\n\n if (loader.loaders) {\n subRows = await this.useLoaders(db, subRows, loader.loaders);\n }\n\n // Group and assign loaded rows\n const subRowGroups = _.groupBy(subRows, toCol);\n rows = rows.map((row) => {\n row[loader.as] = (subRowGroups[row[loader.manyJoin.idField]] ?? []).map(\n (r) => _.omit(r, toCol)\n );\n return row;\n });\n }\n\n return rows;\n }\n\n protected async buildHasManyQuery(\n db: DriverSpec[D][\"adapter\"],\n loader: SubsetQuery[\"loaders\"][number],\n fromIds: any[]\n ) {\n const idColumn = `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`;\n let qb = db.from(loader.manyJoin.toTable);\n\n db.where([idColumn, \"in\", fromIds]).select([...loader.select, idColumn]);\n qb = this.applyJoins(qb, loader.oneJoins);\n\n return {\n subQ: qb,\n col: loader.manyJoin.toCol,\n };\n }\n\n protected async buildManyToManyQuery(\n db: DriverSpec[D][\"adapter\"],\n loader: SubsetQuery[\"loaders\"][number],\n fromIds: any[]\n ) {\n if (!loader.manyJoin.through)\n throw new Error(\"Through table info missing for many-to-many relation\");\n\n const idColumn = `${loader.manyJoin.through.table}.${loader.manyJoin.through.fromCol}`;\n let qb = db.from(loader.manyJoin.through.table);\n\n const throughTable = loader.manyJoin.through.table;\n const targetTable = loader.manyJoin.toTable;\n\n qb = this.applyJoins(qb, [\n {\n join: \"inner\",\n table: targetTable,\n as: targetTable,\n from: `${throughTable}.${loader.manyJoin.through.toCol}`,\n to: `${targetTable}.${loader.manyJoin.toCol}`,\n },\n ]);\n\n qb.where([idColumn, \"in\", fromIds]).select([...loader.select, idColumn]);\n qb = this.applyJoins(qb, loader.oneJoins);\n\n return {\n subQ: qb,\n col: loader.manyJoin.through.fromCol,\n };\n }\n\n myNow(timestamp?: number): string {\n const dt: DateTime =\n timestamp === undefined\n ? DateTime.local()\n : DateTime.fromSeconds(timestamp);\n return dt.toFormat(\"yyyy-MM-dd HH:mm:ss\");\n }\n\n hydrate<T>(rows: T[]): T[] {\n return rows.map((row: any) => {\n const nestedKeys = Object.keys(row).filter((key) => key.includes(\"__\"));\n const groups = _.groupBy(nestedKeys, (key) => key.split(\"__\")[0]);\n const nullKeys = Object.keys(groups).filter(\n (key) =>\n groups[key].length > 1 &&\n groups[key].every((field) => row[field] === null)\n );\n\n const hydrated = Object.keys(row).reduce((r, field) => {\n if (!field.includes(\"__\")) {\n if (Array.isArray(row[field]) && _.isObject(row[field][0])) {\n r[field] = this.hydrate(row[field]);\n } else {\n r[field] = row[field];\n }\n return r;\n }\n\n const parts = field.split(\"__\");\n const objPath =\n parts[0] +\n parts\n .slice(1)\n .map((part) => `[${part}]`)\n .join(\"\");\n\n _.set(\n r,\n objPath,\n row[field] && Array.isArray(row[field]) && _.isObject(row[field][0])\n ? this.hydrate(row[field])\n : row[field]\n );\n\n return r;\n }, {} as any);\n\n nullKeys.forEach((nullKey) => (hydrated[nullKey] = null));\n return hydrated;\n });\n }\n}\n","import _ from \"lodash\";\nimport { AST, ColumnRef, Expr, ExpressionValue, Select } from \"node-sql-parser\";\n\nexport function getTableName(expr: ColumnRef) {\n if (\"table\" in expr && expr.table !== null) {\n return typeof expr.table === \"string\"\n ? expr.table\n : (expr.table as { type: string; value: string }).value;\n }\n return null;\n}\n\n// where 조건에 사용된 테이블명을 추출\nexport function getTableNamesFromWhere(ast: AST | AST[]): string[] {\n const extractTableNames = (where: Select[\"where\"]): string[] => {\n if (where === null || !(where.type === \"binary_expr\" && \"left\" in where)) {\n return [];\n }\n\n const extractTableName = (expr: Expr | ExpressionValue): string[] => {\n if (expr.type === \"column_ref\") {\n const table = getTableName(expr as ColumnRef);\n return table ? [table] : [];\n } else if (expr.type === \"binary_expr\" && \"left\" in expr) {\n return extractTableNames(expr);\n }\n return [];\n };\n\n return [...extractTableName(where.left), ...extractTableName(where.right)];\n };\n\n return _.uniq(\n (Array.isArray(ast) ? ast : [ast]).flatMap((a) =>\n a.type === \"select\" || a.type === \"update\" || a.type === \"delete\"\n ? extractTableNames(a.where)\n : []\n )\n );\n}\n"]}
@@ -1,280 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
-
3
-
4
-
5
- var _chunkWJGRXAXEjs = require('./chunk-WJGRXAXE.js');
6
-
7
- // src/database/upsert-builder.ts
8
- var _uuid = require('uuid');
9
- var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash);
10
-
11
- // src/database/_batch_update.ts
12
- async function batchUpdate(db, tableName, ids, rows, chunkSize = 50, trx = null) {
13
- const chunks = [];
14
- for (let i = 0; i < rows.length; i += chunkSize) {
15
- chunks.push(rows.slice(i, i + chunkSize));
16
- }
17
- const executeUpdate = async (chunk, transaction) => {
18
- const sql = generateBatchUpdateSQL(db, tableName, chunk, ids);
19
- return transaction.raw(sql);
20
- };
21
- if (trx) {
22
- for (const chunk of chunks) {
23
- await executeUpdate(chunk, _chunkWJGRXAXEjs.DB.toClient(trx));
24
- }
25
- } else {
26
- await db.trx(async (newTrx) => {
27
- for (const chunk of chunks) {
28
- await executeUpdate(chunk, newTrx);
29
- }
30
- });
31
- }
32
- }
33
- function generateKeySetFromData(data) {
34
- const keySet = /* @__PURE__ */ new Set();
35
- for (const row of data) {
36
- for (const key of Object.keys(row)) {
37
- keySet.add(key);
38
- }
39
- }
40
- return keySet;
41
- }
42
- function generateBatchUpdateSQL(db, tableName, data, identifiers) {
43
- const keySet = generateKeySetFromData(data);
44
- const bindings = [];
45
- const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));
46
- if (invalidIdentifiers.length > 0) {
47
- throw new Error(
48
- `Invalid identifiers: ${invalidIdentifiers.join(", ")}. Identifiers must exist in the data`
49
- );
50
- }
51
- const cases = [];
52
- for (const key of keySet) {
53
- if (identifiers.includes(key)) continue;
54
- const rows = [];
55
- for (const row of data) {
56
- if (Object.hasOwnProperty.call(row, key)) {
57
- const whereClause = identifiers.map((id) => `\`${id}\` = ?`).join(" AND ");
58
- rows.push(`WHEN (${whereClause}) THEN ?`);
59
- bindings.push(...identifiers.map((i) => row[i]), row[key]);
60
- }
61
- }
62
- const whenThen = rows.join(" ");
63
- cases.push(`\`${key}\` = CASE ${whenThen} ELSE \`${key}\` END`);
64
- }
65
- const whereInClauses = identifiers.map((col) => `${col} IN (${data.map(() => "?").join(", ")})`).join(" AND ");
66
- const whereInBindings = identifiers.flatMap(
67
- (col) => data.map((row) => row[col])
68
- );
69
- const sql = db.createRawQuery(
70
- `UPDATE \`${tableName}\` SET ${cases.join(", ")} WHERE ${whereInClauses}`,
71
- [...bindings, ...whereInBindings]
72
- );
73
- return sql;
74
- }
75
-
76
- // src/database/upsert-builder.ts
77
- function isRefField(field) {
78
- return field !== void 0 && field !== null && field.of !== void 0 && field.uuid !== void 0;
79
- }
80
- var UpsertBuilder = class {
81
-
82
- constructor() {
83
- this.tables = /* @__PURE__ */ new Map();
84
- }
85
- getTable(tableName) {
86
- const table = this.tables.get(tableName);
87
- if (table === void 0) {
88
- const tableSpec = (() => {
89
- try {
90
- return _chunkWJGRXAXEjs.EntityManager.getTableSpec(tableName);
91
- } catch (e) {
92
- return null;
93
- }
94
- })();
95
- this.tables.set(tableName, {
96
- references: /* @__PURE__ */ new Set(),
97
- rows: [],
98
- uniqueIndexes: _nullishCoalesce(_optionalChain([tableSpec, 'optionalAccess', _2 => _2.uniqueIndexes]), () => ( [])),
99
- uniquesMap: /* @__PURE__ */ new Map()
100
- });
101
- }
102
- return this.tables.get(tableName);
103
- }
104
- hasTable(tableName) {
105
- return this.tables.has(tableName);
106
- }
107
- register(tableName, row) {
108
- const table = this.getTable(tableName);
109
- const uniqueKeys = table.uniqueIndexes.map((unqIndex) => {
110
- const uniqueKeyArray = unqIndex.columns.map((unqCol) => {
111
- const val = row[unqCol];
112
- if (isRefField(val)) {
113
- return val.uuid;
114
- } else {
115
- return _nullishCoalesce(row[unqCol], () => ( _uuid.v4.call(void 0, )));
116
- }
117
- });
118
- if (uniqueKeyArray.length === 0) {
119
- return null;
120
- }
121
- return uniqueKeyArray.join("---delimiter--");
122
- }).filter(_chunkWJGRXAXEjs.nonNullable);
123
- const uuid = (() => {
124
- if (uniqueKeys.length > 0) {
125
- for (const uniqueKey of uniqueKeys) {
126
- if (table.uniquesMap.has(uniqueKey)) {
127
- return table.uniquesMap.get(uniqueKey);
128
- }
129
- }
130
- }
131
- return _uuid.v4.call(void 0, );
132
- })();
133
- if (uniqueKeys.length > 0) {
134
- for (const uniqueKey of uniqueKeys) {
135
- table.uniquesMap.set(uniqueKey, uuid);
136
- }
137
- }
138
- row = Object.keys(row).reduce((r, rowKey) => {
139
- const rowValue = row[rowKey];
140
- if (isRefField(rowValue)) {
141
- rowValue.use ??= "id";
142
- table.references.add(rowValue.of + "." + rowValue.use);
143
- r[rowKey] = rowValue;
144
- } else if (typeof rowValue === "object") {
145
- r[rowKey] = rowValue === null ? null : JSON.stringify(rowValue);
146
- } else {
147
- r[rowKey] = rowValue;
148
- }
149
- return r;
150
- }, {});
151
- table.rows.push({
152
- uuid,
153
- ...row
154
- });
155
- return {
156
- of: tableName,
157
- uuid: _nullishCoalesce(row.uuid, () => ( uuid))
158
- };
159
- }
160
- async upsert(wdb, tableName, chunkSize) {
161
- return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
162
- }
163
- async insertOnly(wdb, tableName, chunkSize) {
164
- return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
165
- }
166
- async upsertOrInsert(_wdb, tableName, mode, chunkSize) {
167
- if (this.hasTable(tableName) === false) {
168
- return [];
169
- }
170
- const table = this.tables.get(tableName);
171
- if (table === void 0) {
172
- throw new Error(`\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uD14C\uC774\uBE14 ${tableName}\uC5D0 upsert \uC694\uCCAD`);
173
- } else if (table.rows.length === 0) {
174
- throw new Error(`${tableName}\uC5D0 upsert \uD560 \uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`);
175
- }
176
- if (table.rows.some(
177
- (row) => Object.entries(row).some(
178
- ([, value]) => isRefField(value) && value.of !== tableName
179
- )
180
- )) {
181
- throw new Error(`${tableName} \uD574\uACB0\uB418\uC9C0 \uC54A\uC740 \uCC38\uC870\uAC00 \uC788\uC2B5\uB2C8\uB2E4.`);
182
- }
183
- const wdb = _chunkWJGRXAXEjs.DB.toClient(_wdb);
184
- const { references, refTables } = Array.from(this.tables).reduce(
185
- (r, [, table2]) => {
186
- const reference = Array.from(table2.references.values()).find(
187
- (ref) => ref.includes(tableName + ".")
188
- );
189
- if (reference) {
190
- r.references.push(reference);
191
- r.refTables.push(table2);
192
- }
193
- return r;
194
- },
195
- {
196
- references: [],
197
- refTables: []
198
- }
199
- );
200
- const extractFields = _lodash2.default.uniq(references).map(
201
- (reference) => reference.split(".")[1]
202
- );
203
- const groups = _lodash2.default.groupBy(
204
- table.rows,
205
- (row) => Object.entries(row).some(([, value]) => isRefField(value)) ? "selfRef" : "normal"
206
- );
207
- const normalRows = _nullishCoalesce(groups.normal, () => ( []));
208
- const selfRefRows = _nullishCoalesce(groups.selfRef, () => ( []));
209
- const chunks = chunkSize ? _lodash2.default.chunk(normalRows, chunkSize) : [normalRows];
210
- const uuidMap = /* @__PURE__ */ new Map();
211
- for (const chunk of chunks) {
212
- if (mode === "insert") {
213
- await wdb.insert(tableName, chunk);
214
- } else if (mode === "upsert") {
215
- await wdb.upsert(tableName, chunk);
216
- }
217
- const uuids = chunk.map((row) => row.uuid);
218
- const upsertedRows = await wdb.from(tableName).select(_lodash2.default.uniq(["uuid", "id", ...extractFields])).where(["uuid", "in", uuids]).execute();
219
- upsertedRows.forEach((row) => {
220
- uuidMap.set(row.uuid, row);
221
- });
222
- }
223
- refTables.map((table2) => {
224
- table2.rows = table2.rows.map((row) => {
225
- Object.keys(row).map((key) => {
226
- const prop = row[key];
227
- if (isRefField(prop) && prop.of === tableName) {
228
- const parent = uuidMap.get(prop.uuid);
229
- if (parent === void 0) {
230
- console.error(prop);
231
- throw new Error(
232
- `\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 uuid ${prop.uuid} -- in ${tableName}`
233
- );
234
- }
235
- row[key] = parent[_nullishCoalesce(prop.use, () => ( "id"))];
236
- }
237
- });
238
- return row;
239
- });
240
- });
241
- const allIds = Array.from(uuidMap.values()).map((row) => row.id);
242
- if (selfRefRows.length > 0) {
243
- table.rows = selfRefRows;
244
- const selfRefIds = await this.upsert(_wdb, tableName, chunkSize);
245
- allIds.push(...selfRefIds);
246
- }
247
- return allIds;
248
- }
249
- async updateBatch(wdb, tableName, options) {
250
- options = _lodash2.default.defaults(options, {
251
- chunkSize: 500,
252
- where: "id"
253
- });
254
- if (this.hasTable(tableName) === false) {
255
- return;
256
- }
257
- const table = this.tables.get(tableName);
258
- if (table.rows.length === 0) {
259
- return;
260
- }
261
- const whereColumns = Array.isArray(options.where) ? options.where : [_nullishCoalesce(options.where, () => ( "id"))];
262
- const rows = table.rows.map((_row) => {
263
- const { uuid, ...row } = _row;
264
- return row;
265
- });
266
- await batchUpdate(
267
- _chunkWJGRXAXEjs.DB.toClient(wdb),
268
- tableName,
269
- whereColumns,
270
- rows,
271
- options.chunkSize
272
- );
273
- }
274
- };
275
-
276
-
277
-
278
-
279
- exports.isRefField = isRefField; exports.UpsertBuilder = UpsertBuilder;
280
- //# sourceMappingURL=chunk-UAG3SKFM.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/database/upsert-builder.ts","../src/database/_batch_update.ts"],"names":["table"],"mappings":"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","sourcesContent":["import { v4 as uuidv4 } from \"uuid\";\nimport _ from \"lodash\";\nimport { Knex } from \"knex\";\nimport { Kysely } from \"kysely\";\nimport { EntityManager } from \"../entity/entity-manager\";\nimport { nonNullable } from \"../utils/utils\";\nimport { RowWithId, batchUpdate } from \"./_batch_update\";\nimport { Database, DatabaseDriver, DriverSpec } from \"./types\";\nimport { DB } from \"./db\";\n\ntype TableData = {\n references: Set<string>;\n rows: any[];\n uniqueIndexes: { name?: string; columns: string[] }[];\n uniquesMap: Map<string, string>;\n};\nexport type UBRef = {\n uuid: string;\n of: string;\n use?: string;\n};\nexport function isRefField(field: any): field is UBRef {\n return (\n field !== undefined &&\n field !== null &&\n field.of !== undefined &&\n field.uuid !== undefined\n );\n}\n\nexport class UpsertBuilder<D extends DatabaseDriver> {\n tables: Map<string, TableData>;\n constructor() {\n this.tables = new Map();\n }\n\n getTable(tableName: string): TableData {\n const table = this.tables.get(tableName);\n if (table === undefined) {\n const tableSpec = (() => {\n try {\n return EntityManager.getTableSpec(tableName);\n } catch {\n return null;\n }\n })();\n\n this.tables.set(tableName, {\n references: new Set(),\n rows: [],\n uniqueIndexes: tableSpec?.uniqueIndexes ?? [],\n uniquesMap: new Map<string, string>(),\n });\n }\n\n return this.tables.get(tableName)!;\n }\n\n hasTable(tableName: string): boolean {\n return this.tables.has(tableName);\n }\n\n register<T extends string>(\n tableName: DriverSpec[D][\"table\"],\n row: {\n [key in T]?:\n | UBRef\n | string\n | number\n | boolean\n | bigint\n | null\n | object\n | unknown;\n }\n ): UBRef {\n const table = this.getTable(tableName);\n\n // 해당 테이블의 unique 인덱스를 순회하며 키 생성\n const uniqueKeys = table.uniqueIndexes\n .map((unqIndex) => {\n const uniqueKeyArray = unqIndex.columns.map((unqCol) => {\n const val = row[unqCol as keyof typeof row];\n if (isRefField(val)) {\n return val.uuid;\n } else {\n return row[unqCol as keyof typeof row] ?? uuidv4(); // nullable인 경우 uuid로 랜덤값 삽입\n }\n });\n\n // 값이 모두 null인 경우 키 생성 패스\n if (uniqueKeyArray.length === 0) {\n return null;\n }\n return uniqueKeyArray.join(\"---delimiter--\");\n })\n .filter(nonNullable);\n\n // uuid 생성 로직\n const uuid: string = (() => {\n // 키를 순회하여 이미 존재하는 키가 있는지 확인\n if (uniqueKeys.length > 0) {\n for (const uniqueKey of uniqueKeys) {\n if (table.uniquesMap.has(uniqueKey)) {\n return table.uniquesMap.get(uniqueKey)!; // 이미 has 체크를 했으므로 undefined 불가능\n }\n }\n }\n\n // 찾을 수 없는 경우 생성\n return uuidv4();\n })();\n\n // 모든 유니크키에 대해 유니크맵에 uuid 저장\n if (uniqueKeys.length > 0) {\n for (const uniqueKey of uniqueKeys) {\n table.uniquesMap.set(uniqueKey, uuid);\n }\n }\n\n // 이 테이블에 사용된 RefField를 순회하여, 현재 테이블 정보에 어떤 필드를 참조하는지 추가\n // 이 정보를 나중에 치환할 때 사용\n row = Object.keys(row).reduce((r, rowKey) => {\n const rowValue = row[rowKey as keyof typeof row];\n\n if (isRefField(rowValue)) {\n rowValue.use ??= \"id\";\n table.references.add(rowValue.of + \".\" + rowValue.use);\n r[rowKey] = rowValue;\n } else if (typeof rowValue === \"object\") {\n // object인 경우 JSON으로 변환\n r[rowKey] = rowValue === null ? null : JSON.stringify(rowValue);\n } else {\n r[rowKey] = rowValue;\n }\n return r;\n }, {} as any);\n\n table.rows.push({\n uuid,\n ...row,\n });\n\n return {\n of: tableName,\n uuid: (row as { uuid?: string }).uuid ?? uuid,\n };\n }\n\n async upsert(\n wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n chunkSize?: number\n ): Promise<number[]> {\n return this.upsertOrInsert(wdb, tableName, \"upsert\", chunkSize);\n }\n async insertOnly(\n wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n chunkSize?: number\n ): Promise<number[]> {\n return this.upsertOrInsert(wdb, tableName, \"insert\", chunkSize);\n }\n\n async upsertOrInsert(\n _wdb: DriverSpec[D][\"core\"],\n tableName: DriverSpec[D][\"table\"],\n mode: \"upsert\" | \"insert\",\n chunkSize?: number\n ): Promise<number[]> {\n if (this.hasTable(tableName) === false) {\n return [];\n }\n\n const table = this.tables.get(tableName);\n if (table === undefined) {\n throw new Error(`존재하지 않는 테이블 ${tableName}에 upsert 요청`);\n } else if (table.rows.length === 0) {\n throw new Error(`${tableName}에 upsert 할 데이터가 없습니다.`);\n }\n\n if (\n table.rows.some((row) =>\n Object.entries(row).some(\n ([, value]) => isRefField(value) && value.of !== tableName\n )\n )\n ) {\n throw new Error(`${tableName} 해결되지 않은 참조가 있습니다.`);\n }\n\n const wdb = DB.toClient(_wdb);\n\n // 전체 테이블 순회하여 현재 테이블 참조하는 모든 테이블 추출\n const { references, refTables } = Array.from(this.tables).reduce(\n (r, [, table]) => {\n const reference = Array.from(table.references.values()).find((ref) =>\n ref.includes(tableName + \".\")\n );\n if (reference) {\n r.references.push(reference);\n r.refTables.push(table);\n }\n\n return r;\n },\n {\n references: [] as string[],\n refTables: [] as TableData[],\n }\n );\n const extractFields = _.uniq(references).map(\n (reference) => reference.split(\".\")[1]\n );\n\n // 내부 참조 있는 경우 필터하여 분리\n const groups = _.groupBy(table.rows, (row) =>\n Object.entries(row).some(([, value]) => isRefField(value))\n ? \"selfRef\"\n : \"normal\"\n );\n const normalRows = groups.normal ?? [];\n const selfRefRows = groups.selfRef ?? [];\n\n const chunks = chunkSize ? _.chunk(normalRows, chunkSize) : [normalRows];\n const uuidMap = new Map<string, any>();\n\n for (const chunk of chunks) {\n if (mode === \"insert\") {\n await wdb.insert(tableName, chunk);\n } else if (mode === \"upsert\") {\n await wdb.upsert(tableName, chunk);\n // await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));\n }\n\n // upsert된 row들을 다시 조회하여 uuidMap에 저장\n const uuids = chunk.map((row) => row.uuid);\n const upsertedRows = await wdb\n .from(tableName)\n .select(_.uniq([\"uuid\", \"id\", ...extractFields]))\n .where([\"uuid\", \"in\", uuids])\n .execute();\n upsertedRows.forEach((row: any) => {\n uuidMap.set(row.uuid, row);\n });\n }\n\n // 해당 테이블 참조를 실제 밸류로 변경\n refTables.map((table) => {\n table.rows = table.rows.map((row) => {\n Object.keys(row).map((key) => {\n const prop = row[key];\n if (isRefField(prop) && prop.of === tableName) {\n const parent = uuidMap.get(prop.uuid);\n if (parent === undefined) {\n console.error(prop);\n throw new Error(\n `존재하지 않는 uuid ${prop.uuid} -- in ${tableName}`\n );\n }\n row[key] = parent[prop.use ?? \"id\"];\n }\n });\n return row;\n });\n });\n\n const allIds = Array.from(uuidMap.values()).map((row) => row.id);\n\n // 자기 참조가 있는 경우 재귀적으로 upsert\n if (selfRefRows.length > 0) {\n // 처리된 데이터를 제외하고 다시 upsert\n table.rows = selfRefRows;\n const selfRefIds = await this.upsert(_wdb, tableName, chunkSize);\n allIds.push(...selfRefIds);\n }\n\n return allIds;\n }\n\n async updateBatch(\n wdb: Knex | Kysely<Database>,\n tableName: DriverSpec[D][\"table\"],\n options?: {\n chunkSize?: number;\n where?: DriverSpec[D][\"column\"] | DriverSpec[D][\"column\"][];\n }\n ): Promise<void> {\n options = _.defaults(options, {\n chunkSize: 500,\n where: \"id\",\n });\n\n if (this.hasTable(tableName) === false) {\n return;\n }\n const table = this.tables.get(tableName)!;\n if (table.rows.length === 0) {\n return;\n }\n\n const whereColumns = Array.isArray(options.where)\n ? options.where\n : [options.where ?? \"id\"];\n const rows = table.rows.map((_row) => {\n const { uuid, ...row } = _row;\n return row as RowWithId<string>;\n });\n\n await batchUpdate(\n DB.toClient(wdb),\n tableName,\n whereColumns as string[],\n rows,\n options.chunkSize\n );\n }\n}\n","/*\n 아래의 링크에서 참고해서 가져온 소스코드\n https://github.com/knex/knex/issues/5716\n*/\n\nimport { Knex } from \"knex\";\nimport { DB } from \"./db\";\nimport { KnexClient } from \"./drivers/knex/client\";\nimport { KyselyClient } from \"./drivers/kysely/client\";\nimport { Transaction } from \"kysely\";\nimport { Database } from \"./types\";\n\nexport type RowWithId<Id extends string> = {\n [key in Id]: any;\n} & Record<string, any>;\n\n/**\n * Batch update rows in a table. Technically its a patch since it only updates the specified columns. Any omitted columns will not be affected\n * @param db\n * @param tableName\n * @param ids\n * @param rows\n * @param chunkSize\n * @param trx\n */\nexport async function batchUpdate<Id extends string>(\n db: KnexClient | KyselyClient,\n tableName: string,\n ids: Id[],\n rows: RowWithId<Id>[],\n chunkSize = 50,\n trx: Knex.Transaction | Transaction<Database> | null = null\n) {\n const chunks: RowWithId<Id>[][] = [];\n for (let i = 0; i < rows.length; i += chunkSize) {\n chunks.push(rows.slice(i, i + chunkSize));\n }\n\n const executeUpdate = async (\n chunk: RowWithId<Id>[],\n transaction: KyselyClient | KnexClient\n ) => {\n const sql = generateBatchUpdateSQL(db, tableName, chunk, ids);\n return transaction.raw(sql);\n };\n\n if (trx) {\n for (const chunk of chunks) {\n await executeUpdate(chunk, DB.toClient(trx));\n }\n } else {\n await db.trx(async (newTrx) => {\n for (const chunk of chunks) {\n await executeUpdate(chunk, newTrx);\n }\n });\n }\n}\n\n/**\n * Generate a set of unique keys in a data array\n *\n * Example:\n * [ { a: 1, b: 2 }, { a: 3, c: 4 } ] => Set([ \"a\", \"b\", \"c\" ])\n * @param data\n */\nfunction generateKeySetFromData(data: Record<string, any>[]) {\n const keySet: Set<string> = new Set();\n for (const row of data) {\n for (const key of Object.keys(row)) {\n keySet.add(key);\n }\n }\n return keySet;\n}\n\nfunction generateBatchUpdateSQL<Id extends string>(\n db: KnexClient | KyselyClient,\n tableName: string,\n data: Record<string, any>[],\n identifiers: Id[]\n) {\n const keySet = generateKeySetFromData(data);\n const bindings = [];\n\n const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));\n if (invalidIdentifiers.length > 0) {\n throw new Error(\n `Invalid identifiers: ${invalidIdentifiers.join(\", \")}. Identifiers must exist in the data`\n );\n }\n\n const cases = [];\n for (const key of keySet) {\n if (identifiers.includes(key as Id)) continue;\n\n const rows = [];\n for (const row of data) {\n if (Object.hasOwnProperty.call(row, key)) {\n const whereClause = identifiers\n .map((id) => `\\`${id}\\` = ?`)\n .join(\" AND \");\n rows.push(`WHEN (${whereClause}) THEN ?`);\n bindings.push(...identifiers.map((i) => row[i]), row[key]);\n }\n }\n\n const whenThen = rows.join(\" \");\n cases.push(`\\`${key}\\` = CASE ${whenThen} ELSE \\`${key}\\` END`);\n }\n\n const whereInClauses = identifiers\n .map((col) => `${col} IN (${data.map(() => \"?\").join(\", \")})`)\n .join(\" AND \");\n\n const whereInBindings = identifiers.flatMap((col) =>\n data.map((row) => row[col])\n );\n\n const sql = db.createRawQuery(\n `UPDATE \\`${tableName}\\` SET ${cases.join(\", \")} WHERE ${whereInClauses}`,\n [...bindings, ...whereInBindings]\n );\n\n return sql;\n}\n"]}