@oino-ts/db-bunsqlite 0.16.2 → 0.17.1

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/package.json CHANGED
@@ -1,36 +1,36 @@
1
- {
2
- "name": "@oino-ts/db-bunsqlite",
3
- "version": "0.16.2",
4
- "description": "OINO TS package for using Bun Sqlite 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
- "sqlite"
18
- ],
19
- "main": "./dist/cjs/index.js",
20
- "module": "./dist/esm/index.js",
21
- "types": "./dist/types/index.d.ts",
22
- "dependencies": {
23
- "@oino-ts/db": "^0.16.2"
24
- },
25
- "devDependencies": {
26
- "@types/bun": "latest",
27
- "@types/node": "^20.12.7",
28
- "typescript": "~5.9.0"
29
- },
30
- "files": [
31
- "src/*.ts",
32
- "dist/cjs/*.js",
33
- "dist/esm/*.js",
34
- "dist/types/*.d.ts"
35
- ]
36
- }
1
+ {
2
+ "name": "@oino-ts/db-bunsqlite",
3
+ "version": "0.17.1",
4
+ "description": "OINO TS package for using Bun Sqlite 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
+ "sqlite"
18
+ ],
19
+ "main": "./dist/cjs/index.js",
20
+ "module": "./dist/esm/index.js",
21
+ "types": "./dist/types/index.d.ts",
22
+ "dependencies": {
23
+ "@oino-ts/db": "^0.17.1"
24
+ },
25
+ "devDependencies": {
26
+ "@types/bun": "latest",
27
+ "@types/node": "^20.12.7",
28
+ "typescript": "~5.9.0"
29
+ },
30
+ "files": [
31
+ "src/*.ts",
32
+ "dist/cjs/*.js",
33
+ "dist/esm/*.js",
34
+ "dist/types/*.d.ts"
35
+ ]
36
+ }
@@ -1,347 +1,347 @@
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, OINODbMemoryDataSet, OINODataCell, OINOBenchmark, OINOBlobDataField, OINODatetimeDataField, OINOStr, OINOLog, OINOResult, OINODB_EMPTY_ROWS } from "@oino-ts/db";
8
-
9
- import { Database as BunSqliteDb } from "bun:sqlite";
10
-
11
- /**
12
- * Implmentation of OINODbDataSet for BunSqlite.
13
- *
14
- */
15
- class OINOBunSqliteDataset extends OINODbMemoryDataSet {
16
- constructor(data: unknown, messages:string[]=[]) {
17
- super(data, messages)
18
- }
19
- }
20
-
21
- /**
22
- * Implementation of BunSqlite-database.
23
- *
24
- */
25
- export class OINODbBunSqlite extends OINODb {
26
- private static _tableDescriptionRegex = /^CREATE TABLE\s*[\"\[]?\w+[\"\]]?\s*\(\s*(.*)\s*\)\s*(WITHOUT ROWID)?$/msi
27
- private static _tablePrimarykeyRegex = /PRIMARY KEY \(([^\)]+)\)/i
28
- private static _tableForeignkeyRegex = /FOREIGN KEY \(\[([^\)]+)\]\)/i
29
- private static _tableFieldTypeRegex = /[\"\[\s]?(\w+)[\"\]\s]\s?(INTEGER|REAL|DOUBLE|NUMERIC|DECIMAL|TEXT|BLOB|VARCHAR|DATETIME|DATE|BOOLEAN)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i
30
-
31
- private _db:BunSqliteDb|null
32
-
33
- /**
34
- * OINODbBunSqlite constructor
35
- * @param params database parameters
36
- */
37
- constructor(params:OINODbParams) {
38
- super(params)
39
- this._db = null
40
- if (!this._params.url.startsWith("file://")) {
41
- throw new Error(OINO_ERROR_PREFIX + ": OINODbBunSqlite url must be a file://-url!")
42
- }
43
-
44
- if (this._params.type !== "OINODbBunSqlite") {
45
- throw new Error(OINO_ERROR_PREFIX + ": Not OINODbBunSqlite-type: " + this._params.type)
46
- }
47
- }
48
-
49
- private _parseDbFieldParams(fieldStr:string): OINODbDataFieldParams {
50
- const result:OINODbDataFieldParams = {
51
- isPrimaryKey: fieldStr.indexOf("PRIMARY KEY") >= 0,
52
- isForeignKey: false,
53
- isAutoInc: fieldStr.indexOf("AUTOINCREMENT") >= 0,
54
- isNotNull: fieldStr.indexOf("NOT NULL") >= 0
55
- }
56
- return result
57
- }
58
-
59
- /**
60
- * Print a table name using database specific SQL escaping.
61
- *
62
- * @param sqlTable name of the table
63
- *
64
- */
65
- printSqlTablename(sqlTable:string): string {
66
- return "["+sqlTable+"]"
67
- }
68
-
69
- /**
70
- * Print a column name with correct SQL escaping.
71
- *
72
- * @param sqlColumn name of the column
73
- *
74
- */
75
- printSqlColumnname(sqlColumn:string): string {
76
- return "\""+sqlColumn+"\""
77
- }
78
-
79
- /**
80
- * Print a single data value from serialization using the context of the native data
81
- * type with the correct SQL escaping.
82
- *
83
- * @param cellValue data from sql results
84
- * @param sqlType native type name for table column
85
- *
86
- */
87
- printCellAsSqlValue(cellValue:OINODataCell, sqlType: string): string {
88
- if (cellValue === null) {
89
- return "NULL"
90
-
91
- } else if (cellValue === undefined) {
92
- return "UNDEFINED"
93
-
94
- } else if ((sqlType == "INTEGER") || (sqlType == "REAL") || (sqlType == "DOUBLE" || (sqlType == "NUMERIC") || (sqlType == "DECIMAL"))) {
95
- return cellValue.toString()
96
-
97
- } else if (sqlType == "BLOB") {
98
- if (cellValue instanceof Buffer) {
99
- return "X'" + (cellValue as Buffer).toString("hex") + "'"
100
- } else if (cellValue instanceof Uint8Array) {
101
- return "X'" + Buffer.from(cellValue as Uint8Array).toString("hex") + "'"
102
- } else {
103
- return "'" + cellValue?.toString() + "'"
104
- }
105
-
106
- } else if (((sqlType == "DATETIME") || (sqlType == "DATE")) && (cellValue instanceof Date)) {
107
- return "\'" + cellValue.toISOString() + "\'"
108
-
109
- } else {
110
- return this.printSqlString(cellValue.toString())
111
- }
112
- }
113
-
114
- /**
115
- * Print a single string value as valid sql literal
116
- *
117
- * @param sqlString string value
118
- *
119
- */
120
- printSqlString(sqlString:string): string {
121
- return "\"" + sqlString.replaceAll("\"", "\"\"") + "\""
122
- }
123
-
124
- /**
125
- * Parse a single SQL result value for serialization using the context of the native data
126
- * type.
127
- *
128
- * @param sqlValue data from serialization
129
- * @param sqlType native type name for table column
130
- *
131
- */
132
- parseSqlValueAsCell(sqlValue:OINODataCell, sqlType: string): OINODataCell {
133
- if ((sqlValue === null) || (sqlValue == "NULL")) {
134
- return null
135
-
136
- } else if (sqlValue === undefined) {
137
- return undefined
138
-
139
- } else if (((sqlType == "DATETIME") || (sqlType == "DATE")) && (typeof(sqlValue) == "string") && (sqlValue != "")) {
140
- return new Date(sqlValue)
141
-
142
- } else if ((sqlType == "BOOLEAN")) {
143
- return sqlValue == 1
144
-
145
- } else if ((sqlType == "BLOB")) {
146
- if (sqlValue instanceof Uint8Array) {
147
- return Buffer.from(sqlValue)
148
- } else {
149
- return sqlValue
150
- }
151
- } else {
152
- return sqlValue
153
- }
154
-
155
- }
156
-
157
-
158
- /**
159
- * Connect to database.
160
- *
161
- */
162
- async connect(): Promise<OINOResult> {
163
- OINOBenchmark.startMetric("OINODb", "connect")
164
- let result:OINOResult = new OINOResult()
165
- const filepath:string = this._params.url.substring(7)
166
- try {
167
- this._db = BunSqliteDb.open(filepath, { create: true, readonly: false, readwrite: true })
168
- this.isConnected = true
169
- } catch (e:any) {
170
- result.setError(500, "Exception connecting to database: " + e.message, "OINODbBunSqlite.connect")
171
- OINOLog.exception("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "connect", "exception in connect", {message:e.message, stack:e.stack})
172
- }
173
- OINOBenchmark.endMetric("OINODb", "connect")
174
- return result
175
- }
176
-
177
- /**
178
- * Validate connection to database is working.
179
- *
180
- */
181
- async validate(): Promise<OINOResult> {
182
- OINOBenchmark.startMetric("OINODb", "validate")
183
- let result:OINOResult = new OINOResult()
184
- try {
185
- const sql = this._getValidateSql(this._params.database)
186
- const sql_res:OINODbDataSet = await this.sqlSelect(sql)
187
- if (sql_res.isEmpty()) {
188
- result.setError(400, "DB returned no rows for select!", "OINODbBunSqlite.validate")
189
-
190
- } else if (sql_res.getRow().length == 0) {
191
- result.setError(400, "DB returned no values for database!", "OINODbBunSqlite.validate")
192
-
193
- } else if (sql_res.getRow()[0] == "0") {
194
- result.setError(400, "DB returned no schema for database!", "OINODbBunSqlite.validate")
195
-
196
- } else {
197
- this.isValidated = true
198
- }
199
- } catch (e:any) {
200
- result.setError(500, OINO_ERROR_PREFIX + " (validate): OINODbBunSqlite.validate exception in _db.query: " + e.message, "OINODbBunSqlite.validate")
201
- }
202
- OINOBenchmark.endMetric("OINODb", "validate")
203
- return result
204
- }
205
-
206
- /**
207
- * Execute a select operation.
208
- *
209
- * @param sql SQL statement.
210
- *
211
- */
212
- async sqlSelect(sql:string): Promise<OINODbDataSet> {
213
- OINOBenchmark.startMetric("OINODb", "sqlSelect")
214
- let result:OINODbDataSet
215
- try {
216
- result = new OINOBunSqliteDataset(this._db?.query(sql).values(), [])
217
-
218
- } catch (e:any) {
219
- result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, ["OINODbBunSqlite.sqlSelect exception in _db.query: " + e.message])
220
- }
221
- OINOBenchmark.endMetric("OINODb", "sqlSelect")
222
- return Promise.resolve(result)
223
- }
224
-
225
- /**
226
- * Execute other sql operations.
227
- *
228
- * @param sql SQL statement.
229
- *
230
- */
231
- async sqlExec(sql:string): Promise<OINODbDataSet> {
232
- OINOBenchmark.startMetric("OINODb", "sqlExec")
233
- let result:OINODbDataSet
234
- try {
235
- this._db?.exec(sql)
236
- result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, [])
237
-
238
- } catch (e:any) {
239
- result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + "(sqlExec): exception in _db.exec [" + e.message + "]"])
240
- }
241
- OINOBenchmark.endMetric("OINODb", "sqlExec")
242
- return Promise.resolve(result)
243
- }
244
-
245
- private _getSchemaSql(dbName:string, tableName:string):string {
246
- const sql = "SELECT sql from sqlite_schema WHERE name='" + tableName + "'"
247
- return sql
248
- }
249
-
250
- private _getValidateSql(dbName:string):string {
251
- const sql = "SELECT count(*) as COLUMN_COUNT from sqlite_schema"
252
- return sql
253
- }
254
-
255
- /**
256
- * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
257
- * the model.
258
- *
259
- * @param api api which data model to initialize.
260
- *
261
- */
262
- async initializeApiDatamodel(api:OINODbApi): Promise<void> {
263
- const schema_sql:string = this._getSchemaSql(this._params.database, api.params.tableName)
264
- const res:OINODbDataSet|null = await this.sqlSelect(schema_sql)
265
- const sql_desc:string = (res?.getRow()[0]) as string
266
- const excluded_fields:string[] = []
267
- let table_matches = OINODbBunSqlite._tableDescriptionRegex.exec(sql_desc)
268
- if (!table_matches || table_matches?.length < 2) {
269
- throw new Error("Table " + api.params.tableName + " not recognized as a valid Sqlite table!")
270
-
271
- } else {
272
- let field_strings:string[] = OINOStr.splitExcludingBrackets(table_matches[1], ',', '(', ')')
273
- for (let field_str of field_strings) {
274
- field_str = field_str.trim()
275
- let field_params = this._parseDbFieldParams(field_str)
276
- let field_match = OINODbBunSqlite._tableFieldTypeRegex.exec(field_str)
277
- // console.log("OINODbBunSqlite.initializeApiDatamodel: field_match", field_match)
278
- if ((!field_match) || (field_match.length < 3)) {
279
- let primarykey_match = OINODbBunSqlite._tablePrimarykeyRegex.exec(field_str)
280
- let foreignkey_match = OINODbBunSqlite._tableForeignkeyRegex.exec(field_str)
281
- if (primarykey_match && primarykey_match.length >= 2) {
282
- const primary_keys:string[] = primarykey_match[1].replaceAll("\"", "").split(',') // not sure if will have space or not so split by comma and trim later
283
- for (let i:number=0; i<primary_keys.length; i++) {
284
- const pk:string = primary_keys[i].trim() //..the trim
285
- if (excluded_fields.indexOf(pk) >= 0) {
286
- throw new Error(OINO_ERROR_PREFIX + "Primary key field excluded in API parameters: " + pk)
287
- }
288
- for (let j:number=0; j<api.datamodel.fields.length; j++) {
289
- if (api.datamodel.fields[j].name == pk) {
290
- api.datamodel.fields[j].fieldParams.isPrimaryKey = true
291
- }
292
- }
293
- }
294
-
295
- } else if (foreignkey_match && foreignkey_match.length >= 2) {
296
- const fk:string = foreignkey_match[1].trim()
297
- for (let j:number=0; j<api.datamodel.fields.length; j++) {
298
- if (api.datamodel.fields[j].name == fk) {
299
- api.datamodel.fields[j].fieldParams.isForeignKey = true
300
- }
301
- }
302
-
303
- } else {
304
- OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Unsupported field definition skipped.", { field: field_str })
305
- }
306
-
307
- } else {
308
- // field_str = "NAME TYPE (M, N)" -> 1:NAME, 2:TYPE, 4:M, 5:N
309
- const field_name:string = field_match[1]
310
- const sql_type:string = field_match[2]
311
- const field_length:number = parseInt(field_match[4]) || 0
312
- if (api.isFieldIncluded(field_name) == false) {
313
- excluded_fields.push(field_name)
314
- OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Field excluded in API parameters.", {field:field_name})
315
-
316
- } else {
317
- if ((sql_type == "INTEGER") || (sql_type == "REAL") || (sql_type == "DOUBLE") || (sql_type == "NUMERIC") || (sql_type == "DECIMAL")) {
318
- api.datamodel.addField(new OINONumberDataField(this, field_name, sql_type, field_params ))
319
-
320
- } else if ((sql_type == "BLOB") ) {
321
- api.datamodel.addField(new OINOBlobDataField(this, field_name, sql_type, field_params, field_length))
322
-
323
- } else if ((sql_type == "TEXT")) {
324
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, field_length))
325
-
326
- } else if ((sql_type == "DATETIME") || (sql_type == "DATE")) {
327
- if (api.params.useDatesAsString) {
328
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
329
- } else {
330
- api.datamodel.addField(new OINODatetimeDataField(this, field_name, sql_type, field_params))
331
- }
332
-
333
- } else if ((sql_type == "BOOLEAN")) {
334
- api.datamodel.addField(new OINOBooleanDataField(this, field_name, sql_type, field_params))
335
-
336
- } else {
337
- OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Unrecognized field type treated as string", {field_name: field_name, sql_type:sql_type, field_length:field_length, field_params:field_params })
338
- api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
339
- }
340
- }
341
- }
342
- };
343
- OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "\n" + api.datamodel.printDebug("\n"))
344
- return Promise.resolve()
345
- }
346
- }
347
- }
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, OINODbMemoryDataSet, OINODataCell, OINOBenchmark, OINOBlobDataField, OINODatetimeDataField, OINOStr, OINOLog, OINOResult, OINODB_EMPTY_ROWS } from "@oino-ts/db";
8
+
9
+ import { Database as BunSqliteDb } from "bun:sqlite";
10
+
11
+ /**
12
+ * Implmentation of OINODbDataSet for BunSqlite.
13
+ *
14
+ */
15
+ class OINOBunSqliteDataset extends OINODbMemoryDataSet {
16
+ constructor(data: unknown, messages:string[]=[]) {
17
+ super(data, messages)
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Implementation of BunSqlite-database.
23
+ *
24
+ */
25
+ export class OINODbBunSqlite extends OINODb {
26
+ private static _tableDescriptionRegex = /^CREATE TABLE\s*[\"\[]?\w+[\"\]]?\s*\(\s*(.*)\s*\)\s*(WITHOUT ROWID)?$/msi
27
+ private static _tablePrimarykeyRegex = /PRIMARY KEY \(([^\)]+)\)/i
28
+ private static _tableForeignkeyRegex = /FOREIGN KEY \(\[([^\)]+)\]\)/i
29
+ private static _tableFieldTypeRegex = /[\"\[\s]?(\w+)[\"\]\s]\s?(INTEGER|REAL|DOUBLE|NUMERIC|DECIMAL|TEXT|BLOB|VARCHAR|DATETIME|DATE|BOOLEAN)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i
30
+
31
+ private _db:BunSqliteDb|null
32
+
33
+ /**
34
+ * OINODbBunSqlite constructor
35
+ * @param params database parameters
36
+ */
37
+ constructor(params:OINODbParams) {
38
+ super(params)
39
+ this._db = null
40
+ if (!this._params.url.startsWith("file://")) {
41
+ throw new Error(OINO_ERROR_PREFIX + ": OINODbBunSqlite url must be a file://-url!")
42
+ }
43
+
44
+ if (this._params.type !== "OINODbBunSqlite") {
45
+ throw new Error(OINO_ERROR_PREFIX + ": Not OINODbBunSqlite-type: " + this._params.type)
46
+ }
47
+ }
48
+
49
+ private _parseDbFieldParams(fieldStr:string): OINODbDataFieldParams {
50
+ const result:OINODbDataFieldParams = {
51
+ isPrimaryKey: fieldStr.indexOf("PRIMARY KEY") >= 0,
52
+ isForeignKey: false,
53
+ isAutoInc: fieldStr.indexOf("AUTOINCREMENT") >= 0,
54
+ isNotNull: fieldStr.indexOf("NOT NULL") >= 0
55
+ }
56
+ return result
57
+ }
58
+
59
+ /**
60
+ * Print a table name using database specific SQL escaping.
61
+ *
62
+ * @param sqlTable name of the table
63
+ *
64
+ */
65
+ printSqlTablename(sqlTable:string): string {
66
+ return "["+sqlTable+"]"
67
+ }
68
+
69
+ /**
70
+ * Print a column name with correct SQL escaping.
71
+ *
72
+ * @param sqlColumn name of the column
73
+ *
74
+ */
75
+ printSqlColumnname(sqlColumn:string): string {
76
+ return "\""+sqlColumn+"\""
77
+ }
78
+
79
+ /**
80
+ * Print a single data value from serialization using the context of the native data
81
+ * type with the correct SQL escaping.
82
+ *
83
+ * @param cellValue data from sql results
84
+ * @param sqlType native type name for table column
85
+ *
86
+ */
87
+ printCellAsSqlValue(cellValue:OINODataCell, sqlType: string): string {
88
+ if (cellValue === null) {
89
+ return "NULL"
90
+
91
+ } else if (cellValue === undefined) {
92
+ return "UNDEFINED"
93
+
94
+ } else if ((sqlType == "INTEGER") || (sqlType == "REAL") || (sqlType == "DOUBLE" || (sqlType == "NUMERIC") || (sqlType == "DECIMAL"))) {
95
+ return cellValue.toString()
96
+
97
+ } else if (sqlType == "BLOB") {
98
+ if (cellValue instanceof Buffer) {
99
+ return "X'" + (cellValue as Buffer).toString("hex") + "'"
100
+ } else if (cellValue instanceof Uint8Array) {
101
+ return "X'" + Buffer.from(cellValue as Uint8Array).toString("hex") + "'"
102
+ } else {
103
+ return "'" + cellValue?.toString() + "'"
104
+ }
105
+
106
+ } else if (((sqlType == "DATETIME") || (sqlType == "DATE")) && (cellValue instanceof Date)) {
107
+ return "\'" + cellValue.toISOString() + "\'"
108
+
109
+ } else {
110
+ return this.printSqlString(cellValue.toString())
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Print a single string value as valid sql literal
116
+ *
117
+ * @param sqlString string value
118
+ *
119
+ */
120
+ printSqlString(sqlString:string): string {
121
+ return "\"" + sqlString.replaceAll("\"", "\"\"") + "\""
122
+ }
123
+
124
+ /**
125
+ * Parse a single SQL result value for serialization using the context of the native data
126
+ * type.
127
+ *
128
+ * @param sqlValue data from serialization
129
+ * @param sqlType native type name for table column
130
+ *
131
+ */
132
+ parseSqlValueAsCell(sqlValue:OINODataCell, sqlType: string): OINODataCell {
133
+ if ((sqlValue === null) || (sqlValue == "NULL")) {
134
+ return null
135
+
136
+ } else if (sqlValue === undefined) {
137
+ return undefined
138
+
139
+ } else if (((sqlType == "DATETIME") || (sqlType == "DATE")) && (typeof(sqlValue) == "string") && (sqlValue != "")) {
140
+ return new Date(sqlValue)
141
+
142
+ } else if ((sqlType == "BOOLEAN")) {
143
+ return sqlValue == 1
144
+
145
+ } else if ((sqlType == "BLOB")) {
146
+ if (sqlValue instanceof Uint8Array) {
147
+ return Buffer.from(sqlValue)
148
+ } else {
149
+ return sqlValue
150
+ }
151
+ } else {
152
+ return sqlValue
153
+ }
154
+
155
+ }
156
+
157
+
158
+ /**
159
+ * Connect to database.
160
+ *
161
+ */
162
+ async connect(): Promise<OINOResult> {
163
+ OINOBenchmark.startMetric("OINODb", "connect")
164
+ let result:OINOResult = new OINOResult()
165
+ const filepath:string = this._params.url.substring(7)
166
+ try {
167
+ this._db = BunSqliteDb.open(filepath, { create: true, readonly: false, readwrite: true })
168
+ this.isConnected = true
169
+ } catch (e:any) {
170
+ result.setError(500, "Exception connecting to database: " + e.message, "OINODbBunSqlite.connect")
171
+ OINOLog.exception("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "connect", "exception in connect", {message:e.message, stack:e.stack})
172
+ }
173
+ OINOBenchmark.endMetric("OINODb", "connect")
174
+ return result
175
+ }
176
+
177
+ /**
178
+ * Validate connection to database is working.
179
+ *
180
+ */
181
+ async validate(): Promise<OINOResult> {
182
+ OINOBenchmark.startMetric("OINODb", "validate")
183
+ let result:OINOResult = new OINOResult()
184
+ try {
185
+ const sql = this._getValidateSql(this._params.database)
186
+ const sql_res:OINODbDataSet = await this.sqlSelect(sql)
187
+ if (sql_res.isEmpty()) {
188
+ result.setError(400, "DB returned no rows for select!", "OINODbBunSqlite.validate")
189
+
190
+ } else if (sql_res.getRow().length == 0) {
191
+ result.setError(400, "DB returned no values for database!", "OINODbBunSqlite.validate")
192
+
193
+ } else if (sql_res.getRow()[0] == "0") {
194
+ result.setError(400, "DB returned no schema for database!", "OINODbBunSqlite.validate")
195
+
196
+ } else {
197
+ this.isValidated = true
198
+ }
199
+ } catch (e:any) {
200
+ result.setError(500, OINO_ERROR_PREFIX + " (validate): OINODbBunSqlite.validate exception in _db.query: " + e.message, "OINODbBunSqlite.validate")
201
+ }
202
+ OINOBenchmark.endMetric("OINODb", "validate")
203
+ return result
204
+ }
205
+
206
+ /**
207
+ * Execute a select operation.
208
+ *
209
+ * @param sql SQL statement.
210
+ *
211
+ */
212
+ async sqlSelect(sql:string): Promise<OINODbDataSet> {
213
+ OINOBenchmark.startMetric("OINODb", "sqlSelect")
214
+ let result:OINODbDataSet
215
+ try {
216
+ result = new OINOBunSqliteDataset(this._db?.query(sql).values(), [])
217
+
218
+ } catch (e:any) {
219
+ result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, ["OINODbBunSqlite.sqlSelect exception in _db.query: " + e.message])
220
+ }
221
+ OINOBenchmark.endMetric("OINODb", "sqlSelect")
222
+ return Promise.resolve(result)
223
+ }
224
+
225
+ /**
226
+ * Execute other sql operations.
227
+ *
228
+ * @param sql SQL statement.
229
+ *
230
+ */
231
+ async sqlExec(sql:string): Promise<OINODbDataSet> {
232
+ OINOBenchmark.startMetric("OINODb", "sqlExec")
233
+ let result:OINODbDataSet
234
+ try {
235
+ this._db?.exec(sql)
236
+ result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, [])
237
+
238
+ } catch (e:any) {
239
+ result = new OINOBunSqliteDataset(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + "(sqlExec): exception in _db.exec [" + e.message + "]"])
240
+ }
241
+ OINOBenchmark.endMetric("OINODb", "sqlExec")
242
+ return Promise.resolve(result)
243
+ }
244
+
245
+ private _getSchemaSql(dbName:string, tableName:string):string {
246
+ const sql = "SELECT sql from sqlite_schema WHERE name='" + tableName + "'"
247
+ return sql
248
+ }
249
+
250
+ private _getValidateSql(dbName:string):string {
251
+ const sql = "SELECT count(*) as COLUMN_COUNT from sqlite_schema"
252
+ return sql
253
+ }
254
+
255
+ /**
256
+ * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
257
+ * the model.
258
+ *
259
+ * @param api api which data model to initialize.
260
+ *
261
+ */
262
+ async initializeApiDatamodel(api:OINODbApi): Promise<void> {
263
+ const schema_sql:string = this._getSchemaSql(this._params.database, api.params.tableName)
264
+ const res:OINODbDataSet|null = await this.sqlSelect(schema_sql)
265
+ const sql_desc:string = (res?.getRow()[0]) as string
266
+ const excluded_fields:string[] = []
267
+ let table_matches = OINODbBunSqlite._tableDescriptionRegex.exec(sql_desc)
268
+ if (!table_matches || table_matches?.length < 2) {
269
+ throw new Error("Table " + api.params.tableName + " not recognized as a valid Sqlite table!")
270
+
271
+ } else {
272
+ let field_strings:string[] = OINOStr.splitExcludingBrackets(table_matches[1], ',', '(', ')')
273
+ for (let field_str of field_strings) {
274
+ field_str = field_str.trim()
275
+ let field_params = this._parseDbFieldParams(field_str)
276
+ let field_match = OINODbBunSqlite._tableFieldTypeRegex.exec(field_str)
277
+ // console.log("OINODbBunSqlite.initializeApiDatamodel: field_match", field_match)
278
+ if ((!field_match) || (field_match.length < 3)) {
279
+ let primarykey_match = OINODbBunSqlite._tablePrimarykeyRegex.exec(field_str)
280
+ let foreignkey_match = OINODbBunSqlite._tableForeignkeyRegex.exec(field_str)
281
+ if (primarykey_match && primarykey_match.length >= 2) {
282
+ const primary_keys:string[] = primarykey_match[1].replaceAll("\"", "").split(',') // not sure if will have space or not so split by comma and trim later
283
+ for (let i:number=0; i<primary_keys.length; i++) {
284
+ const pk:string = primary_keys[i].trim() //..the trim
285
+ if (excluded_fields.indexOf(pk) >= 0) {
286
+ throw new Error(OINO_ERROR_PREFIX + "Primary key field excluded in API parameters: " + pk)
287
+ }
288
+ for (let j:number=0; j<api.datamodel.fields.length; j++) {
289
+ if (api.datamodel.fields[j].name == pk) {
290
+ api.datamodel.fields[j].fieldParams.isPrimaryKey = true
291
+ }
292
+ }
293
+ }
294
+
295
+ } else if (foreignkey_match && foreignkey_match.length >= 2) {
296
+ const fk:string = foreignkey_match[1].trim()
297
+ for (let j:number=0; j<api.datamodel.fields.length; j++) {
298
+ if (api.datamodel.fields[j].name == fk) {
299
+ api.datamodel.fields[j].fieldParams.isForeignKey = true
300
+ }
301
+ }
302
+
303
+ } else {
304
+ OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Unsupported field definition skipped.", { field: field_str })
305
+ }
306
+
307
+ } else {
308
+ // field_str = "NAME TYPE (M, N)" -> 1:NAME, 2:TYPE, 4:M, 5:N
309
+ const field_name:string = field_match[1]
310
+ const sql_type:string = field_match[2]
311
+ const field_length:number = parseInt(field_match[4]) || 0
312
+ if (api.isFieldIncluded(field_name) == false) {
313
+ excluded_fields.push(field_name)
314
+ OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Field excluded in API parameters.", {field:field_name})
315
+
316
+ } else {
317
+ if ((sql_type == "INTEGER") || (sql_type == "REAL") || (sql_type == "DOUBLE") || (sql_type == "NUMERIC") || (sql_type == "DECIMAL")) {
318
+ api.datamodel.addField(new OINONumberDataField(this, field_name, sql_type, field_params ))
319
+
320
+ } else if ((sql_type == "BLOB") ) {
321
+ api.datamodel.addField(new OINOBlobDataField(this, field_name, sql_type, field_params, field_length))
322
+
323
+ } else if ((sql_type == "TEXT")) {
324
+ api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, field_length))
325
+
326
+ } else if ((sql_type == "DATETIME") || (sql_type == "DATE")) {
327
+ if (api.params.useDatesAsString) {
328
+ api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
329
+ } else {
330
+ api.datamodel.addField(new OINODatetimeDataField(this, field_name, sql_type, field_params))
331
+ }
332
+
333
+ } else if ((sql_type == "BOOLEAN")) {
334
+ api.datamodel.addField(new OINOBooleanDataField(this, field_name, sql_type, field_params))
335
+
336
+ } else {
337
+ OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "Unrecognized field type treated as string", {field_name: field_name, sql_type:sql_type, field_length:field_length, field_params:field_params })
338
+ api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
339
+ }
340
+ }
341
+ }
342
+ };
343
+ OINOLog.info("@oino-ts/db-bunsqlite", "OINODbBunSqlite", "initializeApiDatamodel", "\n" + api.datamodel.printDebug("\n"))
344
+ return Promise.resolve()
345
+ }
346
+ }
347
+ }
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export { OINODbBunSqlite } from "./OINODbBunSqlite.js"
1
+ export { OINODbBunSqlite } from "./OINODbBunSqlite.js"