@cubejs-backend/mssql-driver 1.3.4 → 1.3.6
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/dist/src/MSSqlDriver.d.ts +96 -0
- package/dist/src/MSSqlDriver.d.ts.map +1 -0
- package/dist/src/MSSqlDriver.js +336 -0
- package/dist/src/MSSqlDriver.js.map +1 -0
- package/dist/src/QueryStream.d.ts +24 -0
- package/dist/src/QueryStream.d.ts.map +1 -0
- package/dist/src/QueryStream.js +59 -0
- package/dist/src/QueryStream.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +20 -0
- package/dist/src/index.js.map +1 -0
- package/index.js +11 -0
- package/package.json +22 -6
- package/CHANGELOG.md +0 -1748
- package/driver/MSSqlDriver.js +0 -340
- package/driver/QueryStream.js +0 -62
- package/driver/index.d.ts +0 -9
package/driver/MSSqlDriver.js
DELETED
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright Cube Dev, Inc.
|
|
3
|
-
* @license Apache-2.0
|
|
4
|
-
* @fileoverview The `MSSqlDriver` and related types declaration.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
getEnv,
|
|
9
|
-
assertDataSource,
|
|
10
|
-
} = require('@cubejs-backend/shared');
|
|
11
|
-
const sql = require('mssql');
|
|
12
|
-
const { BaseDriver } = require('@cubejs-backend/base-driver');
|
|
13
|
-
const QueryStream = require('./QueryStream');
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const GenericTypeToMSSql = {
|
|
17
|
-
boolean: 'bit',
|
|
18
|
-
string: 'nvarchar(max)',
|
|
19
|
-
text: 'nvarchar(max)',
|
|
20
|
-
timestamp: 'datetime2',
|
|
21
|
-
uuid: 'uniqueidentifier'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const MSSqlToGenericType = {
|
|
25
|
-
bit: 'boolean',
|
|
26
|
-
uniqueidentifier: 'uuid',
|
|
27
|
-
datetime2: 'timestamp'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* MS SQL driver class.
|
|
32
|
-
*/
|
|
33
|
-
class MSSqlDriver extends BaseDriver {
|
|
34
|
-
/**
|
|
35
|
-
* Returns default concurrency value.
|
|
36
|
-
*/
|
|
37
|
-
static getDefaultConcurrency() {
|
|
38
|
-
return 2;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Class constructor.
|
|
43
|
-
*/
|
|
44
|
-
constructor(config = {}) {
|
|
45
|
-
super({
|
|
46
|
-
testConnectionTimeout: config.testConnectionTimeout,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const dataSource =
|
|
50
|
-
config.dataSource ||
|
|
51
|
-
assertDataSource('default');
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @type {import('mssql').config}
|
|
55
|
-
*/
|
|
56
|
-
this.config = {
|
|
57
|
-
readOnly: true,
|
|
58
|
-
server: getEnv('dbHost', { dataSource }),
|
|
59
|
-
database: getEnv('dbName', { dataSource }),
|
|
60
|
-
port: getEnv('dbPort', { dataSource }),
|
|
61
|
-
user: getEnv('dbUser', { dataSource }),
|
|
62
|
-
password: getEnv('dbPass', { dataSource }),
|
|
63
|
-
domain: getEnv('dbDomain', { dataSource }),
|
|
64
|
-
requestTimeout: getEnv('dbQueryTimeout') * 1000,
|
|
65
|
-
options: {
|
|
66
|
-
encrypt: getEnv('dbSsl', { dataSource }),
|
|
67
|
-
useUTC: false
|
|
68
|
-
},
|
|
69
|
-
pool: {
|
|
70
|
-
max:
|
|
71
|
-
config.maxPoolSize ||
|
|
72
|
-
getEnv('dbMaxPoolSize', { dataSource }) ||
|
|
73
|
-
8,
|
|
74
|
-
min: 0,
|
|
75
|
-
idleTimeoutMillis: 30 * 1000,
|
|
76
|
-
acquireTimeoutMillis: 20 * 1000
|
|
77
|
-
},
|
|
78
|
-
...config
|
|
79
|
-
};
|
|
80
|
-
const { readOnly, ...poolConfig } = this.config;
|
|
81
|
-
this.connectionPool = new sql.ConnectionPool(poolConfig);
|
|
82
|
-
this.initialConnectPromise = this.connectionPool.connect();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Returns the configurable driver options
|
|
87
|
-
* Note: It returns the unprefixed option names.
|
|
88
|
-
* In case of using multisources options need to be prefixed manually.
|
|
89
|
-
*/
|
|
90
|
-
static driverEnvVariables() {
|
|
91
|
-
return [
|
|
92
|
-
'CUBEJS_DB_HOST',
|
|
93
|
-
'CUBEJS_DB_NAME',
|
|
94
|
-
'CUBEJS_DB_PORT',
|
|
95
|
-
'CUBEJS_DB_USER',
|
|
96
|
-
'CUBEJS_DB_PASS',
|
|
97
|
-
'CUBEJS_DB_DOMAIN',
|
|
98
|
-
];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
testConnection() {
|
|
102
|
-
return this.initialConnectPromise.then((pool) => pool.request().query('SELECT 1 as number'));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Executes query in streaming mode.
|
|
107
|
-
*
|
|
108
|
-
* @param {string} query
|
|
109
|
-
* @param {Array} values
|
|
110
|
-
* @param {{ highWaterMark: number? }} options
|
|
111
|
-
* @return {Promise<StreamTableDataWithTypes>}
|
|
112
|
-
*/
|
|
113
|
-
async stream(
|
|
114
|
-
query,
|
|
115
|
-
values,
|
|
116
|
-
options,
|
|
117
|
-
) {
|
|
118
|
-
const pool = await this.initialConnectPromise;
|
|
119
|
-
const request = pool.request();
|
|
120
|
-
|
|
121
|
-
request.stream = true;
|
|
122
|
-
(values || []).forEach((v, i) => {
|
|
123
|
-
request.input(`_${i + 1}`, v);
|
|
124
|
-
});
|
|
125
|
-
request.query(query);
|
|
126
|
-
|
|
127
|
-
const stream = new QueryStream(request, options?.highWaterMark);
|
|
128
|
-
const fields = await new Promise((resolve, reject) => {
|
|
129
|
-
request.on('recordset', (columns) => {
|
|
130
|
-
resolve(this.mapFields(columns));
|
|
131
|
-
});
|
|
132
|
-
request.on('error', (err) => {
|
|
133
|
-
reject(err);
|
|
134
|
-
});
|
|
135
|
-
stream.on('error', (err) => {
|
|
136
|
-
reject(err);
|
|
137
|
-
})
|
|
138
|
-
});
|
|
139
|
-
return {
|
|
140
|
-
rowStream: stream,
|
|
141
|
-
types: fields,
|
|
142
|
-
release: async () => {
|
|
143
|
-
request.cancel();
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* @param {{
|
|
150
|
-
* [name: string]: {
|
|
151
|
-
* index: number,
|
|
152
|
-
* name: string,
|
|
153
|
-
* type: *,
|
|
154
|
-
* nullable: boolean,
|
|
155
|
-
* caseSensitive: boolean,
|
|
156
|
-
* identity: boolean,
|
|
157
|
-
* readOnly: boolean,
|
|
158
|
-
* length: number?,
|
|
159
|
-
* scale: number?,
|
|
160
|
-
* precision: number?
|
|
161
|
-
* }
|
|
162
|
-
* }} fields
|
|
163
|
-
*/
|
|
164
|
-
mapFields(fields) {
|
|
165
|
-
return Object.keys(fields).map((field) => {
|
|
166
|
-
let type;
|
|
167
|
-
switch (fields[field].type) {
|
|
168
|
-
case sql.Bit:
|
|
169
|
-
type = 'boolean';
|
|
170
|
-
break;
|
|
171
|
-
// integers
|
|
172
|
-
case sql.Int:
|
|
173
|
-
case sql.SmallInt:
|
|
174
|
-
case sql.TinyInt:
|
|
175
|
-
case sql.BigInt:
|
|
176
|
-
type = 'int';
|
|
177
|
-
break;
|
|
178
|
-
// float
|
|
179
|
-
case sql.Money:
|
|
180
|
-
case sql.SmallMoney:
|
|
181
|
-
case sql.Numeric:
|
|
182
|
-
case sql.Decimal:
|
|
183
|
-
type = 'decimal';
|
|
184
|
-
break;
|
|
185
|
-
// double
|
|
186
|
-
case sql.Real:
|
|
187
|
-
case sql.Float:
|
|
188
|
-
type = 'double';
|
|
189
|
-
break;
|
|
190
|
-
// strings
|
|
191
|
-
case sql.Char:
|
|
192
|
-
case sql.NChar:
|
|
193
|
-
case sql.Text:
|
|
194
|
-
case sql.NText:
|
|
195
|
-
case sql.VarChar:
|
|
196
|
-
case sql.NVarChar:
|
|
197
|
-
case sql.Xml:
|
|
198
|
-
type = 'text';
|
|
199
|
-
break;
|
|
200
|
-
// date and time
|
|
201
|
-
case sql.Time:
|
|
202
|
-
type = 'time';
|
|
203
|
-
break;
|
|
204
|
-
case sql.Date:
|
|
205
|
-
type = 'timestamp';
|
|
206
|
-
break;
|
|
207
|
-
case sql.DateTime:
|
|
208
|
-
case sql.DateTime2:
|
|
209
|
-
case sql.SmallDateTime:
|
|
210
|
-
case sql.DateTimeOffset:
|
|
211
|
-
type = 'timestamp';
|
|
212
|
-
break;
|
|
213
|
-
// others
|
|
214
|
-
case sql.UniqueIdentifier:
|
|
215
|
-
case sql.Variant:
|
|
216
|
-
case sql.Binary:
|
|
217
|
-
case sql.VarBinary:
|
|
218
|
-
case sql.Image:
|
|
219
|
-
case sql.UDT:
|
|
220
|
-
case sql.Geography:
|
|
221
|
-
case sql.Geometry:
|
|
222
|
-
case sql.TVP:
|
|
223
|
-
type = 'string';
|
|
224
|
-
break;
|
|
225
|
-
// unknown
|
|
226
|
-
default:
|
|
227
|
-
type = 'string';
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
return { name: fields[field].name, type: this.toGenericType(type) };
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
query(query, values) {
|
|
235
|
-
let cancelFn = null;
|
|
236
|
-
const promise = this.initialConnectPromise.then((pool) => {
|
|
237
|
-
const request = pool.request();
|
|
238
|
-
(values || []).forEach((v, i) => request.input(`_${i + 1}`, v));
|
|
239
|
-
|
|
240
|
-
// TODO time zone UTC set in driver ?
|
|
241
|
-
|
|
242
|
-
cancelFn = () => request.cancel();
|
|
243
|
-
return request.query(query).then(res => res.recordset);
|
|
244
|
-
});
|
|
245
|
-
promise.cancel = () => cancelFn && cancelFn();
|
|
246
|
-
return promise;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
param(paramIndex) {
|
|
250
|
-
return `@_${paramIndex + 1}`;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
async tableColumnTypes(table) {
|
|
254
|
-
const [schema, name] = table.split('.');
|
|
255
|
-
|
|
256
|
-
const columns = await this.query(
|
|
257
|
-
`SELECT column_name as ${this.quoteIdentifier('column_name')},
|
|
258
|
-
table_name as ${this.quoteIdentifier('table_name')},
|
|
259
|
-
table_schema as ${this.quoteIdentifier('table_schema')},
|
|
260
|
-
data_type as ${this.quoteIdentifier('data_type')}
|
|
261
|
-
FROM INFORMATION_SCHEMA.COLUMNS
|
|
262
|
-
WHERE table_name = ${this.param(0)} AND table_schema = ${this.param(1)}`,
|
|
263
|
-
[name, schema]
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
return columns.map(c => ({ name: c.column_name, type: this.toGenericType(c.data_type) }));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
getTablesQuery(schemaName) {
|
|
270
|
-
return this.query(
|
|
271
|
-
`SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ${this.param(0)}`,
|
|
272
|
-
[schemaName]
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
createSchemaIfNotExists(schemaName) {
|
|
277
|
-
return this.query(
|
|
278
|
-
`SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA WHERE schema_name = ${this.param(0)}`,
|
|
279
|
-
[schemaName]
|
|
280
|
-
).then((schemas) => {
|
|
281
|
-
if (schemas.length === 0) {
|
|
282
|
-
return this.query(`CREATE SCHEMA ${schemaName}`);
|
|
283
|
-
}
|
|
284
|
-
return null;
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
informationSchemaQuery() {
|
|
289
|
-
// fix The multi-part identifier "columns.data_type" could not be bound
|
|
290
|
-
return `
|
|
291
|
-
SELECT column_name as ${this.quoteIdentifier('column_name')},
|
|
292
|
-
table_name as ${this.quoteIdentifier('table_name')},
|
|
293
|
-
table_schema as ${this.quoteIdentifier('table_schema')},
|
|
294
|
-
data_type as ${this.quoteIdentifier('data_type')}
|
|
295
|
-
FROM INFORMATION_SCHEMA.COLUMNS
|
|
296
|
-
WHERE table_schema NOT IN ('information_schema', 'sys')
|
|
297
|
-
`;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async downloadQueryResults(query, values, options) {
|
|
301
|
-
if ((options || {}).streamImport) {
|
|
302
|
-
return this.stream(query, values, options);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const result = await this.query(query, values);
|
|
306
|
-
const types = Object.keys(result.columns).map((key) => ({
|
|
307
|
-
name: result.columns[key].name,
|
|
308
|
-
type: this.toGenericType(result.columns[key].type.declaration),
|
|
309
|
-
}));
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
rows: result,
|
|
313
|
-
types,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
fromGenericType(columnType) {
|
|
318
|
-
return GenericTypeToMSSql[columnType] || super.fromGenericType(columnType);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
toGenericType(columnType){
|
|
322
|
-
return MSSqlToGenericType[columnType] || super.toGenericType(columnType);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
readOnly() {
|
|
326
|
-
return !!this.config.readOnly;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
wrapQueryWithLimit(query) {
|
|
330
|
-
query.query = `SELECT TOP ${query.limit} * FROM (${query.query}) AS t`;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
capabilities() {
|
|
334
|
-
return {
|
|
335
|
-
incrementalSchemaLoading: true,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
module.exports = MSSqlDriver;
|
package/driver/QueryStream.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const { Readable } = require('stream');
|
|
2
|
-
const { getEnv } = require('@cubejs-backend/shared');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* MS-SQL query stream class.
|
|
6
|
-
*/
|
|
7
|
-
class QueryStream extends Readable {
|
|
8
|
-
request = null;
|
|
9
|
-
toRead = 0;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @constructor
|
|
13
|
-
*/
|
|
14
|
-
constructor(request, highWaterMark) {
|
|
15
|
-
super({
|
|
16
|
-
objectMode: true,
|
|
17
|
-
highWaterMark:
|
|
18
|
-
highWaterMark || getEnv('dbQueryStreamHighWaterMark'),
|
|
19
|
-
});
|
|
20
|
-
this.request = request;
|
|
21
|
-
this.request.on('row', row => {
|
|
22
|
-
this.transformRow(row);
|
|
23
|
-
const canAdd = this.push(row);
|
|
24
|
-
if (this.toRead-- <= 0 || !canAdd) {
|
|
25
|
-
this.request.pause();
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
this.request.on('done', () => {
|
|
29
|
-
this.push(null);
|
|
30
|
-
})
|
|
31
|
-
this.request.on('error', (err) => {
|
|
32
|
-
this.destroy(err);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @override
|
|
38
|
-
*/
|
|
39
|
-
_read(toRead) {
|
|
40
|
-
this.toRead += toRead;
|
|
41
|
-
this.request.resume();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
transformRow(row) {
|
|
45
|
-
for (const key in row) {
|
|
46
|
-
if (row.hasOwnProperty(key) && row[key] && row[key] instanceof Date) {
|
|
47
|
-
row[key] = row[key].toJSON();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @override
|
|
54
|
-
*/
|
|
55
|
-
_destroy(error, callback) {
|
|
56
|
-
this.request.cancel();
|
|
57
|
-
this.request = null;
|
|
58
|
-
callback(error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = QueryStream;
|
package/driver/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { BaseDriver } from "@cubejs-backend/query-orchestrator";
|
|
2
|
-
import { config } from "mssql";
|
|
3
|
-
|
|
4
|
-
declare module "@cubejs-backend/mssql-driver" {
|
|
5
|
-
export default class MSSqlDriver extends BaseDriver {
|
|
6
|
-
constructor(options?: config);
|
|
7
|
-
release(): Promise<void>
|
|
8
|
-
}
|
|
9
|
-
}
|