@bdkinc/knex-ibmi 0.3.22 → 0.4.0
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 +240 -130
- package/dist/cli.cjs +531 -0
- package/dist/index.d.mts +137 -0
- package/dist/index.d.ts +36 -53
- package/dist/index.js +569 -146
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +971 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -20
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
1
3
|
var __create = Object.create;
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
3
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var
|
|
8
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
-
};
|
|
9
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
10
10
|
var __export = (target, all) => {
|
|
11
11
|
for (var name in all)
|
|
12
12
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -28,49 +28,80 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
mod
|
|
29
29
|
));
|
|
30
30
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
-
|
|
32
|
-
// node_modules/lodash/isObject.js
|
|
33
|
-
var require_isObject = __commonJS({
|
|
34
|
-
"node_modules/lodash/isObject.js"(exports2, module2) {
|
|
35
|
-
function isObject2(value) {
|
|
36
|
-
var type = typeof value;
|
|
37
|
-
return value != null && (type == "object" || type == "function");
|
|
38
|
-
}
|
|
39
|
-
module2.exports = isObject2;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
31
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
42
32
|
|
|
43
33
|
// src/index.ts
|
|
44
|
-
var
|
|
45
|
-
__export(
|
|
34
|
+
var index_exports = {};
|
|
35
|
+
__export(index_exports, {
|
|
46
36
|
DB2Dialect: () => DB2Dialect,
|
|
47
|
-
|
|
37
|
+
IBMiMigrationRunner: () => IBMiMigrationRunner,
|
|
38
|
+
createIBMiMigrationRunner: () => createIBMiMigrationRunner,
|
|
39
|
+
default: () => index_default
|
|
48
40
|
});
|
|
49
|
-
module.exports = __toCommonJS(
|
|
41
|
+
module.exports = __toCommonJS(index_exports);
|
|
50
42
|
var import_node_process = __toESM(require("process"));
|
|
51
|
-
var import_knex = require("knex");
|
|
43
|
+
var import_knex = __toESM(require("knex"));
|
|
52
44
|
var import_odbc = __toESM(require("odbc"));
|
|
53
45
|
|
|
54
46
|
// src/schema/ibmi-compiler.ts
|
|
55
|
-
var import_compiler = __toESM(require("knex/lib/schema/compiler"));
|
|
47
|
+
var import_compiler = __toESM(require("knex/lib/schema/compiler.js"));
|
|
56
48
|
var IBMiSchemaCompiler = class extends import_compiler.default {
|
|
57
49
|
hasTable(tableName) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
const upperName = String(tableName).toUpperCase();
|
|
51
|
+
let schemaName = null;
|
|
52
|
+
let actualTableName = upperName;
|
|
53
|
+
if (upperName.includes(".")) {
|
|
54
|
+
const parts = upperName.split(".");
|
|
55
|
+
schemaName = parts[0];
|
|
56
|
+
actualTableName = parts[1];
|
|
57
|
+
}
|
|
58
|
+
const builderSchema = this.builder._schema;
|
|
59
|
+
if (builderSchema) {
|
|
60
|
+
schemaName = builderSchema.toUpperCase();
|
|
61
|
+
}
|
|
62
|
+
let sql;
|
|
63
|
+
let bindings;
|
|
64
|
+
if (schemaName) {
|
|
65
|
+
sql = `select count(*) as table_count from QSYS2.SYSTABLES where UPPER(TABLE_NAME) = ? AND UPPER(TABLE_SCHEMA) = ?`;
|
|
66
|
+
bindings = [actualTableName, schemaName];
|
|
67
|
+
} else {
|
|
68
|
+
sql = `select count(*) as table_count from QSYS2.SYSTABLES where UPPER(TABLE_NAME) = ?`;
|
|
69
|
+
bindings = [actualTableName];
|
|
68
70
|
}
|
|
69
71
|
this.pushQuery({
|
|
70
72
|
sql,
|
|
71
73
|
bindings,
|
|
72
|
-
output: (resp) => {
|
|
73
|
-
|
|
74
|
+
output: (runner, resp) => {
|
|
75
|
+
if (!resp) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(resp) && resp.length > 0) {
|
|
79
|
+
const firstRow = resp[0];
|
|
80
|
+
if (firstRow && typeof firstRow === "object") {
|
|
81
|
+
const count = firstRow.table_count || firstRow.TABLE_COUNT || firstRow.count || firstRow.COUNT || 0;
|
|
82
|
+
return count > 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (typeof resp === "object" && resp !== null) {
|
|
86
|
+
const keys = Object.keys(resp);
|
|
87
|
+
for (const key of keys) {
|
|
88
|
+
if (!isNaN(parseInt(key))) {
|
|
89
|
+
const row = resp[key];
|
|
90
|
+
if (row && typeof row === "object") {
|
|
91
|
+
const count = row.table_count || row.TABLE_COUNT || row.count || row.COUNT || 0;
|
|
92
|
+
return count > 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (resp.rows && Array.isArray(resp.rows) && resp.rows.length > 0) {
|
|
97
|
+
const firstRow = resp.rows[0];
|
|
98
|
+
if (firstRow && typeof firstRow === "object") {
|
|
99
|
+
const count = firstRow.table_count || firstRow.TABLE_COUNT || firstRow.count || firstRow.COUNT || 0;
|
|
100
|
+
return count > 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
74
105
|
}
|
|
75
106
|
});
|
|
76
107
|
}
|
|
@@ -83,14 +114,10 @@ var IBMiSchemaCompiler = class extends import_compiler.default {
|
|
|
83
114
|
return this.sequence;
|
|
84
115
|
}
|
|
85
116
|
};
|
|
86
|
-
function prefixedTableName(prefix, table) {
|
|
87
|
-
return prefix ? `${prefix}.${table}` : table;
|
|
88
|
-
}
|
|
89
117
|
var ibmi_compiler_default = IBMiSchemaCompiler;
|
|
90
118
|
|
|
91
119
|
// src/schema/ibmi-tablecompiler.ts
|
|
92
|
-
var import_tablecompiler = __toESM(require("knex/lib/schema/tablecompiler"));
|
|
93
|
-
var import_isObject = __toESM(require_isObject());
|
|
120
|
+
var import_tablecompiler = __toESM(require("knex/lib/schema/tablecompiler.js"));
|
|
94
121
|
var IBMiTableCompiler = class extends import_tablecompiler.default {
|
|
95
122
|
createQuery(columns, ifNot, like) {
|
|
96
123
|
let createStatement = ifNot ? `if object_id('${this.tableName()}', 'U') is null ` : "";
|
|
@@ -114,21 +141,24 @@ var IBMiTableCompiler = class extends import_tablecompiler.default {
|
|
|
114
141
|
unique(columns, indexName) {
|
|
115
142
|
let deferrable = "";
|
|
116
143
|
let predicate;
|
|
117
|
-
|
|
118
|
-
|
|
144
|
+
let finalIndexName;
|
|
145
|
+
if (typeof indexName === "object" && indexName !== null) {
|
|
146
|
+
deferrable = indexName.deferrable || "";
|
|
119
147
|
predicate = indexName.predicate;
|
|
120
|
-
|
|
148
|
+
finalIndexName = indexName.indexName;
|
|
149
|
+
} else {
|
|
150
|
+
finalIndexName = indexName;
|
|
121
151
|
}
|
|
122
152
|
if (deferrable && deferrable !== "not deferrable") {
|
|
123
153
|
this.client.logger.warn?.(
|
|
124
|
-
`IBMi: unique index \`${
|
|
154
|
+
`IBMi: unique index \`${finalIndexName}\` will not be deferrable ${deferrable}.`
|
|
125
155
|
);
|
|
126
156
|
}
|
|
127
|
-
|
|
157
|
+
const wrappedIndexName = finalIndexName ? this.formatter.wrap(finalIndexName) : this._indexCommand("unique", this.tableNameRaw, columns);
|
|
128
158
|
columns = this.formatter.columnize(columns);
|
|
129
159
|
const predicateQuery = predicate ? " " + this.client.queryCompiler(predicate).where() : "";
|
|
130
160
|
this.pushQuery(
|
|
131
|
-
`create unique index ${
|
|
161
|
+
`create unique index ${wrappedIndexName} on ${this.tableName()} (${columns})${predicateQuery}`
|
|
132
162
|
);
|
|
133
163
|
}
|
|
134
164
|
// All of the columns to "add" for the query
|
|
@@ -151,7 +181,7 @@ var IBMiTableCompiler = class extends import_tablecompiler.default {
|
|
|
151
181
|
var ibmi_tablecompiler_default = IBMiTableCompiler;
|
|
152
182
|
|
|
153
183
|
// src/schema/ibmi-columncompiler.ts
|
|
154
|
-
var import_columncompiler = __toESM(require("knex/lib/schema/columncompiler"));
|
|
184
|
+
var import_columncompiler = __toESM(require("knex/lib/schema/columncompiler.js"));
|
|
155
185
|
var IBMiColumnCompiler = class extends import_columncompiler.default {
|
|
156
186
|
increments(options = { primaryKey: true }) {
|
|
157
187
|
return "int not null generated always as identity (start with 1, increment by 1)" + (this.tableCompiler._canBeAddPrimaryKey(options) ? " primary key" : "");
|
|
@@ -160,65 +190,124 @@ var IBMiColumnCompiler = class extends import_columncompiler.default {
|
|
|
160
190
|
var ibmi_columncompiler_default = IBMiColumnCompiler;
|
|
161
191
|
|
|
162
192
|
// src/execution/ibmi-transaction.ts
|
|
163
|
-
var import_transaction = __toESM(require("knex/lib/execution/transaction"));
|
|
193
|
+
var import_transaction = __toESM(require("knex/lib/execution/transaction.js"));
|
|
164
194
|
var IBMiTransaction = class extends import_transaction.default {
|
|
165
195
|
begin(connection) {
|
|
166
|
-
|
|
196
|
+
try {
|
|
197
|
+
return connection.beginTransaction();
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (this.isConnectionClosed(error)) {
|
|
200
|
+
console.warn(
|
|
201
|
+
"IBM i DB2: Connection closed during transaction begin, DDL operations may have caused implicit commit"
|
|
202
|
+
);
|
|
203
|
+
throw new Error(
|
|
204
|
+
"Connection closed during transaction begin - consider using migrations.disableTransactions: true"
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
167
209
|
}
|
|
168
210
|
rollback(connection) {
|
|
169
|
-
|
|
211
|
+
try {
|
|
212
|
+
return connection.rollback();
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.warn(
|
|
215
|
+
"IBM i DB2: Rollback encountered an error (likely closed connection):",
|
|
216
|
+
error?.message || error
|
|
217
|
+
);
|
|
218
|
+
return Promise.resolve();
|
|
219
|
+
}
|
|
170
220
|
}
|
|
171
221
|
commit(connection) {
|
|
172
|
-
|
|
222
|
+
try {
|
|
223
|
+
return connection.commit();
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (this.isConnectionClosed(error)) {
|
|
226
|
+
console.warn(
|
|
227
|
+
"IBM i DB2: Connection closed during commit - DDL operations cause implicit commits"
|
|
228
|
+
);
|
|
229
|
+
throw new Error(
|
|
230
|
+
"Connection closed during commit - this is expected with DDL operations on IBM i DB2"
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
isConnectionClosed(error) {
|
|
237
|
+
const message = String(error?.message || error || "").toLowerCase();
|
|
238
|
+
return message.includes("connection") && (message.includes("closed") || message.includes("invalid") || message.includes("terminated") || message.includes("not connected"));
|
|
173
239
|
}
|
|
174
240
|
};
|
|
175
241
|
var ibmi_transaction_default = IBMiTransaction;
|
|
176
242
|
|
|
177
243
|
// src/query/ibmi-querycompiler.ts
|
|
178
|
-
var import_querycompiler = __toESM(require("knex/lib/query/querycompiler"));
|
|
179
|
-
var import_wrappingFormatter = require("knex/lib/formatter/wrappingFormatter");
|
|
180
|
-
var import_date_fns = require("date-fns");
|
|
244
|
+
var import_querycompiler = __toESM(require("knex/lib/query/querycompiler.js"));
|
|
245
|
+
var import_wrappingFormatter = require("knex/lib/formatter/wrappingFormatter.js");
|
|
181
246
|
var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
247
|
+
formatTimestampLocal(date) {
|
|
248
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
249
|
+
const y = date.getFullYear();
|
|
250
|
+
const m = pad(date.getMonth() + 1);
|
|
251
|
+
const d = pad(date.getDate());
|
|
252
|
+
const hh = pad(date.getHours());
|
|
253
|
+
const mm = pad(date.getMinutes());
|
|
254
|
+
const ss = pad(date.getSeconds());
|
|
255
|
+
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
|
|
256
|
+
}
|
|
182
257
|
insert() {
|
|
183
258
|
const insertValues = this.single.insert || [];
|
|
184
|
-
let sql = `select ${this.single.returning ? this.formatter.columnize(this.single.returning) : "IDENTITY_VAL_LOCAL()"} from FINAL TABLE(`;
|
|
185
|
-
sql += this.with() + `insert into ${this.tableName} `;
|
|
186
259
|
const { returning } = this.single;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return "";
|
|
260
|
+
if (this.isEmptyInsertValues(insertValues)) {
|
|
261
|
+
if (this.isEmptyObject(insertValues)) {
|
|
262
|
+
return this.buildEmptyInsertResult(returning);
|
|
191
263
|
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
sql: sql + returningSql + this._emptyInsertValue,
|
|
195
|
-
returning
|
|
196
|
-
};
|
|
264
|
+
return "";
|
|
197
265
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
266
|
+
const selectColumns = returning ? this.formatter.columnize(returning) : "IDENTITY_VAL_LOCAL()";
|
|
267
|
+
const returningSql = returning ? this._returning("insert", returning, void 0) + " " : "";
|
|
268
|
+
const insertSql = [
|
|
269
|
+
this.with(),
|
|
270
|
+
`insert into ${this.tableName}`,
|
|
271
|
+
this._buildInsertData(insertValues, returningSql)
|
|
272
|
+
].filter(Boolean).join(" ");
|
|
273
|
+
const sql = `select ${selectColumns} from FINAL TABLE(${insertSql})`;
|
|
274
|
+
return { sql, returning };
|
|
275
|
+
}
|
|
276
|
+
isEmptyInsertValues(insertValues) {
|
|
277
|
+
return Array.isArray(insertValues) && insertValues.length === 0 || this.isEmptyObject(insertValues);
|
|
278
|
+
}
|
|
279
|
+
isEmptyObject(insertValues) {
|
|
280
|
+
return insertValues !== null && typeof insertValues === "object" && !Array.isArray(insertValues) && Object.keys(insertValues).length === 0;
|
|
281
|
+
}
|
|
282
|
+
buildEmptyInsertResult(returning) {
|
|
283
|
+
const selectColumns = returning ? this.formatter.columnize(returning) : "IDENTITY_VAL_LOCAL()";
|
|
284
|
+
const returningSql = returning ? this._returning("insert", returning, void 0) + " " : "";
|
|
285
|
+
const insertSql = [
|
|
286
|
+
this.with(),
|
|
287
|
+
`insert into ${this.tableName}`,
|
|
288
|
+
returningSql + this._emptyInsertValue
|
|
289
|
+
].filter(Boolean).join(" ");
|
|
290
|
+
const sql = `select ${selectColumns} from FINAL TABLE(${insertSql})`;
|
|
291
|
+
return { sql, returning };
|
|
204
292
|
}
|
|
205
293
|
_buildInsertData(insertValues, returningSql) {
|
|
206
|
-
let sql = "";
|
|
207
294
|
const insertData = this._prepInsert(insertValues);
|
|
208
|
-
if (insertData.columns.length) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return
|
|
295
|
+
if (insertData.columns.length > 0) {
|
|
296
|
+
const columnsSql = `(${this.formatter.columnize(insertData.columns)})`;
|
|
297
|
+
const valuesSql = `(${this._buildInsertValues(insertData)})`;
|
|
298
|
+
return `${columnsSql} ${returningSql}values ${valuesSql}`;
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(insertValues) && insertValues.length === 1 && insertValues[0]) {
|
|
301
|
+
return returningSql + this._emptyInsertValue;
|
|
215
302
|
}
|
|
216
|
-
return
|
|
303
|
+
return "";
|
|
217
304
|
}
|
|
218
305
|
_prepInsert(data) {
|
|
219
|
-
if (typeof data === "object" && data
|
|
306
|
+
if (typeof data === "object" && data?.migration_time) {
|
|
220
307
|
const parsed = new Date(data.migration_time);
|
|
221
|
-
|
|
308
|
+
if (!isNaN(parsed.getTime())) {
|
|
309
|
+
data.migration_time = this.formatTimestampLocal(parsed);
|
|
310
|
+
}
|
|
222
311
|
}
|
|
223
312
|
const isRaw = (0, import_wrappingFormatter.rawOrFn)(
|
|
224
313
|
data,
|
|
@@ -230,36 +319,23 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
230
319
|
if (isRaw) {
|
|
231
320
|
return isRaw;
|
|
232
321
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
data = data ? [data] : [];
|
|
322
|
+
const dataArray = Array.isArray(data) ? data : data ? [data] : [];
|
|
323
|
+
if (dataArray.length === 0) {
|
|
324
|
+
return { columns: [], values: [] };
|
|
237
325
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
|
|
326
|
+
const allColumns = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const item of dataArray) {
|
|
328
|
+
if (item != null) {
|
|
329
|
+
Object.keys(item).forEach((key) => allColumns.add(key));
|
|
242
330
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
while (++j < keys.length) {
|
|
250
|
-
const key = keys[j];
|
|
251
|
-
let idx = columns.indexOf(key);
|
|
252
|
-
if (idx === -1) {
|
|
253
|
-
columns = columns.concat(key).sort();
|
|
254
|
-
idx = columns.indexOf(key);
|
|
255
|
-
let k = -1;
|
|
256
|
-
while (++k < values.length) {
|
|
257
|
-
values[k].splice(idx, 0, void 0);
|
|
258
|
-
}
|
|
259
|
-
row.splice(idx, 0, void 0);
|
|
260
|
-
}
|
|
261
|
-
row[idx] = data[i][key];
|
|
331
|
+
}
|
|
332
|
+
const columns = Array.from(allColumns).sort();
|
|
333
|
+
const values = [];
|
|
334
|
+
for (const item of dataArray) {
|
|
335
|
+
if (item == null) {
|
|
336
|
+
break;
|
|
262
337
|
}
|
|
338
|
+
const row = columns.map((column) => item[column] ?? void 0);
|
|
263
339
|
values.push(row);
|
|
264
340
|
}
|
|
265
341
|
return {
|
|
@@ -274,17 +350,32 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
274
350
|
const order = this.order();
|
|
275
351
|
const limit = this.limit();
|
|
276
352
|
const { returning } = this.single;
|
|
277
|
-
|
|
353
|
+
const baseUpdateSql = [
|
|
354
|
+
withSQL,
|
|
355
|
+
`update ${this.single.only ? "only " : ""}${this.tableName}`,
|
|
356
|
+
"set",
|
|
357
|
+
updates.join(", "),
|
|
358
|
+
where,
|
|
359
|
+
order,
|
|
360
|
+
limit
|
|
361
|
+
].filter(Boolean).join(" ");
|
|
278
362
|
if (returning) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
sql
|
|
285
|
-
}
|
|
286
|
-
return { sql, returning };
|
|
287
|
-
}
|
|
363
|
+
this.client.logger.warn?.(
|
|
364
|
+
"IBMi DB2 does not support returning in update statements, only inserts"
|
|
365
|
+
);
|
|
366
|
+
const selectColumns = this.formatter.columnize(this.single.returning);
|
|
367
|
+
const sql = `select ${selectColumns} from FINAL TABLE(${baseUpdateSql})`;
|
|
368
|
+
return { sql, returning };
|
|
369
|
+
}
|
|
370
|
+
return { sql: baseUpdateSql, returning };
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Handle returning clause for IBMi DB2 queries
|
|
374
|
+
* Note: IBMi DB2 has limited support for RETURNING clauses
|
|
375
|
+
* @param method - The SQL method (insert, update, delete)
|
|
376
|
+
* @param value - The returning value
|
|
377
|
+
* @param withTrigger - Trigger support (currently unused)
|
|
378
|
+
*/
|
|
288
379
|
_returning(method, value, withTrigger) {
|
|
289
380
|
switch (method) {
|
|
290
381
|
case "update":
|
|
@@ -294,6 +385,8 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
294
385
|
return value ? `${withTrigger ? " into #out" : ""}` : "";
|
|
295
386
|
case "rowcount":
|
|
296
387
|
return value ? "select @@rowcount" : "";
|
|
388
|
+
default:
|
|
389
|
+
return "";
|
|
297
390
|
}
|
|
298
391
|
}
|
|
299
392
|
columnizeWithPrefix(prefix, target) {
|
|
@@ -311,8 +404,210 @@ var ibmi_querycompiler_default = IBMiQueryCompiler;
|
|
|
311
404
|
|
|
312
405
|
// src/index.ts
|
|
313
406
|
var import_node_stream = require("stream");
|
|
314
|
-
|
|
407
|
+
|
|
408
|
+
// src/migrations/ibmi-migration-runner.ts
|
|
409
|
+
var import_fs = __toESM(require("fs"));
|
|
410
|
+
var import_path = __toESM(require("path"));
|
|
411
|
+
var IBMiMigrationRunner = class {
|
|
412
|
+
constructor(knex2, config) {
|
|
413
|
+
__publicField(this, "knex");
|
|
414
|
+
__publicField(this, "config");
|
|
415
|
+
this.knex = knex2;
|
|
416
|
+
this.config = {
|
|
417
|
+
directory: "./migrations",
|
|
418
|
+
tableName: "KNEX_MIGRATIONS",
|
|
419
|
+
schemaName: void 0,
|
|
420
|
+
extension: "js",
|
|
421
|
+
...config
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
getFullTableName() {
|
|
425
|
+
return this.config.schemaName ? `${this.config.schemaName}.${this.config.tableName}` : this.config.tableName;
|
|
426
|
+
}
|
|
427
|
+
async latest() {
|
|
428
|
+
try {
|
|
429
|
+
console.log(
|
|
430
|
+
"\u{1F680} IBM i DB2 Migration Runner - bypassing Knex locking system"
|
|
431
|
+
);
|
|
432
|
+
const tableName = this.getFullTableName();
|
|
433
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
434
|
+
tableName
|
|
435
|
+
);
|
|
436
|
+
if (!migrationTableExists) {
|
|
437
|
+
console.log(`\u{1F4DD} Creating migration table: ${tableName}`);
|
|
438
|
+
await this.knex.schema.createTable(tableName, (table) => {
|
|
439
|
+
table.increments("id").primary();
|
|
440
|
+
table.string("name");
|
|
441
|
+
table.integer("batch");
|
|
442
|
+
table.timestamp("migration_time");
|
|
443
|
+
});
|
|
444
|
+
console.log("\u2705 Migration table created");
|
|
445
|
+
}
|
|
446
|
+
const completed = await this.knex(tableName).select("NAME").orderBy("ID");
|
|
447
|
+
const completedNames = completed.map((c) => c.NAME);
|
|
448
|
+
console.log(`\u{1F4CB} Found ${completedNames.length} completed migrations`);
|
|
449
|
+
const migrationFiles = this.getMigrationFiles();
|
|
450
|
+
console.log(`\u{1F4C1} Found ${migrationFiles.length} migration files`);
|
|
451
|
+
const newMigrations = migrationFiles.filter(
|
|
452
|
+
(file) => !completedNames.includes(file)
|
|
453
|
+
);
|
|
454
|
+
if (newMigrations.length === 0) {
|
|
455
|
+
console.log("\u2705 No new migrations to run");
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
console.log(`\u{1F3AF} Running ${newMigrations.length} new migrations:`);
|
|
459
|
+
newMigrations.forEach((file) => console.log(` - ${file}`));
|
|
460
|
+
const batchResult = await this.knex(tableName).max("BATCH as max_batch");
|
|
461
|
+
const nextBatch = (batchResult[0]?.max_batch || 0) + 1;
|
|
462
|
+
console.log(`\u{1F4CA} Using batch number: ${nextBatch}`);
|
|
463
|
+
for (const migrationFile of newMigrations) {
|
|
464
|
+
console.log(`
|
|
465
|
+
\u{1F504} Running migration: ${migrationFile}`);
|
|
466
|
+
try {
|
|
467
|
+
const migrationPath = this.getMigrationPath(migrationFile);
|
|
468
|
+
const migration = await import(migrationPath);
|
|
469
|
+
if (!migration.up || typeof migration.up !== "function") {
|
|
470
|
+
throw new Error(`Migration ${migrationFile} has no 'up' function`);
|
|
471
|
+
}
|
|
472
|
+
console.log(` \u26A1 Executing migration...`);
|
|
473
|
+
await migration.up(this.knex);
|
|
474
|
+
await this.knex(tableName).insert({
|
|
475
|
+
name: migrationFile,
|
|
476
|
+
batch: nextBatch,
|
|
477
|
+
migration_time: /* @__PURE__ */ new Date()
|
|
478
|
+
});
|
|
479
|
+
console.log(` \u2705 Migration ${migrationFile} completed successfully`);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error(
|
|
482
|
+
` \u274C Migration ${migrationFile} failed:`,
|
|
483
|
+
error.message
|
|
484
|
+
);
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
console.log(`
|
|
489
|
+
\u{1F389} All migrations completed successfully!`);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error("\u274C Migration runner failed:", error.message);
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async rollback(steps = 1) {
|
|
496
|
+
try {
|
|
497
|
+
console.log(`\u{1F504} Rolling back ${steps} migration batch(es)...`);
|
|
498
|
+
const tableName = this.getFullTableName();
|
|
499
|
+
const batchesToRollback = await this.knex(tableName).distinct("BATCH").orderBy("BATCH", "desc").limit(steps);
|
|
500
|
+
if (batchesToRollback.length === 0) {
|
|
501
|
+
console.log("\u2705 No migrations to rollback");
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const batchNumbers = batchesToRollback.map((b) => b.BATCH);
|
|
505
|
+
console.log(`\u{1F4CA} Rolling back batches: ${batchNumbers.join(", ")}`);
|
|
506
|
+
const migrationsToRollback = await this.knex(tableName).select("NAME").whereIn("BATCH", batchNumbers).orderBy("ID", "desc");
|
|
507
|
+
console.log(`\u{1F3AF} Rolling back ${migrationsToRollback.length} migrations:`);
|
|
508
|
+
migrationsToRollback.forEach((m) => console.log(` - ${m.NAME}`));
|
|
509
|
+
for (const migrationRecord of migrationsToRollback) {
|
|
510
|
+
const migrationFile = migrationRecord.NAME;
|
|
511
|
+
console.log(`
|
|
512
|
+
\u{1F504} Rolling back migration: ${migrationFile}`);
|
|
513
|
+
try {
|
|
514
|
+
const migrationPath = this.getMigrationPath(migrationFile);
|
|
515
|
+
const migration = await import(migrationPath);
|
|
516
|
+
if (migration.down && typeof migration.down === "function") {
|
|
517
|
+
console.log(` \u26A1 Executing rollback...`);
|
|
518
|
+
await migration.down(this.knex);
|
|
519
|
+
} else {
|
|
520
|
+
console.log(
|
|
521
|
+
` \u26A0\uFE0F Migration ${migrationFile} has no 'down' function, skipping rollback`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
await this.knex(tableName).where("NAME", migrationFile).del();
|
|
525
|
+
console.log(
|
|
526
|
+
` \u2705 Migration ${migrationFile} rolled back successfully`
|
|
527
|
+
);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.error(
|
|
530
|
+
` \u274C Migration ${migrationFile} rollback failed:`,
|
|
531
|
+
error.message
|
|
532
|
+
);
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
console.log(`
|
|
537
|
+
\u{1F389} Rollback completed successfully!`);
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.error("\u274C Rollback failed:", error.message);
|
|
540
|
+
throw error;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
async currentVersion() {
|
|
544
|
+
try {
|
|
545
|
+
const tableName = this.getFullTableName();
|
|
546
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
547
|
+
tableName
|
|
548
|
+
);
|
|
549
|
+
if (!migrationTableExists) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const result = await this.knex(tableName).select("NAME").orderBy("ID", "desc").first();
|
|
553
|
+
return result?.NAME || null;
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error("\u274C Error getting current version:", error.message);
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async listExecuted() {
|
|
560
|
+
try {
|
|
561
|
+
const tableName = this.getFullTableName();
|
|
562
|
+
const migrationTableExists = await this.knex.schema.hasTable(
|
|
563
|
+
tableName
|
|
564
|
+
);
|
|
565
|
+
if (!migrationTableExists) {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
const completed = await this.knex(tableName).select("NAME").orderBy("ID");
|
|
569
|
+
return completed.map((c) => c.NAME);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error("\u274C Error listing executed migrations:", error.message);
|
|
572
|
+
return [];
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
async listPending() {
|
|
576
|
+
try {
|
|
577
|
+
const allFiles = this.getMigrationFiles();
|
|
578
|
+
const executed = await this.listExecuted();
|
|
579
|
+
return allFiles.filter((file) => !executed.includes(file));
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.error("\u274C Error listing pending migrations:", error.message);
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
getMigrationFiles() {
|
|
586
|
+
const { directory, extension } = this.config;
|
|
587
|
+
if (!import_fs.default.existsSync(directory)) {
|
|
588
|
+
throw new Error(`Migration directory does not exist: ${directory}`);
|
|
589
|
+
}
|
|
590
|
+
const validExtensions = ["js", "ts", "mjs", "cjs"];
|
|
591
|
+
const extensionToCheck = extension || "js";
|
|
592
|
+
return import_fs.default.readdirSync(directory).filter((file) => {
|
|
593
|
+
if (extension && extension !== "js") {
|
|
594
|
+
return file.endsWith(`.${extension}`);
|
|
595
|
+
}
|
|
596
|
+
return validExtensions.some((ext) => file.endsWith(`.${ext}`));
|
|
597
|
+
}).sort();
|
|
598
|
+
}
|
|
599
|
+
getMigrationPath(filename) {
|
|
600
|
+
return import_path.default.resolve(this.config.directory, filename);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
function createIBMiMigrationRunner(knex2, config) {
|
|
604
|
+
return new IBMiMigrationRunner(knex2, config);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/index.ts
|
|
608
|
+
var DB2Client = class extends import_knex.default.Client {
|
|
315
609
|
constructor(config) {
|
|
610
|
+
console.log("\u{1F3AF} DB2Client constructor called!");
|
|
316
611
|
super(config);
|
|
317
612
|
this.driverName = "odbc";
|
|
318
613
|
if (this.dialect && !this.config.client) {
|
|
@@ -340,10 +635,24 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
340
635
|
this.valueForUndefined = null;
|
|
341
636
|
}
|
|
342
637
|
}
|
|
638
|
+
// Helper method to safely stringify objects that might have circular references
|
|
639
|
+
safeStringify(obj, indent = 0) {
|
|
640
|
+
try {
|
|
641
|
+
return JSON.stringify(obj, null, indent);
|
|
642
|
+
} catch (error) {
|
|
643
|
+
if (error instanceof Error && error.message.includes("circular")) {
|
|
644
|
+
return `[Circular structure - ${typeof obj}]`;
|
|
645
|
+
}
|
|
646
|
+
return `[Stringify error - ${typeof obj}]`;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
343
649
|
_driver() {
|
|
344
650
|
return import_odbc.default;
|
|
345
651
|
}
|
|
346
652
|
wrapIdentifierImpl(value) {
|
|
653
|
+
if (value.includes("KNEX_MIGRATIONS") || value.includes("knex_migrations")) {
|
|
654
|
+
return value.toUpperCase();
|
|
655
|
+
}
|
|
347
656
|
return value;
|
|
348
657
|
}
|
|
349
658
|
printDebug(message) {
|
|
@@ -376,6 +685,7 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
376
685
|
this.printDebug(
|
|
377
686
|
"connection config: " + this._getConnectionString(connectionConfig)
|
|
378
687
|
);
|
|
688
|
+
let connection;
|
|
379
689
|
if (this.config?.pool) {
|
|
380
690
|
const poolConfig = {
|
|
381
691
|
connectionString: this._getConnectionString(connectionConfig),
|
|
@@ -385,11 +695,13 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
385
695
|
reuseConnection: true
|
|
386
696
|
};
|
|
387
697
|
const pool = await this.driver.pool(poolConfig);
|
|
388
|
-
|
|
698
|
+
connection = await pool.connect();
|
|
699
|
+
} else {
|
|
700
|
+
connection = await this.driver.connect(
|
|
701
|
+
this._getConnectionString(connectionConfig)
|
|
702
|
+
);
|
|
389
703
|
}
|
|
390
|
-
return
|
|
391
|
-
this._getConnectionString(connectionConfig)
|
|
392
|
-
);
|
|
704
|
+
return connection;
|
|
393
705
|
}
|
|
394
706
|
// Used to explicitly close a connection, called internally by the pool manager
|
|
395
707
|
// when a connection times out or the pool is shutdown.
|
|
@@ -405,20 +717,61 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
405
717
|
const value = connectionStringParams[key];
|
|
406
718
|
return `${result}${key}=${value};`;
|
|
407
719
|
}, "");
|
|
408
|
-
return
|
|
720
|
+
return `DRIVER=${connectionConfig.driver};SYSTEM=${connectionConfig.host};HOSTNAME=${connectionConfig.host};PORT=${connectionConfig.port};DATABASE=${connectionConfig.database};UID=${connectionConfig.user};PWD=${connectionConfig.password};` + connectionStringExtension;
|
|
409
721
|
}
|
|
410
722
|
// Runs the query on the specified connection, providing the bindings
|
|
411
723
|
async _query(connection, obj) {
|
|
412
724
|
const queryObject = this.normalizeQueryObject(obj);
|
|
413
725
|
const method = this.determineQueryMethod(queryObject);
|
|
414
726
|
queryObject.sqlMethod = method;
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
727
|
+
if (import_node_process.default.env.DEBUG === "true" && queryObject.sql && (queryObject.sql.toLowerCase().includes("create table") || queryObject.sql.toLowerCase().includes("knex_migrations"))) {
|
|
728
|
+
this.printDebug(
|
|
729
|
+
`Executing ${method} query: ${queryObject.sql.substring(0, 200)}...`
|
|
730
|
+
);
|
|
731
|
+
if (queryObject.bindings?.length) {
|
|
732
|
+
this.printDebug(`Bindings: ${JSON.stringify(queryObject.bindings)}`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
const startTime = Date.now();
|
|
737
|
+
if (this.isSelectMethod(method)) {
|
|
738
|
+
await this.executeSelectQuery(connection, queryObject);
|
|
739
|
+
} else {
|
|
740
|
+
await this.executeStatementQuery(connection, queryObject);
|
|
741
|
+
}
|
|
742
|
+
const endTime = Date.now();
|
|
743
|
+
if (import_node_process.default.env.DEBUG === "true" && queryObject.sql && (queryObject.sql.toLowerCase().includes("create table") || queryObject.sql.toLowerCase().includes("knex_migrations"))) {
|
|
744
|
+
this.printDebug(`${method} completed in ${endTime - startTime}ms`);
|
|
745
|
+
}
|
|
746
|
+
this.printDebug(`Query completed: ${method} (${endTime - startTime}ms)`);
|
|
747
|
+
return queryObject;
|
|
748
|
+
} catch (error) {
|
|
749
|
+
if (this.isConnectionError(error)) {
|
|
750
|
+
this.printError(
|
|
751
|
+
`Connection error during ${method} query: ${error.message}`
|
|
752
|
+
);
|
|
753
|
+
if (queryObject.sql?.toLowerCase().includes("systables")) {
|
|
754
|
+
this.printDebug("Retrying hasTable query due to connection error...");
|
|
755
|
+
try {
|
|
756
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
757
|
+
if (this.isSelectMethod(method)) {
|
|
758
|
+
await this.executeSelectQuery(connection, queryObject);
|
|
759
|
+
} else {
|
|
760
|
+
await this.executeStatementQuery(connection, queryObject);
|
|
761
|
+
}
|
|
762
|
+
return queryObject;
|
|
763
|
+
} catch (retryError) {
|
|
764
|
+
this.printError(`Retry failed: ${retryError.message}`);
|
|
765
|
+
queryObject.response = { rows: [], rowCount: 0 };
|
|
766
|
+
return queryObject;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
throw new Error(
|
|
770
|
+
`Connection closed during ${method} operation - IBM i DB2 DDL operations cause implicit commits`
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
throw error;
|
|
419
774
|
}
|
|
420
|
-
this.printDebug(queryObject);
|
|
421
|
-
return queryObject;
|
|
422
775
|
}
|
|
423
776
|
normalizeQueryObject(obj) {
|
|
424
777
|
if (!obj || typeof obj === "string") {
|
|
@@ -433,14 +786,18 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
433
786
|
return method === "select" || method === "first" || method === "pluck";
|
|
434
787
|
}
|
|
435
788
|
async executeSelectQuery(connection, obj) {
|
|
436
|
-
const rows = await connection.query(
|
|
789
|
+
const rows = await connection.query(
|
|
790
|
+
obj.sql,
|
|
791
|
+
obj.bindings
|
|
792
|
+
);
|
|
437
793
|
if (rows) {
|
|
438
794
|
obj.response = { rows, rowCount: rows.length };
|
|
439
795
|
}
|
|
440
796
|
}
|
|
441
797
|
async executeStatementQuery(connection, obj) {
|
|
798
|
+
let statement;
|
|
442
799
|
try {
|
|
443
|
-
|
|
800
|
+
statement = await connection.createStatement();
|
|
444
801
|
await statement.prepare(obj.sql);
|
|
445
802
|
if (obj.bindings) {
|
|
446
803
|
await statement.bind(obj.bindings);
|
|
@@ -449,20 +806,58 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
449
806
|
this.printDebug(String(result));
|
|
450
807
|
obj.response = this.formatStatementResponse(result);
|
|
451
808
|
} catch (err) {
|
|
452
|
-
|
|
809
|
+
const sql = (obj.sql || "").toLowerCase();
|
|
810
|
+
const isDml = obj.sqlMethod === "update" /* UPDATE */ || sql.includes(" update ") || sql.startsWith("update") || obj.sqlMethod === "del" /* DELETE */ || sql.includes(" delete ") || sql.startsWith("delete");
|
|
811
|
+
const odbcErrors = err?.odbcErrors;
|
|
812
|
+
const isEmptyOdbcError = Array.isArray(odbcErrors) && odbcErrors.length === 0;
|
|
813
|
+
const hasNoDataState = Array.isArray(odbcErrors) ? odbcErrors.some(
|
|
814
|
+
(e) => String(e?.state || e?.SQLSTATE || "").toUpperCase() === "02000"
|
|
815
|
+
) : false;
|
|
816
|
+
if (isDml && (isEmptyOdbcError || hasNoDataState || this.isNoDataError(err))) {
|
|
817
|
+
this.printWarn(
|
|
818
|
+
`ODBC signaled no-data for ${sql.includes("update") ? "UPDATE" : "DELETE"}; treating as 0 rows affected`
|
|
819
|
+
);
|
|
820
|
+
obj.response = { rows: [], rowCount: 0 };
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
this.printError(this.safeStringify(err));
|
|
824
|
+
throw err;
|
|
825
|
+
} finally {
|
|
826
|
+
if (statement && typeof statement.close === "function") {
|
|
827
|
+
try {
|
|
828
|
+
await statement.close();
|
|
829
|
+
} catch (closeErr) {
|
|
830
|
+
this.printDebug(
|
|
831
|
+
`Error closing statement: ${this.safeStringify(closeErr, 2)}`
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
453
835
|
}
|
|
454
836
|
}
|
|
837
|
+
isNoDataError(error) {
|
|
838
|
+
if (!error) return false;
|
|
839
|
+
const msg = String(error?.message || error || "").toLowerCase();
|
|
840
|
+
return msg.includes("02000") || msg.includes("no data") || msg.includes("no rows") || msg.includes("0 rows");
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Format statement response from ODBC driver
|
|
844
|
+
* Handles special case for IDENTITY_VAL_LOCAL() function
|
|
845
|
+
*/
|
|
455
846
|
formatStatementResponse(result) {
|
|
456
|
-
|
|
847
|
+
const isIdentityQuery = result.statement?.includes("IDENTITY_VAL_LOCAL()");
|
|
848
|
+
if (isIdentityQuery && result.columns?.length > 0) {
|
|
457
849
|
return {
|
|
458
850
|
rows: result.map(
|
|
459
|
-
(row) =>
|
|
851
|
+
(row) => row[result.columns[0].name]
|
|
460
852
|
),
|
|
461
853
|
rowCount: result.count
|
|
462
854
|
};
|
|
463
|
-
} else {
|
|
464
|
-
return { rows: result, rowCount: result.count };
|
|
465
855
|
}
|
|
856
|
+
const rowCount = typeof result?.count === "number" ? result.count : 0;
|
|
857
|
+
return {
|
|
858
|
+
rows: result,
|
|
859
|
+
rowCount
|
|
860
|
+
};
|
|
466
861
|
}
|
|
467
862
|
async _stream(connection, obj, stream, options) {
|
|
468
863
|
if (!obj.sql) throw new Error("A query is required to stream results");
|
|
@@ -478,7 +873,9 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
478
873
|
},
|
|
479
874
|
(error, cursor) => {
|
|
480
875
|
if (error) {
|
|
481
|
-
this.printError(
|
|
876
|
+
this.printError(this.safeStringify(error, 2));
|
|
877
|
+
stream.emit("error", error);
|
|
878
|
+
reject(error);
|
|
482
879
|
return;
|
|
483
880
|
}
|
|
484
881
|
const readableStream = this._createCursorStream(cursor);
|
|
@@ -498,7 +895,7 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
498
895
|
read() {
|
|
499
896
|
cursor.fetch((error, result) => {
|
|
500
897
|
if (error) {
|
|
501
|
-
parentThis.printError(
|
|
898
|
+
parentThis.printError(parentThis.safeStringify(error, 2));
|
|
502
899
|
}
|
|
503
900
|
if (!cursor.noData) {
|
|
504
901
|
this.push(result);
|
|
@@ -532,26 +929,49 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
532
929
|
queryCompiler(builder, bindings) {
|
|
533
930
|
return new ibmi_querycompiler_default(this, builder, bindings);
|
|
534
931
|
}
|
|
932
|
+
// Create IBM i-specific migration runner that bypasses Knex's problematic locking system
|
|
933
|
+
createMigrationRunner(config) {
|
|
934
|
+
const knexInstance = this.context || this;
|
|
935
|
+
return createIBMiMigrationRunner(knexInstance, config);
|
|
936
|
+
}
|
|
535
937
|
processResponse(obj, runner) {
|
|
536
938
|
if (obj === null) return null;
|
|
537
|
-
const validationResult = this.validateResponse(obj);
|
|
538
|
-
if (validationResult !== null) return validationResult;
|
|
539
939
|
const { response } = obj;
|
|
540
940
|
if (obj.output) {
|
|
541
|
-
|
|
941
|
+
try {
|
|
942
|
+
const result = obj.output(runner, response);
|
|
943
|
+
return result;
|
|
944
|
+
} catch (error) {
|
|
945
|
+
this.printError(
|
|
946
|
+
`Custom output function failed: ${error.message || error}`
|
|
947
|
+
);
|
|
948
|
+
if (this.isConnectionError(error)) {
|
|
949
|
+
throw new Error(
|
|
950
|
+
"Connection closed during query processing - consider using migrations.disableTransactions: true for DDL operations"
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
throw error;
|
|
954
|
+
}
|
|
542
955
|
}
|
|
956
|
+
const validationResult = this.validateResponse(obj);
|
|
957
|
+
if (validationResult !== null) return validationResult;
|
|
543
958
|
return this.processSqlMethod(obj);
|
|
544
959
|
}
|
|
545
960
|
validateResponse(obj) {
|
|
546
961
|
if (!obj.response) {
|
|
547
|
-
this.printDebug("response undefined" + obj);
|
|
548
|
-
return
|
|
962
|
+
this.printDebug("response undefined" + JSON.stringify(obj));
|
|
963
|
+
return null;
|
|
549
964
|
}
|
|
550
965
|
if (!obj.response.rows) {
|
|
551
|
-
|
|
966
|
+
this.printError("rows undefined" + JSON.stringify(obj));
|
|
967
|
+
return null;
|
|
552
968
|
}
|
|
553
969
|
return null;
|
|
554
970
|
}
|
|
971
|
+
isConnectionError(error) {
|
|
972
|
+
const errorMessage = (error.message || error.toString || error).toLowerCase();
|
|
973
|
+
return errorMessage.includes("connection") && (errorMessage.includes("closed") || errorMessage.includes("invalid") || errorMessage.includes("terminated") || errorMessage.includes("not connected"));
|
|
974
|
+
}
|
|
555
975
|
processSqlMethod(obj) {
|
|
556
976
|
const { rows, rowCount } = obj.response;
|
|
557
977
|
switch (obj.sqlMethod) {
|
|
@@ -566,7 +986,7 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
566
986
|
case "del" /* DELETE */:
|
|
567
987
|
case "delete" /* DELETE_ALT */:
|
|
568
988
|
case "update" /* UPDATE */:
|
|
569
|
-
return obj.select ? rows : rowCount;
|
|
989
|
+
return obj.select ? rows : rowCount ?? 0;
|
|
570
990
|
case "counter" /* COUNTER */:
|
|
571
991
|
return rowCount;
|
|
572
992
|
default:
|
|
@@ -575,8 +995,11 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
575
995
|
}
|
|
576
996
|
};
|
|
577
997
|
var DB2Dialect = DB2Client;
|
|
578
|
-
var
|
|
998
|
+
var index_default = DB2Client;
|
|
579
999
|
// Annotate the CommonJS export names for ESM import in node:
|
|
580
1000
|
0 && (module.exports = {
|
|
581
|
-
DB2Dialect
|
|
1001
|
+
DB2Dialect,
|
|
1002
|
+
IBMiMigrationRunner,
|
|
1003
|
+
createIBMiMigrationRunner
|
|
582
1004
|
});
|
|
1005
|
+
//# sourceMappingURL=index.js.map
|