@oino-ts/db-mariadb 0.0.15 → 0.0.17

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
@@ -10,15 +10,15 @@
10
10
  # GETTING STARTED
11
11
 
12
12
  ### Setup
13
- Install the `@oino-ts/core` npm package and necessary database packages and import them in your code.
13
+ Install the `@oino-ts/db` npm package and necessary database packages and import them in your code.
14
14
  ```
15
- bun install @oino-ts/core
16
- bun install @oino-ts/bunsqlite
15
+ bun install @oino-ts/db
16
+ bun install @oino-ts/db-bunsqlite
17
17
  ```
18
18
 
19
19
  ```
20
- import { OINODb, OINOApi, OINOFactory } from "@oino-ts/core";
21
- import { OINODbBunSqlite } from "@oino-ts/bunsqlite"
20
+ import { OINODb, OINOApi, OINOFactory } from "@oino-ts/db";
21
+ import { OINODbBunSqlite } from "@oino-ts/db-bunsqlite"
22
22
  ```
23
23
 
24
24
  ### Register database and logger
@@ -51,7 +51,7 @@
51
51
  ### Write results back to HTTP Response
52
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
53
  ```
54
- return new Response(result.modelset.writeString(OINOContentType.json))
54
+ return new Response(result.data.writeString(OINOContentType.json))
55
55
  ```
56
56
 
57
57
 
@@ -59,20 +59,8 @@
59
59
 
60
60
  ## RESTfull
61
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
- ### HTTP GET
64
- ```
65
- Request and response:
66
- > curl.exe -X GET http://localhost:3001/orderdetails/11077:77
67
- [
68
- {"_OINOID_":"11077:77","OrderID":11077,"ProductID":77,"UnitPrice":13,"Quantity":2,"Discount":0}
69
- ]
70
-
71
- SQL:
72
- SELECT "OrderID","ProductID","UnitPrice","Quantity","Discount" FROM [OrderDetails] WHERE ("OrderID"=11077 AND "ProductID"=77);
73
- ```
74
62
 
75
- ### HTTP POST
63
+ For example HTTP POST
76
64
  ```
77
65
  Request and response:
78
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}]'
@@ -82,31 +70,12 @@
82
70
  INSERT INTO [OrderDetails] ("OrderID","ProductID","UnitPrice","Quantity","Discount") VALUES (11077,99,19,1,0);
83
71
  ```
84
72
 
85
- ### HTTP PUT
86
- ```
87
- Request and response:
88
- > curl.exe -X PUT http://localhost:3001/orderdetails/11077:99 -H "Content-Type: application/json" --data '[{\"UnitPrice\":20}]'
89
- {"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
90
73
 
91
- SQL:
92
- UPDATE [OrderDetails] SET "UnitPrice"=20 WHERE ("OrderID"=11077 AND "ProductID"=99);
93
- ```
94
-
95
- ### HTTP DELETE
96
- ```
97
- Request and response:
98
- > curl.exe -X DELETE http://localhost:3001/orderdetails/11077:99
99
- {"success":true,"statusCode":200,"statusMessage":"OK","messages":[]}
100
-
101
- SQL:
102
- DELETE FROM [OrderDetails] WHERE ("OrderID"=11077 AND "ProductID"=99);
103
- ```
104
-
105
74
  ## Universal Serialization
106
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.
107
76
 
108
77
  ### Features
109
- - Files can be sent to BLOB fields using BASE64 encoding.
78
+ - Files can be sent to BLOB fields using BASE64 or MIME multipart encoding.
110
79
  - Datetimes are (optionally) normalized to ISO 8601 format.
111
80
  - Extended JSON-encoding
112
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).
@@ -129,12 +98,13 @@
129
98
  - Bun Sqlite through Bun native implementation
