@oino-ts/db-mariadb 1.1.3 → 1.2.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/dist/cjs/OINODbMariadb.js +58 -17
- package/dist/esm/OINODbMariadb.js +58 -17
- package/dist/types/OINODbMariadb.d.ts +24 -1
- package/package.json +4 -4
- package/src/OINODbMariadb.ts +66 -22
|
@@ -104,7 +104,7 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
104
104
|
if (this.dbParams.type !== "OINODbMariadb") {
|
|
105
105
|
throw new Error(common_1.OINO_ERROR_PREFIX + ": Not OINODbMariadb-type: " + this.dbParams.type);
|
|
106
106
|
}
|
|
107
|
-
this._pool = mariadb_1.default.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug: false, rowsAsArray: true, multipleStatements:
|
|
107
|
+
this._pool = mariadb_1.default.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug: false, rowsAsArray: true, multipleStatements: false }); // statements are now executed individually with bind parameters, so stacked/multi-statement execution is disabled
|
|
108
108
|
delete this.dbParams.password; // do not store password in db object
|
|
109
109
|
}
|
|
110
110
|
_parseFieldLength(fieldLengthStr) {
|
|
@@ -114,12 +114,12 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
114
114
|
}
|
|
115
115
|
return result;
|
|
116
116
|
}
|
|
117
|
-
async _query(sql) {
|
|
117
|
+
async _query(sql, params) {
|
|
118
118
|
let connection = null;
|
|
119
119
|
let rows = common_2.OINO_EMPTY_ROWS;
|
|
120
120
|
try {
|
|
121
121
|
connection = await this._pool.getConnection();
|
|
122
|
-
const sql_res = await connection.query(sql);
|
|
122
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql);
|
|
123
123
|
// console.log("_query: sql=", sql, " result=", result)
|
|
124
124
|
if (Array.isArray(sql_res)) {
|
|
125
125
|
rows = sql_res.filter((r) => Array.isArray(r)); // filter out OkPacket results from multiple statements
|
|
@@ -136,12 +136,12 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
136
136
|
}
|
|
137
137
|
return new OINOMariadbData(rows, []);
|
|
138
138
|
}
|
|
139
|
-
async _exec(sql) {
|
|
139
|
+
async _exec(sql, params) {
|
|
140
140
|
let connection = null;
|
|
141
141
|
let rows = common_2.OINO_EMPTY_ROWS;
|
|
142
142
|
try {
|
|
143
143
|
connection = await this._pool.getConnection();
|
|
144
|
-
const sql_res = await connection.query(sql);
|
|
144
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql);
|
|
145
145
|
// console.log("OINODbMariadb._exec: result=", result)
|
|
146
146
|
if (Array.isArray(sql_res)) {
|
|
147
147
|
rows = sql_res.filter((r) => Array.isArray(r)); // filter out OkPacket results from multiple statements
|
|
@@ -166,7 +166,7 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
166
166
|
*
|
|
167
167
|
*/
|
|
168
168
|
printTableName(sqlTable) {
|
|
169
|
-
return "`" + sqlTable + "`";
|
|
169
|
+
return "`" + sqlTable.replaceAll("`", "``") + "`";
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
172
172
|
* Print a column name with correct SQL escaping.
|
|
@@ -175,7 +175,33 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
175
175
|
*
|
|
176
176
|
*/
|
|
177
177
|
printColumnName(sqlColumn) {
|
|
178
|
-
return "`" + sqlColumn + "`";
|
|
178
|
+
return "`" + sqlColumn.replaceAll("`", "``") + "`";
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Print a bind-parameter placeholder for the given zero-based parameter index (MySQL/MariaDB `?`).
|
|
182
|
+
*
|
|
183
|
+
* @param index zero-based parameter index
|
|
184
|
+
*
|
|
185
|
+
*/
|
|
186
|
+
printParameterName(index) {
|
|
187
|
+
return "?";
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Coerce a data value into a MariaDB bind-parameter value. The connector binds numbers,
|
|
191
|
+
* strings, `Date` and `Buffer` natively; booleans are mapped to 1/0 for `bit`/numeric columns.
|
|
192
|
+
*
|
|
193
|
+
* @param cellValue data value to bind
|
|
194
|
+
* @param nativeType native type name for the table column
|
|
195
|
+
*
|
|
196
|
+
*/
|
|
197
|
+
bindCellValue(cellValue, nativeType) {
|
|
198
|
+
if (cellValue === undefined) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
if (typeof cellValue === "boolean") {
|
|
202
|
+
return cellValue ? 1 : 0;
|
|
203
|
+
}
|
|
204
|
+
return cellValue;
|
|
179
205
|
}
|
|
180
206
|
/**
|
|
181
207
|
* Print a single data value from serialization using the context of the native data
|
|
@@ -304,8 +330,8 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
304
330
|
common_1.OINOBenchmark.startMetric("OINODb", "validate");
|
|
305
331
|
let result = new common_1.OINOResult();
|
|
306
332
|
try {
|
|
307
|
-
const sql = this._getValidateSql(
|
|
308
|
-
const sql_res = await this._query(sql);
|
|
333
|
+
const sql = this._getValidateSql();
|
|
334
|
+
const sql_res = await this._query(sql, [this.dbParams.database]);
|
|
309
335
|
if (sql_res.isEmpty()) {
|
|
310
336
|
result.setError(400, "DB returned no rows for schema!", "OINODbMariadb.validate");
|
|
311
337
|
}
|
|
@@ -361,7 +387,22 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
361
387
|
common_1.OINOBenchmark.endMetric("OINODb", "sqlExec", result.status != 500);
|
|
362
388
|
return result;
|
|
363
389
|
}
|
|
364
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Execute a parameterized statement, binding its values as positional `?` parameters.
|
|
392
|
+
*
|
|
393
|
+
* @param statement statement (SQL text + ordered bind values) to execute
|
|
394
|
+
*
|
|
395
|
+
*/
|
|
396
|
+
async runStatement(statement) {
|
|
397
|
+
if (!this.isValidated) {
|
|
398
|
+
throw new Error(common_1.OINO_ERROR_PREFIX + ": Database connection not validated!");
|
|
399
|
+
}
|
|
400
|
+
common_1.OINOBenchmark.startMetric("OINODb", "runStatement");
|
|
401
|
+
let result = await this._exec(statement.sql, statement.values);
|
|
402
|
+
common_1.OINOBenchmark.endMetric("OINODb", "runStatement", result.status != 500);
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
_getSchemaSql() {
|
|
365
406
|
const sql = `SELECT
|
|
366
407
|
C.COLUMN_NAME,
|
|
367
408
|
C.COLUMN_TYPE,
|
|
@@ -369,19 +410,19 @@ class OINODbMariadb extends db_1.OINODb {
|
|
|
369
410
|
C.COLUMN_KEY,
|
|
370
411
|
C.COLUMN_DEFAULT,
|
|
371
412
|
C.EXTRA,
|
|
372
|
-
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
413
|
+
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
373
414
|
FROM information_schema.COLUMNS C
|
|
374
415
|
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
|
|
375
|
-
WHERE C.TABLE_SCHEMA =
|
|
416
|
+
WHERE C.TABLE_SCHEMA = ? AND C.TABLE_NAME = ?
|
|
376
417
|
ORDER BY C.ORDINAL_POSITION;`;
|
|
377
418
|
return sql;
|
|
378
419
|
}
|
|
379
|
-
_getValidateSql(
|
|
420
|
+
_getValidateSql() {
|
|
380
421
|
const sql = `SELECT
|
|
381
422
|
Count(C.COLUMN_NAME) AS COLUMN_COUNT
|
|
382
423
|
FROM information_schema.COLUMNS C
|
|
383
424
|
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
|
|
384
|
-
WHERE C.TABLE_SCHEMA =
|
|
425
|
+
WHERE C.TABLE_SCHEMA = ?;`;
|
|
385
426
|
return sql;
|
|
386
427
|
}
|
|
387
428
|
/**
|
|
@@ -392,7 +433,7 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`;
|
|
|
392
433
|
*/
|
|
393
434
|
async getSchemaFields(tableName) {
|
|
394
435
|
const fields = [];
|
|
395
|
-
const schema_res = await this._query(this._getSchemaSql(this.dbParams.database, tableName)
|
|
436
|
+
const schema_res = await this._query(this._getSchemaSql(), [this.dbParams.database, tableName]);
|
|
396
437
|
while (!schema_res.isEof()) {
|
|
397
438
|
const row = schema_res.getRow();
|
|
398
439
|
const field_name = row[0]?.toString() || "";
|
|
@@ -443,8 +484,8 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`;
|
|
|
443
484
|
*/
|
|
444
485
|
async getSchemaTables() {
|
|
445
486
|
const tables = [];
|
|
446
|
-
const sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA =
|
|
447
|
-
const tables_res = await this._query(sql);
|
|
487
|
+
const sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME;";
|
|
488
|
+
const tables_res = await this._query(sql, [this.dbParams.database]);
|
|
448
489
|
while (!tables_res.isEof()) {
|
|
449
490
|
const row = tables_res.getRow();
|
|
450
491
|
const table_name = row[0]?.toString() || "";
|
|
@@ -101,7 +101,7 @@ export class OINODbMariadb extends OINODb {
|
|
|
101
101
|
if (this.dbParams.type !== "OINODbMariadb") {
|
|
102
102
|
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMariadb-type: " + this.dbParams.type);
|
|
103
103
|
}
|
|
104
|
-
this._pool = mariadb.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug: false, rowsAsArray: true, multipleStatements:
|
|
104
|
+
this._pool = mariadb.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug: false, rowsAsArray: true, multipleStatements: false }); // statements are now executed individually with bind parameters, so stacked/multi-statement execution is disabled
|
|
105
105
|
delete this.dbParams.password; // do not store password in db object
|
|
106
106
|
}
|
|
107
107
|
_parseFieldLength(fieldLengthStr) {
|
|
@@ -111,12 +111,12 @@ export class OINODbMariadb extends OINODb {
|
|
|
111
111
|
}
|
|
112
112
|
return result;
|
|
113
113
|
}
|
|
114
|
-
async _query(sql) {
|
|
114
|
+
async _query(sql, params) {
|
|
115
115
|
let connection = null;
|
|
116
116
|
let rows = OINO_EMPTY_ROWS;
|
|
117
117
|
try {
|
|
118
118
|
connection = await this._pool.getConnection();
|
|
119
|
-
const sql_res = await connection.query(sql);
|
|
119
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql);
|
|
120
120
|
// console.log("_query: sql=", sql, " result=", result)
|
|
121
121
|
if (Array.isArray(sql_res)) {
|
|
122
122
|
rows = sql_res.filter((r) => Array.isArray(r)); // filter out OkPacket results from multiple statements
|
|
@@ -133,12 +133,12 @@ export class OINODbMariadb extends OINODb {
|
|
|
133
133
|
}
|
|
134
134
|
return new OINOMariadbData(rows, []);
|
|
135
135
|
}
|
|
136
|
-
async _exec(sql) {
|
|
136
|
+
async _exec(sql, params) {
|
|
137
137
|
let connection = null;
|
|
138
138
|
let rows = OINO_EMPTY_ROWS;
|
|
139
139
|
try {
|
|
140
140
|
connection = await this._pool.getConnection();
|
|
141
|
-
const sql_res = await connection.query(sql);
|
|
141
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql);
|
|
142
142
|
// console.log("OINODbMariadb._exec: result=", result)
|
|
143
143
|
if (Array.isArray(sql_res)) {
|
|
144
144
|
rows = sql_res.filter((r) => Array.isArray(r)); // filter out OkPacket results from multiple statements
|
|
@@ -163,7 +163,7 @@ export class OINODbMariadb extends OINODb {
|
|
|
163
163
|
*
|
|
164
164
|
*/
|
|
165
165
|
printTableName(sqlTable) {
|
|
166
|
-
return "`" + sqlTable + "`";
|
|
166
|
+
return "`" + sqlTable.replaceAll("`", "``") + "`";
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
169
|
* Print a column name with correct SQL escaping.
|
|
@@ -172,7 +172,33 @@ export class OINODbMariadb extends OINODb {
|
|
|
172
172
|
*
|
|
173
173
|
*/
|
|
174
174
|
printColumnName(sqlColumn) {
|
|
175
|
-
return "`" + sqlColumn + "`";
|
|
175
|
+
return "`" + sqlColumn.replaceAll("`", "``") + "`";
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Print a bind-parameter placeholder for the given zero-based parameter index (MySQL/MariaDB `?`).
|
|
179
|
+
*
|
|
180
|
+
* @param index zero-based parameter index
|
|
181
|
+
*
|
|
182
|
+
*/
|
|
183
|
+
printParameterName(index) {
|
|
184
|
+
return "?";
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Coerce a data value into a MariaDB bind-parameter value. The connector binds numbers,
|
|
188
|
+
* strings, `Date` and `Buffer` natively; booleans are mapped to 1/0 for `bit`/numeric columns.
|
|
189
|
+
*
|
|
190
|
+
* @param cellValue data value to bind
|
|
191
|
+
* @param nativeType native type name for the table column
|
|
192
|
+
*
|
|
193
|
+
*/
|
|
194
|
+
bindCellValue(cellValue, nativeType) {
|
|
195
|
+
if (cellValue === undefined) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (typeof cellValue === "boolean") {
|
|
199
|
+
return cellValue ? 1 : 0;
|
|
200
|
+
}
|
|
201
|
+
return cellValue;
|
|
176
202
|
}
|
|
177
203
|
/**
|
|
178
204
|
* Print a single data value from serialization using the context of the native data
|
|
@@ -301,8 +327,8 @@ export class OINODbMariadb extends OINODb {
|
|
|
301
327
|
OINOBenchmark.startMetric("OINODb", "validate");
|
|
302
328
|
let result = new OINOResult();
|
|
303
329
|
try {
|
|
304
|
-
const sql = this._getValidateSql(
|
|
305
|
-
const sql_res = await this._query(sql);
|
|
330
|
+
const sql = this._getValidateSql();
|
|
331
|
+
const sql_res = await this._query(sql, [this.dbParams.database]);
|
|
306
332
|
if (sql_res.isEmpty()) {
|
|
307
333
|
result.setError(400, "DB returned no rows for schema!", "OINODbMariadb.validate");
|
|
308
334
|
}
|
|
@@ -358,7 +384,22 @@ export class OINODbMariadb extends OINODb {
|
|
|
358
384
|
OINOBenchmark.endMetric("OINODb", "sqlExec", result.status != 500);
|
|
359
385
|
return result;
|
|
360
386
|
}
|
|
361
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Execute a parameterized statement, binding its values as positional `?` parameters.
|
|
389
|
+
*
|
|
390
|
+
* @param statement statement (SQL text + ordered bind values) to execute
|
|
391
|
+
*
|
|
392
|
+
*/
|
|
393
|
+
async runStatement(statement) {
|
|
394
|
+
if (!this.isValidated) {
|
|
395
|
+
throw new Error(OINO_ERROR_PREFIX + ": Database connection not validated!");
|
|
396
|
+
}
|
|
397
|
+
OINOBenchmark.startMetric("OINODb", "runStatement");
|
|
398
|
+
let result = await this._exec(statement.sql, statement.values);
|
|
399
|
+
OINOBenchmark.endMetric("OINODb", "runStatement", result.status != 500);
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
_getSchemaSql() {
|
|
362
403
|
const sql = `SELECT
|
|
363
404
|
C.COLUMN_NAME,
|
|
364
405
|
C.COLUMN_TYPE,
|
|
@@ -366,19 +407,19 @@ export class OINODbMariadb extends OINODb {
|
|
|
366
407
|
C.COLUMN_KEY,
|
|
367
408
|
C.COLUMN_DEFAULT,
|
|
368
409
|
C.EXTRA,
|
|
369
|
-
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
410
|
+
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
370
411
|
FROM information_schema.COLUMNS C
|
|
371
412
|
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
|
|
372
|
-
WHERE C.TABLE_SCHEMA =
|
|
413
|
+
WHERE C.TABLE_SCHEMA = ? AND C.TABLE_NAME = ?
|
|
373
414
|
ORDER BY C.ORDINAL_POSITION;`;
|
|
374
415
|
return sql;
|
|
375
416
|
}
|
|
376
|
-
_getValidateSql(
|
|
417
|
+
_getValidateSql() {
|
|
377
418
|
const sql = `SELECT
|
|
378
419
|
Count(C.COLUMN_NAME) AS COLUMN_COUNT
|
|
379
420
|
FROM information_schema.COLUMNS C
|
|
380
421
|
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
|
|
381
|
-
WHERE C.TABLE_SCHEMA =
|
|
422
|
+
WHERE C.TABLE_SCHEMA = ?;`;
|
|
382
423
|
return sql;
|
|
383
424
|
}
|
|
384
425
|
/**
|
|
@@ -389,7 +430,7 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`;
|
|
|
389
430
|
*/
|
|
390
431
|
async getSchemaFields(tableName) {
|
|
391
432
|
const fields = [];
|
|
392
|
-
const schema_res = await this._query(this._getSchemaSql(this.dbParams.database, tableName)
|
|
433
|
+
const schema_res = await this._query(this._getSchemaSql(), [this.dbParams.database, tableName]);
|
|
393
434
|
while (!schema_res.isEof()) {
|
|
394
435
|
const row = schema_res.getRow();
|
|
395
436
|
const field_name = row[0]?.toString() || "";
|
|
@@ -440,8 +481,8 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`;
|
|
|
440
481
|
*/
|
|
441
482
|
async getSchemaTables() {
|
|
442
483
|
const tables = [];
|
|
443
|
-
const sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA =
|
|
444
|
-
const tables_res = await this._query(sql);
|
|
484
|
+
const sql = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME;";
|
|
485
|
+
const tables_res = await this._query(sql, [this.dbParams.database]);
|
|
445
486
|
while (!tables_res.isEof()) {
|
|
446
487
|
const row = tables_res.getRow();
|
|
447
488
|
const table_name = row[0]?.toString() || "";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OINOResult } from "@oino-ts/common";
|
|
2
2
|
import { OINODataSet, OINODataField, OINODataFieldSchema, OINODataCell } from "@oino-ts/common";
|
|
3
|
-
import { OINODb, OINODbParams } from "@oino-ts/db";
|
|
3
|
+
import { OINODb, OINODbParams, OINODbSqlStatement } from "@oino-ts/db";
|
|
4
4
|
/**
|
|
5
5
|
* Implementation of MariaDb/MySql-database.
|
|
6
6
|
*
|
|
@@ -32,6 +32,22 @@ export declare class OINODbMariadb extends OINODb {
|
|
|
32
32
|
*
|
|
33
33
|
*/
|
|
34
34
|
printColumnName(sqlColumn: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Print a bind-parameter placeholder for the given zero-based parameter index (MySQL/MariaDB `?`).
|
|
37
|
+
*
|
|
38
|
+
* @param index zero-based parameter index
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
printParameterName(index: number): string;
|
|
42
|
+
/**
|
|
43
|
+
* Coerce a data value into a MariaDB bind-parameter value. The connector binds numbers,
|
|
44
|
+
* strings, `Date` and `Buffer` natively; booleans are mapped to 1/0 for `bit`/numeric columns.
|
|
45
|
+
*
|
|
46
|
+
* @param cellValue data value to bind
|
|
47
|
+
* @param nativeType native type name for the table column
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
bindCellValue(cellValue: OINODataCell, nativeType: string): OINODataCell;
|
|
35
51
|
/**
|
|
36
52
|
* Print a single data value from serialization using the context of the native data
|
|
37
53
|
* type with the correct SQL escaping.
|
|
@@ -86,6 +102,13 @@ export declare class OINODbMariadb extends OINODb {
|
|
|
86
102
|
*
|
|
87
103
|
*/
|
|
88
104
|
sqlExec(sql: string): Promise<OINODataSet>;
|
|
105
|
+
/**
|
|
106
|
+
* Execute a parameterized statement, binding its values as positional `?` parameters.
|
|
107
|
+
*
|
|
108
|
+
* @param statement statement (SQL text + ordered bind values) to execute
|
|
109
|
+
*
|
|
110
|
+
*/
|
|
111
|
+
runStatement(statement: OINODbSqlStatement): Promise<OINODataSet>;
|
|
89
112
|
private _getSchemaSql;
|
|
90
113
|
private _getValidateSql;
|
|
91
114
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oino-ts/db-mariadb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "OINO TS package for using Mariadb databases.",
|
|
5
5
|
"author": "Matias Kiviniemi (pragmatta)",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"module": "./dist/esm/index.js",
|
|
22
22
|
"types": "./dist/types/index.d.ts",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@oino-ts/common": "^1.
|
|
25
|
-
"@oino-ts/db": "^1.
|
|
24
|
+
"@oino-ts/common": "^1.2.0",
|
|
25
|
+
"@oino-ts/db": "^1.2.0",
|
|
26
26
|
"mariadb": "^3.2.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@types/bun": "^1.
|
|
29
|
+
"@types/bun": "^1.2.04",
|
|
30
30
|
"@types/node": "^20.12.7",
|
|
31
31
|
"typescript": "~5.9.0"
|
|
32
32
|
},
|
package/src/OINODbMariadb.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { OINO_ERROR_PREFIX, OINOBenchmark, OINO_INFO_PREFIX, OINOLog, OINOResult
|
|
|
8
8
|
|
|
9
9
|
import { OINODataSet, OINOBooleanDataField, OINONumberDataField, OINOStringDataField, OINODataField, OINODataFieldSchema, OINODataFieldParams, OINODataRow, OINODataCell, OINODatetimeDataField, OINOBlobDataField, OINO_EMPTY_ROW, OINO_EMPTY_ROWS } from "@oino-ts/common";
|
|
10
10
|
|
|
11
|
-
import { OINODb, OINODbParams } from "@oino-ts/db";
|
|
11
|
+
import { OINODb, OINODbParams, OINODbSqlStatement } from "@oino-ts/db";
|
|
12
12
|
|
|
13
13
|
import mariadb from "mariadb";
|
|
14
14
|
|
|
@@ -116,7 +116,7 @@ export class OINODbMariadb extends OINODb {
|
|
|
116
116
|
if (this.dbParams.type !== "OINODbMariadb") {
|
|
117
117
|
throw new Error(OINO_ERROR_PREFIX + ": Not OINODbMariadb-type: " + this.dbParams.type)
|
|
118
118
|
}
|
|
119
|
-
this._pool = mariadb.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug:false, rowsAsArray: true, multipleStatements:
|
|
119
|
+
this._pool = mariadb.createPool({ host: this.dbParams.url, database: this.dbParams.database, port: this.dbParams.port, user: this.dbParams.user, password: this.dbParams.password, acquireTimeout: 2000, debug:false, rowsAsArray: true, multipleStatements: false }) // statements are now executed individually with bind parameters, so stacked/multi-statement execution is disabled
|
|
120
120
|
delete this.dbParams.password // do not store password in db object
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -128,12 +128,12 @@ export class OINODbMariadb extends OINODb {
|
|
|
128
128
|
return result
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
private async _query(sql:string):Promise<OINODataSet> {
|
|
131
|
+
private async _query(sql:string, params?:OINODataCell[]):Promise<OINODataSet> {
|
|
132
132
|
let connection:mariadb.PoolConnection|null = null
|
|
133
133
|
let rows:OINODataRow[] = OINO_EMPTY_ROWS
|
|
134
134
|
try {
|
|
135
135
|
connection = await this._pool.getConnection()
|
|
136
|
-
const sql_res = await connection.query(sql)
|
|
136
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql)
|
|
137
137
|
// console.log("_query: sql=", sql, " result=", result)
|
|
138
138
|
if (Array.isArray(sql_res)) {
|
|
139
139
|
rows = sql_res.filter((r) => Array.isArray(r)) as OINODataRow[] // filter out OkPacket results from multiple statements
|
|
@@ -150,12 +150,12 @@ export class OINODbMariadb extends OINODb {
|
|
|
150
150
|
return new OINOMariadbData(rows, [])
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
private async _exec(sql:string):Promise<OINODataSet> {
|
|
153
|
+
private async _exec(sql:string, params?:OINODataCell[]):Promise<OINODataSet> {
|
|
154
154
|
let connection:mariadb.PoolConnection|null = null
|
|
155
155
|
let rows:OINODataRow[] = OINO_EMPTY_ROWS
|
|
156
156
|
try {
|
|
157
157
|
connection = await this._pool.getConnection()
|
|
158
|
-
const sql_res = await connection.query(sql)
|
|
158
|
+
const sql_res = (params && params.length > 0) ? await connection.query(sql, params) : await connection.query(sql)
|
|
159
159
|
// console.log("OINODbMariadb._exec: result=", result)
|
|
160
160
|
if (Array.isArray(sql_res)) {
|
|
161
161
|
rows = sql_res.filter((r) => Array.isArray(r)) // filter out OkPacket results from multiple statements
|
|
@@ -181,17 +181,45 @@ export class OINODbMariadb extends OINODb {
|
|
|
181
181
|
*
|
|
182
182
|
*/
|
|
183
183
|
printTableName(sqlTable:string): string {
|
|
184
|
-
return "`"+sqlTable+"`"
|
|
184
|
+
return "`"+sqlTable.replaceAll("`", "``")+"`"
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* Print a column name with correct SQL escaping.
|
|
189
|
-
*
|
|
189
|
+
*
|
|
190
190
|
* @param sqlColumn name of the column
|
|
191
191
|
*
|
|
192
192
|
*/
|
|
193
193
|
printColumnName(sqlColumn:string): string {
|
|
194
|
-
return "`"+sqlColumn+"`"
|
|
194
|
+
return "`"+sqlColumn.replaceAll("`", "``")+"`"
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Print a bind-parameter placeholder for the given zero-based parameter index (MySQL/MariaDB `?`).
|
|
199
|
+
*
|
|
200
|
+
* @param index zero-based parameter index
|
|
201
|
+
*
|
|
202
|
+
*/
|
|
203
|
+
printParameterName(index:number): string {
|
|
204
|
+
return "?"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Coerce a data value into a MariaDB bind-parameter value. The connector binds numbers,
|
|
209
|
+
* strings, `Date` and `Buffer` natively; booleans are mapped to 1/0 for `bit`/numeric columns.
|
|
210
|
+
*
|
|
211
|
+
* @param cellValue data value to bind
|
|
212
|
+
* @param nativeType native type name for the table column
|
|
213
|
+
*
|
|
214
|
+
*/
|
|
215
|
+
bindCellValue(cellValue:OINODataCell, nativeType: string): OINODataCell {
|
|
216
|
+
if (cellValue === undefined) {
|
|
217
|
+
return null
|
|
218
|
+
}
|
|
219
|
+
if (typeof cellValue === "boolean") {
|
|
220
|
+
return cellValue ? 1 : 0
|
|
221
|
+
}
|
|
222
|
+
return cellValue
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
|
|
@@ -323,8 +351,8 @@ export class OINODbMariadb extends OINODb {
|
|
|
323
351
|
OINOBenchmark.startMetric("OINODb", "validate")
|
|
324
352
|
let result:OINOResult = new OINOResult()
|
|
325
353
|
try {
|
|
326
|
-
const sql = this._getValidateSql(
|
|
327
|
-
const sql_res:OINODataSet = await this._query(sql)
|
|
354
|
+
const sql = this._getValidateSql()
|
|
355
|
+
const sql_res:OINODataSet = await this._query(sql, [this.dbParams.database])
|
|
328
356
|
if (sql_res.isEmpty()) {
|
|
329
357
|
result.setError(400, "DB returned no rows for schema!", "OINODbMariadb.validate")
|
|
330
358
|
|
|
@@ -369,7 +397,7 @@ export class OINODbMariadb extends OINODb {
|
|
|
369
397
|
|
|
370
398
|
/**
|
|
371
399
|
* Execute other sql operations.
|
|
372
|
-
*
|
|
400
|
+
*
|
|
373
401
|
* @param sql SQL statement.
|
|
374
402
|
*
|
|
375
403
|
*/
|
|
@@ -383,8 +411,24 @@ export class OINODbMariadb extends OINODb {
|
|
|
383
411
|
return result
|
|
384
412
|
}
|
|
385
413
|
|
|
386
|
-
|
|
387
|
-
|
|
414
|
+
/**
|
|
415
|
+
* Execute a parameterized statement, binding its values as positional `?` parameters.
|
|
416
|
+
*
|
|
417
|
+
* @param statement statement (SQL text + ordered bind values) to execute
|
|
418
|
+
*
|
|
419
|
+
*/
|
|
420
|
+
async runStatement(statement:OINODbSqlStatement): Promise<OINODataSet> {
|
|
421
|
+
if (!this.isValidated) {
|
|
422
|
+
throw new Error(OINO_ERROR_PREFIX + ": Database connection not validated!")
|
|
423
|
+
}
|
|
424
|
+
OINOBenchmark.startMetric("OINODb", "runStatement")
|
|
425
|
+
let result:OINODataSet = await this._exec(statement.sql, statement.values)
|
|
426
|
+
OINOBenchmark.endMetric("OINODb", "runStatement", result.status != 500)
|
|
427
|
+
return result
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private _getSchemaSql():string {
|
|
431
|
+
const sql =
|
|
388
432
|
`SELECT
|
|
389
433
|
C.COLUMN_NAME,
|
|
390
434
|
C.COLUMN_TYPE,
|
|
@@ -392,21 +436,21 @@ export class OINODbMariadb extends OINODb {
|
|
|
392
436
|
C.COLUMN_KEY,
|
|
393
437
|
C.COLUMN_DEFAULT,
|
|
394
438
|
C.EXTRA,
|
|
395
|
-
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
439
|
+
KCU.CONSTRAINT_NAME AS ForeignKeyName
|
|
396
440
|
FROM information_schema.COLUMNS C
|
|
397
441
|
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
|
|
398
|
-
WHERE C.TABLE_SCHEMA =
|
|
442
|
+
WHERE C.TABLE_SCHEMA = ? AND C.TABLE_NAME = ?
|
|
399
443
|
ORDER BY C.ORDINAL_POSITION;`
|
|
400
444
|
return sql
|
|
401
445
|
}
|
|
402
446
|
|
|
403
|
-
private _getValidateSql(
|
|
404
|
-
const sql =
|
|
447
|
+
private _getValidateSql():string {
|
|
448
|
+
const sql =
|
|
405
449
|
`SELECT
|
|
406
450
|
Count(C.COLUMN_NAME) AS COLUMN_COUNT
|
|
407
451
|
FROM information_schema.COLUMNS C
|
|
408
452
|
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
|
|
409
|
-
WHERE C.TABLE_SCHEMA =
|
|
453
|
+
WHERE C.TABLE_SCHEMA = ?;`
|
|
410
454
|
return sql
|
|
411
455
|
}
|
|
412
456
|
|
|
@@ -419,7 +463,7 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`
|
|
|
419
463
|
*/
|
|
420
464
|
async getSchemaFields(tableName:string): Promise<OINODataField[]> {
|
|
421
465
|
const fields:OINODataField[] = []
|
|
422
|
-
const schema_res:OINODataSet = await this._query(this._getSchemaSql(this.dbParams.database, tableName)
|
|
466
|
+
const schema_res:OINODataSet = await this._query(this._getSchemaSql(), [this.dbParams.database, tableName])
|
|
423
467
|
while (!schema_res.isEof()) {
|
|
424
468
|
const row:OINODataRow = schema_res.getRow()
|
|
425
469
|
const field_name:string = row[0]?.toString() || ""
|
|
@@ -464,8 +508,8 @@ WHERE C.TABLE_SCHEMA = '${dbName}';`
|
|
|
464
508
|
*/
|
|
465
509
|
async getSchemaTables(): Promise<string[]> {
|
|
466
510
|
const tables:string[] = []
|
|
467
|
-
const sql:string = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA =
|
|
468
|
-
const tables_res:OINODataSet = await this._query(sql)
|
|
511
|
+
const sql:string = "SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME;"
|
|
512
|
+
const tables_res:OINODataSet = await this._query(sql, [this.dbParams.database])
|
|
469
513
|
while (!tables_res.isEof()) {
|
|
470
514
|
const row:OINODataRow = tables_res.getRow()
|
|
471
515
|
const table_name:string = row[0]?.toString() || ""
|