@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.
@@ -214,10 +214,18 @@ class OINODbMsSql extends db_1.OINODb {
214
214
  return "'" + cellValue.toISOString().substring(0, 23) + "'";
215
215
  }
216
216
  else {
217
- // return "'" + cellValue?.toString().replaceAll("\\", "\\\\").replaceAll("\'", "''").replaceAll("\r", "\\r").replaceAll("\n", "\\n").replaceAll("\t", "\\t") + "'"
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
  }
@@ -211,10 +211,18 @@ export class OINODbMsSql extends OINODb {
211
211
  return "'" + cellValue.toISOString().substring(0, 23) + "'";
212
212
  }
213
213
  else {
214
- // return "'" + cellValue?.toString().replaceAll("\\", "\\\\").replaceAll("\'", "''").replaceAll("\r", "\\r").replaceAll("\n", "\\n").replaceAll("\t", "\\t") + "'"
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.3.3",
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.3.3",
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
+ }
@@ -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
- // return "'" + cellValue?.toString().replaceAll("\\", "\\\\").replaceAll("\'", "''").replaceAll("\r", "\\r").replaceAll("\n", "\\n").replaceAll("\t", "\\t") + "'"
230
- return "'" + cellValue?.toString().replaceAll("\'", "''") + "'"
231
- }
232
- }
233
-
234
- /**
235
- * Parse a single SQL result value for serialization using the context of the native data
236
- * type.
237
- *
238
- * @param sqlValue data from serialization
239
- * @param sqlType native type name for table column
240
- *
241
- */
242
- parseSqlValueAsCell(sqlValue:OINODataCell, sqlType: string): OINODataCell {
243
- // OINOLog.debug("OINODbMsSql.parseSqlValueAsCell", {sqlValue:sqlValue, sqlType:sqlType})
244
- if ((sqlValue === null) || (sqlValue == "NULL")) {
245
- return null
246
-
247
- } else if (sqlValue === undefined) {
248
- return undefined
249
-
250
- } else if (((sqlType == "date") || (sqlType == "datetime") || (sqlType == "datetime2")) && (typeof(sqlValue) == "string")) {
251
- return new Date(sqlValue)
252
-
253
- } else {
254
- return sqlValue
255
- }
256
-
257
- }
258
-
259
- /**
260
- * Print SQL select statement with DB specific formatting.
261
- *
262
- * @param tableName - The name of the table to select from.
263
- * @param columnNames - The columns to be selected.
264
- * @param whereCondition - The WHERE clause to filter the results.
265
- * @param orderCondition - The ORDER BY clause to sort the results.
266
- * @param limitCondition - The LIMIT clause to limit the number of results.
267
- * @param groupByCondition - The GROUP BY clause to group the results.
268
- *
269
- */
270
- printSqlSelect(tableName:string, columnNames:string, whereCondition:string, orderCondition:string, limitCondition:string, groupByCondition: string): string {
271
- const limit_parts = limitCondition.split(" OFFSET ")
272
- let result:string = "SELECT "
273
- if ((limitCondition != "") && (limit_parts.length == 1)) {
274
- result += "TOP " + limit_parts[0] + " "
275
- }
276
- result += columnNames + " FROM " + tableName
277
- // OINOLog.debug("OINODb.printSqlSelect", {tableName:tableName, columnNames:columnNames, whereCondition:whereCondition, orderCondition:orderCondition, limitCondition:limitCondition })
278
- if (whereCondition != "") {
279
- result += " WHERE " + whereCondition
280
- }
281
- if (orderCondition != "") {
282
- result += " ORDER BY " + orderCondition
283
- }
284
- if ((limitCondition != "") && (limit_parts.length == 2)) {
285
- if (orderCondition == "") {
286
- OINOLog.error("OINODbMsSql.printSqlSelect: LIMIT without ORDER BY is not supported in MS SQL Server")
287
- } else {
288
- result += " OFFSET " + limit_parts[1] + " ROWS FETCH NEXT " + limit_parts[0] + " ROWS ONLY"
289
- }
290
- }
291
- if (groupByCondition != "") {
292
- result += " GROUP BY " + groupByCondition
293
- }
294
- result += ";"
295
- // OINOLog.debug("OINODb.printSqlSelect", {result:result})
296
- return result;
297
- }
298
-
299
- /**
300
- * Connect to database.
301
- *
302
- */
303
- async connect(): Promise<boolean> {
304
- try {
305
- // make sure that any items are correctly URL encoded in the connection string
306
- await this._pool.connect()
307
- // OINOLog.info("OINODbMsSql.connect: Connected to database server.")
308
- await this._pool.request().query("SELECT 1 as test")
309
- return Promise.resolve(true)
310
- } catch (err) {
311
- // ... error checks
312
- throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMsSql server: " + err)
313
- }
314
- }
315
-
316
- /**
317
- * Execute a select operation.
318
- *
319
- * @param sql SQL statement.
320
- *
321
- */
322
- async sqlSelect(sql:string): Promise<OINODbDataSet> {
323
- OINOBenchmark.start("OINODb", "sqlSelect")
324
- let result:OINODbDataSet
325
- try {
326
- // OINOLog.debug("OINODbMsSql.sqlSelect", {sql_rows:sql_rows})
327
- result = await this._query(sql)
328
-
329
- } catch (e:any) {
330
- result = new OINOMsSqlData([[]], [OINO_ERROR_PREFIX + " (sqlSelect): OINODbMsSql.sqlSelect exception in _db.query: " + e.message])
331
- }
332
- OINOBenchmark.end("OINODb", "sqlSelect")
333
- return result
334
- }
335
-
336
- /**
337
- * Execute other sql operations.
338
- *
339
- * @param sql SQL statement.
340
- *
341
- */
342
- async sqlExec(sql:string): Promise<OINODbDataSet> {
343
- OINOBenchmark.start("OINODb", "sqlExec")
344
- let result:OINODbDataSet
345
- try {
346
- result = await this._exec(sql)
347
-
348
- } catch (e:any) {
349
- result = new OINOMsSqlData([[]], [OINO_ERROR_PREFIX + " (sqlExec): exception in _db.exec [" + e.message + "]"])
350
- }
351
- OINOBenchmark.end("OINODb", "sqlExec")
352
- return result
353
- }
354
-
355
- private _getSchemaSql(dbName:string, tableName:string):string {
356
- const sql =
357
- `SELECT
358
- C.COLUMN_NAME,
359
- C.IS_NULLABLE,
360
- C.DATA_TYPE,
361
- C.CHARACTER_MAXIMUM_LENGTH,
362
- C.NUMERIC_PRECISION,
363
- C.NUMERIC_PRECISION_RADIX,
364
- CONST.CONSTRAINT_TYPES,
365
- COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsIdentity') AS IS_AUTO_INCREMENT,
366
- COLUMNPROPERTY(OBJECT_ID(C.TABLE_SCHEMA + '.' + C.TABLE_NAME), C.COLUMN_NAME, 'IsComputed') AS IS_COMPUTED
367
- FROM
368
- INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
369
- (
370
- SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
371
- FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
372
- INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
373
- GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
374
- ) as CONST
375
- ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
376
- WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
377
- ORDER BY C.ORDINAL_POSITION;`
378
- return sql
379
- }
380
- /**
381
- * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
382
- * the model.
383
- *
384
- * @param api api which data model to initialize.
385
- *
386
- */
387
- async initializeApiDatamodel(api:OINODbApi): Promise<void> {
388
-
389
- //"SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX
390
- const res:OINODbDataSet = await this.sqlSelect(this._getSchemaSql(this._params.database, api.params.tableName))
391
- while (!res.isEof()) {
392
- const row:OINODataRow = res.getRow()
393
- // OINOLog.debug("OINODbMsSql.initializeApiDatamodel", { description:row })
394
- const field_name:string = row[0]?.toString() || ""
395
- const sql_type:string = row[2] as string || ""
396
- const char_field_length:number = row[3] as number || 0
397
- const numeric_field_length1:number = row[4] as number || 0
398
- const numeric_field_length2:number = row[5] as number || 0
399
- const constraint_types:string = row[6] as string || ""
400
- const field_params:OINODbDataFieldParams = {
401
- isPrimaryKey: constraint_types.indexOf("PRIMARY KEY") >= 0,
402
- isForeignKey: constraint_types.indexOf("FOREIGN KEY") >= 0,
403
- isAutoInc: row[7] == 1,
404
- isNotNull: row[1] == "NO"
405
- }
406
- if (api.isFieldIncluded(field_name) == false) {
407
- OINOLog.info("OINODbMsSql.initializeApiDatamodel: field excluded in API parameters.", {field:field_name})
408
- if (field_params.isPrimaryKey) {
409
- throw new Error(OINO_ERROR_PREFIX + "Primary key field excluded in API parameters: " + field_name)
410
- }
411
-
412
- } else {
413
- // 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 })
414
- if ((sql_type == "tinyint") || (sql_type == "smallint") || (sql_type == "int") || (sql_type == "bigint") || (sql_type == "float") || (sql_type == "real")) {
415
- api.datamodel.addField(new OINONumberDataField(this, field_name, sql_type, field_params ))
416
-
417
- } else if ((sql_type == "date") || (sql_type == "datetime") || (sql_type == "datetime2")) {
418
- if (api.params.useDatesAsString) {
419
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
420
- } else {
421
- api.datamodel.addField(new OINODatetimeDataField(this, field_name, sql_type, field_params))
422
- }
423
-
424
- } else if ((sql_type == "ntext") || (sql_type == "nchar") || (sql_type == "nvarchar") || (sql_type == "text") || (sql_type == "char") || (sql_type == "varchar")) {
425
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, char_field_length))
426
-
427
- } else if ((sql_type == "binary") || (sql_type == "varbinary") || (sql_type == "image")) {
428
- api.datamodel.addField(new OINOBlobDataField(this, field_name, sql_type, field_params, char_field_length))
429
-
430
- } else if ((sql_type == "numeric") || (sql_type == "decimal") || (sql_type == "money")) {
431
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, numeric_field_length1 + numeric_field_length2 + 1))
432
-
433
- } else if ((sql_type == "bit")) {
434
- api.datamodel.addField(new OINOBooleanDataField(this, field_name, sql_type, field_params))
435
-
436
- } else {
437
- 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 })
438
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
439
- }
440
- }
441
- await res.next()
442
- }
443
- OINOLog.debug("OINODbMsSql.initializeDatasetModel:\n" + api.datamodel.printDebug("\n"))
444
- return Promise.resolve()
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"