@oino-ts/db-mssql 0.6.0 → 0.7.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/README.md +182 -189
- package/dist/cjs/OINODbMsSql.js +40 -24
- package/dist/esm/OINODbMsSql.js +40 -24
- package/dist/types/OINODbMsSql.d.ts +1 -1
- package/package.json +2 -2
- package/src/OINODbMsSql.ts +45 -26
package/README.md
CHANGED
|
@@ -1,190 +1,183 @@
|
|
|
1
1
|
# OINO TS
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
- Custom base encoding [base-x package](https://www.npmjs.com/package/base-x)
|
|
185
|
-
|
|
186
|
-
## Bun
|
|
187
|
-
OINO has been developed using the Bun runtime, not because of the speed improvements but for the first class Typescript support and integrated developper experience. Kudos on the bun team for making Typescript work more exiting again.
|
|
188
|
-
|
|
189
|
-
## SQL Scripts
|
|
190
|
-
The SQL scripts for creating the sample Northwind database are based on [Google Code archive](https://code.google.com/archive/p/northwindextended/downloads) and have been further customized to ensure they would have identical data (in the scope of the automated testing).
|
|
2
|
+
OINO Is Not an ORM but it's trying to solve a similar problem for API development. Instead of mirroring your DB schema in code that needs manual updates, OINO will get the data schema from DBMS using SQL in real time. Every time your app starts, it has an updated data model which enables automatic (de)serialize SQL results to JSON/CSV and back. OINO works on the level below routing where you pass the method, URL ID, body and request parameters to the API-object. OINO will parse and validate the data against the data model and generate proper SQL for your DB. Because OINO knows how data is serialized (e.g. JSON), what column it belongs to (e.g. floating point number) and what the target database is, it knows how to parse, format and escape the value as valid SQL.
|
|
3
|
+
|
|
4
|
+
```
|
|
5
|
+
const result:OINOApiResult = await api_orderdetails.doRequest("GET", id, body, params)
|
|
6
|
+
return new Response(result.modelset.writeString(OINOContentType.json))
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# GETTING STARTED
|
|
11
|
+
|
|
12
|
+
### Setup
|
|
13
|
+
Install the `@oino-ts/db` npm package and necessary database packages and import them in your code.
|
|
14
|
+
```
|
|
15
|
+
bun install @oino-ts/db
|
|
16
|
+
bun install @oino-ts/db-bunsqlite
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
import { OINODb, OINOApi, OINOFactory } from "@oino-ts/db";
|
|
21
|
+
import { OINODbBunSqlite } from "@oino-ts/db-bunsqlite"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Register database and logger
|
|
25
|
+
Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/types_src.OINOConsoleLog.html) how to implement your own)
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
OINOLog.setLogger(new OINOConsoleLog())
|
|
29
|
+
OINOFactory.registerDb("OINODbBunSqlite", OINODbBunSqlite)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Create a database
|
|
33
|
+
Creating a database connection [`OINODb`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODb.html) is done by passing [`OINODbParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbParams.html) to the factory method. For [`OINODbBunSqlite`](https://pragmatta.github.io/oino-ts/classes/db_bunsqlite_src.OINODbBunSqlite.html) that means a file url for the database file, for others network host, port, credentials etc.
|
|
34
|
+
```
|
|
35
|
+
const db:OINODb = await OINOFactory.createDb( { type: "OINODbBunSqlite", url: "file://../localDb/northwind.sqlite" } )
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Create an API
|
|
39
|
+
From a database you can create an [`OINOApi`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbApi.html) by passing [`OINOApiParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbApiParams.html) with table name and preferences to the factory method.
|
|
40
|
+
```
|
|
41
|
+
const api_employees:OINOApi = await OINOFactory.createApi(db, { tableName: "Employees", excludeFields:["BirthDate"] })
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Pass HTTP requests to API
|
|
45
|
+
When you receive a HTTP request, just pass the method, URL ID, body and params to the correct API, which will parse and validate input and return results.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
const result:OINOApiResult = await api_orderdetails.doRequest("GET", id, body, params)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Write results back to HTTP Response
|
|
52
|
+
The results for a GET request will contain [`OINOModelSet`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbModelSet.html) data that can be written out as JSON or CSV as needed. For other requests result is just success or error with messages.
|
|
53
|
+
```
|
|
54
|
+
return new Response(result.data.writeString(OINOContentType.json))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# MAIN FEATURES
|
|
59
|
+
|
|
60
|
+
## RESTfull
|
|
61
|
+
OINO maps HTTP methods GET/POST/PUT/DELETE to SQL operations SELECT/INSERT/UPDATE/DELETE. The GET/POST requests can be made without URL ID to get all rows or insert new ones and others target a single row using URL ID.
|
|
62
|
+
|
|
63
|
+
For example HTTP POST
|
|
64
|
+
```
|
|
65
|
+
Request and response:
|
|
66
|
+
> curl.exe -X POST http://localhost:3001/orderdetails -H "Content-Type: application/json" --data '[{\"OrderID\":11077,\"ProductID\":99,\"UnitPrice\":19,\"Quantity\":1,\"Discount\":0}]'
|
|
67
|
+
{"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
|
|
68
|
+
|
|
69
|
+
SQL:
|
|
70
|
+
INSERT INTO [OrderDetails] ("OrderID","ProductID","UnitPrice","Quantity","Discount") VALUES (11077,99,19,1,0);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Universal Serialization
|
|
75
|
+
OINO handles serialization of data to JSON/CSV/etc. and back based on the data model. It knows what columns exist, what is their data type and how to convert each to JSON/CSV and back. This allows also partial data to be sent, i.e. you can send only columns that need updating or even send extra columns and have them ignored.
|
|
76
|
+
|
|
77
|
+
- Files can be sent to BLOB fields using BASE64 or MIME multipart encoding. Also supports standard HTML form file submission to blob fields and returning them data url images.
|
|
78
|
+
- Datetimes are (optionally) normalized to ISO 8601 format.
|
|
79
|
+
- Extended JSON-encoding
|
|
80
|
+
- Unquoted literal `undefined` can be used to represent non-existent values (leaving property out works too but preserving structure might be easier e.g. when translating data).
|
|
81
|
+
- CSV
|
|
82
|
+
- Comma-separated, doublequotes.
|
|
83
|
+
- Unquoted literal `null` represents null values.
|
|
84
|
+
- Unquoted empty string represents undefined values.
|
|
85
|
+
- Form data
|
|
86
|
+
- Multipart-mixed and binary files not supported.
|
|
87
|
+
- Non-existent value line (i.e. nothing after the empty line) treated as a null value.
|
|
88
|
+
- Url-encoded
|
|
89
|
+
- No null values, missing properties treated as undefined.
|
|
90
|
+
- Multiple lines could be used to post multiple rows.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Database Abstraction
|
|
94
|
+
OINO functions as a database abstraction, providing a consistent interface for working with different databases. It abstracts out different conventions in connecting, making queries and formatting data.
|
|
95
|
+
|
|
96
|
+
Currently supported databases:
|
|
97
|
+
- Bun Sqlite through Bun native implementation
|
|
98
|
+
- Postgresql through [pg](https://www.npmjs.com/package/pg)-package
|
|
99
|
+
- Mariadb / Mysql-support through [mariadb](https://www.npmjs.com/package/mariadb)-package
|
|
100
|
+
- Sql Server through [mssql](https://www.npmjs.com/package/mssql)-package
|
|
101
|
+
|
|
102
|
+
## Composite Keys
|
|
103
|
+
To support tables with multipart primary keys OINO generates a composite key `_OINOID_` that is included in the result and can be used as the REST ID. For example in the example above table `OrderDetails` has two primary keys `OrderID` and `ProductID` making the `_OINOID_` of form `11077:99`.
|
|
104
|
+
|
|
105
|
+
## Power Of SQL
|
|
106
|
+
Since OINO is just generating SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html), order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.html) and limits/paging with [`OINOSqlLimit`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlLimit.html) that are passed as HTTP request parameters. No more API development where you make unique API endpoints for each filter that fetch all data with original API and filter in backend code. Every API can be filtered when and as needed without unnessecary data tranfer and utilizing SQL indexing when available.
|
|
107
|
+
|
|
108
|
+
## Swagger Support
|
|
109
|
+
Swagger is great as long as the definitions are updated and with OINO you can automatically get a Swagger definition including a data model schema.
|
|
110
|
+
```
|
|
111
|
+
if (url.pathname == "/swagger.json") {
|
|
112
|
+
return new Response(JSON.stringify(OINOSwagger.getApiDefinition(api_array)))
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
## Node support
|
|
118
|
+
OINO is developped Typescript first but compiles to standard CommonJS and the NPM packages should work on either ESM / CommonJS. Checkout sample apps `readmeApp` (ESM) and `nodeApp` (CommonJS).
|
|
119
|
+
|
|
120
|
+
## HTMX support
|
|
121
|
+
OINO is [htmx.org](https://htmx.org) friendly, allowing easy translation of [`OINODataRow`](https://pragmatta.github.io/oino-ts/types/db_src.OINODataRow.html) to HTML output using templates (cf. the [htmx sample app](https://github.com/pragmatta/oino-ts/tree/main/samples/htmxApp)).
|
|
122
|
+
|
|
123
|
+
## Hashids
|
|
124
|
+
Autoinc numeric id's are very pragmatic and fit well with OINO (e.g. using a form without primary key fields to insert new rows with database assigned ids). However it's not always sensible to share information about the sequence. Hashids solve this by masking the original values by encrypting the ids using AES-128 and some randomness. Length of the hashid can be chosen from 12-32 characters where longer ids provide more security. However this should not be considereded a cryptographic solution for keeping ids secret but rather making it infeasible to iterate all ids.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# STATUS
|
|
128
|
+
OINO is currently a side project which should considered in beta status. That means that semantic versioning is the goal but backwards incompatible changes can still happen in point releases if serious architectual issues are discovered.
|
|
129
|
+
|
|
130
|
+
## Roadmap
|
|
131
|
+
Major features that are considered in future releases ()
|
|
132
|
+
|
|
133
|
+
### Support for views
|
|
134
|
+
Simple cases of views probably work already in some databases but edge cases might get complicated.
|
|
135
|
+
|
|
136
|
+
For example issues could be
|
|
137
|
+
- How to handle a view which does not have a complete private key?
|
|
138
|
+
- What edge cases exist in updating views?
|
|
139
|
+
- Can views be handled transparently to the DBMS or is there some fundamentally platform specific behavior?
|
|
140
|
+
|
|
141
|
+
### Batch updates
|
|
142
|
+
Supporting batch updates similar to batch inserts is slightly bending the RESTfull principles but would still be a useful optional feature in some situations.
|
|
143
|
+
|
|
144
|
+
### Streaming
|
|
145
|
+
One core idea is to be efficient in not making unnecessary copies of the data and minimizing garbage collection debt. This can be taken further by implementing streaming, allowing large dataset to be written to HTTP response as SQL result rows are received.
|
|
146
|
+
|
|
147
|
+
### SQL generation callbacks
|
|
148
|
+
It would be useful to allow developer to validate / override SQL generation to cover cases OINO does not support or even workaround issues.
|
|
149
|
+
|
|
150
|
+
### Transactions
|
|
151
|
+
Even though the basic case for OINO is executing SQL operations on individual rows, having an option to use SQL transactions could make sense at least for batch operations.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# HELP
|
|
155
|
+
|
|
156
|
+
## Bug reports
|
|
157
|
+
Fixing bugs is a priority and getting good quality bug reports helps. It's recommended to use the sample Northwind database included with project to replicate issues or make an SQL script export of the relevant table.
|
|
158
|
+
|
|
159
|
+
## Feedback
|
|
160
|
+
Understanding and prioritizing the use cases for OINO is also important and feedback about how you'd use OINO is interesting. Feel free to raise issues and feature requests in Github, but understand that short term most of the effort goes towards reaching the beta stage.
|
|
161
|
+
|
|
162
|
+
## Typescript / Javascript architecture
|
|
163
|
+
Typescript building with different targets and module-systemts and a ton of configuration is a complex domain and something I have little experience un so help in fixing problems and how thing ought to be done is appreciated.
|
|
164
|
+
|
|
165
|
+
# LINKS
|
|
166
|
+
- [Github repository](https://github.com/pragmatta/oino-ts)
|
|
167
|
+
- [NPM repository](https://www.npmjs.com/org/oino-ts)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# ACKNOWLEDGEMENTS
|
|
171
|
+
|
|
172
|
+
## Libraries
|
|
173
|
+
OINO uses the following open source libraries and npm packages and I would like to thank everyone for their contributions:
|
|
174
|
+
- Postgresql [node-postgres package](https://www.npmjs.com/package/pg)
|
|
175
|
+
- Mariadb / Mysql [mariadb package](https://www.npmjs.com/package/mariadb)
|
|
176
|
+
- Sql Server [mssql package](https://www.npmjs.com/package/mssql)
|
|
177
|
+
- Custom base encoding [base-x package](https://www.npmjs.com/package/base-x)
|
|
178
|
+
|
|
179
|
+
## Bun
|
|
180
|
+
OINO has been developed using the Bun runtime, not because of the speed improvements but for the first class Typescript support and integrated developper experience. Kudos on the bun team for making Typescript work more exiting again.
|
|
181
|
+
|
|
182
|
+
## SQL Scripts
|
|
183
|
+
The SQL scripts for creating the sample Northwind database are based on [Google Code archive](https://code.google.com/archive/p/northwindextended/downloads) and have been further customized to ensure they would have identical data (in the scope of the automated testing).
|
package/dist/cjs/OINODbMsSql.js
CHANGED
|
@@ -91,6 +91,13 @@ class OINOMsSqlData extends db_1.OINODbDataSet {
|
|
|
91
91
|
return db_1.OINODB_EMPTY_ROW;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets all rows of data.
|
|
96
|
+
*
|
|
97
|
+
*/
|
|
98
|
+
async getAllRows() {
|
|
99
|
+
return this._rows; // at the moment theres no result streaming, so we can just return the rows
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
/**
|
|
96
103
|
* Implementation of MariaDb/MySql-database.
|
|
@@ -109,11 +116,11 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
109
116
|
throw new Error(db_1.OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type);
|
|
110
117
|
}
|
|
111
118
|
this._pool = new mssql_1.ConnectionPool({
|
|
112
|
-
user:
|
|
113
|
-
password:
|
|
114
|
-
server:
|
|
115
|
-
port:
|
|
116
|
-
database:
|
|
119
|
+
user: this._params.user,
|
|
120
|
+
password: this._params.password,
|
|
121
|
+
server: this._params.url,
|
|
122
|
+
port: this._params.port,
|
|
123
|
+
database: this._params.database,
|
|
117
124
|
arrayRowMode: true,
|
|
118
125
|
options: {
|
|
119
126
|
encrypt: true, // Use encryption for Azure SQL Database
|
|
@@ -122,18 +129,19 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
122
129
|
trustServerCertificate: true // Change to false for production
|
|
123
130
|
}
|
|
124
131
|
});
|
|
125
|
-
|
|
132
|
+
delete this._params.password; // do not store password in db object
|
|
126
133
|
this._pool.on("error", (conn) => {
|
|
127
|
-
|
|
128
|
-
});
|
|
129
|
-
this._pool.on("debug", (event) => {
|
|
130
|
-
// console.log("OINODbMsSql debug",event)
|
|
134
|
+
db_1.OINOLog.error("OINODbMsSql error event", conn);
|
|
131
135
|
});
|
|
136
|
+
// this._pool.on("debug", (event:any) => {
|
|
137
|
+
// console.log("OINODbMsSql debug",event)
|
|
138
|
+
// })
|
|
132
139
|
}
|
|
133
140
|
async _query(sql) {
|
|
134
141
|
// OINOLog.debug("OINODbMsSql._query", {sql:sql})
|
|
135
142
|
try {
|
|
136
|
-
const
|
|
143
|
+
const request = this._pool.request(); // this does not need to be released but the pool will handle it
|
|
144
|
+
const sql_res = await request.query(sql);
|
|
137
145
|
// console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
|
|
138
146
|
const result = new OINOMsSqlData(sql_res.recordsets);
|
|
139
147
|
return Promise.resolve(result);
|
|
@@ -152,7 +160,7 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
152
160
|
try {
|
|
153
161
|
const sql_res = await this._pool.request().query(sql);
|
|
154
162
|
// console.log("OINODbMsSql._exec result", sql_res);
|
|
155
|
-
return Promise.resolve(new OINOMsSqlData(
|
|
163
|
+
return Promise.resolve(new OINOMsSqlData(db_1.OINODB_EMPTY_ROWS));
|
|
156
164
|
}
|
|
157
165
|
catch (err) {
|
|
158
166
|
db_1.OINOLog.error("OINODbMsSql._exec exception", { err: err });
|
|
@@ -294,45 +302,53 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
294
302
|
*
|
|
295
303
|
*/
|
|
296
304
|
async connect() {
|
|
305
|
+
let result = new db_1.OINOResult();
|
|
297
306
|
try {
|
|
298
307
|
// make sure that any items are correctly URL encoded in the connection string
|
|
299
308
|
await this._pool.connect();
|
|
300
309
|
// OINOLog.info("OINODbMsSql.connect: Connected to database server.")
|
|
301
|
-
await this._pool.request().query("SELECT 1 as test")
|
|
302
|
-
|
|
310
|
+
// await this._pool.request().query("SELECT 1 as test")
|
|
311
|
+
this.isConnected = true;
|
|
303
312
|
}
|
|
304
313
|
catch (err) {
|
|
305
314
|
// ... error checks
|
|
306
|
-
|
|
315
|
+
result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect");
|
|
316
|
+
db_1.OINOLog.error(result.statusMessage, { error: err });
|
|
307
317
|
}
|
|
318
|
+
return Promise.resolve(result);
|
|
308
319
|
}
|
|
309
320
|
/**
|
|
310
321
|
* Validate connection to database is working.
|
|
311
322
|
*
|
|
312
323
|
*/
|
|
313
324
|
async validate() {
|
|
314
|
-
db_1.OINOBenchmark.start("OINODb", "validate");
|
|
315
325
|
let result = new db_1.OINOResult();
|
|
326
|
+
if (!this.isConnected) {
|
|
327
|
+
result.setError(400, "Database is not connected!", "OINODbMsSql.validate");
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
db_1.OINOBenchmark.start("OINODb", "validate");
|
|
316
331
|
try {
|
|
317
332
|
const sql = this._getValidateSql(this._params.database);
|
|
318
|
-
// OINOLog.debug("
|
|
333
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql:sql})
|
|
319
334
|
const sql_res = await this.sqlSelect(sql);
|
|
320
|
-
// OINOLog.debug("
|
|
335
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
|
|
321
336
|
if (sql_res.isEmpty()) {
|
|
322
|
-
result.setError(400, "DB returned no rows for select!", "
|
|
337
|
+
result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate");
|
|
323
338
|
}
|
|
324
339
|
else if (sql_res.getRow().length == 0) {
|
|
325
|
-
result.setError(400, "DB returned no values for database!", "
|
|
340
|
+
result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate");
|
|
326
341
|
}
|
|
327
342
|
else if (sql_res.getRow()[0] == "0") {
|
|
328
|
-
result.setError(400, "DB returned no schema for database!", "
|
|
343
|
+
result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate");
|
|
329
344
|
}
|
|
330
345
|
else {
|
|
331
346
|
// connection is working
|
|
347
|
+
this.isValidated = true;
|
|
332
348
|
}
|
|
333
349
|
}
|
|
334
350
|
catch (e) {
|
|
335
|
-
result.setError(500,
|
|
351
|
+
result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate");
|
|
336
352
|
}
|
|
337
353
|
db_1.OINOBenchmark.end("OINODb", "validate");
|
|
338
354
|
return result;
|
|
@@ -351,7 +367,7 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
351
367
|
result = await this._query(sql);
|
|
352
368
|
}
|
|
353
369
|
catch (e) {
|
|
354
|
-
result = new OINOMsSqlData(
|
|
370
|
+
result = new OINOMsSqlData(db_1.OINODB_EMPTY_ROWS, [db_1.OINO_ERROR_PREFIX + " (sqlSelect): OINODbMsSql.sqlSelect exception in _db.query: " + e.message]);
|
|
355
371
|
}
|
|
356
372
|
db_1.OINOBenchmark.end("OINODb", "sqlSelect");
|
|
357
373
|
return result;
|
|
@@ -369,7 +385,7 @@ class OINODbMsSql extends db_1.OINODb {
|
|
|
369
385
|
result = await this._exec(sql);
|
|
370
386
|
}
|
|
371
387
|
catch (e) {
|
|
372
|
-
result = new OINOMsSqlData(
|
|
388
|
+
result = new OINOMsSqlData(db_1.OINODB_EMPTY_ROWS, [db_1.OINO_ERROR_PREFIX + " (sqlExec): exception in _db.exec [" + e.message + "]"]);
|
|
373
389
|
}
|
|
374
390
|
db_1.OINOBenchmark.end("OINODb", "sqlExec");
|
|
375
391
|
return result;
|
package/dist/esm/OINODbMsSql.js
CHANGED
|
@@ -88,6 +88,13 @@ class OINOMsSqlData extends OINODbDataSet {
|
|
|
88
88
|
return OINODB_EMPTY_ROW;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Gets all rows of data.
|
|
93
|
+
*
|
|
94
|
+
*/
|
|
95
|
+
async getAllRows() {
|
|
96
|
+
return this._rows; // at the moment theres no result streaming, so we can just return the rows
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
/**
|
|
93
100
|
* Implementation of MariaDb/MySql-database.
|
|
@@ -106,11 +113,11 @@ export class OINODbMsSql extends OINODb {
|
|
|
106
113
|
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type);
|
|
107
114
|
}
|
|
108
115
|
this._pool = new ConnectionPool({
|
|
109
|
-
user:
|
|
110
|
-
password:
|
|
111
|
-
server:
|
|
112
|
-
port:
|
|
113
|
-
database:
|
|
116
|
+
user: this._params.user,
|
|
117
|
+
password: this._params.password,
|
|
118
|
+
server: this._params.url,
|
|
119
|
+
port: this._params.port,
|
|
120
|
+
database: this._params.database,
|
|
114
121
|
arrayRowMode: true,
|
|
115
122
|
options: {
|
|
116
123
|
encrypt: true, // Use encryption for Azure SQL Database
|
|
@@ -119,18 +126,19 @@ export class OINODbMsSql extends OINODb {
|
|
|
119
126
|
trustServerCertificate: true // Change to false for production
|
|
120
127
|
}
|
|
121
128
|
});
|
|
122
|
-
|
|
129
|
+
delete this._params.password; // do not store password in db object
|
|
123
130
|
this._pool.on("error", (conn) => {
|
|
124
|
-
|
|
125
|
-
});
|
|
126
|
-
this._pool.on("debug", (event) => {
|
|
127
|
-
// console.log("OINODbMsSql debug",event)
|
|
131
|
+
OINOLog.error("OINODbMsSql error event", conn);
|
|
128
132
|
});
|
|
133
|
+
// this._pool.on("debug", (event:any) => {
|
|
134
|
+
// console.log("OINODbMsSql debug",event)
|
|
135
|
+
// })
|
|
129
136
|
}
|
|
130
137
|
async _query(sql) {
|
|
131
138
|
// OINOLog.debug("OINODbMsSql._query", {sql:sql})
|
|
132
139
|
try {
|
|
133
|
-
const
|
|
140
|
+
const request = this._pool.request(); // this does not need to be released but the pool will handle it
|
|
141
|
+
const sql_res = await request.query(sql);
|
|
134
142
|
// console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
|
|
135
143
|
const result = new OINOMsSqlData(sql_res.recordsets);
|
|
136
144
|
return Promise.resolve(result);
|
|
@@ -149,7 +157,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
149
157
|
try {
|
|
150
158
|
const sql_res = await this._pool.request().query(sql);
|
|
151
159
|
// console.log("OINODbMsSql._exec result", sql_res);
|
|
152
|
-
return Promise.resolve(new OINOMsSqlData(
|
|
160
|
+
return Promise.resolve(new OINOMsSqlData(OINODB_EMPTY_ROWS));
|
|
153
161
|
}
|
|
154
162
|
catch (err) {
|
|
155
163
|
OINOLog.error("OINODbMsSql._exec exception", { err: err });
|
|
@@ -291,45 +299,53 @@ export class OINODbMsSql extends OINODb {
|
|
|
291
299
|
*
|
|
292
300
|
*/
|
|
293
301
|
async connect() {
|
|
302
|
+
let result = new OINOResult();
|
|
294
303
|
try {
|
|
295
304
|
// make sure that any items are correctly URL encoded in the connection string
|
|
296
305
|
await this._pool.connect();
|
|
297
306
|
// OINOLog.info("OINODbMsSql.connect: Connected to database server.")
|
|
298
|
-
await this._pool.request().query("SELECT 1 as test")
|
|
299
|
-
|
|
307
|
+
// await this._pool.request().query("SELECT 1 as test")
|
|
308
|
+
this.isConnected = true;
|
|
300
309
|
}
|
|
301
310
|
catch (err) {
|
|
302
311
|
// ... error checks
|
|
303
|
-
|
|
312
|
+
result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect");
|
|
313
|
+
OINOLog.error(result.statusMessage, { error: err });
|
|
304
314
|
}
|
|
315
|
+
return Promise.resolve(result);
|
|
305
316
|
}
|
|
306
317
|
/**
|
|
307
318
|
* Validate connection to database is working.
|
|
308
319
|
*
|
|
309
320
|
*/
|
|
310
321
|
async validate() {
|
|
311
|
-
OINOBenchmark.start("OINODb", "validate");
|
|
312
322
|
let result = new OINOResult();
|
|
323
|
+
if (!this.isConnected) {
|
|
324
|
+
result.setError(400, "Database is not connected!", "OINODbMsSql.validate");
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
OINOBenchmark.start("OINODb", "validate");
|
|
313
328
|
try {
|
|
314
329
|
const sql = this._getValidateSql(this._params.database);
|
|
315
|
-
// OINOLog.debug("
|
|
330
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql:sql})
|
|
316
331
|
const sql_res = await this.sqlSelect(sql);
|
|
317
|
-
// OINOLog.debug("
|
|
332
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
|
|
318
333
|
if (sql_res.isEmpty()) {
|
|
319
|
-
result.setError(400, "DB returned no rows for select!", "
|
|
334
|
+
result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate");
|
|
320
335
|
}
|
|
321
336
|
else if (sql_res.getRow().length == 0) {
|
|
322
|
-
result.setError(400, "DB returned no values for database!", "
|
|
337
|
+
result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate");
|
|
323
338
|
}
|
|
324
339
|
else if (sql_res.getRow()[0] == "0") {
|
|
325
|
-
result.setError(400, "DB returned no schema for database!", "
|
|
340
|
+
result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate");
|
|
326
341
|
}
|
|
327
342
|
else {
|
|
328
343
|
// connection is working
|
|
344
|
+
this.isValidated = true;
|
|
329
345
|
}
|
|
330
346
|
}
|
|
331
347
|
catch (e) {
|
|
332
|
-
result.setError(500,
|
|
348
|
+
result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate");
|
|
333
349
|
}
|
|
334
350
|
OINOBenchmark.end("OINODb", "validate");
|
|
335
351
|
return result;
|
|
@@ -348,7 +364,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
348
364
|
result = await this._query(sql);
|
|
349
365
|
}
|
|
350
366
|
catch (e) {
|
|
351
|
-
result = new OINOMsSqlData(
|
|
367
|
+
result = new OINOMsSqlData(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + " (sqlSelect): OINODbMsSql.sqlSelect exception in _db.query: " + e.message]);
|
|
352
368
|
}
|
|
353
369
|
OINOBenchmark.end("OINODb", "sqlSelect");
|
|
354
370
|
return result;
|
|
@@ -366,7 +382,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
366
382
|
result = await this._exec(sql);
|
|
367
383
|
}
|
|
368
384
|
catch (e) {
|
|
369
|
-
result = new OINOMsSqlData(
|
|
385
|
+
result = new OINOMsSqlData(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + " (sqlExec): exception in _db.exec [" + e.message + "]"]);
|
|
370
386
|
}
|
|
371
387
|
OINOBenchmark.end("OINODb", "sqlExec");
|
|
372
388
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oino-ts/db-mssql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "OINO TS package for using Microsoft Sql databases.",
|
|
5
5
|
"author": "Matias Kiviniemi (pragmatta)",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"module": "./dist/esm/index.js",
|
|
23
23
|
"types": "./dist/types/index.d.ts",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@oino-ts/db": "0.
|
|
25
|
+
"@oino-ts/db": "0.7.0",
|
|
26
26
|
"mssql": "^11.0.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
package/src/OINODbMsSql.ts
CHANGED
|
@@ -95,6 +95,15 @@ class OINOMsSqlData extends OINODbDataSet {
|
|
|
95
95
|
return OINODB_EMPTY_ROW
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Gets all rows of data.
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
async getAllRows(): Promise<OINODataRow[]> {
|
|
104
|
+
return this._rows // at the moment theres no result streaming, so we can just return the rows
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
/**
|
|
@@ -117,11 +126,11 @@ export class OINODbMsSql extends OINODb {
|
|
|
117
126
|
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type)
|
|
118
127
|
}
|
|
119
128
|
this._pool = new ConnectionPool({
|
|
120
|
-
user:
|
|
121
|
-
password:
|
|
122
|
-
server:
|
|
123
|
-
port:
|
|
124
|
-
database:
|
|
129
|
+
user: this._params.user,
|
|
130
|
+
password: this._params.password,
|
|
131
|
+
server: this._params.url,
|
|
132
|
+
port: this._params.port,
|
|
133
|
+
database: this._params.database,
|
|
125
134
|
arrayRowMode:true,
|
|
126
135
|
options: {
|
|
127
136
|
encrypt: true, // Use encryption for Azure SQL Database
|
|
@@ -130,20 +139,21 @@ export class OINODbMsSql extends OINODb {
|
|
|
130
139
|
trustServerCertificate: true // Change to false for production
|
|
131
140
|
}
|
|
132
141
|
})
|
|
133
|
-
|
|
142
|
+
delete this._params.password // do not store password in db object
|
|
134
143
|
|
|
135
144
|
this._pool.on("error", (conn:any) => {
|
|
136
|
-
|
|
137
|
-
})
|
|
138
|
-
this._pool.on("debug", (event:any) => {
|
|
139
|
-
// console.log("OINODbMsSql debug",event)
|
|
145
|
+
OINOLog.error("OINODbMsSql error event", conn)
|
|
140
146
|
})
|
|
147
|
+
// this._pool.on("debug", (event:any) => {
|
|
148
|
+
// console.log("OINODbMsSql debug",event)
|
|
149
|
+
// })
|
|
141
150
|
}
|
|
142
151
|
|
|
143
152
|
private async _query(sql:string):Promise<OINOMsSqlData> {
|
|
144
153
|
// OINOLog.debug("OINODbMsSql._query", {sql:sql})
|
|
145
154
|
try {
|
|
146
|
-
const
|
|
155
|
+
const request = this._pool.request() // this does not need to be released but the pool will handle it
|
|
156
|
+
const sql_res = await request.query(sql)
|
|
147
157
|
// console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
|
|
148
158
|
const result:OINOMsSqlData = new OINOMsSqlData(sql_res.recordsets)
|
|
149
159
|
return Promise.resolve(result)
|
|
@@ -163,7 +173,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
163
173
|
try {
|
|
164
174
|
const sql_res = await this._pool.request().query(sql);
|
|
165
175
|
// console.log("OINODbMsSql._exec result", sql_res);
|
|
166
|
-
return Promise.resolve(new OINOMsSqlData(
|
|
176
|
+
return Promise.resolve(new OINOMsSqlData(OINODB_EMPTY_ROWS))
|
|
167
177
|
|
|
168
178
|
} catch (err) {
|
|
169
179
|
OINOLog.error("OINODbMsSql._exec exception", {err:err})
|
|
@@ -309,17 +319,21 @@ export class OINODbMsSql extends OINODb {
|
|
|
309
319
|
* Connect to database.
|
|
310
320
|
*
|
|
311
321
|
*/
|
|
312
|
-
async connect(): Promise<
|
|
322
|
+
async connect(): Promise<OINOResult> {
|
|
323
|
+
let result:OINOResult = new OINOResult()
|
|
313
324
|
try {
|
|
314
325
|
// make sure that any items are correctly URL encoded in the connection string
|
|
315
326
|
await this._pool.connect()
|
|
316
327
|
// OINOLog.info("OINODbMsSql.connect: Connected to database server.")
|
|
317
|
-
await this._pool.request().query("SELECT 1 as test")
|
|
318
|
-
|
|
319
|
-
|
|
328
|
+
// await this._pool.request().query("SELECT 1 as test")
|
|
329
|
+
this.isConnected = true
|
|
330
|
+
|
|
331
|
+
} catch (err:any) {
|
|
320
332
|
// ... error checks
|
|
321
|
-
|
|
333
|
+
result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect")
|
|
334
|
+
OINOLog.error(result.statusMessage, {error:err})
|
|
322
335
|
}
|
|
336
|
+
return Promise.resolve(result)
|
|
323
337
|
}
|
|
324
338
|
|
|
325
339
|
/**
|
|
@@ -327,27 +341,32 @@ export class OINODbMsSql extends OINODb {
|
|
|
327
341
|
*
|
|
328
342
|
*/
|
|
329
343
|
async validate(): Promise<OINOResult> {
|
|
330
|
-
OINOBenchmark.start("OINODb", "validate")
|
|
331
344
|
let result:OINOResult = new OINOResult()
|
|
345
|
+
if (!this.isConnected) {
|
|
346
|
+
result.setError(400, "Database is not connected!", "OINODbMsSql.validate")
|
|
347
|
+
return result
|
|
348
|
+
}
|
|
349
|
+
OINOBenchmark.start("OINODb", "validate")
|
|
332
350
|
try {
|
|
333
351
|
const sql = this._getValidateSql(this._params.database)
|
|
334
|
-
// OINOLog.debug("
|
|
352
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql:sql})
|
|
335
353
|
const sql_res:OINODbDataSet = await this.sqlSelect(sql)
|
|
336
|
-
// OINOLog.debug("
|
|
354
|
+
// OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
|
|
337
355
|
if (sql_res.isEmpty()) {
|
|
338
|
-
result.setError(400, "DB returned no rows for select!", "
|
|
356
|
+
result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate")
|
|
339
357
|
|
|
340
358
|
} else if (sql_res.getRow().length == 0) {
|
|
341
|
-
result.setError(400, "DB returned no values for database!", "
|
|
359
|
+
result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate")
|
|
342
360
|
|
|
343
361
|
} else if (sql_res.getRow()[0] == "0") {
|
|
344
|
-
result.setError(400, "DB returned no schema for database!", "
|
|
362
|
+
result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate")
|
|
345
363
|
|
|
346
364
|
} else {
|
|
347
365
|
// connection is working
|
|
366
|
+
this.isValidated = true
|
|
348
367
|
}
|
|
349
368
|
} catch (e:any) {
|
|
350
|
-
result.setError(500,
|
|
369
|
+
result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate")
|
|
351
370
|
}
|
|
352
371
|
OINOBenchmark.end("OINODb", "validate")
|
|
353
372
|
return result
|
|
@@ -367,7 +386,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
367
386
|
result = await this._query(sql)
|
|
368
387
|
|
|
369
388
|
} catch (e:any) {
|
|
370
|
-
result = new OINOMsSqlData(
|
|
389
|
+
result = new OINOMsSqlData(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + " (sqlSelect): OINODbMsSql.sqlSelect exception in _db.query: " + e.message])
|
|
371
390
|
}
|
|
372
391
|
OINOBenchmark.end("OINODb", "sqlSelect")
|
|
373
392
|
return result
|
|
@@ -386,7 +405,7 @@ export class OINODbMsSql extends OINODb {
|
|
|
386
405
|
result = await this._exec(sql)
|
|
387
406
|
|
|
388
407
|
} catch (e:any) {
|
|
389
|
-
result = new OINOMsSqlData(
|
|
408
|
+
result = new OINOMsSqlData(OINODB_EMPTY_ROWS, [OINO_ERROR_PREFIX + " (sqlExec): exception in _db.exec [" + e.message + "]"])
|
|
390
409
|
}
|
|
391
410
|
OINOBenchmark.end("OINODb", "sqlExec")
|
|
392
411
|
return result
|