@bdkinc/knex-ibmi 0.3.22 → 0.4.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.
- package/README.md +290 -139
- package/dist/cli.cjs +531 -0
- package/dist/index.d.mts +156 -0
- package/dist/index.d.ts +55 -53
- package/dist/index.js +969 -158
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1359 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -20
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1359 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
import process from "process";
|
|
9
|
+
import knex from "knex";
|
|
10
|
+
import odbc from "odbc";
|
|
11
|
+
|
|
12
|
+
// src/schema/ibmi-compiler.ts
|
|
13
|
+
import SchemaCompiler from "knex/lib/schema/compiler.js";
|
|
14
|
+
var IBMiSchemaCompiler = class extends SchemaCompiler {
|
|
15
|
+
hasTable(tableName) {
|
|
16
|
+
const upperName = String(tableName).toUpperCase();
|
|
17
|
+
let schemaName = null;
|
|
18
|
+
let actualTableName = upperName;
|
|
19
|
+
if (upperName.includes(".")) {
|
|
20
|
+
const parts = upperName.split(".");
|
|
21
|
+
schemaName = parts[0];
|
|
22
|
+
actualTableName = parts[1];
|
|
23
|
+
}
|
|
24
|
+
const builderSchema = this.builder._schema;
|
|
25
|
+
if (builderSchema) {
|
|
26
|
+
schemaName = builderSchema.toUpperCase();
|
|
27
|
+
}
|
|
28
|
+
let sql;
|
|
29
|
+
let bindings;
|
|
30
|
+
if (schemaName) {
|
|
31
|
+
sql = `select count(*) as table_count from QSYS2.SYSTABLES where UPPER(TABLE_NAME) = ? AND UPPER(TABLE_SCHEMA) = ?`;
|
|
32
|
+
bindings = [actualTableName, schemaName];
|
|
33
|
+
} else {
|
|
34
|
+
sql = `select count(*) as table_count from QSYS2.SYSTABLES where UPPER(TABLE_NAME) = ?`;
|
|
35
|
+
bindings = [actualTableName];
|
|
36
|
+
}
|
|
37
|
+
this.pushQuery({
|
|
38
|
+
sql,
|
|
39
|
+
bindings,
|
|
40
|
+
output: (runner, resp) => {
|
|
41
|
+
if (!resp) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(resp) && resp.length > 0) {
|
|
45
|
+
const firstRow = resp[0];
|
|
46
|
+
if (firstRow && typeof firstRow === "object") {
|
|
47
|
+
const count = firstRow.table_count || firstRow.TABLE_COUNT || firstRow.count || firstRow.COUNT || 0;
|
|
48
|
+
return count > 0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (typeof resp === "object" && resp !== null) {
|
|
52
|
+
const keys = Object.keys(resp);
|
|
53
|
+
for (const key of keys) {
|
|
54
|
+
if (!isNaN(parseInt(key))) {
|
|
55
|
+
const row = resp[key];
|
|
56
|
+
if (row && typeof row === "object") {
|
|
57
|
+
const count = row.table_count || row.TABLE_COUNT || row.count || row.COUNT || 0;
|
|
58
|
+
return count > 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (resp.rows && Array.isArray(resp.rows) && resp.rows.length > 0) {
|
|
63
|
+
const firstRow = resp.rows[0];
|
|
64
|
+
if (firstRow && typeof firstRow === "object") {
|
|
65
|
+
const count = firstRow.table_count || firstRow.TABLE_COUNT || firstRow.count || firstRow.COUNT || 0;
|
|
66
|
+
return count > 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
toSQL() {
|
|
75
|
+
const sequence = this.builder._sequence;
|
|
76
|
+
for (let i = 0, l = sequence.length; i < l; i++) {
|
|
77
|
+
const query = sequence[i];
|
|
78
|
+
this[query.method].apply(this, query.args);
|
|
79
|
+
}
|
|
80
|
+
return this.sequence;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var ibmi_compiler_default = IBMiSchemaCompiler;
|
|
84
|
+
|
|
85
|
+
// src/schema/ibmi-tablecompiler.ts
|
|
86
|
+
import TableCompiler from "knex/lib/schema/tablecompiler.js";
|
|
87
|
+
var IBMiTableCompiler = class extends TableCompiler {
|
|
88
|
+
createQuery(columns, ifNot, like) {
|
|
89
|
+
let createStatement = ifNot ? `if object_id('${this.tableName()}', 'U') is null ` : "";
|
|
90
|
+
if (like) {
|
|
91
|
+
createStatement += `select * into ${this.tableName()} from ${this.tableNameLike()} WHERE 0=1`;
|
|
92
|
+
} else {
|
|
93
|
+
createStatement += "create table " + this.tableName() + (this._formatting ? " (\n " : " (") + columns.sql.join(this._formatting ? ",\n " : ", ") + this._addChecks() + ")";
|
|
94
|
+
}
|
|
95
|
+
this.pushQuery(createStatement);
|
|
96
|
+
if (this.single.comment) {
|
|
97
|
+
this.comment(this.single.comment);
|
|
98
|
+
}
|
|
99
|
+
if (like) {
|
|
100
|
+
this.addColumns(columns, this.addColumnsPrefix);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
dropUnique(columns, indexName) {
|
|
104
|
+
indexName = indexName ? this.formatter.wrap(indexName) : this._indexCommand("unique", this.tableNameRaw, columns);
|
|
105
|
+
this.pushQuery(`drop index ${indexName}`);
|
|
106
|
+
}
|
|
107
|
+
unique(columns, indexName) {
|
|
108
|
+
let deferrable = "";
|
|
109
|
+
let predicate;
|
|
110
|
+
let finalIndexName;
|
|
111
|
+
if (typeof indexName === "object" && indexName !== null) {
|
|
112
|
+
deferrable = indexName.deferrable || "";
|
|
113
|
+
predicate = indexName.predicate;
|
|
114
|
+
finalIndexName = indexName.indexName;
|
|
115
|
+
} else {
|
|
116
|
+
finalIndexName = indexName;
|
|
117
|
+
}
|
|
118
|
+
if (deferrable && deferrable !== "not deferrable") {
|
|
119
|
+
this.client.logger.warn?.(
|
|
120
|
+
`IBMi: unique index \`${finalIndexName}\` will not be deferrable ${deferrable}.`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const wrappedIndexName = finalIndexName ? this.formatter.wrap(finalIndexName) : this._indexCommand("unique", this.tableNameRaw, columns);
|
|
124
|
+
columns = this.formatter.columnize(columns);
|
|
125
|
+
const predicateQuery = predicate ? " " + this.client.queryCompiler(predicate).where() : "";
|
|
126
|
+
this.pushQuery(
|
|
127
|
+
`create unique index ${wrappedIndexName} on ${this.tableName()} (${columns})${predicateQuery}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
// All of the columns to "add" for the query
|
|
131
|
+
addColumns(columns, prefix) {
|
|
132
|
+
prefix = prefix || this.addColumnsPrefix;
|
|
133
|
+
if (columns.sql.length > 0) {
|
|
134
|
+
const columnSql = columns.sql.map((column) => {
|
|
135
|
+
return prefix + column;
|
|
136
|
+
});
|
|
137
|
+
this.pushQuery({
|
|
138
|
+
sql: (this.lowerCase ? "alter table " : "ALTER TABLE ") + this.tableName() + " " + columnSql.join(" "),
|
|
139
|
+
bindings: columns.bindings
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async commit(connection) {
|
|
144
|
+
return await connection.commit();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var ibmi_tablecompiler_default = IBMiTableCompiler;
|
|
148
|
+
|
|
149
|
+
// src/schema/ibmi-columncompiler.ts
|
|
150
|
+
import ColumnCompiler from "knex/lib/schema/columncompiler.js";
|
|
151
|
+
var IBMiColumnCompiler = class extends ColumnCompiler {
|
|
152
|
+
increments(options = { primaryKey: true }) {
|
|
153
|
+
return "int not null generated always as identity (start with 1, increment by 1)" + (this.tableCompiler._canBeAddPrimaryKey(options) ? " primary key" : "");
|
|
154
|
+
}
|
|
155
|
+
// Add more IBM i DB2 specific column types for better support
|
|
156
|
+
bigIncrements(options = { primaryKey: true }) {
|
|
157
|
+
return "bigint not null generated always as identity (start with 1, increment by 1)" + (this.tableCompiler._canBeAddPrimaryKey(options) ? " primary key" : "");
|
|
158
|
+
}
|
|
159
|
+
varchar(length) {
|
|
160
|
+
return length ? `varchar(${length})` : "varchar(255)";
|
|
161
|
+
}
|
|
162
|
+
char(length) {
|
|
163
|
+
return length ? `char(${length})` : "char(1)";
|
|
164
|
+
}
|
|
165
|
+
text() {
|
|
166
|
+
return "clob(1M)";
|
|
167
|
+
}
|
|
168
|
+
mediumtext() {
|
|
169
|
+
return "clob(16M)";
|
|
170
|
+
}
|
|
171
|
+
longtext() {
|
|
172
|
+
return "clob(2G)";
|
|
173
|
+
}
|
|
174
|
+
binary(length) {
|
|
175
|
+
return length ? `binary(${length})` : "binary(1)";
|
|
176
|
+
}
|
|
177
|
+
varbinary(length) {
|
|
178
|
+
return length ? `varbinary(${length})` : "varbinary(255)";
|
|
179
|
+
}
|
|
180
|
+
// IBM i DB2 decimal with precision/scale
|
|
181
|
+
decimal(precision, scale) {
|
|
182
|
+
if (precision && scale) {
|
|
183
|
+
return `decimal(${precision}, ${scale})`;
|
|
184
|
+
} else if (precision) {
|
|
185
|
+
return `decimal(${precision})`;
|
|
186
|
+
}
|
|
187
|
+
return "decimal(10, 2)";
|
|
188
|
+
}
|
|
189
|
+
// IBM i DB2 timestamp
|
|
190
|
+
timestamp(options) {
|
|
191
|
+
if (options?.useTz) {
|
|
192
|
+
return "timestamp with time zone";
|
|
193
|
+
}
|
|
194
|
+
return "timestamp";
|
|
195
|
+
}
|
|
196
|
+
datetime(options) {
|
|
197
|
+
return this.timestamp(options);
|
|
198
|
+
}
|
|
199
|
+
// IBM i DB2 date and time types
|
|
200
|
+
date() {
|
|
201
|
+
return "date";
|
|
202
|
+
}
|
|
203
|
+
time() {
|
|
204
|
+
return "time";
|
|
205
|
+
}
|
|
206
|
+
// JSON support (IBM i 7.3+)
|
|
207
|
+
json() {
|
|
208
|
+
return "clob(16M) check (json_valid(json_column))";
|
|
209
|
+
}
|
|
210
|
+
jsonb() {
|
|
211
|
+
return "clob(16M) check (json_valid(jsonb_column))";
|
|
212
|
+
}
|
|
213
|
+
// UUID support using CHAR(36)
|
|
214
|
+
uuid() {
|
|
215
|
+
return "char(36)";
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var ibmi_columncompiler_default = IBMiColumnCompiler;
|
|
219
|
+
|
|
220
|
+
// src/execution/ibmi-transaction.ts
|
|
221
|
+
import Transaction from "knex/lib/execution/transaction.js";
|
|
222
|
+
var IBMiTransaction = class extends Transaction {
|
|
223
|
+
begin(connection) {
|
|
224
|
+
try {
|
|
225
|
+
return connection.beginTransaction();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (this.isConnectionClosed(error)) {
|
|
228
|
+
console.warn(
|
|
229
|
+
"IBM i DB2: Connection closed during transaction begin, DDL operations may have caused implicit commit"
|
|
230
|
+
);
|
|
231
|
+
throw new Error(
|
|
232
|
+
"Connection closed during transaction begin - consider using migrations.disableTransactions: true"
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
rollback(connection) {
|
|
239
|
+
try {
|
|
240
|
+
return connection.rollback();
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.warn(
|
|
243
|
+
"IBM i DB2: Rollback encountered an error (likely closed connection):",
|
|
244
|
+
error?.message || error
|
|
245
|
+
);
|
|
246
|
+
return Promise.resolve();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
commit(connection) {
|
|
250
|
+
try {
|
|
251
|
+
return connection.commit();
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (this.isConnectionClosed(error)) {
|
|
254
|
+
console.warn(
|
|
255
|
+
"IBM i DB2: Connection closed during commit - DDL operations cause implicit commits"
|
|
256
|
+
);
|
|
257
|
+
throw new Error(
|
|
258
|
+
"Connection closed during commit - this is expected with DDL operations on IBM i DB2"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
isConnectionClosed(error) {
|
|
265
|
+
const message = String(error?.message || error || "").toLowerCase();
|
|
266
|
+
return message.includes("connection") && (message.includes("closed") || message.includes("invalid") || message.includes("terminated") || message.includes("not connected"));
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var ibmi_transaction_default = IBMiTransaction;
|
|
270
|
+
|
|
271
|
+
// src/query/ibmi-querycompiler.ts
|
|
272
|
+
import QueryCompiler from "knex/lib/query/querycompiler.js";
|
|
273
|
+
import { rawOrFn as rawOrFn_ } from "knex/lib/formatter/wrappingFormatter.js";
|
|
274
|
+
var IBMiQueryCompiler = class extends QueryCompiler {
|
|
275
|
+
constructor() {
|
|
276
|
+
super(...arguments);
|
|
277
|
+
// Cache for column metadata to improve performance with repeated operations
|
|
278
|
+
__publicField(this, "columnCache", /* @__PURE__ */ new Map());
|
|
279
|
+
}
|
|
280
|
+
// Override select method to add IBM i optimization hints
|
|
281
|
+
select() {
|
|
282
|
+
const originalResult = super.select.call(this);
|
|
283
|
+
return originalResult;
|
|
284
|
+
}
|
|
285
|
+
formatTimestampLocal(date) {
|
|
286
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
287
|
+
const y = date.getFullYear();
|
|
288
|
+
const m = pad(date.getMonth() + 1);
|
|
289
|
+
const d = pad(date.getDate());
|
|
290
|
+
const hh = pad(date.getHours());
|
|
291
|
+
const mm = pad(date.getMinutes());
|
|
292
|
+
const ss = pad(date.getSeconds());
|
|
293
|
+
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
|
|
294
|
+
}
|
|
295
|
+
insert() {
|
|
296
|
+
const insertValues = this.single.insert || [];
|
|
297
|
+
const { returning } = this.single;
|
|
298
|
+
if (this.isEmptyInsertValues(insertValues)) {
|
|
299
|
+
if (this.isEmptyObject(insertValues)) {
|
|
300
|
+
return this.buildEmptyInsertResult(returning);
|
|
301
|
+
}
|
|
302
|
+
return "";
|
|
303
|
+
}
|
|
304
|
+
const ibmiConfig = this.client?.config?.ibmi || {};
|
|
305
|
+
const multiRowStrategy = ibmiConfig.multiRowInsert || "auto";
|
|
306
|
+
const isArrayInsert = Array.isArray(insertValues) && insertValues.length > 1;
|
|
307
|
+
const originalValues = isArrayInsert ? insertValues.slice() : insertValues;
|
|
308
|
+
const forceSingleRow = multiRowStrategy === "disabled" || multiRowStrategy === "sequential" && isArrayInsert;
|
|
309
|
+
let workingValues = insertValues;
|
|
310
|
+
if (forceSingleRow && isArrayInsert) {
|
|
311
|
+
workingValues = [insertValues[0]];
|
|
312
|
+
this.single.insert = workingValues;
|
|
313
|
+
}
|
|
314
|
+
const standardInsert = super.insert();
|
|
315
|
+
const insertSql = typeof standardInsert === "object" && standardInsert.sql ? standardInsert.sql : standardInsert;
|
|
316
|
+
const multiRow = isArrayInsert && !forceSingleRow;
|
|
317
|
+
if (multiRow && returning === "*") {
|
|
318
|
+
if (this.client?.printWarn) {
|
|
319
|
+
this.client.printWarn("multi-row insert with returning * may be large");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const selectColumns = returning ? this.formatter.columnize(returning) : multiRow ? "*" : "IDENTITY_VAL_LOCAL()";
|
|
323
|
+
const sql = `select ${selectColumns} from FINAL TABLE(${insertSql})`;
|
|
324
|
+
if (multiRowStrategy === "sequential" && isArrayInsert) {
|
|
325
|
+
const first = originalValues[0];
|
|
326
|
+
const columns = Object.keys(first).sort();
|
|
327
|
+
return {
|
|
328
|
+
sql,
|
|
329
|
+
returning: void 0,
|
|
330
|
+
_ibmiSequentialInsert: {
|
|
331
|
+
columns,
|
|
332
|
+
rows: originalValues,
|
|
333
|
+
tableName: this.tableName,
|
|
334
|
+
returning: returning || null,
|
|
335
|
+
identityOnly: !returning
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return { sql, returning: void 0 };
|
|
340
|
+
}
|
|
341
|
+
isEmptyInsertValues(insertValues) {
|
|
342
|
+
return Array.isArray(insertValues) && insertValues.length === 0 || this.isEmptyObject(insertValues);
|
|
343
|
+
}
|
|
344
|
+
isEmptyObject(insertValues) {
|
|
345
|
+
return insertValues !== null && typeof insertValues === "object" && !Array.isArray(insertValues) && Object.keys(insertValues).length === 0;
|
|
346
|
+
}
|
|
347
|
+
buildEmptyInsertResult(returning) {
|
|
348
|
+
const selectColumns = returning ? this.formatter.columnize(returning) : "IDENTITY_VAL_LOCAL()";
|
|
349
|
+
const returningSql = returning ? this._returning("insert", returning, void 0) + " " : "";
|
|
350
|
+
const insertSql = [
|
|
351
|
+
this.with(),
|
|
352
|
+
`insert into ${this.tableName}`,
|
|
353
|
+
returningSql + this._emptyInsertValue
|
|
354
|
+
].filter(Boolean).join(" ");
|
|
355
|
+
const sql = `select ${selectColumns} from FINAL TABLE(${insertSql})`;
|
|
356
|
+
return { sql, returning };
|
|
357
|
+
}
|
|
358
|
+
_buildInsertData(insertValues, returningSql) {
|
|
359
|
+
const insertData = this._prepInsert(insertValues);
|
|
360
|
+
if (insertData.columns.length > 0) {
|
|
361
|
+
const parts = [];
|
|
362
|
+
parts.push("(" + this.formatter.columnize(insertData.columns) + ") ");
|
|
363
|
+
if (returningSql) parts.push(returningSql);
|
|
364
|
+
parts.push("values ");
|
|
365
|
+
const rowsSql = [];
|
|
366
|
+
for (const row of insertData.values) {
|
|
367
|
+
const placeholders = row.map(() => "?").join(", ");
|
|
368
|
+
rowsSql.push("(" + placeholders + ")");
|
|
369
|
+
}
|
|
370
|
+
parts.push(rowsSql.join(", "));
|
|
371
|
+
return parts.join("");
|
|
372
|
+
}
|
|
373
|
+
if (Array.isArray(insertValues) && insertValues.length === 1 && insertValues[0]) {
|
|
374
|
+
return (returningSql || "") + this._emptyInsertValue;
|
|
375
|
+
}
|
|
376
|
+
return "";
|
|
377
|
+
}
|
|
378
|
+
generateCacheKey(data) {
|
|
379
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
380
|
+
return Object.keys(data[0] || {}).sort().join("|");
|
|
381
|
+
}
|
|
382
|
+
if (data && typeof data === "object") {
|
|
383
|
+
return Object.keys(data).sort().join("|");
|
|
384
|
+
}
|
|
385
|
+
return "";
|
|
386
|
+
}
|
|
387
|
+
buildFromCache(data, cachedColumns) {
|
|
388
|
+
const dataArray = Array.isArray(data) ? data : data ? [data] : [];
|
|
389
|
+
const values = [];
|
|
390
|
+
for (const item of dataArray) {
|
|
391
|
+
if (item == null) {
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
const row = cachedColumns.map((column) => item[column] ?? void 0);
|
|
395
|
+
values.push(row);
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
columns: cachedColumns,
|
|
399
|
+
values
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
_prepInsert(data) {
|
|
403
|
+
if (typeof data === "object" && data?.migration_time) {
|
|
404
|
+
const parsed = new Date(data.migration_time);
|
|
405
|
+
if (!isNaN(parsed.getTime())) {
|
|
406
|
+
data.migration_time = this.formatTimestampLocal(parsed);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const isRaw = rawOrFn_(
|
|
410
|
+
data,
|
|
411
|
+
void 0,
|
|
412
|
+
this.builder,
|
|
413
|
+
this.client,
|
|
414
|
+
this.bindingsHolder
|
|
415
|
+
);
|
|
416
|
+
if (isRaw) {
|
|
417
|
+
return isRaw;
|
|
418
|
+
}
|
|
419
|
+
const dataArray = Array.isArray(data) ? data : data ? [data] : [];
|
|
420
|
+
if (dataArray.length === 0) {
|
|
421
|
+
return { columns: [], values: [] };
|
|
422
|
+
}
|
|
423
|
+
const firstItem = dataArray[0];
|
|
424
|
+
if (!firstItem || typeof firstItem !== "object") {
|
|
425
|
+
return { columns: [], values: [] };
|
|
426
|
+
}
|
|
427
|
+
const cacheKey = this.generateCacheKey(firstItem);
|
|
428
|
+
let columns;
|
|
429
|
+
if (cacheKey && this.columnCache.has(cacheKey)) {
|
|
430
|
+
columns = this.columnCache.get(cacheKey);
|
|
431
|
+
} else {
|
|
432
|
+
columns = Object.keys(firstItem).sort();
|
|
433
|
+
if (cacheKey && columns.length > 0)
|
|
434
|
+
this.columnCache.set(cacheKey, columns);
|
|
435
|
+
}
|
|
436
|
+
const values = [];
|
|
437
|
+
for (const item of dataArray) {
|
|
438
|
+
if (!item || typeof item !== "object") continue;
|
|
439
|
+
values.push(columns.map((c) => item[c] ?? void 0));
|
|
440
|
+
}
|
|
441
|
+
return { columns, values };
|
|
442
|
+
}
|
|
443
|
+
update() {
|
|
444
|
+
const withSQL = this.with();
|
|
445
|
+
const updates = this._prepUpdate(this.single.update);
|
|
446
|
+
const where = this.where();
|
|
447
|
+
const order = this.order();
|
|
448
|
+
const limit = this.limit();
|
|
449
|
+
const { returning } = this.single;
|
|
450
|
+
const optimizationHints = "";
|
|
451
|
+
const baseUpdateSql = [
|
|
452
|
+
withSQL,
|
|
453
|
+
`update ${this.single.only ? "only " : ""}${this.tableName}`,
|
|
454
|
+
"set",
|
|
455
|
+
updates.join(", "),
|
|
456
|
+
where,
|
|
457
|
+
order,
|
|
458
|
+
limit,
|
|
459
|
+
optimizationHints
|
|
460
|
+
].filter(Boolean).join(" ");
|
|
461
|
+
if (returning) {
|
|
462
|
+
const selectColumns = this.formatter.columnize(this.single.returning);
|
|
463
|
+
const expectedSql = `select ${selectColumns} from FINAL TABLE(${baseUpdateSql})`;
|
|
464
|
+
return {
|
|
465
|
+
sql: expectedSql,
|
|
466
|
+
returning,
|
|
467
|
+
_ibmiUpdateReturning: {
|
|
468
|
+
updateSql: baseUpdateSql,
|
|
469
|
+
selectColumns,
|
|
470
|
+
whereClause: where,
|
|
471
|
+
tableName: this.tableName
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return { sql: baseUpdateSql, returning };
|
|
476
|
+
}
|
|
477
|
+
// Emulate DELETE ... RETURNING by compiling a FINAL TABLE wrapper for display and attaching metadata
|
|
478
|
+
del() {
|
|
479
|
+
const baseDelete = super.del();
|
|
480
|
+
const { returning } = this.single;
|
|
481
|
+
if (!returning) {
|
|
482
|
+
return { sql: baseDelete, returning: void 0 };
|
|
483
|
+
}
|
|
484
|
+
const deleteSql = typeof baseDelete === "object" && baseDelete.sql ? baseDelete.sql : baseDelete;
|
|
485
|
+
const selectColumns = this.formatter.columnize(returning);
|
|
486
|
+
const expectedSql = `select ${selectColumns} from FINAL TABLE(${deleteSql})`;
|
|
487
|
+
return {
|
|
488
|
+
sql: expectedSql,
|
|
489
|
+
returning,
|
|
490
|
+
_ibmiDeleteReturning: {
|
|
491
|
+
deleteSql,
|
|
492
|
+
selectColumns,
|
|
493
|
+
whereClause: this.where(),
|
|
494
|
+
tableName: this.tableName
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Handle returning clause for IBMi DB2 queries
|
|
500
|
+
* Note: IBMi DB2 has limited support for RETURNING clauses
|
|
501
|
+
* @param method - The SQL method (insert, update, delete)
|
|
502
|
+
* @param value - The returning value
|
|
503
|
+
* @param withTrigger - Trigger support (currently unused)
|
|
504
|
+
*/
|
|
505
|
+
_returning(method, value, withTrigger) {
|
|
506
|
+
switch (method) {
|
|
507
|
+
case "update":
|
|
508
|
+
case "insert":
|
|
509
|
+
return value ? `${withTrigger ? " into #out" : ""}` : "";
|
|
510
|
+
case "del":
|
|
511
|
+
return value ? `${withTrigger ? " into #out" : ""}` : "";
|
|
512
|
+
case "rowcount":
|
|
513
|
+
return value ? "select @@rowcount" : "";
|
|
514
|
+
default:
|
|
515
|
+
return "";
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
getOptimizationHints(queryType, data) {
|
|
519
|
+
const hints = [];
|
|
520
|
+
if (queryType === "select") {
|
|
521
|
+
hints.push("WITH UR");
|
|
522
|
+
}
|
|
523
|
+
return hints.length > 0 ? " " + hints.join(" ") : "";
|
|
524
|
+
}
|
|
525
|
+
getSelectOptimizationHints(sql) {
|
|
526
|
+
const hints = [];
|
|
527
|
+
hints.push("WITH UR");
|
|
528
|
+
return hints.length > 0 ? " " + hints.join(" ") : "";
|
|
529
|
+
}
|
|
530
|
+
columnizeWithPrefix(prefix, target) {
|
|
531
|
+
const columns = typeof target === "string" ? [target] : target;
|
|
532
|
+
const parts = [];
|
|
533
|
+
for (let i = 0; i < columns.length; i++) {
|
|
534
|
+
if (i > 0) parts.push(", ");
|
|
535
|
+
parts.push(prefix + this.wrap(columns[i]));
|
|
536
|
+
}
|
|
537
|
+
return parts.join("");
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
var ibmi_querycompiler_default = IBMiQueryCompiler;
|
|
541
|
+
|
|
542
|
+
// src/index.ts
|
|
543
|
+
import { Readable } from "stream";
|
|
544
|
+
|
|
545
|
+
// src/migrations/ibmi-migration-runner.ts
|
|
546
|
+
import fs from "fs";
|
|
547
|
+
import path from "path";
|
|
548
|
+
var IBMiMigrationRunner = class {
|
|
549
|
+
constructor(knex2, config) {
|
|
550
|
+
__publicField(this, "knex");
|
|
551
|
+
__publicField(this, "config");
|
|
552
|
+
this.knex = knex2;
|
|
553
|
+
this.config = {
|
|
554
|
+
directory: "./migrations",
|
|
555
|
+
tableName: "KNEX_MIGRATIONS",
|
|
556
|
+
schemaName: void 0,
|
|
557
|
+
extension: "js",
|
|
558
|
+
...config
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
getFullTableName() {
|
|
562
|
+
return this.config.schemaName ? `${this.config.schemaName}.${this.config.tableName}` : this.config.tableName;
|
|
563
|
+
}
|
|
564
|
+
async latest() {
|
|
565
|
+
try {
|
|
566
|
+
console.log(
|
|
567
|
+
"\u{1F680} IBM i DB2 Migration Runner - bypassing Knex locking system"
|
|
568
|
+
);
|
|
569
|
+
const tableName = this.getFullTableName();
|
|
570
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
571
|
+
tableName
|
|
572
|
+
);
|
|
573
|
+
if (!migrationTableExists) {
|
|
574
|
+
console.log(`\u{1F4DD} Creating migration table: ${tableName}`);
|
|
575
|
+
await this.knex.schema.createTable(tableName, (table) => {
|
|
576
|
+
table.increments("id").primary();
|
|
577
|
+
table.string("name");
|
|
578
|
+
table.integer("batch");
|
|
579
|
+
table.timestamp("migration_time");
|
|
580
|
+
});
|
|
581
|
+
console.log("\u2705 Migration table created");
|
|
582
|
+
}
|
|
583
|
+
const completed = await this.knex(tableName).select("NAME").orderBy("ID");
|
|
584
|
+
const completedNames = completed.map((c) => c.NAME);
|
|
585
|
+
console.log(`\u{1F4CB} Found ${completedNames.length} completed migrations`);
|
|
586
|
+
const migrationFiles = this.getMigrationFiles();
|
|
587
|
+
console.log(`\u{1F4C1} Found ${migrationFiles.length} migration files`);
|
|
588
|
+
const newMigrations = migrationFiles.filter(
|
|
589
|
+
(file) => !completedNames.includes(file)
|
|
590
|
+
);
|
|
591
|
+
if (newMigrations.length === 0) {
|
|
592
|
+
console.log("\u2705 No new migrations to run");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
console.log(`\u{1F3AF} Running ${newMigrations.length} new migrations:`);
|
|
596
|
+
newMigrations.forEach((file) => console.log(` - ${file}`));
|
|
597
|
+
const batchResult = await this.knex(tableName).max("BATCH as max_batch");
|
|
598
|
+
const nextBatch = (batchResult[0]?.max_batch || 0) + 1;
|
|
599
|
+
console.log(`\u{1F4CA} Using batch number: ${nextBatch}`);
|
|
600
|
+
for (const migrationFile of newMigrations) {
|
|
601
|
+
console.log(`
|
|
602
|
+
\u{1F504} Running migration: ${migrationFile}`);
|
|
603
|
+
try {
|
|
604
|
+
const migrationPath = this.getMigrationPath(migrationFile);
|
|
605
|
+
const migration = await import(migrationPath);
|
|
606
|
+
if (!migration.up || typeof migration.up !== "function") {
|
|
607
|
+
throw new Error(`Migration ${migrationFile} has no 'up' function`);
|
|
608
|
+
}
|
|
609
|
+
console.log(` \u26A1 Executing migration...`);
|
|
610
|
+
await migration.up(this.knex);
|
|
611
|
+
await this.knex(tableName).insert({
|
|
612
|
+
name: migrationFile,
|
|
613
|
+
batch: nextBatch,
|
|
614
|
+
migration_time: /* @__PURE__ */ new Date()
|
|
615
|
+
});
|
|
616
|
+
console.log(` \u2705 Migration ${migrationFile} completed successfully`);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
console.error(
|
|
619
|
+
` \u274C Migration ${migrationFile} failed:`,
|
|
620
|
+
error.message
|
|
621
|
+
);
|
|
622
|
+
throw error;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
console.log(`
|
|
626
|
+
\u{1F389} All migrations completed successfully!`);
|
|
627
|
+
} catch (error) {
|
|
628
|
+
console.error("\u274C Migration runner failed:", error.message);
|
|
629
|
+
throw error;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async rollback(steps = 1) {
|
|
633
|
+
try {
|
|
634
|
+
console.log(`\u{1F504} Rolling back ${steps} migration batch(es)...`);
|
|
635
|
+
const tableName = this.getFullTableName();
|
|
636
|
+
const batchesToRollback = await this.knex(tableName).distinct("BATCH").orderBy("BATCH", "desc").limit(steps);
|
|
637
|
+
if (batchesToRollback.length === 0) {
|
|
638
|
+
console.log("\u2705 No migrations to rollback");
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const batchNumbers = batchesToRollback.map((b) => b.BATCH);
|
|
642
|
+
console.log(`\u{1F4CA} Rolling back batches: ${batchNumbers.join(", ")}`);
|
|
643
|
+
const migrationsToRollback = await this.knex(tableName).select("NAME").whereIn("BATCH", batchNumbers).orderBy("ID", "desc");
|
|
644
|
+
console.log(`\u{1F3AF} Rolling back ${migrationsToRollback.length} migrations:`);
|
|
645
|
+
migrationsToRollback.forEach((m) => console.log(` - ${m.NAME}`));
|
|
646
|
+
for (const migrationRecord of migrationsToRollback) {
|
|
647
|
+
const migrationFile = migrationRecord.NAME;
|
|
648
|
+
console.log(`
|
|
649
|
+
\u{1F504} Rolling back migration: ${migrationFile}`);
|
|
650
|
+
try {
|
|
651
|
+
const migrationPath = this.getMigrationPath(migrationFile);
|
|
652
|
+
const migration = await import(migrationPath);
|
|
653
|
+
if (migration.down && typeof migration.down === "function") {
|
|
654
|
+
console.log(` \u26A1 Executing rollback...`);
|
|
655
|
+
await migration.down(this.knex);
|
|
656
|
+
} else {
|
|
657
|
+
console.log(
|
|
658
|
+
` \u26A0\uFE0F Migration ${migrationFile} has no 'down' function, skipping rollback`
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
await this.knex(tableName).where("NAME", migrationFile).del();
|
|
662
|
+
console.log(
|
|
663
|
+
` \u2705 Migration ${migrationFile} rolled back successfully`
|
|
664
|
+
);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error(
|
|
667
|
+
` \u274C Migration ${migrationFile} rollback failed:`,
|
|
668
|
+
error.message
|
|
669
|
+
);
|
|
670
|
+
throw error;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
console.log(`
|
|
674
|
+
\u{1F389} Rollback completed successfully!`);
|
|
675
|
+
} catch (error) {
|
|
676
|
+
console.error("\u274C Rollback failed:", error.message);
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async currentVersion() {
|
|
681
|
+
try {
|
|
682
|
+
const tableName = this.getFullTableName();
|
|
683
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
684
|
+
tableName
|
|
685
|
+
);
|
|
686
|
+
if (!migrationTableExists) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const result = await this.knex(tableName).select("NAME").orderBy("ID", "desc").first();
|
|
690
|
+
return result?.NAME || null;
|
|
691
|
+
} catch (error) {
|
|
692
|
+
console.error("\u274C Error getting current version:", error.message);
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async listExecuted() {
|
|
697
|
+
try {
|
|
698
|
+
const tableName = this.getFullTableName();
|
|
699
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
700
|
+
tableName
|
|
701
|
+
);
|
|
702
|
+
if (!migrationTableExists) {
|
|
703
|
+
return [];
|
|
704
|
+
}
|
|
705
|
+
const completed = await this.knex(tableName).select("NAME").orderBy("ID");
|
|
706
|
+
return completed.map((c) => c.NAME);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
console.error("\u274C Error listing executed migrations:", error.message);
|
|
709
|
+
return [];
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
async listPending() {
|
|
713
|
+
try {
|
|
714
|
+
const allFiles = this.getMigrationFiles();
|
|
715
|
+
const executed = await this.listExecuted();
|
|
716
|
+
return allFiles.filter((file) => !executed.includes(file));
|
|
717
|
+
} catch (error) {
|
|
718
|
+
console.error("\u274C Error listing pending migrations:", error.message);
|
|
719
|
+
return [];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
getMigrationFiles() {
|
|
723
|
+
const { directory, extension } = this.config;
|
|
724
|
+
if (!fs.existsSync(directory)) {
|
|
725
|
+
throw new Error(`Migration directory does not exist: ${directory}`);
|
|
726
|
+
}
|
|
727
|
+
const validExtensions = ["js", "ts", "mjs", "cjs"];
|
|
728
|
+
const extensionToCheck = extension || "js";
|
|
729
|
+
return fs.readdirSync(directory).filter((file) => {
|
|
730
|
+
if (extension && extension !== "js") {
|
|
731
|
+
return file.endsWith(`.${extension}`);
|
|
732
|
+
}
|
|
733
|
+
return validExtensions.some((ext) => file.endsWith(`.${ext}`));
|
|
734
|
+
}).sort();
|
|
735
|
+
}
|
|
736
|
+
getMigrationPath(filename) {
|
|
737
|
+
return path.resolve(this.config.directory, filename);
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
function createIBMiMigrationRunner(knex2, config) {
|
|
741
|
+
return new IBMiMigrationRunner(knex2, config);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/index.ts
|
|
745
|
+
var DB2Client = class extends knex.Client {
|
|
746
|
+
constructor(config) {
|
|
747
|
+
super(config);
|
|
748
|
+
this.driverName = "odbc";
|
|
749
|
+
if (this.dialect && !this.config.client) {
|
|
750
|
+
this.printWarn(
|
|
751
|
+
`Using 'this.dialect' to identify the client is deprecated and support for it will be removed in the future. Please use configuration option 'client' instead.`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
const dbClient = this.config.client || this.dialect;
|
|
755
|
+
if (!dbClient) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
`knex: Required configuration option 'client' is missing.`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
if (config.version) {
|
|
761
|
+
this.version = config.version;
|
|
762
|
+
}
|
|
763
|
+
if (this.driverName && config.connection) {
|
|
764
|
+
this.initializeDriver();
|
|
765
|
+
if (!config.pool || config.pool && config.pool.max !== 0) {
|
|
766
|
+
this.initializePool(config);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
this.valueForUndefined = this.raw("DEFAULT");
|
|
770
|
+
if (config.useNullAsDefault) {
|
|
771
|
+
this.valueForUndefined = null;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// Helper method to safely stringify objects that might have circular references
|
|
775
|
+
safeStringify(obj, indent = 0) {
|
|
776
|
+
try {
|
|
777
|
+
return JSON.stringify(obj, null, indent);
|
|
778
|
+
} catch (error) {
|
|
779
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
780
|
+
return `[Circular structure - ${typeof obj}]`;
|
|
781
|
+
}
|
|
782
|
+
return `[Stringify error - ${typeof obj}]`;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
_driver() {
|
|
786
|
+
return odbc;
|
|
787
|
+
}
|
|
788
|
+
wrapIdentifierImpl(value) {
|
|
789
|
+
if (!value) return value;
|
|
790
|
+
if (value.includes("KNEX_MIGRATIONS") || value.includes("knex_migrations")) {
|
|
791
|
+
return value.toUpperCase();
|
|
792
|
+
}
|
|
793
|
+
return value;
|
|
794
|
+
}
|
|
795
|
+
printDebug(message) {
|
|
796
|
+
if (process.env.DEBUG === "true") {
|
|
797
|
+
if (this.logger.debug) {
|
|
798
|
+
this.logger.debug("knex-ibmi: " + message);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
printError(message) {
|
|
803
|
+
if (this.logger.error) {
|
|
804
|
+
this.logger.error("knex-ibmi: " + message);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
printWarn(message) {
|
|
808
|
+
if (process.env.DEBUG === "true") {
|
|
809
|
+
if (this.logger.warn) {
|
|
810
|
+
this.logger.warn("knex-ibmi: " + message);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
// Get a raw connection, called by the pool manager whenever a new
|
|
815
|
+
// connection needs to be added to the pool.
|
|
816
|
+
async acquireRawConnection() {
|
|
817
|
+
this.printDebug("acquiring raw connection");
|
|
818
|
+
const connectionConfig = this.config.connection;
|
|
819
|
+
if (!connectionConfig) {
|
|
820
|
+
return this.printError("There is no connection config defined");
|
|
821
|
+
}
|
|
822
|
+
this.printDebug(
|
|
823
|
+
"connection config: " + this._getConnectionString(connectionConfig)
|
|
824
|
+
);
|
|
825
|
+
let connection;
|
|
826
|
+
if (this.config?.pool) {
|
|
827
|
+
const poolConfig = {
|
|
828
|
+
connectionString: this._getConnectionString(connectionConfig),
|
|
829
|
+
connectionTimeout: this.config?.acquireConnectionTimeout || 6e4,
|
|
830
|
+
initialSize: this.config?.pool?.min || 2,
|
|
831
|
+
maxSize: this.config?.pool?.max || 10,
|
|
832
|
+
reuseConnection: true
|
|
833
|
+
};
|
|
834
|
+
const pool = await this.driver.pool(poolConfig);
|
|
835
|
+
connection = await pool.connect();
|
|
836
|
+
} else {
|
|
837
|
+
connection = await this.driver.connect(
|
|
838
|
+
this._getConnectionString(connectionConfig)
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
return connection;
|
|
842
|
+
}
|
|
843
|
+
// Used to explicitly close a connection, called internally by the pool manager
|
|
844
|
+
// when a connection times out or the pool is shutdown.
|
|
845
|
+
async destroyRawConnection(connection) {
|
|
846
|
+
this.printDebug("destroy connection");
|
|
847
|
+
return await connection.close();
|
|
848
|
+
}
|
|
849
|
+
_getConnectionString(connectionConfig) {
|
|
850
|
+
const connectionStringParams = connectionConfig.connectionStringParams || {};
|
|
851
|
+
const connectionStringExtension = Object.keys(
|
|
852
|
+
connectionStringParams
|
|
853
|
+
).reduce((result, key) => {
|
|
854
|
+
const value = connectionStringParams[key];
|
|
855
|
+
return `${result}${key}=${value};`;
|
|
856
|
+
}, "");
|
|
857
|
+
return `DRIVER=${connectionConfig.driver};SYSTEM=${connectionConfig.host};HOSTNAME=${connectionConfig.host};PORT=${connectionConfig.port};DATABASE=${connectionConfig.database};UID=${connectionConfig.user};PWD=${connectionConfig.password};` + connectionStringExtension;
|
|
858
|
+
}
|
|
859
|
+
// Runs the query on the specified connection, providing the bindings
|
|
860
|
+
async _query(connection, obj) {
|
|
861
|
+
const queryObject = this.normalizeQueryObject(obj);
|
|
862
|
+
const method = this.determineQueryMethod(queryObject);
|
|
863
|
+
queryObject.sqlMethod = method;
|
|
864
|
+
if (queryObject._ibmiUpdateReturning) {
|
|
865
|
+
return await this.executeUpdateReturning(connection, queryObject);
|
|
866
|
+
}
|
|
867
|
+
if (queryObject._ibmiSequentialInsert) {
|
|
868
|
+
return await this.executeSequentialInsert(connection, queryObject);
|
|
869
|
+
}
|
|
870
|
+
if (queryObject._ibmiDeleteReturning) {
|
|
871
|
+
return await this.executeDeleteReturning(connection, queryObject);
|
|
872
|
+
}
|
|
873
|
+
if (process.env.DEBUG === "true" && queryObject.sql && (queryObject.sql.toLowerCase().includes("create table") || queryObject.sql.toLowerCase().includes("knex_migrations"))) {
|
|
874
|
+
this.printDebug(
|
|
875
|
+
`Executing ${method} query: ${queryObject.sql.substring(0, 200)}...`
|
|
876
|
+
);
|
|
877
|
+
if (queryObject.bindings?.length) {
|
|
878
|
+
this.printDebug(`Bindings: ${JSON.stringify(queryObject.bindings)}`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const startTime = Date.now();
|
|
883
|
+
if (this.isSelectMethod(method)) {
|
|
884
|
+
await this.executeSelectQuery(connection, queryObject);
|
|
885
|
+
} else {
|
|
886
|
+
await this.executeStatementQuery(connection, queryObject);
|
|
887
|
+
}
|
|
888
|
+
const endTime = Date.now();
|
|
889
|
+
if (process.env.DEBUG === "true" && queryObject.sql && (queryObject.sql.toLowerCase().includes("create table") || queryObject.sql.toLowerCase().includes("knex_migrations"))) {
|
|
890
|
+
this.printDebug(`${method} completed in ${endTime - startTime}ms`);
|
|
891
|
+
}
|
|
892
|
+
this.printDebug(`Query completed: ${method} (${endTime - startTime}ms)`);
|
|
893
|
+
return queryObject;
|
|
894
|
+
} catch (error) {
|
|
895
|
+
const wrappedError = this.wrapError(error, method, queryObject);
|
|
896
|
+
if (this.isConnectionError(error)) {
|
|
897
|
+
this.printError(
|
|
898
|
+
`Connection error during ${method} query: ${error.message}`
|
|
899
|
+
);
|
|
900
|
+
if (this.shouldRetryQuery(queryObject, method)) {
|
|
901
|
+
return await this.retryQuery(connection, queryObject, method);
|
|
902
|
+
}
|
|
903
|
+
throw wrappedError;
|
|
904
|
+
}
|
|
905
|
+
throw wrappedError;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Execute UPDATE with returning clause using transaction + SELECT approach
|
|
910
|
+
* Since IBM i DB2 doesn't support FINAL TABLE with UPDATE, we:
|
|
911
|
+
* 1. Execute the UPDATE statement
|
|
912
|
+
* 2. Execute a SELECT to get the updated values using the same WHERE clause
|
|
913
|
+
*/
|
|
914
|
+
async executeUpdateReturning(connection, obj) {
|
|
915
|
+
const { _ibmiUpdateReturning } = obj;
|
|
916
|
+
const { updateSql, selectColumns, whereClause, tableName } = _ibmiUpdateReturning;
|
|
917
|
+
this.printDebug(
|
|
918
|
+
"Executing UPDATE with returning using transaction approach"
|
|
919
|
+
);
|
|
920
|
+
try {
|
|
921
|
+
const updateObj = {
|
|
922
|
+
sql: updateSql,
|
|
923
|
+
bindings: obj.bindings,
|
|
924
|
+
sqlMethod: "update"
|
|
925
|
+
};
|
|
926
|
+
await this.executeStatementQuery(connection, updateObj);
|
|
927
|
+
const selectSql = whereClause ? `select ${selectColumns} from ${tableName} ${whereClause}` : `select ${selectColumns} from ${tableName}`;
|
|
928
|
+
const updateSqlParts = updateSql.split(" where ");
|
|
929
|
+
const setClausePart = updateSqlParts[0];
|
|
930
|
+
const setBindingCount = (setClausePart.match(/\?/g) || []).length;
|
|
931
|
+
const whereBindings = obj.bindings ? obj.bindings.slice(setBindingCount) : [];
|
|
932
|
+
const selectObj = {
|
|
933
|
+
sql: selectSql,
|
|
934
|
+
bindings: whereBindings,
|
|
935
|
+
sqlMethod: "select",
|
|
936
|
+
response: void 0
|
|
937
|
+
};
|
|
938
|
+
await this.executeSelectQuery(connection, selectObj);
|
|
939
|
+
obj.response = selectObj.response;
|
|
940
|
+
obj.sqlMethod = "update";
|
|
941
|
+
obj.select = true;
|
|
942
|
+
return obj;
|
|
943
|
+
} catch (error) {
|
|
944
|
+
this.printError(`UPDATE with returning failed: ${error.message}`);
|
|
945
|
+
throw this.wrapError(error, "update_returning", obj);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async executeSequentialInsert(connection, obj) {
|
|
949
|
+
const meta = obj._ibmiSequentialInsert;
|
|
950
|
+
const { rows, columns, tableName, returning, identityOnly } = meta;
|
|
951
|
+
this.printDebug("Executing sequential multi-row insert");
|
|
952
|
+
const insertedRows = [];
|
|
953
|
+
const transactional = this.config?.ibmi?.sequentialInsertTransactional === true;
|
|
954
|
+
let beganTx = false;
|
|
955
|
+
if (transactional) {
|
|
956
|
+
try {
|
|
957
|
+
await connection.query("BEGIN");
|
|
958
|
+
beganTx = true;
|
|
959
|
+
} catch (e) {
|
|
960
|
+
this.printWarn(
|
|
961
|
+
"Could not begin transaction for sequential insert; proceeding without"
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
for (let i = 0; i < rows.length; i++) {
|
|
966
|
+
const row = rows[i];
|
|
967
|
+
const colList = columns.join(", ");
|
|
968
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
969
|
+
const singleValues = columns.map((c) => row[c]);
|
|
970
|
+
const baseInsert = `insert into ${tableName} (${colList}) values (${placeholders})`;
|
|
971
|
+
const selectCols = returning ? this.queryCompiler({}).formatter.columnize(returning) : "IDENTITY_VAL_LOCAL()";
|
|
972
|
+
const wrapped = `select ${selectCols} from FINAL TABLE(${baseInsert})`;
|
|
973
|
+
const singleObj = {
|
|
974
|
+
sql: wrapped,
|
|
975
|
+
bindings: singleValues,
|
|
976
|
+
sqlMethod: "insert",
|
|
977
|
+
response: void 0
|
|
978
|
+
};
|
|
979
|
+
await this.executeStatementQuery(connection, singleObj);
|
|
980
|
+
const resp = singleObj.response?.rows;
|
|
981
|
+
if (resp) insertedRows.push(...resp);
|
|
982
|
+
}
|
|
983
|
+
if (transactional && beganTx) {
|
|
984
|
+
try {
|
|
985
|
+
await connection.query("COMMIT");
|
|
986
|
+
} catch (commitErr) {
|
|
987
|
+
this.printError(
|
|
988
|
+
"Commit failed for sequential insert, attempting rollback: " + commitErr?.message
|
|
989
|
+
);
|
|
990
|
+
try {
|
|
991
|
+
await connection.query("ROLLBACK");
|
|
992
|
+
} catch {
|
|
993
|
+
}
|
|
994
|
+
throw commitErr;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
obj.response = { rows: insertedRows, rowCount: insertedRows.length };
|
|
998
|
+
obj.sqlMethod = "insert";
|
|
999
|
+
obj.select = true;
|
|
1000
|
+
return obj;
|
|
1001
|
+
}
|
|
1002
|
+
async executeDeleteReturning(connection, obj) {
|
|
1003
|
+
const meta = obj._ibmiDeleteReturning;
|
|
1004
|
+
const { deleteSql, selectColumns, whereClause, tableName } = meta;
|
|
1005
|
+
this.printDebug("Executing DELETE with returning emulation");
|
|
1006
|
+
try {
|
|
1007
|
+
const selectSql = whereClause ? `select ${selectColumns} from ${tableName} ${whereClause}` : `select ${selectColumns} from ${tableName}`;
|
|
1008
|
+
const selectObj = {
|
|
1009
|
+
sql: selectSql,
|
|
1010
|
+
bindings: obj.bindings,
|
|
1011
|
+
sqlMethod: "select",
|
|
1012
|
+
response: void 0
|
|
1013
|
+
};
|
|
1014
|
+
await this.executeSelectQuery(connection, selectObj);
|
|
1015
|
+
const rowsToReturn = selectObj.response?.rows || [];
|
|
1016
|
+
const deleteObj = {
|
|
1017
|
+
sql: deleteSql,
|
|
1018
|
+
bindings: obj.bindings,
|
|
1019
|
+
sqlMethod: "del",
|
|
1020
|
+
response: void 0
|
|
1021
|
+
};
|
|
1022
|
+
await this.executeStatementQuery(connection, deleteObj);
|
|
1023
|
+
obj.response = { rows: rowsToReturn, rowCount: rowsToReturn.length };
|
|
1024
|
+
obj.sqlMethod = "del";
|
|
1025
|
+
obj.select = true;
|
|
1026
|
+
return obj;
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
this.printError(`DELETE with returning failed: ${error.message}`);
|
|
1029
|
+
throw this.wrapError(error, "delete_returning", obj);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
normalizeQueryObject(obj) {
|
|
1033
|
+
if (!obj || typeof obj === "string") {
|
|
1034
|
+
return { sql: obj };
|
|
1035
|
+
}
|
|
1036
|
+
return obj;
|
|
1037
|
+
}
|
|
1038
|
+
determineQueryMethod(obj) {
|
|
1039
|
+
return (obj.hasOwnProperty("method") && obj.method !== "raw" ? obj.method : obj.sql.split(" ")[0]).toLowerCase();
|
|
1040
|
+
}
|
|
1041
|
+
isSelectMethod(method) {
|
|
1042
|
+
return method === "select" || method === "first" || method === "pluck";
|
|
1043
|
+
}
|
|
1044
|
+
async executeSelectQuery(connection, obj) {
|
|
1045
|
+
const rows = await connection.query(
|
|
1046
|
+
obj.sql,
|
|
1047
|
+
obj.bindings
|
|
1048
|
+
);
|
|
1049
|
+
if (rows) {
|
|
1050
|
+
obj.response = { rows, rowCount: rows.length };
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
async executeStatementQuery(connection, obj) {
|
|
1054
|
+
let statement;
|
|
1055
|
+
try {
|
|
1056
|
+
statement = await connection.createStatement();
|
|
1057
|
+
await statement.prepare(obj.sql);
|
|
1058
|
+
if (obj.bindings) {
|
|
1059
|
+
await statement.bind(obj.bindings);
|
|
1060
|
+
}
|
|
1061
|
+
const result = await statement.execute();
|
|
1062
|
+
this.printDebug(String(result));
|
|
1063
|
+
obj.response = this.formatStatementResponse(result);
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
const sql = (obj.sql || "").toLowerCase();
|
|
1066
|
+
const isDml = obj.sqlMethod === "update" /* UPDATE */ || sql.includes(" update ") || sql.startsWith("update") || obj.sqlMethod === "del" /* DELETE */ || sql.includes(" delete ") || sql.startsWith("delete");
|
|
1067
|
+
const odbcErrors = err?.odbcErrors;
|
|
1068
|
+
const isEmptyOdbcError = Array.isArray(odbcErrors) && odbcErrors.length === 0;
|
|
1069
|
+
const hasNoDataState = Array.isArray(odbcErrors) ? odbcErrors.some(
|
|
1070
|
+
(e) => String(e?.state || e?.SQLSTATE || "").toUpperCase() === "02000"
|
|
1071
|
+
) : false;
|
|
1072
|
+
if (isDml && (isEmptyOdbcError || hasNoDataState || this.isNoDataError(err))) {
|
|
1073
|
+
this.printWarn(
|
|
1074
|
+
`ODBC signaled no-data for ${sql.includes("update") ? "UPDATE" : "DELETE"}; treating as 0 rows affected`
|
|
1075
|
+
);
|
|
1076
|
+
obj.response = { rows: [], rowCount: 0 };
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
this.printError(this.safeStringify(err));
|
|
1080
|
+
throw err;
|
|
1081
|
+
} finally {
|
|
1082
|
+
if (statement && typeof statement.close === "function") {
|
|
1083
|
+
try {
|
|
1084
|
+
await statement.close();
|
|
1085
|
+
} catch (closeErr) {
|
|
1086
|
+
this.printDebug(
|
|
1087
|
+
`Error closing statement: ${this.safeStringify(closeErr, 2)}`
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
isNoDataError(error) {
|
|
1094
|
+
if (!error) return false;
|
|
1095
|
+
const msg = String(error?.message || error || "").toLowerCase();
|
|
1096
|
+
return msg.includes("02000") || msg.includes("no data") || msg.includes("no rows") || msg.includes("0 rows");
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Format statement response from ODBC driver
|
|
1100
|
+
* Handles special case for IDENTITY_VAL_LOCAL() function
|
|
1101
|
+
*/
|
|
1102
|
+
formatStatementResponse(result) {
|
|
1103
|
+
const isIdentityQuery = result.statement?.includes("IDENTITY_VAL_LOCAL()");
|
|
1104
|
+
if (isIdentityQuery && result.columns?.length > 0) {
|
|
1105
|
+
return {
|
|
1106
|
+
rows: result.map(
|
|
1107
|
+
(row) => row[result.columns[0].name]
|
|
1108
|
+
),
|
|
1109
|
+
rowCount: result.count
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
const rowCount = typeof result?.count === "number" ? result.count : 0;
|
|
1113
|
+
return {
|
|
1114
|
+
rows: result,
|
|
1115
|
+
rowCount
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
async _stream(connection, obj, stream, options) {
|
|
1119
|
+
if (!obj.sql) throw new Error("A query is required to stream results");
|
|
1120
|
+
const optimizedFetchSize = options?.fetchSize || this.calculateOptimalFetchSize(obj.sql);
|
|
1121
|
+
return new Promise((resolve, reject) => {
|
|
1122
|
+
let isResolved = false;
|
|
1123
|
+
const cleanup = () => {
|
|
1124
|
+
if (!isResolved) {
|
|
1125
|
+
isResolved = true;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
stream.on("error", (err) => {
|
|
1129
|
+
cleanup();
|
|
1130
|
+
reject(err);
|
|
1131
|
+
});
|
|
1132
|
+
stream.on("end", () => {
|
|
1133
|
+
cleanup();
|
|
1134
|
+
resolve(void 0);
|
|
1135
|
+
});
|
|
1136
|
+
connection.query(
|
|
1137
|
+
obj.sql,
|
|
1138
|
+
obj.bindings,
|
|
1139
|
+
{
|
|
1140
|
+
cursor: true,
|
|
1141
|
+
fetchSize: optimizedFetchSize
|
|
1142
|
+
},
|
|
1143
|
+
(error, cursor) => {
|
|
1144
|
+
if (error) {
|
|
1145
|
+
this.printError(this.safeStringify(error, 2));
|
|
1146
|
+
cleanup();
|
|
1147
|
+
reject(error);
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const readableStream = this._createCursorStream(cursor);
|
|
1151
|
+
readableStream.on("error", (err) => {
|
|
1152
|
+
cleanup();
|
|
1153
|
+
reject(err);
|
|
1154
|
+
});
|
|
1155
|
+
readableStream.pipe(stream);
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
calculateOptimalFetchSize(sql) {
|
|
1161
|
+
const sqlLower = sql.toLowerCase();
|
|
1162
|
+
const hasJoins = /\s+join\s+/i.test(sql);
|
|
1163
|
+
const hasAggregates = /\s+(count|sum|avg|max|min)\s*\(/i.test(sql);
|
|
1164
|
+
const hasOrderBy = /\s+order\s+by\s+/i.test(sql);
|
|
1165
|
+
const hasGroupBy = /\s+group\s+by\s+/i.test(sql);
|
|
1166
|
+
if (hasJoins || hasAggregates || hasOrderBy || hasGroupBy) {
|
|
1167
|
+
return 500;
|
|
1168
|
+
}
|
|
1169
|
+
return 100;
|
|
1170
|
+
}
|
|
1171
|
+
_createCursorStream(cursor) {
|
|
1172
|
+
const parentThis = this;
|
|
1173
|
+
let isClosed = false;
|
|
1174
|
+
return new Readable({
|
|
1175
|
+
objectMode: true,
|
|
1176
|
+
read() {
|
|
1177
|
+
if (isClosed) return;
|
|
1178
|
+
cursor.fetch((error, result) => {
|
|
1179
|
+
if (error) {
|
|
1180
|
+
parentThis.printError(parentThis.safeStringify(error, 2));
|
|
1181
|
+
isClosed = true;
|
|
1182
|
+
this.emit("error", error);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (!cursor.noData) {
|
|
1186
|
+
this.push(result);
|
|
1187
|
+
} else {
|
|
1188
|
+
isClosed = true;
|
|
1189
|
+
cursor.close((closeError) => {
|
|
1190
|
+
if (closeError) {
|
|
1191
|
+
parentThis.printError(JSON.stringify(closeError, null, 2));
|
|
1192
|
+
}
|
|
1193
|
+
if (result) {
|
|
1194
|
+
this.push(result);
|
|
1195
|
+
}
|
|
1196
|
+
this.push(null);
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
},
|
|
1201
|
+
destroy(err, callback) {
|
|
1202
|
+
if (!isClosed) {
|
|
1203
|
+
isClosed = true;
|
|
1204
|
+
cursor.close((closeError) => {
|
|
1205
|
+
if (closeError) {
|
|
1206
|
+
parentThis.printDebug(
|
|
1207
|
+
"Error closing cursor during destroy: " + parentThis.safeStringify(closeError)
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
callback(err);
|
|
1211
|
+
});
|
|
1212
|
+
} else {
|
|
1213
|
+
callback(err);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
transaction(container, config, outerTx) {
|
|
1219
|
+
return new ibmi_transaction_default(this, container, config, outerTx);
|
|
1220
|
+
}
|
|
1221
|
+
schemaCompiler(tableBuilder) {
|
|
1222
|
+
return new ibmi_compiler_default(this, tableBuilder);
|
|
1223
|
+
}
|
|
1224
|
+
tableCompiler(tableBuilder) {
|
|
1225
|
+
return new ibmi_tablecompiler_default(this, tableBuilder);
|
|
1226
|
+
}
|
|
1227
|
+
columnCompiler(tableCompiler, columnCompiler) {
|
|
1228
|
+
return new ibmi_columncompiler_default(this, tableCompiler, columnCompiler);
|
|
1229
|
+
}
|
|
1230
|
+
queryCompiler(builder, bindings) {
|
|
1231
|
+
return new ibmi_querycompiler_default(this, builder, bindings);
|
|
1232
|
+
}
|
|
1233
|
+
// Create IBM i-specific migration runner that bypasses Knex's problematic locking system
|
|
1234
|
+
createMigrationRunner(config) {
|
|
1235
|
+
const knexInstance = this.context || this;
|
|
1236
|
+
return createIBMiMigrationRunner(knexInstance, config);
|
|
1237
|
+
}
|
|
1238
|
+
processResponse(obj, runner) {
|
|
1239
|
+
if (obj === null) return null;
|
|
1240
|
+
const { response } = obj;
|
|
1241
|
+
if (obj.output) {
|
|
1242
|
+
try {
|
|
1243
|
+
const result = obj.output(runner, response);
|
|
1244
|
+
return result;
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
const wrappedError = this.wrapError(error, "custom_output", obj);
|
|
1247
|
+
this.printError(
|
|
1248
|
+
`Custom output function failed: ${wrappedError.message}`
|
|
1249
|
+
);
|
|
1250
|
+
if (this.isConnectionError(error)) {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
"Connection closed during query processing - consider using migrations.disableTransactions: true for DDL operations"
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
throw wrappedError;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
const validationResult = this.validateResponse(obj);
|
|
1259
|
+
if (validationResult !== null) return validationResult;
|
|
1260
|
+
return this.processSqlMethod(obj);
|
|
1261
|
+
}
|
|
1262
|
+
validateResponse(obj) {
|
|
1263
|
+
if (!obj.response) {
|
|
1264
|
+
this.printDebug("response undefined" + JSON.stringify(obj));
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
if (!obj.response.rows) {
|
|
1268
|
+
this.printError("rows undefined" + JSON.stringify(obj));
|
|
1269
|
+
return null;
|
|
1270
|
+
}
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
wrapError(error, method, queryObject) {
|
|
1274
|
+
const context = {
|
|
1275
|
+
method,
|
|
1276
|
+
sql: queryObject.sql ? queryObject.sql.substring(0, 100) + "..." : "unknown",
|
|
1277
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1278
|
+
};
|
|
1279
|
+
if (this.isConnectionError(error)) {
|
|
1280
|
+
return new Error(
|
|
1281
|
+
`IBM i DB2 connection error during ${method}: ${error.message} | Context: ${JSON.stringify(context)}`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
if (this.isTimeoutError(error)) {
|
|
1285
|
+
return new Error(
|
|
1286
|
+
`IBM i DB2 timeout during ${method}: ${error.message} | Context: ${JSON.stringify(context)}`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
if (this.isSQLError(error)) {
|
|
1290
|
+
return new Error(
|
|
1291
|
+
`IBM i DB2 SQL error during ${method}: ${error.message} | Context: ${JSON.stringify(context)}`
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
return new Error(
|
|
1295
|
+
`IBM i DB2 error during ${method}: ${error.message} | Context: ${JSON.stringify(context)}`
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
shouldRetryQuery(queryObject, method) {
|
|
1299
|
+
return queryObject.sql?.toLowerCase().includes("systables") || queryObject.sql?.toLowerCase().includes("knex_migrations");
|
|
1300
|
+
}
|
|
1301
|
+
async retryQuery(connection, queryObject, method) {
|
|
1302
|
+
this.printDebug(`Retrying ${method} query due to connection error...`);
|
|
1303
|
+
try {
|
|
1304
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1305
|
+
if (this.isSelectMethod(method)) {
|
|
1306
|
+
await this.executeSelectQuery(connection, queryObject);
|
|
1307
|
+
} else {
|
|
1308
|
+
await this.executeStatementQuery(connection, queryObject);
|
|
1309
|
+
}
|
|
1310
|
+
return queryObject;
|
|
1311
|
+
} catch (retryError) {
|
|
1312
|
+
this.printError(`Retry failed: ${retryError.message}`);
|
|
1313
|
+
queryObject.response = { rows: [], rowCount: 0 };
|
|
1314
|
+
return queryObject;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
isConnectionError(error) {
|
|
1318
|
+
const errorMessage = (error.message || error.toString || error).toLowerCase();
|
|
1319
|
+
return errorMessage.includes("connection") && (errorMessage.includes("closed") || errorMessage.includes("invalid") || errorMessage.includes("terminated") || errorMessage.includes("not connected"));
|
|
1320
|
+
}
|
|
1321
|
+
isTimeoutError(error) {
|
|
1322
|
+
const errorMessage = (error.message || error.toString || error).toLowerCase();
|
|
1323
|
+
return errorMessage.includes("timeout") || errorMessage.includes("timed out");
|
|
1324
|
+
}
|
|
1325
|
+
isSQLError(error) {
|
|
1326
|
+
const errorMessage = (error.message || error.toString || error).toLowerCase();
|
|
1327
|
+
return errorMessage.includes("sql") || errorMessage.includes("syntax") || errorMessage.includes("table") || errorMessage.includes("column");
|
|
1328
|
+
}
|
|
1329
|
+
processSqlMethod(obj) {
|
|
1330
|
+
const { rows, rowCount } = obj.response;
|
|
1331
|
+
switch (obj.sqlMethod) {
|
|
1332
|
+
case "select" /* SELECT */:
|
|
1333
|
+
return rows;
|
|
1334
|
+
case "pluck" /* PLUCK */:
|
|
1335
|
+
return rows.map(obj.pluck);
|
|
1336
|
+
case "first" /* FIRST */:
|
|
1337
|
+
return rows[0];
|
|
1338
|
+
case "insert" /* INSERT */:
|
|
1339
|
+
return rows;
|
|
1340
|
+
case "del" /* DELETE */:
|
|
1341
|
+
case "delete" /* DELETE_ALT */:
|
|
1342
|
+
case "update" /* UPDATE */:
|
|
1343
|
+
return obj.select ? rows : rowCount ?? 0;
|
|
1344
|
+
case "counter" /* COUNTER */:
|
|
1345
|
+
return rowCount;
|
|
1346
|
+
default:
|
|
1347
|
+
return rows;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
var DB2Dialect = DB2Client;
|
|
1352
|
+
var index_default = DB2Client;
|
|
1353
|
+
export {
|
|
1354
|
+
DB2Dialect,
|
|
1355
|
+
IBMiMigrationRunner,
|
|
1356
|
+
createIBMiMigrationRunner,
|
|
1357
|
+
index_default as default
|
|
1358
|
+
};
|
|
1359
|
+
//# sourceMappingURL=index.mjs.map
|