mythix-orm-sql-base 1.9.5 → 1.10.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/.biblorc.js +30 -0
- package/README.md +2 -4
- package/docs/Home.md +3 -0
- package/lib/sql-connection-base.js +242 -11
- package/lib/sql-query-generator-base.js +50 -27
- package/package.json +3 -2
package/.biblorc.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* global __dirname */
|
|
4
|
+
|
|
5
|
+
const Path = require('path');
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
rootDir: __dirname,
|
|
9
|
+
inputDir: Path.resolve(__dirname),
|
|
10
|
+
outputDir: Path.resolve(__dirname, '..', 'mythix-orm-sql-base.wiki'),
|
|
11
|
+
files: [
|
|
12
|
+
{
|
|
13
|
+
include: /\/lib\/.*\.js$/,
|
|
14
|
+
parser: 'typescript',
|
|
15
|
+
compiler: 'typescript',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
include: /\/docs\/.*\.md$/,
|
|
19
|
+
parser: 'markdown',
|
|
20
|
+
compiler: 'markdown',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
exclude: [
|
|
24
|
+
/node_modules|\/spec\//
|
|
25
|
+
],
|
|
26
|
+
generatorOptions: {
|
|
27
|
+
repositoryURL: 'https://github.com/th317erd/mythix-orm-sql-base',
|
|
28
|
+
baseURL: './',
|
|
29
|
+
},
|
|
30
|
+
};
|
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# mythix-orm-sql-base
|
|
2
2
|
|
|
3
|
-
SQL base support for Mythix ORM
|
|
3
|
+
SQL base support for [Mythix ORM](https://www.npmjs.com/package/mythix-orm).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
This module isn't intended to be used by itself. It is a support module for other SQL-based database drivers.
|
|
5
|
+
This module isn't intended to be used by itself. It is a support module for other SQL database drivers.
|
package/docs/Home.md
ADDED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const SqlString = require('sqlstring');
|
|
3
4
|
const Nife = require('nife');
|
|
4
5
|
const UUID = require('uuid');
|
|
5
6
|
const {
|
|
@@ -13,11 +14,88 @@ const {
|
|
|
13
14
|
const SQLQueryGeneratorBase = require('./sql-query-generator-base');
|
|
14
15
|
|
|
15
16
|
const SAVE_POINT_NAME_CHARS = [ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P' ];
|
|
16
|
-
const MODEL_RELATIONS = Symbol.for('
|
|
17
|
-
|
|
17
|
+
const MODEL_RELATIONS = Symbol.for('@_mythix/orm-sql-base/SQLConnectionBase/ModelRelations');
|
|
18
|
+
|
|
19
|
+
/// `SQLConnectionBase` is a support class for all other
|
|
20
|
+
/// SQL connection drivers built for Mythix ORM. It isn't
|
|
21
|
+
/// intended to be used on its own, but rather to add SQL
|
|
22
|
+
/// support to all other Mythix ORM SQL connections.
|
|
23
|
+
///
|
|
24
|
+
/// Extends: [ConnectionBase](https://github.com/th317erd/mythix-orm/wiki/ConnectionBase)
|
|
18
25
|
class SQLConnectionBase extends ConnectionBase {
|
|
19
26
|
static DefaultQueryGenerator = SQLQueryGeneratorBase;
|
|
20
27
|
|
|
28
|
+
/// The low-level DB interface for escaping a
|
|
29
|
+
/// value. By default this function uses the
|
|
30
|
+
/// [sqlstring](https://www.npmjs.com/package/sqlstring)
|
|
31
|
+
/// module to escape values. However, the `escape`
|
|
32
|
+
/// method for whatever database the connection is
|
|
33
|
+
/// using should be used instead of this. This is
|
|
34
|
+
/// a "default implementation" that is meant as a
|
|
35
|
+
/// fallback when a connection doesn't provide its
|
|
36
|
+
/// own, but each connection should provide its own
|
|
37
|
+
/// when it is able.
|
|
38
|
+
///
|
|
39
|
+
/// Note:
|
|
40
|
+
/// This method escapes "values" that are given in
|
|
41
|
+
/// the underlying query language of the database.
|
|
42
|
+
/// To escape identifiers, use the <see>ConnectionBase._escapeID</see>
|
|
43
|
+
/// instead.
|
|
44
|
+
///
|
|
45
|
+
/// Return: string
|
|
46
|
+
/// The value provided, escaped for the specific
|
|
47
|
+
/// underlying database driver.
|
|
48
|
+
///
|
|
49
|
+
/// Arguments:
|
|
50
|
+
/// value: any
|
|
51
|
+
/// The value to escape. This could be a number, a boolean,
|
|
52
|
+
/// a string, or anything else that can be provided to your
|
|
53
|
+
/// specific database.
|
|
54
|
+
_escape(value) {
|
|
55
|
+
if (Nife.instanceOf(value, 'string'))
|
|
56
|
+
return `'${value.replace(/'/g, '\'\'')}'`;
|
|
57
|
+
|
|
58
|
+
return SqlString.escape(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Low-level database method for escaping an identifier.
|
|
62
|
+
/// Each database driver should provide its own version of
|
|
63
|
+
/// this method. This is the "default" method Mythix ORM
|
|
64
|
+
/// provides as a "fallback" to database drivers that don't
|
|
65
|
+
/// supply their own.
|
|
66
|
+
///
|
|
67
|
+
/// It works by first stripping all quotes (single `'`, double `"`, and backtick `` ` ``)
|
|
68
|
+
/// from the provided `value`. After this, it will split on the period (dot) character
|
|
69
|
+
/// `.`, and then will map each resulting part through [sqlstring](https://www.npmjs.com/package/sqlstring)
|
|
70
|
+
/// `escapeId` method, finally re-joining the parts with a period `.` character.
|
|
71
|
+
///
|
|
72
|
+
/// The extra processing is to allow for already escaped identifiers to not be double-escaped.
|
|
73
|
+
///
|
|
74
|
+
/// Return: string
|
|
75
|
+
/// The provided identifier, escaped for the underlying database.
|
|
76
|
+
///
|
|
77
|
+
/// Arguments:
|
|
78
|
+
/// value: string
|
|
79
|
+
/// The identifier to escape.
|
|
80
|
+
_escapeID(value) {
|
|
81
|
+
let parts = value.replace(/['"`]/g, '').split(/\.+/g);
|
|
82
|
+
return parts.map((part) => SqlString.escapeId(part).replace(/^`/, '"').replace(/`$/, '"')).join('.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Prepare an array of values for use in an `IN` statement.
|
|
86
|
+
/// This method will flatten the provided array, and then
|
|
87
|
+
/// will filter out all non-primitive values from the array.
|
|
88
|
+
/// Only `null`, `boolean`, `number`, `bigint`, and `string`
|
|
89
|
+
/// values will remain in the resulting array. The array is
|
|
90
|
+
/// also reduced to only unique values, with duplicates removed.
|
|
91
|
+
///
|
|
92
|
+
/// Arguments:
|
|
93
|
+
/// array: Array<any>
|
|
94
|
+
/// The array to flatten, filter, and remove duplicates from.
|
|
95
|
+
///
|
|
96
|
+
/// Return: Array<any>
|
|
97
|
+
/// Return a copy of the array provided, after being flattened, filtered,
|
|
98
|
+
/// and duplicates removed.
|
|
21
99
|
prepareArrayValuesForSQL(_array) {
|
|
22
100
|
let array = Nife.arrayFlatten(_array);
|
|
23
101
|
|
|
@@ -37,6 +115,13 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
37
115
|
return Nife.uniq(array);
|
|
38
116
|
}
|
|
39
117
|
|
|
118
|
+
/// Generate a `SAVEPOINT` name for use in sub-transactions.
|
|
119
|
+
/// This will generate a UUID v4 ID, and then mutate it so
|
|
120
|
+
/// that the name is SQL-safe. Finally, it will add an `SP`
|
|
121
|
+
/// prefix to the `SAVEPOINT` name.
|
|
122
|
+
///
|
|
123
|
+
/// Return: string
|
|
124
|
+
/// A random SQL-safe `SAVEPOINT` name.
|
|
40
125
|
generateSavePointName() {
|
|
41
126
|
let id = UUID.v4();
|
|
42
127
|
|
|
@@ -48,6 +133,23 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
48
133
|
return `SP${id}`;
|
|
49
134
|
}
|
|
50
135
|
|
|
136
|
+
/// Given a `Map` from a projection field-set, find all matching
|
|
137
|
+
/// projected fields through this connection, and return
|
|
138
|
+
/// them as an array.
|
|
139
|
+
///
|
|
140
|
+
/// Arguments:
|
|
141
|
+
/// projectionFieldMap: Map<string, string>
|
|
142
|
+
/// A set of projected fields, as a `Map`. This `Map` will usually be generated
|
|
143
|
+
/// by <see>SQLQueryGeneratorBase.getProjectedFields</see>. The `Map` is expected
|
|
144
|
+
/// to have fully qualified field names and expanded literal strings as keys, with
|
|
145
|
+
/// the projected database field (or literal) in database format for values.
|
|
146
|
+
///
|
|
147
|
+
/// Return: Array<[Field](https://github.com/th317erd/mythix-orm/wiki/Field) | string>
|
|
148
|
+
/// All fields found from the projection `Map`. Any field that wasn't able to be
|
|
149
|
+
/// found on this `connection` will instead just be returned as its raw `string` form.
|
|
150
|
+
/// Any raw strings returned are likely literals, and so couldn't be matched to fields.
|
|
151
|
+
/// However, they may not always be literals, but instead may be a custom field or field
|
|
152
|
+
/// alias that the user requested on the projection.
|
|
51
153
|
findAllFieldsFromFieldProjectionMap(projectionFieldMap) {
|
|
52
154
|
let fullFieldNames = (Array.isArray(projectionFieldMap)) ? projectionFieldMap : Array.from(projectionFieldMap.keys());
|
|
53
155
|
|
|
@@ -64,6 +166,26 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
64
166
|
}).filter(Boolean);
|
|
65
167
|
}
|
|
66
168
|
|
|
169
|
+
/// This method will take a `{ rows: Array<any>; columns: Array<string>; }` result from
|
|
170
|
+
/// a `SELECT` statement, and build a map of model data and sub-model data
|
|
171
|
+
/// to be later turned into model instances by <see>SQLConnectionBase.buildModelsFromModelDataMap</see>.
|
|
172
|
+
///
|
|
173
|
+
/// Arguments:
|
|
174
|
+
/// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
|
|
175
|
+
/// The query engine that was used to generate the `SELECT` statement that returned these results.
|
|
176
|
+
/// result: { rows: Array<any>; columns: Array<string>; }
|
|
177
|
+
/// The results as returned by the database, used to build the data map to later construct model instances.
|
|
178
|
+
///
|
|
179
|
+
/// Return: { [key: string]: Array<object> }
|
|
180
|
+
/// Return `result` from the database, mapped to the models that were projected. Each key in
|
|
181
|
+
/// this object will be a model name, and each value an array of model attributes. The model
|
|
182
|
+
/// attributes in each array of models represents a model instance that will be created later
|
|
183
|
+
/// using these attributes. There is a special key `Symbol.for('@_mythix/orm-sql-base/SQLConnectionBase/ModelRelations')`
|
|
184
|
+
/// that can be present on any object of model attributes (a model). This special key--if present--will be
|
|
185
|
+
/// another mapped object that represents "sub-models" that are "owned" by the model that has this
|
|
186
|
+
/// special key. This is used when projecting and loading "related models" during a load operation.
|
|
187
|
+
///
|
|
188
|
+
/// See: SQLConnectionBase.buildModelsFromModelDataMap
|
|
67
189
|
buildModelDataMapFromSelectResults(queryEngine, result) {
|
|
68
190
|
if (!result)
|
|
69
191
|
return {};
|
|
@@ -249,11 +371,26 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
249
371
|
return modelData;
|
|
250
372
|
}
|
|
251
373
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
374
|
+
/// Take the result from a <see>SQLConnectionBase.buildModelDataMapFromSelectResults</see> call
|
|
375
|
+
/// and instantiate all models defined by the model attribute map.
|
|
376
|
+
///
|
|
377
|
+
/// Arguments:
|
|
378
|
+
/// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
|
|
379
|
+
/// The query engine that was used to generate the `SELECT` statement that returned these results.
|
|
380
|
+
/// modelDataMap: { [key: string]: Array<object> }
|
|
381
|
+
/// The result from a call to <see>SQLConnectionBase.buildModelDataMapFromSelectResults</see>.
|
|
382
|
+
/// callback?: (RootModel: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model), model: [Model](https://github.com/th317erd/mythix-orm/wiki/Model)) => [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
|
|
383
|
+
/// A callback that is called for each *root model* instance created from the provided `modelDataMap`.
|
|
384
|
+
/// Sub-models, or other models projected for the operation will not be passed through this callback.
|
|
385
|
+
/// Only instances of the "root model" (or "target model") of the query will be passed through this
|
|
386
|
+
/// callback. This callback **must** return the original model instance provided to it, or an equivalent
|
|
387
|
+
/// model instance (possibly the same instance modified by the callback).
|
|
388
|
+
///
|
|
389
|
+
/// Return: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)>
|
|
390
|
+
/// An array of all fully-instantiated "root models" returned from the `SELECT` query, including
|
|
391
|
+
/// any "sub-models" that were loaded along-side them. All models will be marked "clean".
|
|
392
|
+
///
|
|
393
|
+
/// See: SQLConnectionBase.buildModelDataMapFromSelectResults
|
|
257
394
|
buildModelsFromModelDataMap(queryEngine, modelDataMap, callback) {
|
|
258
395
|
if (Nife.isEmpty(modelDataMap))
|
|
259
396
|
return [];
|
|
@@ -305,6 +442,26 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
305
442
|
return rootModels;
|
|
306
443
|
}
|
|
307
444
|
|
|
445
|
+
/// Take the `{ rows: Array<any>; columns: Array<string>; }` result from
|
|
446
|
+
/// the database for a `RETURNING` statement, and update the effected models
|
|
447
|
+
/// with the results.
|
|
448
|
+
///
|
|
449
|
+
/// This is used by `UPDATE` and `INSERT` statements to sync model attributes
|
|
450
|
+
/// with the database update/insert operation that just took place.
|
|
451
|
+
///
|
|
452
|
+
/// This will also set all models provided to the call as "persisted".
|
|
453
|
+
///
|
|
454
|
+
/// Arguments:
|
|
455
|
+
/// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
|
|
456
|
+
/// The model class of the array of `storedModels` that are being operated upon.
|
|
457
|
+
/// storedModels: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)>
|
|
458
|
+
/// The models that were just persisted.
|
|
459
|
+
/// results: { rows: Array<any>; columns: Array<string>; }
|
|
460
|
+
/// The raw results from the database, used to update all persisted models.
|
|
461
|
+
///
|
|
462
|
+
/// Return: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)>
|
|
463
|
+
/// Return `storedModels`, with each model being updated with the returned results
|
|
464
|
+
/// from the database, and each model marked as "persisted".
|
|
308
465
|
updateModelsFromResults(Model, storedModels, results) {
|
|
309
466
|
let {
|
|
310
467
|
rows,
|
|
@@ -328,6 +485,21 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
328
485
|
return storedModels;
|
|
329
486
|
}
|
|
330
487
|
|
|
488
|
+
/// Parse the database-specific return value for an
|
|
489
|
+
/// `UPDATE` or `DELETE` operation to retrieve the
|
|
490
|
+
/// number of rows that were updated or deleted.
|
|
491
|
+
///
|
|
492
|
+
/// Note:
|
|
493
|
+
/// Each database driver should overload this method to
|
|
494
|
+
/// properly parse the results of an `UPDATE` or `DELETE`
|
|
495
|
+
/// statement to return the number of rows affected.
|
|
496
|
+
///
|
|
497
|
+
/// Arguments:
|
|
498
|
+
/// queryResult: any
|
|
499
|
+
/// The raw database response for an `UPDATE` or `DELETE` statement.
|
|
500
|
+
///
|
|
501
|
+
/// Return: number
|
|
502
|
+
/// The number of rows that were affected by the operation.
|
|
331
503
|
getUpdateOrDeleteChangeCount(queryResult) {
|
|
332
504
|
if (!queryResult)
|
|
333
505
|
return 0;
|
|
@@ -343,20 +515,68 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
343
515
|
|
|
344
516
|
// --------------------------------------------- //
|
|
345
517
|
|
|
518
|
+
/// For databases that support it, enable or disable foreign key constraints.
|
|
519
|
+
///
|
|
520
|
+
/// Arguments:
|
|
521
|
+
/// enable: boolean
|
|
522
|
+
/// If `true`, enable foreign key constraints in the underlying database, otherwise
|
|
523
|
+
/// disable foreign key constraints in the underlying database.
|
|
524
|
+
// eslint-disable-next-line no-unused-vars
|
|
525
|
+
async enableForeignKeyConstraints(enable) {
|
|
526
|
+
throw new Error(`${this.constructor.name}::enableForeignKeyConstraints: This operation is not supported for this connection type.`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/// Drop the table/bucket defined by `Model`.
|
|
530
|
+
///
|
|
531
|
+
/// Note:
|
|
532
|
+
/// Mythix ORM always refers to data-spaces as "tables", even though
|
|
533
|
+
/// they might actually be "buckets" for example for no-SQL databases.
|
|
534
|
+
///
|
|
535
|
+
/// Arguments:
|
|
536
|
+
/// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
|
|
537
|
+
/// The model that defines the table to be dropped.
|
|
538
|
+
/// options?: object
|
|
539
|
+
/// Though these options can be database specific, they are commonly just
|
|
540
|
+
/// the following options.
|
|
541
|
+
/// | Option | Type | Default Value | Description |
|
|
542
|
+
/// | ------ | ---- | ------------- | ----------- |
|
|
543
|
+
/// | `ifExists` | `boolean` | `false` | Add an `IF EXISTS` clause to the drop table statement. |
|
|
544
|
+
/// | `cascade` | `boolean` | `true` | Add a `CASCADE` clause to the drop table statement (destroying rows in other tables defined by foreign key constraints). |
|
|
545
|
+
///
|
|
546
|
+
/// Return: undefined
|
|
547
|
+
/// This method returns nothing.
|
|
346
548
|
async dropTable(Model, options) {
|
|
347
549
|
let queryGenerator = this.getQueryGenerator();
|
|
348
550
|
let createTableSQL = queryGenerator.generateDropTableStatement(Model, options);
|
|
349
551
|
|
|
350
552
|
// Drop table
|
|
351
|
-
|
|
553
|
+
await this.query(createTableSQL, options);
|
|
352
554
|
}
|
|
353
555
|
|
|
556
|
+
/// Create the table/bucket defined by `Model`.
|
|
557
|
+
///
|
|
558
|
+
/// Note:
|
|
559
|
+
/// Mythix ORM always refers to data-spaces as "tables", even though
|
|
560
|
+
/// they might actually be "buckets" for example for no-SQL databases.
|
|
561
|
+
///
|
|
562
|
+
/// Arguments:
|
|
563
|
+
/// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
|
|
564
|
+
/// The model that defines the table to be created.
|
|
565
|
+
/// options?: object
|
|
566
|
+
/// Though these options can be database specific, they are commonly just
|
|
567
|
+
/// the following options.
|
|
568
|
+
/// | Option | Type | Default Value | Description |
|
|
569
|
+
/// | ------ | ---- | ------------- | ----------- |
|
|
570
|
+
/// | `ifNotExists` | `boolean` | `false` | Add an `IF NOT EXISTS` clause to the create table statement. |
|
|
571
|
+
///
|
|
572
|
+
/// Return: undefined
|
|
573
|
+
/// This method returns nothing.
|
|
354
574
|
async createTable(Model, options) {
|
|
355
575
|
let queryGenerator = this.getQueryGenerator();
|
|
356
576
|
let createTableSQL = queryGenerator.generateCreateTableStatement(Model, options);
|
|
357
577
|
|
|
358
578
|
// Create table
|
|
359
|
-
|
|
579
|
+
await this.query(createTableSQL, options);
|
|
360
580
|
|
|
361
581
|
// Create indexes and constraints
|
|
362
582
|
let trailingStatements = Nife.toArray(queryGenerator.generateCreateTableStatementOuterTail(Model, options)).filter(Boolean);
|
|
@@ -366,8 +586,6 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
366
586
|
await this.query(trailingStatement, options);
|
|
367
587
|
}
|
|
368
588
|
}
|
|
369
|
-
|
|
370
|
-
return result;
|
|
371
589
|
}
|
|
372
590
|
|
|
373
591
|
async insert(Model, models, _options) {
|
|
@@ -558,6 +776,19 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
558
776
|
return this.getUpdateOrDeleteChangeCount(await this.query(sqlStr, options));
|
|
559
777
|
}
|
|
560
778
|
|
|
779
|
+
/// Convert the raw `{ rows: Array<any>; columns: Array<string>; }` results
|
|
780
|
+
/// from a database into an array of mapped objects.
|
|
781
|
+
///
|
|
782
|
+
/// This method simply takes the rows and columns reported by the database
|
|
783
|
+
/// for a `SELECT` operation, and maps them to objects, where each key is
|
|
784
|
+
/// a column name, and each value is a column from one of the returned rows.
|
|
785
|
+
///
|
|
786
|
+
/// Arguments:
|
|
787
|
+
/// result: { rows: Array<any>; columns: Array<string>; }
|
|
788
|
+
/// The raw results as returned by the database.
|
|
789
|
+
///
|
|
790
|
+
/// Return: Array<object>
|
|
791
|
+
/// The raw results from the database, with each row mapped to an object.
|
|
561
792
|
queryResultRowsToRawData(result) {
|
|
562
793
|
if (!result)
|
|
563
794
|
return [];
|
|
@@ -14,6 +14,7 @@ const {
|
|
|
14
14
|
const DefaultHelpers = Types.DefaultHelpers;
|
|
15
15
|
const LiteralBase = Literals.LiteralBase;
|
|
16
16
|
|
|
17
|
+
/// The "base" SQL generator for all SQL-type databases.
|
|
17
18
|
class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
18
19
|
getEscapedFieldName(_Model, field, options) {
|
|
19
20
|
let isString = Nife.instanceOf(field, 'string');
|
|
@@ -194,6 +195,12 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
_getLiteralAlias(literal, options) {
|
|
198
|
+
if (options && options.isSubField)
|
|
199
|
+
return '';
|
|
200
|
+
|
|
201
|
+
if (options && options.noProjectionAliases)
|
|
202
|
+
return '';
|
|
203
|
+
|
|
197
204
|
let as = (literal.options && literal.options.as) || (options && options.as);
|
|
198
205
|
if (Nife.isEmpty(as))
|
|
199
206
|
return '';
|
|
@@ -206,14 +213,14 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
206
213
|
return;
|
|
207
214
|
|
|
208
215
|
let field = literal.getField(this.connection);
|
|
209
|
-
let
|
|
216
|
+
let escapedColumnName;
|
|
210
217
|
|
|
211
218
|
if (LiteralBase.isLiteral(field))
|
|
212
|
-
|
|
219
|
+
escapedColumnName = field.toString(this.connection, this.stackAssign(options, { isSubField: true }));
|
|
213
220
|
else
|
|
214
|
-
|
|
221
|
+
escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
215
222
|
|
|
216
|
-
return `AVG(${
|
|
223
|
+
return `AVG(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
_countLiteralToString(literal, options) {
|
|
@@ -221,18 +228,18 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
221
228
|
return;
|
|
222
229
|
|
|
223
230
|
let field = literal.getField(this.connection);
|
|
224
|
-
let
|
|
231
|
+
let escapedColumnName;
|
|
225
232
|
|
|
226
233
|
if (field) {
|
|
227
234
|
if (LiteralBase.isLiteral(field))
|
|
228
|
-
|
|
235
|
+
escapedColumnName = field.toString(this.connection, this.stackAssign(options, { isSubField: true }));
|
|
229
236
|
else
|
|
230
|
-
|
|
237
|
+
escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
231
238
|
} else {
|
|
232
|
-
|
|
239
|
+
escapedColumnName = '*';
|
|
233
240
|
}
|
|
234
241
|
|
|
235
|
-
return `COUNT(${
|
|
242
|
+
return `COUNT(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
_distinctLiteralToString(literal, options) {
|
|
@@ -240,13 +247,28 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
240
247
|
return;
|
|
241
248
|
|
|
242
249
|
let field = literal.getField(this.connection);
|
|
250
|
+
if (!field)
|
|
251
|
+
field = literal.valueOf();
|
|
252
|
+
|
|
243
253
|
if (!field)
|
|
244
254
|
return 'DISTINCT';
|
|
245
255
|
|
|
246
|
-
if (LiteralBase.isLiteral(field))
|
|
247
|
-
|
|
256
|
+
if (LiteralBase.isLiteral(field)) {
|
|
257
|
+
let fieldStr = field.toString(this.connection, this.stackAssign(options, { noProjectionAliases: true }));
|
|
258
|
+
if (!fieldStr)
|
|
259
|
+
return '';
|
|
260
|
+
|
|
261
|
+
if (options && options.isSubField)
|
|
262
|
+
return `DISTINCT ${fieldStr}`;
|
|
263
|
+
|
|
264
|
+
return `DISTINCT ON(${fieldStr})`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options, { noProjectionAliases: true }));
|
|
268
|
+
if (options && options.isSubField)
|
|
269
|
+
return `DISTINCT ${escapedColumnName}`;
|
|
248
270
|
|
|
249
|
-
return `DISTINCT ON(${
|
|
271
|
+
return `DISTINCT ON(${escapedColumnName})`;
|
|
250
272
|
}
|
|
251
273
|
|
|
252
274
|
_fieldLiteralToString(literal, options) {
|
|
@@ -257,7 +279,8 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
257
279
|
if (LiteralBase.isLiteral(field))
|
|
258
280
|
return field.toString(this.connection, options);
|
|
259
281
|
|
|
260
|
-
|
|
282
|
+
let noProjectionAliases = (options && (options.isSubField || !options.isProjection || options.as === false));
|
|
283
|
+
return this.getEscapedProjectionName(field.Model, field, this.stackAssign(options, { noProjectionAliases }, literal.options));
|
|
261
284
|
}
|
|
262
285
|
|
|
263
286
|
_maxLiteralToString(literal, options) {
|
|
@@ -265,14 +288,14 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
265
288
|
return;
|
|
266
289
|
|
|
267
290
|
let field = literal.getField(this.connection);
|
|
268
|
-
let
|
|
291
|
+
let escapedColumnName;
|
|
269
292
|
|
|
270
293
|
if (LiteralBase.isLiteral(field))
|
|
271
|
-
|
|
294
|
+
escapedColumnName = field.toString(this.connection, this.stackAssign(options, { isSubField: true }));
|
|
272
295
|
else
|
|
273
|
-
|
|
296
|
+
escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
274
297
|
|
|
275
|
-
return `MAX(${
|
|
298
|
+
return `MAX(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
|
|
276
299
|
}
|
|
277
300
|
|
|
278
301
|
_minLiteralToString(literal, options) {
|
|
@@ -280,14 +303,14 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
280
303
|
return;
|
|
281
304
|
|
|
282
305
|
let field = literal.getField(this.connection);
|
|
283
|
-
let
|
|
306
|
+
let escapedColumnName;
|
|
284
307
|
|
|
285
308
|
if (LiteralBase.isLiteral(field))
|
|
286
|
-
|
|
309
|
+
escapedColumnName = field.toString(this.connection, this.stackAssign(options, { isSubField: true }));
|
|
287
310
|
else
|
|
288
|
-
|
|
311
|
+
escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
289
312
|
|
|
290
|
-
return `MIN(${
|
|
313
|
+
return `MIN(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
|
|
291
314
|
}
|
|
292
315
|
|
|
293
316
|
_sumLiteralToString(literal, options) {
|
|
@@ -295,14 +318,14 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
295
318
|
return;
|
|
296
319
|
|
|
297
320
|
let field = literal.getField(this.connection);
|
|
298
|
-
let
|
|
321
|
+
let escapedColumnName;
|
|
299
322
|
|
|
300
323
|
if (LiteralBase.isLiteral(field))
|
|
301
|
-
|
|
324
|
+
escapedColumnName = field.toString(this.connection, this.stackAssign(options, { isSubField: true }));
|
|
302
325
|
else
|
|
303
|
-
|
|
326
|
+
escapedColumnName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
304
327
|
|
|
305
|
-
return `SUM(${
|
|
328
|
+
return `SUM(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
|
|
306
329
|
}
|
|
307
330
|
|
|
308
331
|
prepareArrayValuesForSQL(array) {
|
|
@@ -504,7 +527,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
504
527
|
if (arrayValues.length > 0)
|
|
505
528
|
subParts.push(this.generateSelectQueryCondition(queryPart, arrayValues, options));
|
|
506
529
|
|
|
507
|
-
return `(${subParts.join(' OR ')})`;
|
|
530
|
+
return `(${subParts.join((operator === 'NEQ') ? ' AND ' : ' OR ')})`;
|
|
508
531
|
}
|
|
509
532
|
|
|
510
533
|
// If no values left in array, then
|
|
@@ -1825,7 +1848,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
1825
1848
|
let options = _options || {};
|
|
1826
1849
|
let prefix = `ALTER TABLE ${this.getEscapedTableName(Model, options)}`;
|
|
1827
1850
|
|
|
1828
|
-
return `${prefix} ADD COLUMN ${this.generateColumnDeclarationStatement(Model, field, options)}`;
|
|
1851
|
+
return `${prefix} ADD COLUMN${(options.ifNotExists) ? ' IF NOT EXISTS' : ''} ${this.generateColumnDeclarationStatement(Model, field, options)}`;
|
|
1829
1852
|
}
|
|
1830
1853
|
|
|
1831
1854
|
toConnectionString(queryEngine, options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mythix-orm-sql-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "SQL base support for Mythix ORM",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/th317erd/mythix-orm-sql-base#readme",
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"mythix-orm": "^1.
|
|
37
|
+
"mythix-orm": "^1.12.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"luxon": "^3.1.0",
|
|
41
41
|
"nife": "^1.12.1",
|
|
42
|
+
"sqlstring": "^2.3.3",
|
|
42
43
|
"uuid": "^9.0.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|