@oino-ts/db-mssql 0.5.2 → 0.6.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/README.md CHANGED
@@ -1,190 +1,183 @@
1
1
  # OINO TS
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
- # 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
- ### Features
78
- - 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.
79
- - Datetimes are (optionally) normalized to ISO 8601 format.
80
- - Extended JSON-encoding
81
- - 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).
82
- - CSV
83
- - Comma-separated, doublequotes.
84
- - Unquoted literal `null` represents null values.
85
- - Unquoted empty string represents undefined values.
86
- - Form data
87
- - Multipart-mixed and binary files not supported.
88
- - Non-existent value line (i.e. nothing after the empty line) treated as a null value.
89
- - Url-encoded
90
- - No null values, missing properties treated as undefined.
91
- - Multiple lines could be used to post multiple rows.
92
-
93
-
94
- ## Database Abstraction
95
- 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.
96
-
97
- Currently supported databases:
98
- - Bun Sqlite through Bun native implementation
99
- - Postgresql through [pg](https://www.npmjs.com/package/pg)-package
100
- - Mariadb / Mysql-support through [mariadb](https://www.npmjs.com/package/mariadb)-package
101
- - Sql Server through [mssql](https://www.npmjs.com/package/mssql)-package
102
-
103
- ## Composite Keys
104
- 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`.
105
-
106
- ## Power Of SQL
107
- 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.
108
-
109
- ## Swagger Support
110
- 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.
111
- ```
112
- if (url.pathname == "/swagger.json") {
113
- return new Response(JSON.stringify(OINOSwagger.getApiDefinition(api_array)))
114
- }
115
- ```
116
- ![Swagger definition with a data model schema](img/readme-swagger.png)
117
-
118
- ## Node support
119
- 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).
120
-
121
- ## HTMX support
122
- 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)).
123
-
124
- ## Hashids
125
- 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.
126
-
127
-
128
- # STATUS
129
- OINO is currently a hobby project which should and should considered in alpha status. That also means compatibility breaking changes can be made without prior notice when architectual issues are discovered.
130
-
131
- ## Beta
132
- For a beta status following milestones are planned:
133
-
134
- ### Realistic app
135
- There needs to be a realistic app built on top of OINO to get a better grasp of the edge cases.
136
-
137
- ## Roadmap
138
- Things that need to happen in some order before beta-status are at least following:
139
-
140
- ### Support for views
141
- Simple cases of views would work already in some databases but edge cases might get complicated. For example
142
- - How to handle a view which does not have a complete private key?
143
- - What edge cases exist in updating views?
144
-
145
- ### Batch updates
146
- Supporting batch updates similar to batch inserts is slightly bending the RESTfull principles but would still be a useful optional feature.
147
-
148
- ### Aggregation
149
- Similar to filtering, ordering and limits, aggregation could be implemented as HTTP request parameters telling what column is aggregated or used for ordering or how many results to return.
150
-
151
- ### Streaming
152
- 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.
153
-
154
- ### SQL generation callbacks
155
- It would be useful to allow developer to validate / override SQL generation to cover cases OINO does not support or even workaround issues.
156
-
157
- ### Transactions
158
- 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.
159
-
160
-
161
- # HELP
162
-
163
- ## Bug reports
164
- 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.
165
-
166
- ## Feedback
167
- 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.
168
-
169
- ## Typescript / Javascript architecture
170
- 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.
171
-
172
- # LINKS
173
- - [Github repository](https://github.com/pragmatta/oino-ts)
174
- - [NPM repository](https://www.npmjs.com/org/oino-ts)
175
-
176
-
177
- # ACKNOWLEDGEMENTS
178
-
179
- ## Libraries
180
- OINO uses the following open source libraries and npm packages and I would like to thank everyone for their contributions:
181
- - Postgresql [node-postgres package](https://www.npmjs.com/package/pg)
182
- - Mariadb / Mysql [mariadb package](https://www.npmjs.com/package/mariadb)
183
- - Sql Server [mssql package](https://www.npmjs.com/package/mssql)
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
+ ![Swagger definition with a data model schema](img/readme-swagger.png)
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).
@@ -109,11 +109,11 @@ class OINODbMsSql extends db_1.OINODb {
109
109
  throw new Error(db_1.OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type);
110
110
  }
111
111
  this._pool = new mssql_1.ConnectionPool({
112
- user: params.user,
113
- password: params.password,
114
- server: params.url,
115
- port: params.port,
116
- database: params.database,
112
+ user: this._params.user,
113
+ password: this._params.password,
114
+ server: this._params.url,
115
+ port: this._params.port,
116
+ database: this._params.database,
117
117
  arrayRowMode: true,
118
118
  options: {
119
119
  encrypt: true, // Use encryption for Azure SQL Database
@@ -122,18 +122,19 @@ class OINODbMsSql extends db_1.OINODb {
122
122
  trustServerCertificate: true // Change to false for production
123
123
  }
124
124
  });
125
- //this._pool = new ConnectionPool({connectionString: "Server=localhost,1433;Database=database;User Id=username;Password=" + params.password + ";Encrypt=true"})
125
+ delete this._params.password; // do not store password in db object
126
126
  this._pool.on("error", (conn) => {
127
- // console.log("OINODbMsSql error", conn)
128
- });
129
- this._pool.on("debug", (event) => {
130
- // console.log("OINODbMsSql debug",event)
127
+ db_1.OINOLog.error("OINODbMsSql error event", conn);
131
128
  });
129
+ // this._pool.on("debug", (event:any) => {
130
+ // console.log("OINODbMsSql debug",event)
131
+ // })
132
132
  }
133
133
  async _query(sql) {
134
134
  // OINOLog.debug("OINODbMsSql._query", {sql:sql})
135
135
  try {
136
- const sql_res = await this._pool.request().query(sql);
136
+ const request = this._pool.request(); // this does not need to be released but the pool will handle it
137
+ const sql_res = await request.query(sql);
137
138
  // console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
138
139
  const result = new OINOMsSqlData(sql_res.recordsets);
139
140
  return Promise.resolve(result);
@@ -271,6 +272,9 @@ class OINODbMsSql extends db_1.OINODb {
271
272
  if (whereCondition != "") {
272
273
  result += " WHERE " + whereCondition;
273
274
  }
275
+ if (groupByCondition != "") {
276
+ result += " GROUP BY " + groupByCondition;
277
+ }
274
278
  if (orderCondition != "") {
275
279
  result += " ORDER BY " + orderCondition;
276
280
  }
@@ -282,9 +286,6 @@ class OINODbMsSql extends db_1.OINODb {
282
286
  result += " OFFSET " + limit_parts[1] + " ROWS FETCH NEXT " + limit_parts[0] + " ROWS ONLY";
283
287
  }
284
288
  }
285
- if (groupByCondition != "") {
286
- result += " GROUP BY " + groupByCondition;
287
- }
288
289
  result += ";";
289
290
  // OINOLog.debug("OINODb.printSqlSelect", {result:result})
290
291
  return result;
@@ -294,17 +295,56 @@ class OINODbMsSql extends db_1.OINODb {
294
295
  *
295
296
  */
296
297
  async connect() {
298
+ let result = new db_1.OINOResult();
297
299
  try {
298
300
  // make sure that any items are correctly URL encoded in the connection string
299
301
  await this._pool.connect();
300
302
  // OINOLog.info("OINODbMsSql.connect: Connected to database server.")
301
- await this._pool.request().query("SELECT 1 as test");
302
- return Promise.resolve(true);
303
+ // await this._pool.request().query("SELECT 1 as test")
304
+ this.isConnected = true;
303
305
  }
304
306
  catch (err) {
305
307
  // ... error checks
306
- throw new Error(db_1.OINO_ERROR_PREFIX + ": Error connecting to OINODbMsSql server: " + err);
308
+ result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect");
309
+ db_1.OINOLog.error(result.statusMessage, { error: err });
307
310
  }
311
+ return Promise.resolve(result);
312
+ }
313
+ /**
314
+ * Validate connection to database is working.
315
+ *
316
+ */
317
+ async validate() {
318
+ let result = new db_1.OINOResult();
319
+ if (!this.isConnected) {
320
+ result.setError(400, "Database is not connected!", "OINODbMsSql.validate");
321
+ return result;
322
+ }
323
+ db_1.OINOBenchmark.start("OINODb", "validate");
324
+ try {
325
+ const sql = this._getValidateSql(this._params.database);
326
+ // OINOLog.debug("OINODbMsSql.validate", {sql:sql})
327
+ const sql_res = await this.sqlSelect(sql);
328
+ // OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
329
+ if (sql_res.isEmpty()) {
330
+ result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate");
331
+ }
332
+ else if (sql_res.getRow().length == 0) {
333
+ result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate");
334
+ }
335
+ else if (sql_res.getRow()[0] == "0") {
336
+ result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate");
337
+ }
338
+ else {
339
+ // connection is working
340
+ this.isValidated = true;
341
+ }
342
+ }
343
+ catch (e) {
344
+ result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate");
345
+ }
346
+ db_1.OINOBenchmark.end("OINODb", "validate");
347
+ return result;
308
348
  }
309
349
  /**
310
350
  * Execute a select operation.
@@ -316,7 +356,7 @@ class OINODbMsSql extends db_1.OINODb {
316
356
  db_1.OINOBenchmark.start("OINODb", "sqlSelect");
317
357
  let result;
318
358
  try {
319
- // OINOLog.debug("OINODbMsSql.sqlSelect", {sql_rows:sql_rows})
359
+ // OINOLog.debug("OINODbMsSql.sqlSelect", {sql:sql})
320
360
  result = await this._query(sql);
321
361
  }
322
362
  catch (e) {
@@ -367,6 +407,21 @@ WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
367
407
  ORDER BY C.ORDINAL_POSITION;`;
368
408
  return sql;
369
409
  }
410
+ _getValidateSql(dbName) {
411
+ const sql = `SELECT
412
+ count(C.COLUMN_NAME) AS COLUMN_COUNT
413
+ FROM
414
+ INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
415
+ (
416
+ SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
417
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
418
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
419
+ GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
420
+ ) as CONST
421
+ ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
422
+ WHERE C.TABLE_CATALOG = '${dbName}';`;
423
+ return sql;
424
+ }
370
425
  /**
371
426
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
372
427
  * the model.
@@ -3,7 +3,7 @@
3
3
  * License, v. 2.0. If a copy of the MPL was not distributed with this
4
4
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
- import { OINODb, OINODbDataSet, OINOBooleanDataField, OINONumberDataField, OINOStringDataField, OINO_ERROR_PREFIX, OINOBenchmark, OINODatetimeDataField, OINOBlobDataField, OINO_INFO_PREFIX, OINODB_EMPTY_ROW, OINODB_EMPTY_ROWS, OINOLog } from "@oino-ts/db";
6
+ import { OINODb, OINODbDataSet, OINOBooleanDataField, OINONumberDataField, OINOStringDataField, OINO_ERROR_PREFIX, OINOBenchmark, OINODatetimeDataField, OINOBlobDataField, OINO_INFO_PREFIX, OINODB_EMPTY_ROW, OINODB_EMPTY_ROWS, OINOLog, OINOResult } from "@oino-ts/db";
7
7
  import { ConnectionPool } from "mssql";
8
8
  /**
9
9
  * Implmentation of OINODbDataSet for MariaDb.
@@ -106,11 +106,11 @@ export class OINODbMsSql extends OINODb {
106
106
  throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type);
107
107
  }
108
108
  this._pool = new ConnectionPool({
109
- user: params.user,
110
- password: params.password,
111
- server: params.url,
112
- port: params.port,
113
- database: params.database,
109
+ user: this._params.user,
110
+ password: this._params.password,
111
+ server: this._params.url,
112
+ port: this._params.port,
113
+ database: this._params.database,
114
114
  arrayRowMode: true,
115
115
  options: {
116
116
  encrypt: true, // Use encryption for Azure SQL Database
@@ -119,18 +119,19 @@ export class OINODbMsSql extends OINODb {
119
119
  trustServerCertificate: true // Change to false for production
120
120
  }
121
121
  });
122
- //this._pool = new ConnectionPool({connectionString: "Server=localhost,1433;Database=database;User Id=username;Password=" + params.password + ";Encrypt=true"})
122
+ delete this._params.password; // do not store password in db object
123
123
  this._pool.on("error", (conn) => {
124
- // console.log("OINODbMsSql error", conn)
125
- });
126
- this._pool.on("debug", (event) => {
127
- // console.log("OINODbMsSql debug",event)
124
+ OINOLog.error("OINODbMsSql error event", conn);
128
125
  });
126
+ // this._pool.on("debug", (event:any) => {
127
+ // console.log("OINODbMsSql debug",event)
128
+ // })
129
129
  }
130
130
  async _query(sql) {
131
131
  // OINOLog.debug("OINODbMsSql._query", {sql:sql})
132
132
  try {
133
- const sql_res = await this._pool.request().query(sql);
133
+ const request = this._pool.request(); // this does not need to be released but the pool will handle it
134
+ const sql_res = await request.query(sql);
134
135
  // console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
135
136
  const result = new OINOMsSqlData(sql_res.recordsets);
136
137
  return Promise.resolve(result);
@@ -268,6 +269,9 @@ export class OINODbMsSql extends OINODb {
268
269
  if (whereCondition != "") {
269
270
  result += " WHERE " + whereCondition;
270
271
  }
272
+ if (groupByCondition != "") {
273
+ result += " GROUP BY " + groupByCondition;
274
+ }
271
275
  if (orderCondition != "") {
272
276
  result += " ORDER BY " + orderCondition;
273
277
  }
@@ -279,9 +283,6 @@ export class OINODbMsSql extends OINODb {
279
283
  result += " OFFSET " + limit_parts[1] + " ROWS FETCH NEXT " + limit_parts[0] + " ROWS ONLY";
280
284
  }
281
285
  }
282
- if (groupByCondition != "") {
283
- result += " GROUP BY " + groupByCondition;
284
- }
285
286
  result += ";";
286
287
  // OINOLog.debug("OINODb.printSqlSelect", {result:result})
287
288
  return result;
@@ -291,17 +292,56 @@ export class OINODbMsSql extends OINODb {
291
292
  *
292
293
  */
293
294
  async connect() {
295
+ let result = new OINOResult();
294
296
  try {
295
297
  // make sure that any items are correctly URL encoded in the connection string
296
298
  await this._pool.connect();
297
299
  // OINOLog.info("OINODbMsSql.connect: Connected to database server.")
298
- await this._pool.request().query("SELECT 1 as test");
299
- return Promise.resolve(true);
300
+ // await this._pool.request().query("SELECT 1 as test")
301
+ this.isConnected = true;
300
302
  }
301
303
  catch (err) {
302
304
  // ... error checks
303
- throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMsSql server: " + err);
305
+ result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect");
306
+ OINOLog.error(result.statusMessage, { error: err });
304
307
  }
308
+ return Promise.resolve(result);
309
+ }
310
+ /**
311
+ * Validate connection to database is working.
312
+ *
313
+ */
314
+ async validate() {
315
+ let result = new OINOResult();
316
+ if (!this.isConnected) {
317
+ result.setError(400, "Database is not connected!", "OINODbMsSql.validate");
318
+ return result;
319
+ }
320
+ OINOBenchmark.start("OINODb", "validate");
321
+ try {
322
+ const sql = this._getValidateSql(this._params.database);
323
+ // OINOLog.debug("OINODbMsSql.validate", {sql:sql})
324
+ const sql_res = await this.sqlSelect(sql);
325
+ // OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
326
+ if (sql_res.isEmpty()) {
327
+ result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate");
328
+ }
329
+ else if (sql_res.getRow().length == 0) {
330
+ result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate");
331
+ }
332
+ else if (sql_res.getRow()[0] == "0") {
333
+ result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate");
334
+ }
335
+ else {
336
+ // connection is working
337
+ this.isValidated = true;
338
+ }
339
+ }
340
+ catch (e) {
341
+ result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate");
342
+ }
343
+ OINOBenchmark.end("OINODb", "validate");
344
+ return result;
305
345
  }
306
346
  /**
307
347
  * Execute a select operation.
@@ -313,7 +353,7 @@ export class OINODbMsSql extends OINODb {
313
353
  OINOBenchmark.start("OINODb", "sqlSelect");
314
354
  let result;
315
355
  try {
316
- // OINOLog.debug("OINODbMsSql.sqlSelect", {sql_rows:sql_rows})
356
+ // OINOLog.debug("OINODbMsSql.sqlSelect", {sql:sql})
317
357
  result = await this._query(sql);
318
358
  }
319
359
  catch (e) {
@@ -364,6 +404,21 @@ WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
364
404
  ORDER BY C.ORDINAL_POSITION;`;
365
405
  return sql;
366
406
  }
407
+ _getValidateSql(dbName) {
408
+ const sql = `SELECT
409
+ count(C.COLUMN_NAME) AS COLUMN_COUNT
410
+ FROM
411
+ INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
412
+ (
413
+ SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
414
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
415
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
416
+ GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
417
+ ) as CONST
418
+ ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
419
+ WHERE C.TABLE_CATALOG = '${dbName}';`;
420
+ return sql;
421
+ }
367
422
  /**
368
423
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
369
424
  * the model.
@@ -1,4 +1,4 @@
1
- import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINODataCell } from "@oino-ts/db";
1
+ import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINODataCell, OINOResult } from "@oino-ts/db";
2
2
  /**
3
3
  * Implementation of MariaDb/MySql-database.
4
4
  *
@@ -67,7 +67,12 @@ export declare class OINODbMsSql extends OINODb {
67
67
  * Connect to database.
68
68
  *
69
69
  */
70
- connect(): Promise<boolean>;
70
+ connect(): Promise<OINOResult>;
71
+ /**
72
+ * Validate connection to database is working.
73
+ *
74
+ */
75
+ validate(): Promise<OINOResult>;
71
76
  /**
72
77
  * Execute a select operation.
73
78
  *
@@ -83,6 +88,7 @@ export declare class OINODbMsSql extends OINODb {
83
88
  */
84
89
  sqlExec(sql: string): Promise<OINODbDataSet>;
85
90
  private _getSchemaSql;
91
+ private _getValidateSql;
86
92
  /**
87
93
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
88
94
  * the model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oino-ts/db-mssql",
3
- "version": "0.5.2",
3
+ "version": "0.6.1",
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.5.2",
25
+ "@oino-ts/db": "0.6.1",
26
26
  "mssql": "^11.0.1"
27
27
  },
28
28
  "devDependencies": {
@@ -4,7 +4,7 @@
4
4
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
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";
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, OINOResult } from "@oino-ts/db";
8
8
 
9
9
  import {ConnectionPool, config} from "mssql";
10
10
 
@@ -117,11 +117,11 @@ export class OINODbMsSql extends OINODb {
117
117
  throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMsSql-type: " + this._params.type)
118
118
  }
119
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,
120
+ user: this._params.user,
121
+ password: this._params.password,
122
+ server: this._params.url,
123
+ port: this._params.port,
124
+ database: this._params.database,
125
125
  arrayRowMode:true,
126
126
  options: {
127
127
  encrypt: true, // Use encryption for Azure SQL Database
@@ -130,20 +130,21 @@ export class OINODbMsSql extends OINODb {
130
130
  trustServerCertificate: true // Change to false for production
131
131
  }
132
132
  })
133
- //this._pool = new ConnectionPool({connectionString: "Server=localhost,1433;Database=database;User Id=username;Password=" + params.password + ";Encrypt=true"})
133
+ delete this._params.password // do not store password in db object
134
134
 
135
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)
136
+ OINOLog.error("OINODbMsSql error event", conn)
140
137
  })
138
+ // this._pool.on("debug", (event:any) => {
139
+ // console.log("OINODbMsSql debug",event)
140
+ // })
141
141
  }
142
142
 
143
143
  private async _query(sql:string):Promise<OINOMsSqlData> {
144
144
  // OINOLog.debug("OINODbMsSql._query", {sql:sql})
145
145
  try {
146
- const sql_res = await this._pool.request().query(sql);
146
+ const request = this._pool.request() // this does not need to be released but the pool will handle it
147
+ const sql_res = await request.query(sql)
147
148
  // console.log("OINODbMsSql._query result:" + JSON.stringify(sql_res.recordsets))
148
149
  const result:OINOMsSqlData = new OINOMsSqlData(sql_res.recordsets)
149
150
  return Promise.resolve(result)
@@ -287,6 +288,9 @@ export class OINODbMsSql extends OINODb {
287
288
  if (whereCondition != "") {
288
289
  result += " WHERE " + whereCondition
289
290
  }
291
+ if (groupByCondition != "") {
292
+ result += " GROUP BY " + groupByCondition
293
+ }
290
294
  if (orderCondition != "") {
291
295
  result += " ORDER BY " + orderCondition
292
296
  }
@@ -297,9 +301,6 @@ export class OINODbMsSql extends OINODb {
297
301
  result += " OFFSET " + limit_parts[1] + " ROWS FETCH NEXT " + limit_parts[0] + " ROWS ONLY"
298
302
  }
299
303
  }
300
- if (groupByCondition != "") {
301
- result += " GROUP BY " + groupByCondition
302
- }
303
304
  result += ";"
304
305
  // OINOLog.debug("OINODb.printSqlSelect", {result:result})
305
306
  return result;
@@ -309,17 +310,57 @@ export class OINODbMsSql extends OINODb {
309
310
  * Connect to database.
310
311
  *
311
312
  */
312
- async connect(): Promise<boolean> {
313
+ async connect(): Promise<OINOResult> {
314
+ let result:OINOResult = new OINOResult()
313
315
  try {
314
316
  // make sure that any items are correctly URL encoded in the connection string
315
317
  await this._pool.connect()
316
318
  // 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) {
319
+ // await this._pool.request().query("SELECT 1 as test")
320
+ this.isConnected = true
321
+
322
+ } catch (err:any) {
320
323
  // ... error checks
321
- throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMsSql server: " + err)
324
+ result.setError(500, "Exception connecting to database: " + err.message, "OINODbMsSql.connect")
325
+ OINOLog.error(result.statusMessage, {error:err})
322
326
  }
327
+ return Promise.resolve(result)
328
+ }
329
+
330
+ /**
331
+ * Validate connection to database is working.
332
+ *
333
+ */
334
+ async validate(): Promise<OINOResult> {
335
+ let result:OINOResult = new OINOResult()
336
+ if (!this.isConnected) {
337
+ result.setError(400, "Database is not connected!", "OINODbMsSql.validate")
338
+ return result
339
+ }
340
+ OINOBenchmark.start("OINODb", "validate")
341
+ try {
342
+ const sql = this._getValidateSql(this._params.database)
343
+ // OINOLog.debug("OINODbMsSql.validate", {sql:sql})
344
+ const sql_res:OINODbDataSet = await this.sqlSelect(sql)
345
+ // OINOLog.debug("OINODbMsSql.validate", {sql_res:sql_res})
346
+ if (sql_res.isEmpty()) {
347
+ result.setError(400, "DB returned no rows for select!", "OINODbMsSql.validate")
348
+
349
+ } else if (sql_res.getRow().length == 0) {
350
+ result.setError(400, "DB returned no values for database!", "OINODbMsSql.validate")
351
+
352
+ } else if (sql_res.getRow()[0] == "0") {
353
+ result.setError(400, "DB returned no schema for database!", "OINODbMsSql.validate")
354
+
355
+ } else {
356
+ // connection is working
357
+ this.isValidated = true
358
+ }
359
+ } catch (e:any) {
360
+ result.setError(500, "Exception in validating connection: " + e.message, "OINODbMsSql.validate")
361
+ }
362
+ OINOBenchmark.end("OINODb", "validate")
363
+ return result
323
364
  }
324
365
 
325
366
  /**
@@ -332,7 +373,7 @@ export class OINODbMsSql extends OINODb {
332
373
  OINOBenchmark.start("OINODb", "sqlSelect")
333
374
  let result:OINODbDataSet
334
375
  try {
335
- // OINOLog.debug("OINODbMsSql.sqlSelect", {sql_rows:sql_rows})
376
+ // OINOLog.debug("OINODbMsSql.sqlSelect", {sql:sql})
336
377
  result = await this._query(sql)
337
378
 
338
379
  } catch (e:any) {
@@ -386,6 +427,24 @@ WHERE C.TABLE_CATALOG = '${dbName}' AND C.TABLE_NAME = '${tableName}'
386
427
  ORDER BY C.ORDINAL_POSITION;`
387
428
  return sql
388
429
  }
430
+
431
+ private _getValidateSql(dbName:string):string {
432
+ const sql =
433
+ `SELECT
434
+ count(C.COLUMN_NAME) AS COLUMN_COUNT
435
+ FROM
436
+ INFORMATION_SCHEMA.COLUMNS as C LEFT JOIN
437
+ (
438
+ SELECT TC.TABLE_NAME, KU.COLUMN_NAME, STRING_AGG(TC.CONSTRAINT_TYPE, ',') as CONSTRAINT_TYPES
439
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
440
+ INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU ON TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME
441
+ GROUP BY TC.TABLE_NAME, KU.COLUMN_NAME
442
+ ) as CONST
443
+ ON C.TABLE_NAME = CONST.TABLE_NAME AND C.COLUMN_NAME = CONST.COLUMN_NAME
444
+ WHERE C.TABLE_CATALOG = '${dbName}';`
445
+ return sql
446
+ }
447
+
389
448
  /**
390
449
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
391
450
  * the model.