@bdkinc/knex-ibmi 0.3.21 → 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/LICENSE +0 -0
- package/README.md +250 -127
- package/dist/cli.cjs +531 -0
- package/dist/index.d.mts +137 -0
- package/dist/index.d.ts +77 -53
- package/dist/index.js +654 -199
- 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,69 +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 (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
sql += returningSql + this._emptyInsertValue;
|
|
216
|
-
} else {
|
|
217
|
-
return "";
|
|
218
|
-
}
|
|
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;
|
|
219
302
|
}
|
|
220
|
-
return
|
|
303
|
+
return "";
|
|
221
304
|
}
|
|
222
305
|
_prepInsert(data) {
|
|
223
|
-
if (typeof data === "object" && data
|
|
306
|
+
if (typeof data === "object" && data?.migration_time) {
|
|
224
307
|
const parsed = new Date(data.migration_time);
|
|
225
|
-
|
|
308
|
+
if (!isNaN(parsed.getTime())) {
|
|
309
|
+
data.migration_time = this.formatTimestampLocal(parsed);
|
|
310
|
+
}
|
|
226
311
|
}
|
|
227
312
|
const isRaw = (0, import_wrappingFormatter.rawOrFn)(
|
|
228
313
|
data,
|
|
@@ -234,36 +319,23 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
234
319
|
if (isRaw) {
|
|
235
320
|
return isRaw;
|
|
236
321
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
data = data ? [data] : [];
|
|
322
|
+
const dataArray = Array.isArray(data) ? data : data ? [data] : [];
|
|
323
|
+
if (dataArray.length === 0) {
|
|
324
|
+
return { columns: [], values: [] };
|
|
241
325
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
if (i === 0) {
|
|
248
|
-
columns = Object.keys(data[i]).sort();
|
|
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));
|
|
249
330
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (idx === -1) {
|
|
257
|
-
columns = columns.concat(key).sort();
|
|
258
|
-
idx = columns.indexOf(key);
|
|
259
|
-
let k = -1;
|
|
260
|
-
while (++k < values.length) {
|
|
261
|
-
values[k].splice(idx, 0, void 0);
|
|
262
|
-
}
|
|
263
|
-
row.splice(idx, 0, void 0);
|
|
264
|
-
}
|
|
265
|
-
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;
|
|
266
337
|
}
|
|
338
|
+
const row = columns.map((column) => item[column] ?? void 0);
|
|
267
339
|
values.push(row);
|
|
268
340
|
}
|
|
269
341
|
return {
|
|
@@ -278,16 +350,32 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
278
350
|
const order = this.order();
|
|
279
351
|
const limit = this.limit();
|
|
280
352
|
const { returning } = this.single;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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(" ");
|
|
286
362
|
if (returning) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
*/
|
|
291
379
|
_returning(method, value, withTrigger) {
|
|
292
380
|
switch (method) {
|
|
293
381
|
case "update":
|
|
@@ -297,6 +385,8 @@ var IBMiQueryCompiler = class extends import_querycompiler.default {
|
|
|
297
385
|
return value ? `${withTrigger ? " into #out" : ""}` : "";
|
|
298
386
|
case "rowcount":
|
|
299
387
|
return value ? "select @@rowcount" : "";
|
|
388
|
+
default:
|
|
389
|
+
return "";
|
|
300
390
|
}
|
|
301
391
|
}
|
|
302
392
|
columnizeWithPrefix(prefix, target) {
|
|
@@ -314,8 +404,210 @@ var ibmi_querycompiler_default = IBMiQueryCompiler;
|
|
|
314
404
|
|
|
315
405
|
// src/index.ts
|
|
316
406
|
var import_node_stream = require("stream");
|
|
317
|
-
|
|
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 {
|
|
318
609
|
constructor(config) {
|
|
610
|
+
console.log("\u{1F3AF} DB2Client constructor called!");
|
|
319
611
|
super(config);
|
|
320
612
|
this.driverName = "odbc";
|
|
321
613
|
if (this.dialect && !this.config.client) {
|
|
@@ -343,10 +635,24 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
343
635
|
this.valueForUndefined = null;
|
|
344
636
|
}
|
|
345
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
|
+
}
|
|
346
649
|
_driver() {
|
|
347
650
|
return import_odbc.default;
|
|
348
651
|
}
|
|
349
652
|
wrapIdentifierImpl(value) {
|
|
653
|
+
if (value.includes("KNEX_MIGRATIONS") || value.includes("knex_migrations")) {
|
|
654
|
+
return value.toUpperCase();
|
|
655
|
+
}
|
|
350
656
|
return value;
|
|
351
657
|
}
|
|
352
658
|
printDebug(message) {
|
|
@@ -379,6 +685,7 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
379
685
|
this.printDebug(
|
|
380
686
|
"connection config: " + this._getConnectionString(connectionConfig)
|
|
381
687
|
);
|
|
688
|
+
let connection;
|
|
382
689
|
if (this.config?.pool) {
|
|
383
690
|
const poolConfig = {
|
|
384
691
|
connectionString: this._getConnectionString(connectionConfig),
|
|
@@ -388,11 +695,13 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
388
695
|
reuseConnection: true
|
|
389
696
|
};
|
|
390
697
|
const pool = await this.driver.pool(poolConfig);
|
|
391
|
-
|
|
698
|
+
connection = await pool.connect();
|
|
699
|
+
} else {
|
|
700
|
+
connection = await this.driver.connect(
|
|
701
|
+
this._getConnectionString(connectionConfig)
|
|
702
|
+
);
|
|
392
703
|
}
|
|
393
|
-
return
|
|
394
|
-
this._getConnectionString(connectionConfig)
|
|
395
|
-
);
|
|
704
|
+
return connection;
|
|
396
705
|
}
|
|
397
706
|
// Used to explicitly close a connection, called internally by the pool manager
|
|
398
707
|
// when a connection times out or the pool is shutdown.
|
|
@@ -408,46 +717,148 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
408
717
|
const value = connectionStringParams[key];
|
|
409
718
|
return `${result}${key}=${value};`;
|
|
410
719
|
}, "");
|
|
411
|
-
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;
|
|
412
721
|
}
|
|
413
722
|
// Runs the query on the specified connection, providing the bindings
|
|
414
723
|
async _query(connection, obj) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
|
|
724
|
+
const queryObject = this.normalizeQueryObject(obj);
|
|
725
|
+
const method = this.determineQueryMethod(queryObject);
|
|
726
|
+
queryObject.sqlMethod = method;
|
|
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)}`);
|
|
424
733
|
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
+
}
|
|
443
768
|
}
|
|
444
|
-
|
|
445
|
-
|
|
769
|
+
throw new Error(
|
|
770
|
+
`Connection closed during ${method} operation - IBM i DB2 DDL operations cause implicit commits`
|
|
771
|
+
);
|
|
446
772
|
}
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
normalizeQueryObject(obj) {
|
|
777
|
+
if (!obj || typeof obj === "string") {
|
|
778
|
+
return { sql: obj };
|
|
447
779
|
}
|
|
448
|
-
this.printDebug(obj);
|
|
449
780
|
return obj;
|
|
450
781
|
}
|
|
782
|
+
determineQueryMethod(obj) {
|
|
783
|
+
return (obj.hasOwnProperty("method") && obj.method !== "raw" ? obj.method : obj.sql.split(" ")[0]).toLowerCase();
|
|
784
|
+
}
|
|
785
|
+
isSelectMethod(method) {
|
|
786
|
+
return method === "select" || method === "first" || method === "pluck";
|
|
787
|
+
}
|
|
788
|
+
async executeSelectQuery(connection, obj) {
|
|
789
|
+
const rows = await connection.query(
|
|
790
|
+
obj.sql,
|
|
791
|
+
obj.bindings
|
|
792
|
+
);
|
|
793
|
+
if (rows) {
|
|
794
|
+
obj.response = { rows, rowCount: rows.length };
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
async executeStatementQuery(connection, obj) {
|
|
798
|
+
let statement;
|
|
799
|
+
try {
|
|
800
|
+
statement = await connection.createStatement();
|
|
801
|
+
await statement.prepare(obj.sql);
|
|
802
|
+
if (obj.bindings) {
|
|
803
|
+
await statement.bind(obj.bindings);
|
|
804
|
+
}
|
|
805
|
+
const result = await statement.execute();
|
|
806
|
+
this.printDebug(String(result));
|
|
807
|
+
obj.response = this.formatStatementResponse(result);
|
|
808
|
+
} catch (err) {
|
|
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
|
+
}
|
|
835
|
+
}
|
|
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
|
+
*/
|
|
846
|
+
formatStatementResponse(result) {
|
|
847
|
+
const isIdentityQuery = result.statement?.includes("IDENTITY_VAL_LOCAL()");
|
|
848
|
+
if (isIdentityQuery && result.columns?.length > 0) {
|
|
849
|
+
return {
|
|
850
|
+
rows: result.map(
|
|
851
|
+
(row) => row[result.columns[0].name]
|
|
852
|
+
),
|
|
853
|
+
rowCount: result.count
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
const rowCount = typeof result?.count === "number" ? result.count : 0;
|
|
857
|
+
return {
|
|
858
|
+
rows: result,
|
|
859
|
+
rowCount
|
|
860
|
+
};
|
|
861
|
+
}
|
|
451
862
|
async _stream(connection, obj, stream, options) {
|
|
452
863
|
if (!obj.sql) throw new Error("A query is required to stream results");
|
|
453
864
|
return new Promise((resolve, reject) => {
|
|
@@ -462,30 +873,12 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
462
873
|
},
|
|
463
874
|
(error, cursor) => {
|
|
464
875
|
if (error) {
|
|
465
|
-
this.printError(
|
|
876
|
+
this.printError(this.safeStringify(error, 2));
|
|
877
|
+
stream.emit("error", error);
|
|
878
|
+
reject(error);
|
|
879
|
+
return;
|
|
466
880
|
}
|
|
467
|
-
const readableStream =
|
|
468
|
-
objectMode: true,
|
|
469
|
-
read() {
|
|
470
|
-
cursor.fetch((error2, result) => {
|
|
471
|
-
if (error2) {
|
|
472
|
-
console.log(JSON.stringify(error2, null, 2));
|
|
473
|
-
}
|
|
474
|
-
if (!cursor.noData) {
|
|
475
|
-
this.push(result);
|
|
476
|
-
} else {
|
|
477
|
-
cursor.close((error3) => {
|
|
478
|
-
if (error3) {
|
|
479
|
-
console.log(JSON.stringify(error3, null, 2));
|
|
480
|
-
}
|
|
481
|
-
if (result) {
|
|
482
|
-
this.push(result);
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
});
|
|
881
|
+
const readableStream = this._createCursorStream(cursor);
|
|
489
882
|
readableStream.on("error", (err) => {
|
|
490
883
|
reject(err);
|
|
491
884
|
stream.emit("error", err);
|
|
@@ -495,6 +888,32 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
495
888
|
);
|
|
496
889
|
});
|
|
497
890
|
}
|
|
891
|
+
_createCursorStream(cursor) {
|
|
892
|
+
const parentThis = this;
|
|
893
|
+
return new import_node_stream.Readable({
|
|
894
|
+
objectMode: true,
|
|
895
|
+
read() {
|
|
896
|
+
cursor.fetch((error, result) => {
|
|
897
|
+
if (error) {
|
|
898
|
+
parentThis.printError(parentThis.safeStringify(error, 2));
|
|
899
|
+
}
|
|
900
|
+
if (!cursor.noData) {
|
|
901
|
+
this.push(result);
|
|
902
|
+
} else {
|
|
903
|
+
cursor.close((closeError) => {
|
|
904
|
+
if (closeError) {
|
|
905
|
+
parentThis.printError(JSON.stringify(closeError, null, 2));
|
|
906
|
+
}
|
|
907
|
+
if (result) {
|
|
908
|
+
this.push(result);
|
|
909
|
+
}
|
|
910
|
+
this.push(null);
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
}
|
|
498
917
|
transaction(container, config, outerTx) {
|
|
499
918
|
return new ibmi_transaction_default(this, container, config, outerTx);
|
|
500
919
|
}
|
|
@@ -510,32 +929,65 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
510
929
|
queryCompiler(builder, bindings) {
|
|
511
930
|
return new ibmi_querycompiler_default(this, builder, bindings);
|
|
512
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
|
+
}
|
|
513
937
|
processResponse(obj, runner) {
|
|
514
938
|
if (obj === null) return null;
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
939
|
+
const { response } = obj;
|
|
940
|
+
if (obj.output) {
|
|
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
|
+
}
|
|
519
955
|
}
|
|
520
|
-
const
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
956
|
+
const validationResult = this.validateResponse(obj);
|
|
957
|
+
if (validationResult !== null) return validationResult;
|
|
958
|
+
return this.processSqlMethod(obj);
|
|
959
|
+
}
|
|
960
|
+
validateResponse(obj) {
|
|
961
|
+
if (!obj.response) {
|
|
962
|
+
this.printDebug("response undefined" + JSON.stringify(obj));
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
if (!obj.response.rows) {
|
|
966
|
+
this.printError("rows undefined" + JSON.stringify(obj));
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
return null;
|
|
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
|
+
}
|
|
975
|
+
processSqlMethod(obj) {
|
|
976
|
+
const { rows, rowCount } = obj.response;
|
|
977
|
+
switch (obj.sqlMethod) {
|
|
978
|
+
case "select" /* SELECT */:
|
|
524
979
|
return rows;
|
|
525
|
-
case "pluck"
|
|
980
|
+
case "pluck" /* PLUCK */:
|
|
526
981
|
return rows.map(obj.pluck);
|
|
527
|
-
case "first"
|
|
982
|
+
case "first" /* FIRST */:
|
|
528
983
|
return rows[0];
|
|
529
|
-
case "insert"
|
|
984
|
+
case "insert" /* INSERT */:
|
|
530
985
|
return rows;
|
|
531
|
-
case "del"
|
|
532
|
-
case "delete"
|
|
533
|
-
case "update"
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
537
|
-
return rowCount;
|
|
538
|
-
case "counter":
|
|
986
|
+
case "del" /* DELETE */:
|
|
987
|
+
case "delete" /* DELETE_ALT */:
|
|
988
|
+
case "update" /* UPDATE */:
|
|
989
|
+
return obj.select ? rows : rowCount ?? 0;
|
|
990
|
+
case "counter" /* COUNTER */:
|
|
539
991
|
return rowCount;
|
|
540
992
|
default:
|
|
541
993
|
return rows;
|
|
@@ -543,8 +995,11 @@ var DB2Client = class extends import_knex.knex.Client {
|
|
|
543
995
|
}
|
|
544
996
|
};
|
|
545
997
|
var DB2Dialect = DB2Client;
|
|
546
|
-
var
|
|
998
|
+
var index_default = DB2Client;
|
|
547
999
|
// Annotate the CommonJS export names for ESM import in node:
|
|
548
1000
|
0 && (module.exports = {
|
|
549
|
-
DB2Dialect
|
|
1001
|
+
DB2Dialect,
|
|
1002
|
+
IBMiMigrationRunner,
|
|
1003
|
+
createIBMiMigrationRunner
|
|
550
1004
|
});
|
|
1005
|
+
//# sourceMappingURL=index.js.map
|