@oino-ts/db-mssql 0.3.3 → 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/dist/cjs/OINODbMsSql.js +30 -22
- package/dist/esm/OINODbMsSql.js +30 -22
- package/dist/types/OINODbMsSql.d.ts +7 -0
- package/package.json +39 -39
- package/src/OINODbMsSql.ts +459 -450
- package/src/index.ts +1 -1
package/dist/cjs/OINODbMsSql.js
CHANGED
|
@@ -214,10 +214,18 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
214
214
|
return "'" + cellValue.toISOString().substring(0, 23) + "'";
|
|
215
215
|
}
|
|
216
216
|
else {
|
|
217
|
-
|
|
218
|
-
return "'" + cellValue?.toString().replaceAll("\'", "''") + "'";
|
|
217
|
+
return this.printSqlString(cellValue.toString());
|
|
219
218
|
}
|
|
220
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Print a single string value as valid sql literal
|
|
222
|
+
*
|
|
223
|
+
* @param sqlString string value
|
|
224
|
+
*
|
|
225
|
+
*/
|
|
226
|
+
printSqlString(sqlString) {
|
|
227
|
+
return "'" + sqlString.replaceAll("'", "''") + "'";
|
|
228
|
+
}
|
|
221
229
|
/**
|
|
222
230
|
* Parse a single SQL result value for serialization using the context of the native data
|
|
223
231
|
* type.
|
|
@@ -336,26 +344,26 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
336
344
|
return result;
|
|
337
345
|
}
|
|
338
346
|
_getSchemaSql(dbName, tableName) {
|
|
339
|
-
const sql = `SELECT
|
|
340
|
-
C.COLUMN_NAME,
|
|
341
|
-
C.IS_NULLABLE,
|
|
342
|
-
C.DATA_TYPE,
|
|
343
|
-
C.CHARACTER_MAXIMUM_LENGTH,
|
|
344
|
-
C.NUMERIC_PRECISION,
|
|
345
|
-
C.NUMERIC_PRECISION_RADIX,
|
|
346
|
-
CONST.CONSTRAINT_TYPES,
|
|
347
|
-
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
|
|
348
|
-
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
|
|
349
|
-
FROM
|
|
350
|
-
INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
|
|
351
|
-
(
|
|
352
|
-
SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
|
|
353
|
-
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
354
|
-
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
|
|
355
|
-
GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
|
|
356
|
-
) as CONST
|
|
357
|
-
ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
|
|
358
|
-
WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
|
|
347
|
+
const sql = `SELECT
|
|
348
|
+
C.COLUMN_NAME,
|
|
349
|
+
C.IS_NULLABLE,
|
|
350
|
+
C.DATA_TYPE,
|
|
351
|
+
C.CHARACTER_MAXIMUM_LENGTH,
|
|
352
|
+
C.NUMERIC_PRECISION,
|
|
353
|
+
C.NUMERIC_PRECISION_RADIX,
|
|
354
|
+
CONST.CONSTRAINT_TYPES,
|
|
355
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
|
|
356
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
|
|
357
|
+
FROM
|
|
358
|
+
INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
|
|
359
|
+
(
|
|
360
|
+
SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
|
|
361
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
362
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
|
|
363
|
+
GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
|
|
364
|
+
) as CONST
|
|
365
|
+
ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
|
|
366
|
+
WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
|
|
359
367
|
ORDER BY C.ORDINAL_POSITION;`;
|
|
360
368
|
return sql;
|
|
361
369
|
}
|
package/dist/esm/OINODbMsSql.js
CHANGED
|
@@ -211,10 +211,18 @@ export class OINODbMsSql extends OINODb {
|
|
|
211
211
|
return "'" + cellValue.toISOString().substring(0, 23) + "'";
|
|
212
212
|
}
|
|
213
213
|
else {
|
|
214
|
-
|
|
215
|
-
return "'" + cellValue?.toString().replaceAll("\'", "''") + "'";
|
|
214
|
+
return this.printSqlString(cellValue.toString());
|
|
216
215
|
}
|
|
217
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Print a single string value as valid sql literal
|
|
219
|
+
*
|
|
220
|
+
* @param sqlString string value
|
|
221
|
+
*
|
|
222
|
+
*/
|
|
223
|
+
printSqlString(sqlString) {
|
|
224
|
+
return "'" + sqlString.replaceAll("'", "''") + "'";
|
|
225
|
+
}
|
|
218
226
|
/**
|
|
219
227
|
* Parse a single SQL result value for serialization using the context of the native data
|
|
220
228
|
* type.
|
|
@@ -333,26 +341,26 @@ export class OINODbMsSql extends OINODb {
|
|
|
333
341
|
return result;
|
|
334
342
|
}
|
|
335
343
|
_getSchemaSql(dbName, tableName) {
|
|
336
|
-
const sql = `SELECT
|
|
337
|
-
C.COLUMN_NAME,
|
|
338
|
-
C.IS_NULLABLE,
|
|
339
|
-
C.DATA_TYPE,
|
|
340
|
-
C.CHARACTER_MAXIMUM_LENGTH,
|
|
341
|
-
C.NUMERIC_PRECISION,
|
|
342
|
-
C.NUMERIC_PRECISION_RADIX,
|
|
343
|
-
CONST.CONSTRAINT_TYPES,
|
|
344
|
-
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
|
|
345
|
-
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
|
|
346
|
-
FROM
|
|
347
|
-
INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
|
|
348
|
-
(
|
|
349
|
-
SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
|
|
350
|
-
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
351
|
-
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
|
|
352
|
-
GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
|
|
353
|
-
) as CONST
|
|
354
|
-
ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
|
|
355
|
-
WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
|
|
344
|
+
const sql = `SELECT
|
|
345
|
+
C.COLUMN_NAME,
|
|
346
|
+
C.IS_NULLABLE,
|
|
347
|
+
C.DATA_TYPE,
|
|
348
|
+
C.CHARACTER_MAXIMUM_LENGTH,
|
|
349
|
+
C.NUMERIC_PRECISION,
|
|
350
|
+
C.NUMERIC_PRECISION_RADIX,
|
|
351
|
+
CONST.CONSTRAINT_TYPES,
|
|
352
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
|
|
353
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
|
|
354
|
+
FROM
|
|
355
|
+
INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
|
|
356
|
+
(
|
|
357
|
+
SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
|
|
358
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
359
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
|
|
360
|
+
GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
|
|
361
|
+
) as CONST
|
|
362
|
+
ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
|
|
363
|
+
WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
|
|
356
364
|
ORDER BY C.ORDINAL_POSITION;`;
|
|
357
365
|
return sql;
|
|
358
366
|
}
|
|
@@ -35,6 +35,13 @@ export declare class OINODbMsSql extends OINODb {
|
|
|
35
35
|
*
|
|
36
36
|
*/
|
|
37
37
|
printCellAsSqlValue(cellValue: OINODataCell, sqlType: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Print a single string value as valid sql literal
|
|
40
|
+
*
|
|
41
|
+
* @param sqlString string value
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
printSqlString(sqlString: string): string;
|
|
38
45
|
/**
|
|
39
46
|
* Parse a single SQL result value for serialization using the context of the native data
|
|
40
47
|
* type.
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@oino-ts/db-mssql",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OINO TS package for using Microsoft Sql databases.",
|
|
5
|
-
"author": "Matias Kiviniemi (pragmatta)",
|
|
6
|
-
"license": "MPL-2.0",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/pragmatta/oino-ts.git"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"sql",
|
|
13
|
-
"database",
|
|
14
|
-
"rest-api",
|
|
15
|
-
"typescript",
|
|
16
|
-
"library",
|
|
17
|
-
"mssql",
|
|
18
|
-
"sql-server",
|
|
19
|
-
"azure-sql"
|
|
20
|
-
],
|
|
21
|
-
"main": "./dist/cjs/index.js",
|
|
22
|
-
"module": "./dist/esm/index.js",
|
|
23
|
-
"types": "./dist/types/index.d.ts",
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@oino-ts/db": "0.
|
|
26
|
-
"mssql": "^11.0.1"
|
|
27
|
-
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"@types/bun": "^1.1.14",
|
|
30
|
-
"@types/mssql": "^9.1.5",
|
|
31
|
-
"@types/node": "^20.12.7"
|
|
32
|
-
},
|
|
33
|
-
"files": [
|
|
34
|
-
"src/*.ts",
|
|
35
|
-
"dist/cjs/*.js",
|
|
36
|
-
"dist/esm/*.js",
|
|
37
|
-
"dist/types/*.d.ts"
|
|
38
|
-
]
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@oino-ts/db-mssql",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "OINO TS package for using Microsoft Sql databases.",
|
|
5
|
+
"author": "Matias Kiviniemi (pragmatta)",
|
|
6
|
+
"license": "MPL-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/pragmatta/oino-ts.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"sql",
|
|
13
|
+
"database",
|
|
14
|
+
"rest-api",
|
|
15
|
+
"typescript",
|
|
16
|
+
"library",
|
|
17
|
+
"mssql",
|
|
18
|
+
"sql-server",
|
|
19
|
+
"azure-sql"
|
|
20
|
+
],
|
|
21
|
+
"main": "./dist/cjs/index.js",
|
|
22
|
+
"module": "./dist/esm/index.js",
|
|
23
|
+
"types": "./dist/types/index.d.ts",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@oino-ts/db": "0.4.0",
|
|
26
|
+
"mssql": "^11.0.1"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/bun": "^1.1.14",
|
|
30
|
+
"@types/mssql": "^9.1.5",
|
|
31
|
+
"@types/node": "^20.12.7"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"src/*.ts",
|
|
35
|
+
"dist/cjs/*.js",
|
|
36
|
+
"dist/esm/*.js",
|
|
37
|
+
"dist/types/*.d.ts"
|
|
38
|
+
]
|
|
39
|
+
}
|
package/src/OINODbMsSql.ts
CHANGED
|
@@ -1,450 +1,459 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
-
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINOBooleanDataField, OINONumberDataField, OINOStringDataField, OINODbDataFieldParams, OINO_ERROR_PREFIX, OINODataRow, OINODataCell, OINOBenchmark, OINODatetimeDataField, OINOBlobDataField, OINO_INFO_PREFIX, OINODB_EMPTY_ROW, OINODB_EMPTY_ROWS, OINOLog } from "@oino-ts/db";
|
|
8
|
-
|
|
9
|
-
import {ConnectionPool, config} from "mssql";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Implmentation of OINODbDataSet for MariaDb.
|
|
13
|
-
*
|
|
14
|
-
*/
|
|
15
|
-
class OINOMsSqlData extends OINODbDataSet {
|
|
16
|
-
private _recordsets:OINODataRow[][] = [OINODB_EMPTY_ROWS]
|
|
17
|
-
private _rows:OINODataRow[] = OINODB_EMPTY_ROWS
|
|
18
|
-
|
|
19
|
-
private _currentRecordset: number
|
|
20
|
-
private _currentRow: number
|
|
21
|
-
private _eof: boolean
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* OINOMsSqlData constructor
|
|
25
|
-
* @param params database parameters
|
|
26
|
-
*/
|
|
27
|
-
constructor(data: any, messages:string[]=[]) {
|
|
28
|
-
super(data, messages)
|
|
29
|
-
if (data == null) {
|
|
30
|
-
this.messages.push(OINO_INFO_PREFIX + "SQL result is empty")
|
|
31
|
-
|
|
32
|
-
} else if (!(Array.isArray(data) && (data.length>0) && Array.isArray(data[0]))) {
|
|
33
|
-
throw new Error(OINO_ERROR_PREFIX + ": OINOMsSqlData constructor: invalid data!")
|
|
34
|
-
|
|
35
|
-
} else {
|
|
36
|
-
this._recordsets = data as OINODataRow[][]
|
|
37
|
-
this._rows = this._recordsets[0]
|
|
38
|
-
}
|
|
39
|
-
// OINOLog.debug("OINOMsSqlData.constructor", {_rows:this._rows})
|
|
40
|
-
if (this.isEmpty()) {
|
|
41
|
-
this._currentRecordset = -1
|
|
42
|
-
this._currentRow = -1
|
|
43
|
-
this._eof = true
|
|
44
|
-
} else {
|
|
45
|
-
this._currentRecordset = 0
|
|
46
|
-
this._currentRow = 0
|
|
47
|
-
this._eof = false
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Is data set empty.
|
|
53
|
-
*
|
|
54
|
-
*/
|
|
55
|
-
isEmpty():boolean {
|
|
56
|
-
return (this._rows.length == 0)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Is there no more content, i.e. either dataset is empty or we have moved beyond last line
|
|
61
|
-
*
|
|
62
|
-
*/
|
|
63
|
-
isEof():boolean {
|
|
64
|
-
return (this._eof)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
|
|
69
|
-
*
|
|
70
|
-
*/
|
|
71
|
-
async next():Promise<boolean> {
|
|
72
|
-
// OINOLog.debug("OINODbDataSet.next", {currentRow:this._currentRow, length:this.sqlResult.data.length})
|
|
73
|
-
if (this._currentRow < this._rows.length-1) {
|
|
74
|
-
this._currentRow = this._currentRow + 1
|
|
75
|
-
|
|
76
|
-
} else if (this._currentRecordset < this._recordsets.length-1) {
|
|
77
|
-
this._currentRecordset = this._currentRecordset + 1
|
|
78
|
-
this._rows = this._recordsets[this._currentRecordset]
|
|
79
|
-
this._currentRow = 0
|
|
80
|
-
|
|
81
|
-
} else {
|
|
82
|
-
this._eof = true
|
|
83
|
-
}
|
|
84
|
-
return Promise.resolve(!this._eof)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Gets current row of data.
|
|
89
|
-
*
|
|
90
|
-
*/
|
|
91
|
-
getRow(): OINODataRow {
|
|
92
|
-
if ((this._currentRow >=0) && (this._currentRow < this._rows.length)) {
|
|
93
|
-
return this._rows[this._currentRow]
|
|
94
|
-
} else {
|
|
95
|
-
return OINODB_EMPTY_ROW
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Implementation of MariaDb/MySql-database.
|
|
102
|
-
*
|
|
103
|
-
*/
|
|
104
|
-
export class OINODbMsSql extends OINODb {
|
|
105
|
-
|
|
106
|
-
private _pool:ConnectionPool
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Constructor of `OINODbMsSql`
|
|
110
|
-
* @param params database parameters
|
|
111
|
-
*/
|
|
112
|
-
constructor(params:OINODbParams) {
|
|
113
|
-
super(params)
|
|
114
|
-
|
|
115
|
-
// OINOLog.debug("OINODbMsSql.constructor", {params:params})
|
|
116
|
-
if (this._params.type !== "OINODbMsSql") {
|
|
117
|
-
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type)
|
|
118
|
-
}
|
|
119
|
-
this._pool = new ConnectionPool({
|
|
120
|
-
user: params.user,
|
|
121
|
-
password: params.password,
|
|
122
|
-
server: params.url,
|
|
123
|
-
port: params.port,
|
|
124
|
-
database: params.database,
|
|
125
|
-
arrayRowMode:true,
|
|
126
|
-
options: {
|
|
127
|
-
encrypt: true, // Use encryption for Azure SQL Database
|
|
128
|
-
rowCollectionOnRequestCompletion:false,
|
|
129
|
-
rowCollectionOnDone:false,
|
|
130
|
-
trustServerCertificate: true // Change to false for production
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
//this._pool = new ConnectionPool({connectionString: "Server=localhost,1433;Database=database;User Id=username;Password=" + params.password + ";Encrypt=true"})
|
|
134
|
-
|
|
135
|
-
this._pool.on("error", (conn:any) => {
|
|
136
|
-
// console.log("OINODbMsSql error", conn)
|
|
137
|
-
})
|
|
138
|
-
this._pool.on("debug", (event:any) => {
|
|
139
|
-
// console.log("OINODbMsSql debug",event)
|
|
140
|
-
})
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private async _query(sql:string):Promise<OINOMsSqlData> {
|
|
144
|
-
// OINOLog.debug("OINODbMsSql._query", {sql:sql})
|
|
145
|
-
try {
|
|
146
|
-
const sql_res = await this._pool.request().query(sql);
|
|
147
|
-
// console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
|
|
148
|
-
const result:OINOMsSqlData = new OINOMsSqlData(sql_res.recordsets)
|
|
149
|
-
return Promise.resolve(result)
|
|
150
|
-
|
|
151
|
-
} catch (err) {
|
|
152
|
-
OINOLog.error("OINODbMsSql._query exception", {err:err})
|
|
153
|
-
throw err;
|
|
154
|
-
|
|
155
|
-
} finally {
|
|
156
|
-
// console.log("OINODbMsSql._query finally");
|
|
157
|
-
}
|
|
158
|
-
// OINOLog.debug("OINODbMsSql._query", {result:query_result})
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private async _exec(sql:string):Promise<OINOMsSqlData> {
|
|
162
|
-
// OINOLog.debug("OINODbMsSql._exec", {sql:sql})
|
|
163
|
-
try {
|
|
164
|
-
const sql_res = await this._pool.request().query(sql);
|
|
165
|
-
// console.log("OINODbMsSql._exec result", sql_res);
|
|
166
|
-
return Promise.resolve(new OINOMsSqlData([[]]))
|
|
167
|
-
|
|
168
|
-
} catch (err) {
|
|
169
|
-
OINOLog.error("OINODbMsSql._exec exception", {err:err})
|
|
170
|
-
throw err;
|
|
171
|
-
} finally {
|
|
172
|
-
}
|
|
173
|
-
// OINOLog.debug("OINODbMsSql._exec", {result:query_result})
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Print a table name using database specific SQL escaping.
|
|
178
|
-
*
|
|
179
|
-
* @param sqlTable name of the table
|
|
180
|
-
*
|
|
181
|
-
*/
|
|
182
|
-
printSqlTablename(sqlTable:string): string {
|
|
183
|
-
return "["+sqlTable+"]"
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Print a column name with correct SQL escaping.
|
|
188
|
-
*
|
|
189
|
-
* @param sqlColumn name of the column
|
|
190
|
-
*
|
|
191
|
-
*/
|
|
192
|
-
printSqlColumnname(sqlColumn:string): string {
|
|
193
|
-
return "["+sqlColumn+"]"
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Print a single data value from serialization using the context of the native data
|
|
199
|
-
* type with the correct SQL escaping.
|
|
200
|
-
*
|
|
201
|
-
* @param cellValue data from sql results
|
|
202
|
-
* @param sqlType native type name for table column
|
|
203
|
-
*
|
|
204
|
-
*/
|
|
205
|
-
printCellAsSqlValue(cellValue:OINODataCell, sqlType: string): string {
|
|
206
|
-
// OINOLog.debug("OINODbMsSql.printCellAsSqlValue", {cellValue:cellValue, sqlType:sqlType})
|
|
207
|
-
if (cellValue === null) {
|
|
208
|
-
return "NULL"
|
|
209
|
-
|
|
210
|
-
} else if (cellValue === undefined) {
|
|
211
|
-
return "UNDEFINED"
|
|
212
|
-
|
|
213
|
-
} else if ((sqlType == "int") || (sqlType == "smallint") || (sqlType == "float")) {
|
|
214
|
-
return cellValue.toString()
|
|
215
|
-
|
|
216
|
-
} else if ((sqlType == "longblob") || (sqlType == "binary") || (sqlType == "varbinary")) {
|
|
217
|
-
if (cellValue instanceof Buffer) {
|
|
218
|
-
return "0x" + (cellValue as Buffer).toString("hex") + ""
|
|
219
|
-
} else if (cellValue instanceof Uint8Array) {
|
|
220
|
-
return "0x" + Buffer.from(cellValue as Uint8Array).toString("hex") + ""
|
|
221
|
-
} else {
|
|
222
|
-
return "'" + cellValue?.toString() + "'"
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
} else if (((sqlType == "date") || (sqlType == "datetime") || (sqlType == "datetime2") || (sqlType == "timestamp")) && (cellValue instanceof Date)) {
|
|
226
|
-
return "'" + cellValue.toISOString().substring(0, 23) + "'"
|
|
227
|
-
|
|
228
|
-
} else {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
OINOBenchmark.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
} else if ((sql_type == "
|
|
434
|
-
api.datamodel.addField(new
|
|
435
|
-
|
|
436
|
-
} else {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINOBooleanDataField, OINONumberDataField, OINOStringDataField, OINODbDataFieldParams, OINO_ERROR_PREFIX, OINODataRow, OINODataCell, OINOBenchmark, OINODatetimeDataField, OINOBlobDataField, OINO_INFO_PREFIX, OINODB_EMPTY_ROW, OINODB_EMPTY_ROWS, OINOLog } from "@oino-ts/db";
|
|
8
|
+
|
|
9
|
+
import {ConnectionPool, config} from "mssql";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Implmentation of OINODbDataSet for MariaDb.
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
class OINOMsSqlData extends OINODbDataSet {
|
|
16
|
+
private _recordsets:OINODataRow[][] = [OINODB_EMPTY_ROWS]
|
|
17
|
+
private _rows:OINODataRow[] = OINODB_EMPTY_ROWS
|
|
18
|
+
|
|
19
|
+
private _currentRecordset: number
|
|
20
|
+
private _currentRow: number
|
|
21
|
+
private _eof: boolean
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* OINOMsSqlData constructor
|
|
25
|
+
* @param params database parameters
|
|
26
|
+
*/
|
|
27
|
+
constructor(data: any, messages:string[]=[]) {
|
|
28
|
+
super(data, messages)
|
|
29
|
+
if (data == null) {
|
|
30
|
+
this.messages.push(OINO_INFO_PREFIX + "SQL result is empty")
|
|
31
|
+
|
|
32
|
+
} else if (!(Array.isArray(data) && (data.length>0) && Array.isArray(data[0]))) {
|
|
33
|
+
throw new Error(OINO_ERROR_PREFIX + ": OINOMsSqlData constructor: invalid data!")
|
|
34
|
+
|
|
35
|
+
} else {
|
|
36
|
+
this._recordsets = data as OINODataRow[][]
|
|
37
|
+
this._rows = this._recordsets[0]
|
|
38
|
+
}
|
|
39
|
+
// OINOLog.debug("OINOMsSqlData.constructor", {_rows:this._rows})
|
|
40
|
+
if (this.isEmpty()) {
|
|
41
|
+
this._currentRecordset = -1
|
|
42
|
+
this._currentRow = -1
|
|
43
|
+
this._eof = true
|
|
44
|
+
} else {
|
|
45
|
+
this._currentRecordset = 0
|
|
46
|
+
this._currentRow = 0
|
|
47
|
+
this._eof = false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Is data set empty.
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
isEmpty():boolean {
|
|
56
|
+
return (this._rows.length == 0)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Is there no more content, i.e. either dataset is empty or we have moved beyond last line
|
|
61
|
+
*
|
|
62
|
+
*/
|
|
63
|
+
isEof():boolean {
|
|
64
|
+
return (this._eof)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
async next():Promise<boolean> {
|
|
72
|
+
// OINOLog.debug("OINODbDataSet.next", {currentRow:this._currentRow, length:this.sqlResult.data.length})
|
|
73
|
+
if (this._currentRow < this._rows.length-1) {
|
|
74
|
+
this._currentRow = this._currentRow + 1
|
|
75
|
+
|
|
76
|
+
} else if (this._currentRecordset < this._recordsets.length-1) {
|
|
77
|
+
this._currentRecordset = this._currentRecordset + 1
|
|
78
|
+
this._rows = this._recordsets[this._currentRecordset]
|
|
79
|
+
this._currentRow = 0
|
|
80
|
+
|
|
81
|
+
} else {
|
|
82
|
+
this._eof = true
|
|
83
|
+
}
|
|
84
|
+
return Promise.resolve(!this._eof)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Gets current row of data.
|
|
89
|
+
*
|
|
90
|
+
*/
|
|
91
|
+
getRow(): OINODataRow {
|
|
92
|
+
if ((this._currentRow >=0) && (this._currentRow < this._rows.length)) {
|
|
93
|
+
return this._rows[this._currentRow]
|
|
94
|
+
} else {
|
|
95
|
+
return OINODB_EMPTY_ROW
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Implementation of MariaDb/MySql-database.
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
export class OINODbMsSql extends OINODb {
|
|
105
|
+
|
|
106
|
+
private _pool:ConnectionPool
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Constructor of `OINODbMsSql`
|
|
110
|
+
* @param params database parameters
|
|
111
|
+
*/
|
|
112
|
+
constructor(params:OINODbParams) {
|
|
113
|
+
super(params)
|
|
114
|
+
|
|
115
|
+
// OINOLog.debug("OINODbMsSql.constructor", {params:params})
|
|
116
|
+
if (this._params.type !== "OINODbMsSql") {
|
|
117
|
+
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type)
|
|
118
|
+
}
|
|
119
|
+
this._pool = new ConnectionPool({
|
|
120
|
+
user: params.user,
|
|
121
|
+
password: params.password,
|
|
122
|
+
server: params.url,
|
|
123
|
+
port: params.port,
|
|
124
|
+
database: params.database,
|
|
125
|
+
arrayRowMode:true,
|
|
126
|
+
options: {
|
|
127
|
+
encrypt: true, // Use encryption for Azure SQL Database
|
|
128
|
+
rowCollectionOnRequestCompletion:false,
|
|
129
|
+
rowCollectionOnDone:false,
|
|
130
|
+
trustServerCertificate: true // Change to false for production
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
//this._pool = new ConnectionPool({connectionString: "Server=localhost,1433;Database=database;User Id=username;Password=" + params.password + ";Encrypt=true"})
|
|
134
|
+
|
|
135
|
+
this._pool.on("error", (conn:any) => {
|
|
136
|
+
// console.log("OINODbMsSql error", conn)
|
|
137
|
+
})
|
|
138
|
+
this._pool.on("debug", (event:any) => {
|
|
139
|
+
// console.log("OINODbMsSql debug",event)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private async _query(sql:string):Promise<OINOMsSqlData> {
|
|
144
|
+
// OINOLog.debug("OINODbMsSql._query", {sql:sql})
|
|
145
|
+
try {
|
|
146
|
+
const sql_res = await this._pool.request().query(sql);
|
|
147
|
+
// console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
|
|
148
|
+
const result:OINOMsSqlData = new OINOMsSqlData(sql_res.recordsets)
|
|
149
|
+
return Promise.resolve(result)
|
|
150
|
+
|
|
151
|
+
} catch (err) {
|
|
152
|
+
OINOLog.error("OINODbMsSql._query exception", {err:err})
|
|
153
|
+
throw err;
|
|
154
|
+
|
|
155
|
+
} finally {
|
|
156
|
+
// console.log("OINODbMsSql._query finally");
|
|
157
|
+
}
|
|
158
|
+
// OINOLog.debug("OINODbMsSql._query", {result:query_result})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async _exec(sql:string):Promise<OINOMsSqlData> {
|
|
162
|
+
// OINOLog.debug("OINODbMsSql._exec", {sql:sql})
|
|
163
|
+
try {
|
|
164
|
+
const sql_res = await this._pool.request().query(sql);
|
|
165
|
+
// console.log("OINODbMsSql._exec result", sql_res);
|
|
166
|
+
return Promise.resolve(new OINOMsSqlData([[]]))
|
|
167
|
+
|
|
168
|
+
} catch (err) {
|
|
169
|
+
OINOLog.error("OINODbMsSql._exec exception", {err:err})
|
|
170
|
+
throw err;
|
|
171
|
+
} finally {
|
|
172
|
+
}
|
|
173
|
+
// OINOLog.debug("OINODbMsSql._exec", {result:query_result})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Print a table name using database specific SQL escaping.
|
|
178
|
+
*
|
|
179
|
+
* @param sqlTable name of the table
|
|
180
|
+
*
|
|
181
|
+
*/
|
|
182
|
+
printSqlTablename(sqlTable:string): string {
|
|
183
|
+
return "["+sqlTable+"]"
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Print a column name with correct SQL escaping.
|
|
188
|
+
*
|
|
189
|
+
* @param sqlColumn name of the column
|
|
190
|
+
*
|
|
191
|
+
*/
|
|
192
|
+
printSqlColumnname(sqlColumn:string): string {
|
|
193
|
+
return "["+sqlColumn+"]"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Print a single data value from serialization using the context of the native data
|
|
199
|
+
* type with the correct SQL escaping.
|
|
200
|
+
*
|
|
201
|
+
* @param cellValue data from sql results
|
|
202
|
+
* @param sqlType native type name for table column
|
|
203
|
+
*
|
|
204
|
+
*/
|
|
205
|
+
printCellAsSqlValue(cellValue:OINODataCell, sqlType: string): string {
|
|
206
|
+
// OINOLog.debug("OINODbMsSql.printCellAsSqlValue", {cellValue:cellValue, sqlType:sqlType})
|
|
207
|
+
if (cellValue === null) {
|
|
208
|
+
return "NULL"
|
|
209
|
+
|
|
210
|
+
} else if (cellValue === undefined) {
|
|
211
|
+
return "UNDEFINED"
|
|
212
|
+
|
|
213
|
+
} else if ((sqlType == "int") || (sqlType == "smallint") || (sqlType == "float")) {
|
|
214
|
+
return cellValue.toString()
|
|
215
|
+
|
|
216
|
+
} else if ((sqlType == "longblob") || (sqlType == "binary") || (sqlType == "varbinary")) {
|
|
217
|
+
if (cellValue instanceof Buffer) {
|
|
218
|
+
return "0x" + (cellValue as Buffer).toString("hex") + ""
|
|
219
|
+
} else if (cellValue instanceof Uint8Array) {
|
|
220
|
+
return "0x" + Buffer.from(cellValue as Uint8Array).toString("hex") + ""
|
|
221
|
+
} else {
|
|
222
|
+
return "'" + cellValue?.toString() + "'"
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
} else if (((sqlType == "date") || (sqlType == "datetime") || (sqlType == "datetime2") || (sqlType == "timestamp")) && (cellValue instanceof Date)) {
|
|
226
|
+
return "'" + cellValue.toISOString().substring(0, 23) + "'"
|
|
227
|
+
|
|
228
|
+
} else {
|
|
229
|
+
return this.printSqlString(cellValue.toString())
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Print a single string value as valid sql literal
|
|
235
|
+
*
|
|
236
|
+
* @param sqlString string value
|
|
237
|
+
*
|
|
238
|
+
*/
|
|
239
|
+
printSqlString(sqlString:string): string {
|
|
240
|
+
return "'" + sqlString.replaceAll("'", "''") + "'"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Parse a single SQL result value for serialization using the context of the native data
|
|
245
|
+
* type.
|
|
246
|
+
*
|
|
247
|
+
* @param sqlValue data from serialization
|
|
248
|
+
* @param sqlType native type name for table column
|
|
249
|
+
*
|
|
250
|
+
*/
|
|
251
|
+
parseSqlValueAsCell(sqlValue:OINODataCell, sqlType: string): OINODataCell {
|
|
252
|
+
// OINOLog.debug("OINODbMsSql.parseSqlValueAsCell", {sqlValue:sqlValue, sqlType:sqlType})
|
|
253
|
+
if ((sqlValue === null) || (sqlValue == "NULL")) {
|
|
254
|
+
return null
|
|
255
|
+
|
|
256
|
+
} else if (sqlValue === undefined) {
|
|
257
|
+
return undefined
|
|
258
|
+
|
|
259
|
+
} else if (((sqlType == "date") || (sqlType == "datetime") || (sqlType == "datetime2")) && (typeof(sqlValue) == "string")) {
|
|
260
|
+
return new Date(sqlValue)
|
|
261
|
+
|
|
262
|
+
} else {
|
|
263
|
+
return sqlValue
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Print SQL select statement with DB specific formatting.
|
|
270
|
+
*
|
|
271
|
+
* @param tableName - The name of the table to select from.
|
|
272
|
+
* @param columnNames - The columns to be selected.
|
|
273
|
+
* @param whereCondition - The WHERE clause to filter the results.
|
|
274
|
+
* @param orderCondition - The ORDER BY clause to sort the results.
|
|
275
|
+
* @param limitCondition - The LIMIT clause to limit the number of results.
|
|
276
|
+
* @param groupByCondition - The GROUP BY clause to group the results.
|
|
277
|
+
*
|
|
278
|
+
*/
|
|
279
|
+
printSqlSelect(tableName:string, columnNames:string, whereCondition:string, orderCondition:string, limitCondition:string, groupByCondition: string): string {
|
|
280
|
+
const limit_parts = limitCondition.split(" OFFSET ")
|
|
281
|
+
let result:string = "SELECT "
|
|
282
|
+
if ((limitCondition != "") && (limit_parts.length == 1)) {
|
|
283
|
+
result += "TOP " + limit_parts[0] + " "
|
|
284
|
+
}
|
|
285
|
+
result += columnNames + " FROM " + tableName
|
|
286
|
+
// OINOLog.debug("OINODb.printSqlSelect", {tableName:tableName, columnNames:columnNames, whereCondition:whereCondition, orderCondition:orderCondition, limitCondition:limitCondition })
|
|
287
|
+
if (whereCondition != "") {
|
|
288
|
+
result += " WHERE " + whereCondition
|
|
289
|
+
}
|
|
290
|
+
if (orderCondition != "") {
|
|
291
|
+
result += " ORDER BY " + orderCondition
|
|
292
|
+
}
|
|
293
|
+
if ((limitCondition != "") && (limit_parts.length == 2)) {
|
|
294
|
+
if (orderCondition == "") {
|
|
295
|
+
OINOLog.error("OINODbMsSql.printSqlSelect: LIMIT without ORDER BY is not supported in MS SQL Server")
|
|
296
|
+
} else {
|
|
297
|
+
result += " OFFSET " + limit_parts[1] + " ROWS FETCH NEXT " + limit_parts[0] + " ROWS ONLY"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (groupByCondition != "") {
|
|
301
|
+
result += " GROUP BY " + groupByCondition
|
|
302
|
+
}
|
|
303
|
+
result += ";"
|
|
304
|
+
// OINOLog.debug("OINODb.printSqlSelect", {result:result})
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Connect to database.
|
|
310
|
+
*
|
|
311
|
+
*/
|
|
312
|
+
async connect(): Promise<boolean> {
|
|
313
|
+
try {
|
|
314
|
+
// make sure that any items are correctly URL encoded in the connection string
|
|
315
|
+
await this._pool.connect()
|
|
316
|
+
// OINOLog.info("OINODbMsSql.connect: Connected to database server.")
|
|
317
|
+
await this._pool.request().query("SELECT 1 as test")
|
|
318
|
+
return Promise.resolve(true)
|
|
319
|
+
} catch (err) {
|
|
320
|
+
// ... error checks
|
|
321
|
+
throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMsSql server: " + err)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Execute a select operation.
|
|
327
|
+
*
|
|
328
|
+
* @param sql SQL statement.
|
|
329
|
+
*
|
|
330
|
+
*/
|
|
331
|
+
async sqlSelect(sql:string): Promise<OINODbDataSet> {
|
|
332
|
+
OINOBenchmark.start("OINODb", "sqlSelect")
|
|
333
|
+
let result:OINODbDataSet
|
|
334
|
+
try {
|
|
335
|
+
// OINOLog.debug("OINODbMsSql.sqlSelect", {sql_rows:sql_rows})
|
|
336
|
+
result = await this._query(sql)
|
|
337
|
+
|
|
338
|
+
} catch (e:any) {
|
|
339
|
+
result = new OINOMsSqlData([[]], [OINO_ERROR_PREFIX + " (sqlSelect): OINODbMsSql.sqlSelect exception in _db.query: " + e.message])
|
|
340
|
+
}
|
|
341
|
+
OINOBenchmark.end("OINODb", "sqlSelect")
|
|
342
|
+
return result
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Execute other sql operations.
|
|
347
|
+
*
|
|
348
|
+
* @param sql SQL statement.
|
|
349
|
+
*
|
|
350
|
+
*/
|
|
351
|
+
async sqlExec(sql:string): Promise<OINODbDataSet> {
|
|
352
|
+
OINOBenchmark.start("OINODb", "sqlExec")
|
|
353
|
+
let result:OINODbDataSet
|
|
354
|
+
try {
|
|
355
|
+
result = await this._exec(sql)
|
|
356
|
+
|
|
357
|
+
} catch (e:any) {
|
|
358
|
+
result = new OINOMsSqlData([[]], [OINO_ERROR_PREFIX + " (sqlExec): exception in _db.exec [" + e.message + "]"])
|
|
359
|
+
}
|
|
360
|
+
OINOBenchmark.end("OINODb", "sqlExec")
|
|
361
|
+
return result
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private _getSchemaSql(dbName:string, tableName:string):string {
|
|
365
|
+
const sql =
|
|
366
|
+
`SELECT
|
|
367
|
+
C.COLUMN_NAME,
|
|
368
|
+
C.IS_NULLABLE,
|
|
369
|
+
C.DATA_TYPE,
|
|
370
|
+
C.CHARACTER_MAXIMUM_LENGTH,
|
|
371
|
+
C.NUMERIC_PRECISION,
|
|
372
|
+
C.NUMERIC_PRECISION_RADIX,
|
|
373
|
+
CONST.CONSTRAINT_TYPES,
|
|
374
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
|
|
375
|
+
COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
|
|
376
|
+
FROM
|
|
377
|
+
INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
|
|
378
|
+
(
|
|
379
|
+
SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
|
|
380
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
|
|
381
|
+
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
|
|
382
|
+
GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
|
|
383
|
+
) as CONST
|
|
384
|
+
ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
|
|
385
|
+
WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
|
|
386
|
+
ORDER BY C.ORDINAL_POSITION;`
|
|
387
|
+
return sql
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Initialize a data model by getting the SQL schema and populating OINODbDataFields of
|
|
391
|
+
* the model.
|
|
392
|
+
*
|
|
393
|
+
* @param api api which data model to initialize.
|
|
394
|
+
*
|
|
395
|
+
*/
|
|
396
|
+
async initializeApiDatamodel(api:OINODbApi): Promise<void> {
|
|
397
|
+
|
|
398
|
+
//"SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX
|
|
399
|
+
const res:OINODbDataSet = await this.sqlSelect(this._getSchemaSql(this._params.database, api.params.tableName))
|
|
400
|
+
while (!res.isEof()) {
|
|
401
|
+
const row:OINODataRow = res.getRow()
|
|
402
|
+
// OINOLog.debug("OINODbMsSql.initializeApiDatamodel", { description:row })
|
|
403
|
+
const field_name:string = row[0]?.toString() || ""
|
|
404
|
+
const sql_type:string = row[2] as string || ""
|
|
405
|
+
const char_field_length:number = row[3] as number || 0
|
|
406
|
+
const numeric_field_length1:number = row[4] as number || 0
|
|
407
|
+
const numeric_field_length2:number = row[5] as number || 0
|
|
408
|
+
const constraint_types:string = row[6] as string || ""
|
|
409
|
+
const field_params:OINODbDataFieldParams = {
|
|
410
|
+
isPrimaryKey: constraint_types.indexOf("PRIMARY KEY") >= 0,
|
|
411
|
+
isForeignKey: constraint_types.indexOf("FOREIGN KEY") >= 0,
|
|
412
|
+
isAutoInc: row[7] == 1,
|
|
413
|
+
isNotNull: row[1] == "NO"
|
|
414
|
+
}
|
|
415
|
+
if (api.isFieldIncluded(field_name) == false) {
|
|
416
|
+
OINOLog.info("OINODbMsSql.initializeApiDatamodel: field excluded in API parameters.", {field:field_name})
|
|
417
|
+
if (field_params.isPrimaryKey) {
|
|
418
|
+
throw new Error(OINO_ERROR_PREFIX + "Primary key field excluded in API parameters: " + field_name)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
} else {
|
|
422
|
+
// OINOLog.debug("OINODbMsSql.initializeApiDatamodel: next field ", {field_name: field_name, sql_type:sql_type, char_field_length:char_field_length, numeric_field_length1:numeric_field_length1, numeric_field_length2:numeric_field_length2, field_params:field_params })
|
|
423
|
+
if ((sql_type == "tinyint") || (sql_type == "smallint") || (sql_type == "int") || (sql_type == "bigint") || (sql_type == "float") || (sql_type == "real")) {
|
|
424
|
+
api.datamodel.addField(new OINONumberDataField(this, field_name, sql_type, field_params ))
|
|
425
|
+
|
|
426
|
+
} else if ((sql_type == "date") || (sql_type == "datetime") || (sql_type == "datetime2")) {
|
|
427
|
+
if (api.params.useDatesAsString) {
|
|
428
|
+
api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
|
|
429
|
+
} else {
|
|
430
|
+
api.datamodel.addField(new OINODatetimeDataField(this, field_name, sql_type, field_params))
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
} else if ((sql_type == "ntext") || (sql_type == "nchar") || (sql_type == "nvarchar") || (sql_type == "text") || (sql_type == "char") || (sql_type == "varchar")) {
|
|
434
|
+
api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, char_field_length))
|
|
435
|
+
|
|
436
|
+
} else if ((sql_type == "binary") || (sql_type == "varbinary") || (sql_type == "image")) {
|
|
437
|
+
api.datamodel.addField(new OINOBlobDataField(this, field_name, sql_type, field_params, char_field_length))
|
|
438
|
+
|
|
439
|
+
} else if ((sql_type == "numeric") || (sql_type == "decimal") || (sql_type == "money")) {
|
|
440
|
+
api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, numeric_field_length1 + numeric_field_length2 + 1))
|
|
441
|
+
|
|
442
|
+
} else if ((sql_type == "bit")) {
|
|
443
|
+
api.datamodel.addField(new OINOBooleanDataField(this, field_name, sql_type, field_params))
|
|
444
|
+
|
|
445
|
+
} else {
|
|
446
|
+
OINOLog.info("OINODbMsSql.initializeApiDatamodel: unrecognized field type treated as string", {field_name: field_name, sql_type:sql_type, char_length: char_field_length, numeric_field_length1:numeric_field_length1, numeric_field_length2:numeric_field_length2, field_params:field_params })
|
|
447
|
+
api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
await res.next()
|
|
451
|
+
}
|
|
452
|
+
OINOLog.debug("OINODbMsSql.initializeDatasetModel:\n" + api.datamodel.printDebug("\n"))
|
|
453
|
+
return Promise.resolve()
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { OINODbMsSql } from "./OINODbMsSql.js"
|
|
1
|
+
export { OINODbMsSql } from "./OINODbMsSql.js"
|