@bdkinc/knex-ibmi 0.0.2 → 0.0.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 +20 -9
- package/dist/index.js +466 -138
- package/dist/index.mjs +434 -103
- package/package.json +3 -1
- package/src/execution/ibmi-transaction.ts +25 -0
- package/src/index.ts +197 -129
- package/src/query/ibmi-querycompiler.ts +132 -0
- package/src/schema/ibmi-columncompiler.ts +34 -0
- package/src/schema/ibmi-compiler.ts +50 -0
- package/src/schema/ibmi-tablecompiler.ts +109 -0
package/src/index.ts
CHANGED
|
@@ -1,129 +1,197 @@
|
|
|
1
|
-
import * as process from "process";
|
|
2
|
-
import { Connection } from "odbc";
|
|
3
|
-
import knex from "knex";
|
|
4
|
-
import * as odbc from "odbc";
|
|
5
|
-
import * as console from "console";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
import * as process from "process";
|
|
2
|
+
import { Connection } from "odbc";
|
|
3
|
+
import knex from "knex";
|
|
4
|
+
import * as odbc from "odbc";
|
|
5
|
+
import * as console from "console";
|
|
6
|
+
import SchemaCompiler from "./schema/ibmi-compiler";
|
|
7
|
+
import TableCompiler from "./schema/ibmi-tablecompiler";
|
|
8
|
+
import ColumnCompiler from "./schema/ibmi-columncompiler";
|
|
9
|
+
import Transaction from "./execution/ibmi-transaction";
|
|
10
|
+
import QueryCompiler from "./query/ibmi-querycompiler";
|
|
11
|
+
|
|
12
|
+
class DB2Client extends knex.Client {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super(config);
|
|
15
|
+
|
|
16
|
+
this.driverName = "odbc";
|
|
17
|
+
|
|
18
|
+
if (this.dialect && !this.config.client) {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
this.logger.warn(
|
|
21
|
+
`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.`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const dbClient = this.config.client || this.dialect;
|
|
26
|
+
if (!dbClient) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`knex: Required configuration option 'client' is missing.`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (config.version) {
|
|
33
|
+
this.version = config.version;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (this.driverName && config.connection) {
|
|
37
|
+
this.initializeDriver();
|
|
38
|
+
if (!config.pool || (config.pool && config.pool.max !== 0)) {
|
|
39
|
+
this.initializePool(config);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.valueForUndefined = this.raw("DEFAULT");
|
|
43
|
+
if (config.useNullAsDefault) {
|
|
44
|
+
this.valueForUndefined = null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_driver() {
|
|
49
|
+
return odbc;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
wrapIdentifierImpl(value: any) {
|
|
53
|
+
// override default wrapper ("). we don't want to use it since
|
|
54
|
+
// it makes identifiers case-sensitive in DB2
|
|
55
|
+
if (value.includes("knex_migrations")) {
|
|
56
|
+
return value.toUpperCase();
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
printDebug(message: string) {
|
|
62
|
+
if (process.env.DEBUG === "true") {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
this.logger.debug(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Get a raw connection, called by the pool manager whenever a new
|
|
69
|
+
// connection needs to be added to the pool.
|
|
70
|
+
async acquireRawConnection() {
|
|
71
|
+
this.printDebug("acquiring raw connection");
|
|
72
|
+
const connectionConfig = this.config.connection;
|
|
73
|
+
console.log(this._getConnectionString(connectionConfig));
|
|
74
|
+
return await this.driver.pool(this._getConnectionString(connectionConfig));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Used to explicitly close a connection, called internally by the pool manager
|
|
78
|
+
// when a connection times out or the pool is shutdown.
|
|
79
|
+
async destroyRawConnection(connection: Connection) {
|
|
80
|
+
console.log("destroy connection");
|
|
81
|
+
return await connection.close();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_getConnectionString(connectionConfig) {
|
|
85
|
+
const connectionStringParams =
|
|
86
|
+
connectionConfig.connectionStringParams || {};
|
|
87
|
+
const connectionStringExtension = Object.keys(
|
|
88
|
+
connectionStringParams,
|
|
89
|
+
).reduce((result, key) => {
|
|
90
|
+
const value = connectionStringParams[key];
|
|
91
|
+
return `${result}${key}=${value};`;
|
|
92
|
+
}, "");
|
|
93
|
+
|
|
94
|
+
return `${
|
|
95
|
+
`DRIVER=${connectionConfig.driver};SYSTEM=${connectionConfig.host};HOSTNAME=${connectionConfig.host};` +
|
|
96
|
+
`PORT=${connectionConfig.port};DATABASE=${connectionConfig.database};` +
|
|
97
|
+
`UID=${connectionConfig.user};PWD=${connectionConfig.password};`
|
|
98
|
+
}${connectionStringExtension}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Runs the query on the specified connection, providing the bindings
|
|
102
|
+
// and any other necessary prep work.
|
|
103
|
+
async _query(pool: any, obj: any) {
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
// TODO: verify correctness
|
|
106
|
+
if (!obj || typeof obj == "string") obj = { sql: obj };
|
|
107
|
+
const method = (
|
|
108
|
+
obj.hasOwnProperty("method") && obj.method !== "raw"
|
|
109
|
+
? obj.method
|
|
110
|
+
: obj.sql.split(" ")[0]
|
|
111
|
+
).toLowerCase();
|
|
112
|
+
obj.sqlMethod = method;
|
|
113
|
+
|
|
114
|
+
// Different functions are used since query() doesn't return # of rows affected,
|
|
115
|
+
// which is needed for queries that modify the database
|
|
116
|
+
|
|
117
|
+
if (method === "select" || method === "first" || method === "pluck") {
|
|
118
|
+
const rows: any = await pool.query(obj.sql, obj.bindings);
|
|
119
|
+
if (rows) {
|
|
120
|
+
obj.response = { rows, rowCount: rows.length };
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
try {
|
|
124
|
+
const connection = await pool.connect();
|
|
125
|
+
const statement = await connection.createStatement();
|
|
126
|
+
await statement.prepare(obj.sql);
|
|
127
|
+
if (obj.bindings) {
|
|
128
|
+
await statement.bind(obj.bindings);
|
|
129
|
+
}
|
|
130
|
+
const result = await statement.execute();
|
|
131
|
+
obj.response = { rows: [result.count], rowCount: result.count };
|
|
132
|
+
} catch (err: any) {
|
|
133
|
+
console.error(err);
|
|
134
|
+
throw new Error(err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
console.log({ obj });
|
|
138
|
+
|
|
139
|
+
return obj;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
transaction() {
|
|
143
|
+
// @ts-ignore
|
|
144
|
+
return new Transaction(this, ...arguments);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
schemaCompiler() {
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
return new SchemaCompiler(this, ...arguments);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
tableCompiler() {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
return new TableCompiler(this, ...arguments);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
columnCompiler() {
|
|
158
|
+
// @ts-ignore
|
|
159
|
+
return new ColumnCompiler(this, ...arguments);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
queryCompiler() {
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
return new QueryCompiler(this, ...arguments);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
processResponse(obj: any, runner: any) {
|
|
168
|
+
// TODO: verify correctness
|
|
169
|
+
if (obj === null) return null;
|
|
170
|
+
|
|
171
|
+
const resp = obj.response;
|
|
172
|
+
const method = obj.sqlMethod;
|
|
173
|
+
const { rows } = resp;
|
|
174
|
+
|
|
175
|
+
if (obj.output) return obj.output.call(runner, resp);
|
|
176
|
+
|
|
177
|
+
switch (method) {
|
|
178
|
+
case "select":
|
|
179
|
+
case "pluck":
|
|
180
|
+
case "first": {
|
|
181
|
+
if (method === "pluck") return rows.map(obj.pluck);
|
|
182
|
+
return method === "first" ? rows[0] : rows;
|
|
183
|
+
}
|
|
184
|
+
case "insert":
|
|
185
|
+
case "del":
|
|
186
|
+
case "delete":
|
|
187
|
+
case "update":
|
|
188
|
+
case "counter":
|
|
189
|
+
return resp.rowCount;
|
|
190
|
+
default:
|
|
191
|
+
return resp;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const DB2Dialect = DB2Client;
|
|
197
|
+
export default DB2Client;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import QueryCompiler from "knex/lib/query/querycompiler";
|
|
2
|
+
import has from "lodash/has";
|
|
3
|
+
import isEmpty from "lodash/isEmpty";
|
|
4
|
+
import omitBy from "lodash/omitBy";
|
|
5
|
+
import isObject from "lodash/isObject";
|
|
6
|
+
import {
|
|
7
|
+
wrap as wrap_,
|
|
8
|
+
rawOrFn as rawOrFn_,
|
|
9
|
+
} from "knex/lib/formatter/wrappingFormatter";
|
|
10
|
+
import { format, parseISO } from "date-fns";
|
|
11
|
+
import * as console from "console";
|
|
12
|
+
|
|
13
|
+
class IBMiQueryCompiler extends QueryCompiler {
|
|
14
|
+
_prepInsert(data) {
|
|
15
|
+
if (isObject(data)) {
|
|
16
|
+
console.log("data is object", data);
|
|
17
|
+
if (data.hasOwnProperty("migration_time")) {
|
|
18
|
+
console.log("data has migration_time", data.migration_time);
|
|
19
|
+
const parsed = new Date(data.migration_time);
|
|
20
|
+
console.log(parsed);
|
|
21
|
+
data.migration_time = format(parsed, "yyyy-MM-dd HH:mm:ss");
|
|
22
|
+
console.log(data.migration_time);
|
|
23
|
+
}
|
|
24
|
+
console.log("data date after change", data);
|
|
25
|
+
}
|
|
26
|
+
const isRaw = rawOrFn_(
|
|
27
|
+
data,
|
|
28
|
+
undefined,
|
|
29
|
+
this.builder,
|
|
30
|
+
this.client,
|
|
31
|
+
this.bindingsHolder,
|
|
32
|
+
);
|
|
33
|
+
if (isRaw) return isRaw;
|
|
34
|
+
let columns: any[] = [];
|
|
35
|
+
const values: any[] = [];
|
|
36
|
+
if (!Array.isArray(data)) data = data ? [data] : [];
|
|
37
|
+
let i = -1;
|
|
38
|
+
while (++i < data.length) {
|
|
39
|
+
if (data[i] == null) break;
|
|
40
|
+
if (i === 0) columns = Object.keys(data[i]).sort();
|
|
41
|
+
const row = new Array(columns.length);
|
|
42
|
+
const keys = Object.keys(data[i]);
|
|
43
|
+
let j = -1;
|
|
44
|
+
while (++j < keys.length) {
|
|
45
|
+
const key = keys[j];
|
|
46
|
+
let idx = columns.indexOf(key);
|
|
47
|
+
if (idx === -1) {
|
|
48
|
+
columns = columns.concat(key).sort();
|
|
49
|
+
idx = columns.indexOf(key);
|
|
50
|
+
let k = -1;
|
|
51
|
+
while (++k < values.length) {
|
|
52
|
+
values[k].splice(idx, 0, undefined);
|
|
53
|
+
}
|
|
54
|
+
row.splice(idx, 0, undefined);
|
|
55
|
+
}
|
|
56
|
+
row[idx] = data[i][key];
|
|
57
|
+
}
|
|
58
|
+
values.push(row);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
columns,
|
|
62
|
+
values,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_prepUpdate(data = {}): any[] {
|
|
67
|
+
const { counter = {} } = this.single;
|
|
68
|
+
|
|
69
|
+
for (const column of Object.keys(counter)) {
|
|
70
|
+
//Skip?
|
|
71
|
+
if (has(data, column)) {
|
|
72
|
+
//Needed?
|
|
73
|
+
this.client.logger.warn(
|
|
74
|
+
`increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call.`,
|
|
75
|
+
);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let value = counter[column];
|
|
80
|
+
|
|
81
|
+
const symbol = value < 0 ? "-" : "+";
|
|
82
|
+
|
|
83
|
+
if (symbol === "-") {
|
|
84
|
+
value = -value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
data[column] = this.client.raw(`?? ${symbol} ?`, [column, value]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
data = omitBy(data, (value) => typeof value === "undefined");
|
|
91
|
+
|
|
92
|
+
const vals = [];
|
|
93
|
+
const columns = Object.keys(data);
|
|
94
|
+
let i = -1;
|
|
95
|
+
|
|
96
|
+
while (++i < columns.length) {
|
|
97
|
+
vals.push(
|
|
98
|
+
wrap_(
|
|
99
|
+
columns[i],
|
|
100
|
+
undefined,
|
|
101
|
+
this.builder,
|
|
102
|
+
this.client,
|
|
103
|
+
this.bindingsHolder,
|
|
104
|
+
) +
|
|
105
|
+
" = " +
|
|
106
|
+
this.client.parameter(
|
|
107
|
+
data[columns[i]],
|
|
108
|
+
this.builder,
|
|
109
|
+
this.bindingsHolder,
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (isEmpty(vals)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
[
|
|
117
|
+
"Empty .update() call detected!",
|
|
118
|
+
"Update data does not contain any values to update.",
|
|
119
|
+
"This will result in a faulty query.",
|
|
120
|
+
this.single.table ? `Table: ${this.single.table}.` : "",
|
|
121
|
+
this.single.update
|
|
122
|
+
? `Columns: ${Object.keys(this.single.update)}.`
|
|
123
|
+
: "",
|
|
124
|
+
].join(" "),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return vals;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default IBMiQueryCompiler;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import ColumnCompiler from "knex/lib/schema/columncompiler";
|
|
2
|
+
import * as console from "console";
|
|
3
|
+
|
|
4
|
+
class IBMiColumnCompiler extends ColumnCompiler {
|
|
5
|
+
constructor(client, tableCompiler, columnBuilder) {
|
|
6
|
+
super(client, tableCompiler, columnBuilder);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
increments(options = { primaryKey: true }) {
|
|
10
|
+
return (
|
|
11
|
+
"int not null generated always as identity (start with 1, increment by 1)" +
|
|
12
|
+
(this.tableCompiler._canBeAddPrimaryKey(options) ? " primary key" : "")
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
datetime(withoutTz = false, precision) {
|
|
17
|
+
let useTz;
|
|
18
|
+
if (isObject(withoutTz)) {
|
|
19
|
+
({ useTz, precision } = withoutTz);
|
|
20
|
+
} else {
|
|
21
|
+
useTz = !withoutTz;
|
|
22
|
+
}
|
|
23
|
+
useTz = typeof useTz === "boolean" ? useTz : true;
|
|
24
|
+
precision =
|
|
25
|
+
precision !== undefined && precision !== null
|
|
26
|
+
? "(" + precision + ")"
|
|
27
|
+
: "";
|
|
28
|
+
|
|
29
|
+
console.log(useTz, precision);
|
|
30
|
+
return `${useTz ? "timestamptz" : "timestamp"}${precision}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default IBMiColumnCompiler;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import SchemaCompiler from "knex/lib/schema/compiler";
|
|
2
|
+
import * as console from "console";
|
|
3
|
+
|
|
4
|
+
class IBMiSchemaCompiler extends SchemaCompiler {
|
|
5
|
+
constructor(client, builder) {
|
|
6
|
+
super(client, builder);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
hasTable(tableName) {
|
|
10
|
+
const formattedTable = this.client.parameter(
|
|
11
|
+
prefixedTableName(this.schema, tableName),
|
|
12
|
+
this.builder,
|
|
13
|
+
this.bindingsHolder,
|
|
14
|
+
);
|
|
15
|
+
const bindings = [tableName.toUpperCase()];
|
|
16
|
+
let sql =
|
|
17
|
+
`SELECT TABLE_NAME FROM QSYS2.SYSTABLES ` +
|
|
18
|
+
`WHERE TYPE = 'T' AND TABLE_NAME = ${formattedTable}`;
|
|
19
|
+
if (this.schema) {
|
|
20
|
+
sql += " AND TABLE_SCHEMA = ?";
|
|
21
|
+
bindings.push(this.schema);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.pushQuery({
|
|
25
|
+
sql,
|
|
26
|
+
bindings,
|
|
27
|
+
output: (resp) => {
|
|
28
|
+
return resp.rowCount > 0;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
toSQL() {
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
const sequence = this.builder._sequence;
|
|
36
|
+
for (let i = 0, l = sequence.length; i < l; i++) {
|
|
37
|
+
const query = sequence[i];
|
|
38
|
+
console.log(query.method, query);
|
|
39
|
+
this[query.method].apply(this, query.args);
|
|
40
|
+
}
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
return this.sequence;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function prefixedTableName(prefix, table) {
|
|
47
|
+
return prefix ? `${prefix}.${table}` : table;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default IBMiSchemaCompiler;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import TableCompiler from "knex/lib/schema/tablecompiler";
|
|
2
|
+
|
|
3
|
+
class IBMiTableCompiler extends TableCompiler {
|
|
4
|
+
constructor(client, tableBuilder) {
|
|
5
|
+
super(client, tableBuilder);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
unique(columns, indexName) {
|
|
9
|
+
/** @type {string | undefined} */
|
|
10
|
+
let deferrable;
|
|
11
|
+
let useConstraint = false;
|
|
12
|
+
let predicate;
|
|
13
|
+
if (typeof indexName === "object") {
|
|
14
|
+
({ indexName, deferrable, useConstraint, predicate } = indexName);
|
|
15
|
+
}
|
|
16
|
+
if (deferrable && deferrable !== 'not deferrable') {
|
|
17
|
+
this.client.logger.warn(
|
|
18
|
+
`ibmi: unique index [${indexName}] will not be deferrable ${deferrable} because mssql does not support deferred constraints.`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (useConstraint && predicate) {
|
|
22
|
+
throw new Error('ibmi cannot create constraint with predicate');
|
|
23
|
+
}
|
|
24
|
+
indexName = indexName
|
|
25
|
+
? this.formatter.wrap(indexName)
|
|
26
|
+
: this._indexCommand('unique', this.tableNameRaw, columns);
|
|
27
|
+
|
|
28
|
+
if (!Array.isArray(columns)) {
|
|
29
|
+
columns = [columns];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (useConstraint) {
|
|
33
|
+
// mssql supports unique indexes and unique constraints.
|
|
34
|
+
// unique indexes cannot be used with foreign key relationships hence unique constraints are used instead.
|
|
35
|
+
this.pushQuery(
|
|
36
|
+
`ALTER TABLE ${this.tableName()} ADD CONSTRAINT ${indexName} UNIQUE (${this.formatter.columnize(
|
|
37
|
+
columns
|
|
38
|
+
)})`
|
|
39
|
+
);
|
|
40
|
+
} else {
|
|
41
|
+
// default to making unique index that allows null https://stackoverflow.com/a/767702/360060
|
|
42
|
+
// to be more or less compatible with other DBs (if any of the columns is NULL then "duplicates" are allowed)
|
|
43
|
+
const predicateQuery = predicate
|
|
44
|
+
? ' ' + this.client.queryCompiler(predicate).where()
|
|
45
|
+
: ' WHERE ' +
|
|
46
|
+
columns
|
|
47
|
+
.map((column) => this.formatter.columnize(column) + ' IS NOT NULL')
|
|
48
|
+
.join(' AND ');
|
|
49
|
+
this.pushQuery(
|
|
50
|
+
`CREATE UNIQUE INDEX ${indexName} ON ${this.tableName()} (${this.formatter.columnize(
|
|
51
|
+
columns
|
|
52
|
+
)})${predicateQuery}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
createQuery(columns, ifNot, like) {
|
|
58
|
+
let createStatement = ifNot
|
|
59
|
+
? `if object_id('${this.tableName()}', 'U') is null `
|
|
60
|
+
: "";
|
|
61
|
+
|
|
62
|
+
if (like) {
|
|
63
|
+
// This query copy only columns and not all indexes and keys like other databases.
|
|
64
|
+
createStatement += `SELECT * INTO ${this.tableName()} FROM ${this.tableNameLike()} WHERE 0=1`;
|
|
65
|
+
} else {
|
|
66
|
+
createStatement +=
|
|
67
|
+
"CREATE TABLE " +
|
|
68
|
+
this.tableName() +
|
|
69
|
+
(this._formatting ? " (\n " : " (") +
|
|
70
|
+
columns.sql.join(this._formatting ? ",\n " : ", ") +
|
|
71
|
+
this._addChecks() +
|
|
72
|
+
")";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.pushQuery(createStatement);
|
|
76
|
+
|
|
77
|
+
if (this.single.comment) {
|
|
78
|
+
this.comment(this.single.comment);
|
|
79
|
+
}
|
|
80
|
+
if (like) {
|
|
81
|
+
this.addColumns(columns, this.addColumnsPrefix);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// All of the columns to "add" for the query
|
|
86
|
+
addColumns(columns, prefix) {
|
|
87
|
+
prefix = prefix || this.addColumnsPrefix;
|
|
88
|
+
|
|
89
|
+
if (columns.sql.length > 0) {
|
|
90
|
+
const columnSql = columns.sql.map((column) => {
|
|
91
|
+
return prefix + column;
|
|
92
|
+
});
|
|
93
|
+
this.pushQuery({
|
|
94
|
+
sql:
|
|
95
|
+
(this.lowerCase ? 'alter table ' : 'ALTER TABLE ') +
|
|
96
|
+
this.tableName() +
|
|
97
|
+
' ' +
|
|
98
|
+
columnSql.join(' '),
|
|
99
|
+
bindings: columns.bindings,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async commit(conn, value) {
|
|
105
|
+
return await conn.commit();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default IBMiTableCompiler
|