130
99
  - Postgresql through [pg](https://www.npmjs.com/package/pg)-package
131
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
132
102
 
133
103
  ## Complex Keys
134
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`.
135
105
 
136
106
  ## Power Of SQL
137
- Since OINO controls the SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html) and order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.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
+ 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 [`OINOSqlOrder`](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.
138
108
 
139
109
  ## Swagger Support
140
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.
@@ -158,7 +128,7 @@
158
128
  # STATUS
159
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.
160
130
 
161
- ## Beta
131
+ ## Beta
162
132
  For a beta status following milestones are planned:
163
133
 
164
134
  ### Realistic app
@@ -178,8 +148,8 @@
178
148
  ### Batch updates
179
149
  Supporting batch updates similar to batch inserts is slightly bending the RESTfull principles but would still be a useful optional feature.
180
150
 
181
- ### Aggregation and limits
182
- Similar to filtering and ordering, aggregation and limits can be implemented as HTTP request parameters telling what column is aggregated or used for ordering or how many results to return.
151
+ ### Aggregation
152
+ 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.
183
153
 
184
154
  ### Streaming
185
155
  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.
@@ -205,18 +175,19 @@
205
175
  # LINKS
206
176
  - [Github repository](https://github.com/pragmatta/oino-ts)
207
177
  - [NPM repository](https://www.npmjs.com/org/oino-ts)
208
-
178
+
209
179
 
210
180
  # ACKNOWLEDGEMENTS
211
181
 
212
182
  ## Libraries
213
183
  OINO uses the following open source libraries and npm packages and I would like to thank everyone for their contributions:
214
- - Postgresql support by [node-postgres package](https://www.npmjs.com/package/pg)
215
- - Mariadb / Mysql-support by [mariadb package](https://www.npmjs.com/package/mariadb)
184
+ - Postgresql [node-postgres package](https://www.npmjs.com/package/pg)
185
+ - Mariadb / Mysql [mariadb package](https://www.npmjs.com/package/mariadb)
186
+ - Sql Server [mssql package](https://www.npmjs.com/package/mssql)
187
+ - Custom base encoding [base-x package](https://www.npmjs.com/package/base-x)
216
188
 
217
189
  ## Bun
218
190
  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.
219
191
 
220
192
  ## SQL Scripts
221
193
  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).
222
-
@@ -38,14 +38,25 @@ class OINOMariadbData extends db_1.OINODbDataSet {
38
38
  }
39
39
  _currentRow;
40
40
  _eof;
41
+ /**
42
+ * Is data set empty.
43
+ *
44
+ */
41
45
  isEmpty() {
42
46
  return (this._rows.length == 0);
43
47
  }
44
- // EOF means "there is no more content", i.e. either dataset is empty or we have moved beyond last line
48
+ /**
49
+ * Is there no more content, i.e. either dataset is empty or we have moved beyond last line
50
+ *
51
+ */
45
52
  isEof() {
46
53
  return (this._eof);
47
54
  }
48
- next() {
55
+ /**
56
+ * Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
57
+ *
58
+ */
59
+ async next() {
49
60
  // OINOLog.debug("OINODbDataSet.next", {currentRow:this._currentRow, length:this.sqlResult.data.length})
50
61
  if (this._currentRow < this._rows.length - 1) {
51
62
  this._currentRow = this._currentRow + 1;
@@ -53,8 +64,12 @@ class OINOMariadbData extends db_1.OINODbDataSet {
53
64
  else {
54
65
  this._eof = true;
55
66
  }
56
- return !this._eof;
67
+ return Promise.resolve(!this._eof);
57
68
  }
69
+ /**
70
+ * Gets current row of data.
71
+ *
72
+ */
58
73
  getRow() {
59
74
  if ((this._currentRow >= 0) && (this._currentRow < this._rows.length)) {
60
75
  return this._rows[this._currentRow];
@@ -71,7 +86,6 @@ class OINOMariadbData extends db_1.OINODbDataSet {
71
86
  class OINODbMariadb extends db_1.OINODb {
72
87
  static _fieldLengthRegex = /([^\(\)]+)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i;
73
88
  static _exceptionMessageRegex = /\(([^\)]*)\) (.*)\nsql\:(.*)?/i;
74
- static _tableSchemaSql = `SHOW COLUMNS from `;
75
89
  _pool;
76
90
  /**
77
91
  * Constructor of `OINODbMariadb`
@@ -256,7 +270,7 @@ class OINODbMariadb extends db_1.OINODb {
256
270
  }
257
271
  catch (err) {
258
272
  // ... error checks
259
- throw new Error(db_1.OINO_ERROR_PREFIX + ": Error connecting to Postgresql server: " + err);
273
+ throw new Error(db_1.OINO_ERROR_PREFIX + ": Error connecting to OINODbMariadb server: " + err);
260
274
  }
261
275
  }
262
276
  /**
@@ -299,6 +313,21 @@ class OINODbMariadb extends db_1.OINODb {
299
313
  db_1.OINOBenchmark.end("OINODb", "sqlExec");
300
314
  return result;
301
315
  }
316
+ _getSchemaSql(dbName, tableName) {
317
+ const sql = `SELECT
318
+ c.COLUMN_NAME,
319
+ c.COLUMN_TYPE,
320
+ c.IS_NULLABLE,
321
+ c.COLUMN_KEY,
322
+ c.COLUMN_DEFAULT,
323
+ c.EXTRA,
324
+ KCU.CONSTRAINT_NAME AS ForeignKeyName
325
+ FROM information_schema.COLUMNS C
326
+ LEFT JOIN information_schema.KEY_COLUMN_USAGE KCU ON KCU.TABLE_SCHEMA = C.TABLE_SCHEMA AND KCU.TABLE_NAME = C.TABLE_NAME AND C.COLUMN_NAME = KCU.COLUMN_NAME and KCU.REFERENCED_TABLE_NAME IS NOT NULL
327
+ WHERE C.TABLE_SCHEMA = '${dbName}' AND C.TABLE_NAME = '${tableName}'
328
+ ORDER BY C.ORDINAL_POSITION;`;
329
+ return sql;
330
+ }
302
331
  /**
303
332
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
304
333
  * the model.
@@ -307,7 +336,7 @@ class OINODbMariadb extends db_1.OINODb {
307
336
  *
308
337
  */
309
338
  async initializeApiDatamodel(api) {
310
- const res = await this.sqlSelect(OINODbMariadb._tableSchemaSql + this._params.database + "." + api.params.tableName + ";");
339
+ const res = await this.sqlSelect(this._getSchemaSql(this._params.database, api.params.tableName));
311
340
  while (!res.isEof()) {
312
341
  const row = res.getRow();
313
342
  // OINOLog.debug("OINODbMariadb.initializeApiDatamodel", { description:row })
@@ -320,6 +349,7 @@ class OINODbMariadb extends db_1.OINODb {
320
349
  const extra = row[5]?.toString() || "";
321
350
  const field_params = {
322
351
  isPrimaryKey: row[3] == "PRI",
352
+ isForeignKey: row[6] != null,
323
353
  isAutoInc: extra.indexOf('auto_increment') >= 0,
324
354
  isNotNull: row[2] == "NO"
325
355
  };
@@ -361,7 +391,7 @@ class OINODbMariadb extends db_1.OINODb {
361
391
  api.datamodel.addField(new db_1.OINOStringDataField(this, field_name, sql_type, field_params, 0));
362
392
  }
363
393
  }
364
- res.next();
394
+ await res.next();
365
395
  }
366
396
  db_1.OINOLog.debug("OINODbMariadb.initializeDatasetModel:\n" + api.datamodel.printDebug("\n"));
367
397
  return Promise.resolve();
@@ -35,14 +35,25 @@ class OINOMariadbData extends OINODbDataSet {
35
35
  }
36
36
  _currentRow;
37
37
  _eof;
38
+ /**
39
+ * Is data set empty.
40
+ *
41
+ */
38
42
  isEmpty() {
39
43
  return (this._rows.length == 0);
40
44
  }
41
- // EOF means "there is no more content", i.e. either dataset is empty or we have moved beyond last line
45
+ /**
46
+ * Is there no more content, i.e. either dataset is empty or we have moved beyond last line
47
+ *
48
+ */
42
49
  isEof() {
43
50
  return (this._eof);
44
51
  }
45
- next() {
52
+ /**
53
+ * Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
54
+ *
55
+ */
56
+ async next() {
46
57
  // OINOLog.debug("OINODbDataSet.next", {currentRow:this._currentRow, length:this.sqlResult.data.length})
47
58
  if (this._currentRow < this._rows.length - 1) {
48
59
  this._currentRow = this._currentRow + 1;
@@ -50,8 +61,12 @@ class OINOMariadbData extends OINODbDataSet {
50
61
  else {
51
62
  this._eof = true;
52
63
  }
53
- return !this._eof;
64
+ return Promise.resolve(!this._eof);
54
65
  }
66
+ /**
67
+ * Gets current row of data.
68
+ *
69
+ */
55
70
  getRow() {
56
71
  if ((this._currentRow >= 0) && (this._currentRow < this._rows.length)) {
57
72
  return this._rows[this._currentRow];
@@ -68,7 +83,6 @@ class OINOMariadbData extends OINODbDataSet {
68
83
  export class OINODbMariadb extends OINODb {
69
84
  static _fieldLengthRegex = /([^\(\)]+)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i;
70
85
  static _exceptionMessageRegex = /\(([^\)]*)\) (.*)\nsql\:(.*)?/i;
71
- static _tableSchemaSql = `SHOW COLUMNS from `;
72
86
  _pool;
73
87
  /**
74
88
  * Constructor of `OINODbMariadb`
@@ -253,7 +267,7 @@ export class OINODbMariadb extends OINODb {
253
267
  }
254
268
  catch (err) {
255
269
  // ... error checks
256
- throw new Error(OINO_ERROR_PREFIX + ": Error connecting to Postgresql server: " + err);
270
+ throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMariadb server: " + err);
257
271
  }
258
272
  }
259
273
  /**
@@ -296,6 +310,21 @@ export class OINODbMariadb extends OINODb {
296
310
  OINOBenchmark.end("OINODb", "sqlExec");
297
311
  return result;
298
312
  }
313
+ _getSchemaSql(dbName, tableName) {
314
+ const sql = `SELECT
315
+ c.COLUMN_NAME,
316
+ c.COLUMN_TYPE,
317
+ c.IS_NULLABLE,
318
+ c.COLUMN_KEY,
319
+ c.COLUMN_DEFAULT,
320
+ c.EXTRA,
321
+ KCU.CONSTRAINT_NAME AS ForeignKeyName
322
+ FROM information_schema.COLUMNS C
323
+ LEFT JOIN information_schema.KEY_COLUMN_USAGE KCU ON KCU.TABLE_SCHEMA = C.TABLE_SCHEMA AND KCU.TABLE_NAME = C.TABLE_NAME AND C.COLUMN_NAME = KCU.COLUMN_NAME and KCU.REFERENCED_TABLE_NAME IS NOT NULL
324
+ WHERE C.TABLE_SCHEMA = '${dbName}' AND C.TABLE_NAME = '${tableName}'
325
+ ORDER BY C.ORDINAL_POSITION;`;
326
+ return sql;
327
+ }
299
328
  /**
300
329
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
301
330
  * the model.
@@ -304,7 +333,7 @@ export class OINODbMariadb extends OINODb {
304
333
  *
305
334
  */
306
335
  async initializeApiDatamodel(api) {
307
- const res = await this.sqlSelect(OINODbMariadb._tableSchemaSql + this._params.database + "." + api.params.tableName + ";");
336
+ const res = await this.sqlSelect(this._getSchemaSql(this._params.database, api.params.tableName));
308
337
  while (!res.isEof()) {
309
338
  const row = res.getRow();
310
339
  // OINOLog.debug("OINODbMariadb.initializeApiDatamodel", { description:row })
@@ -317,6 +346,7 @@ export class OINODbMariadb extends OINODb {
317
346
  const extra = row[5]?.toString() || "";
318
347
  const field_params = {
319
348
  isPrimaryKey: row[3] == "PRI",
349
+ isForeignKey: row[6] != null,
320
350
  isAutoInc: extra.indexOf('auto_increment') >= 0,
321
351
  isNotNull: row[2] == "NO"
322
352
  };
@@ -358,7 +388,7 @@ export class OINODbMariadb extends OINODb {
358
388
  api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0));
359
389
  }
360
390
  }
361
- res.next();
391
+ await res.next();
362
392
  }
363
393
  OINOLog.debug("OINODbMariadb.initializeDatasetModel:\n" + api.datamodel.printDebug("\n"));
364
394
  return Promise.resolve();
@@ -6,7 +6,6 @@ import { OINODb, OINODbParams, OINODbDataSet, OINODbApi, OINODataCell } from "@o
6
6
  export declare class OINODbMariadb extends OINODb {
7
7
  private static _fieldLengthRegex;
8
8
  private static _exceptionMessageRegex;
9
- private static _tableSchemaSql;
10
9
  private _pool;
11
10
  /**
12
11
  * Constructor of `OINODbMariadb`
@@ -67,6 +66,7 @@ export declare class OINODbMariadb extends OINODb {
67
66
  *
68
67
  */
69
68
  sqlExec(sql: string): Promise<OINODbDataSet>;
69
+ private _getSchemaSql;
70
70
  /**
71
71
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
72
72
  * the model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oino-ts/db-mariadb",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "OINO TS package for using Mariadb databases.",
5
5
  "author": "Matias Kiviniemi (pragmatta)",
6
6
  "license": "MPL-2.0",
@@ -21,7 +21,7 @@
21
21
  "module": "./dist/esm/index.js",
22
22
  "types": "./dist/types/index.d.ts",
23
23
  "dependencies": {
24
- "@oino-ts/db": "^0.0.15",
24
+ "@oino-ts/db": "^0.0.17",
25
25
  "mariadb": "3.2.3"
26
26
  },
27
27
  "devDependencies": {
@@ -41,25 +41,40 @@ class OINOMariadbData extends OINODbDataSet {
41
41
  private _currentRow: number
42
42
  private _eof: boolean
43
43
 
44
+ /**
45
+ * Is data set empty.
46
+ *
47
+ */
44
48
  isEmpty():boolean {
45
49
  return (this._rows.length == 0)
46
50
  }
47
51
 
48
- // EOF means "there is no more content", i.e. either dataset is empty or we have moved beyond last line
52
+ /**
53
+ * Is there no more content, i.e. either dataset is empty or we have moved beyond last line
54
+ *
55
+ */
49
56
  isEof():boolean {
50
57
  return (this._eof)
51
58
  }
52
59
 
53
- next():boolean {
60
+ /**
61
+ * Attempts to moves dataset to the next row, possibly waiting for more data to become available. Returns !isEof().
62
+ *
63
+ */
64
+ async next():Promise<boolean> {
54
65
  // OINOLog.debug("OINODbDataSet.next", {currentRow:this._currentRow, length:this.sqlResult.data.length})
55
66
  if (this._currentRow < this._rows.length-1) {
56
67
  this._currentRow = this._currentRow + 1
57
68
  } else {
58
69
  this._eof = true
59
70
  }
60
- return !this._eof
71
+ return Promise.resolve(!this._eof)
61
72
  }
62
73
 
74
+ /**
75
+ * Gets current row of data.
76
+ *
77
+ */
63
78
  getRow(): OINODataRow {
64
79
  if ((this._currentRow >=0) && (this._currentRow < this._rows.length)) {
65
80
  return this._rows[this._currentRow]
@@ -77,7 +92,6 @@ export class OINODbMariadb extends OINODb {
77
92
 
78
93
  private static _fieldLengthRegex = /([^\(\)]+)(\s?\((\d+)\s?\,?\s?(\d*)?\))?/i
79
94
  private static _exceptionMessageRegex = /\(([^\)]*)\) (.*)\nsql\:(.*)?/i
80
- private static _tableSchemaSql:string = `SHOW COLUMNS from `
81
95
 
82
96
  private _pool:mariadb.Pool
83
97
 
@@ -269,7 +283,7 @@ export class OINODbMariadb extends OINODb {
269
283
  return Promise.resolve(true)
270
284
  } catch (err) {
271
285
  // ... error checks
272
- throw new Error(OINO_ERROR_PREFIX + ": Error connecting to Postgresql server: " + err)
286
+ throw new Error(OINO_ERROR_PREFIX + ": Error connecting to OINODbMariadb server: " + err)
273
287
  }
274
288
  }
275
289
 
@@ -315,6 +329,23 @@ export class OINODbMariadb extends OINODb {
315
329
  return result
316
330
  }
317
331
 
332
+ private _getSchemaSql(dbName:string, tableName:string):string {
333
+ const sql =
334
+ `SELECT
335
+ c.COLUMN_NAME,
336
+ c.COLUMN_TYPE,
337
+ c.IS_NULLABLE,
338
+ c.COLUMN_KEY,
339
+ c.COLUMN_DEFAULT,
340
+ c.EXTRA,
341
+ KCU.CONSTRAINT_NAME AS ForeignKeyName
342
+ FROM information_schema.COLUMNS C
343
+ LEFT JOIN information_schema.KEY_COLUMN_USAGE KCU ON KCU.TABLE_SCHEMA = C.TABLE_SCHEMA AND KCU.TABLE_NAME = C.TABLE_NAME AND C.COLUMN_NAME = KCU.COLUMN_NAME and KCU.REFERENCED_TABLE_NAME IS NOT NULL
344
+ WHERE C.TABLE_SCHEMA = '${dbName}' AND C.TABLE_NAME = '${tableName}'
345
+ ORDER BY C.ORDINAL_POSITION;`
346
+ return sql
347
+ }
348
+
318
349
  /**
319
350
  * Initialize a data model by getting the SQL schema and populating OINODbDataFields of
320
351
  * the model.
@@ -324,7 +355,7 @@ export class OINODbMariadb extends OINODb {
324
355
  */
325
356
  async initializeApiDatamodel(api:OINODbApi): Promise<void> {
326
357
 
327
- const res:OINODbDataSet = await this.sqlSelect(OINODbMariadb._tableSchemaSql + this._params.database + "." + api.params.tableName + ";")
358
+ const res:OINODbDataSet = await this.sqlSelect(this._getSchemaSql(this._params.database, api.params.tableName))
328
359
  while (!res.isEof()) {
329
360
  const row:OINODataRow = res.getRow()
330
361
  // OINOLog.debug("OINODbMariadb.initializeApiDatamodel", { description:row })
@@ -337,6 +368,7 @@ export class OINODbMariadb extends OINODb {
337
368
  const extra:string = row[5]?.toString() || ""
338
369
  const field_params:OINODbDataFieldParams = {
339
370
  isPrimaryKey: row[3] == "PRI",
371
+ isForeignKey: row[6] != null,
340
372
  isAutoInc: extra.indexOf('auto_increment') >= 0,
341
373
  isNotNull: row[2] == "NO"
342
374
  }
@@ -375,7 +407,7 @@ export class OINODbMariadb extends OINODb {
375
407
  api.datamodel.addField(new OINOStringDataField(this, field_name, sql_type, field_params, 0))
376
408
  }
377
409
  }
378
- res.next()
410
+ await res.next()
379
411
  }
380
412
  OINOLog.debug("OINODbMariadb.initializeDatasetModel:\n" + api.datamodel.printDebug("\n"))
381
413
  return Promise.resolve()