mythix-orm-sql-base 1.10.0 → 1.11.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.
@@ -15,7 +15,38 @@ const DefaultHelpers = Types.DefaultHelpers;
15
15
  const LiteralBase = Literals.LiteralBase;
16
16
 
17
17
  /// The "base" SQL generator for all SQL-type databases.
18
+ ///
19
+ /// This class is used to generate SQL statements for the
20
+ /// underlying SQL database. Database drivers can and often
21
+ /// will create their own class that extends from this class.
22
+ ///
23
+ /// Extends: [QueryGeneratorBase](https://github.com/th317erd/mythix-orm/wiki/QueryGeneratorBase)
18
24
  class SQLQueryGeneratorBase extends QueryGeneratorBase {
25
+ /// Escape a field name, usually for a projection alias.
26
+ /// This method is primarily used for generating aliases
27
+ /// for projected fields.
28
+ ///
29
+ /// This method will take the field it is given, and turn
30
+ /// it into a fully qualified field name, escaped as an identifier
31
+ /// for the underlying database.
32
+ ///
33
+ /// For example, given the field `User.fields.id`, this method will return
34
+ /// `"User:id"`--assuming that double quotes are used in the underlying
35
+ /// database to escape an identifier.
36
+ ///
37
+ /// Arguments:
38
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
39
+ /// The model that owns the `field` provided.
40
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
41
+ /// The field to operate on.
42
+ /// options?: object
43
+ /// Options for the operation.
44
+ /// | Option | Type | Default Value | Description |
45
+ /// | ------ | ---- | ------------- | ----------- |
46
+ /// | `fieldNameOnly` | `boolean` | `false` | If `true`, then only use the `fieldName` of the field, ignoring the model name in the operation (resulting in a non-fully-qualified field name) |
47
+ ///
48
+ /// Return: string
49
+ /// The field's name, escaped for the underlying database. i.e. `"User:id"`.
19
50
  getEscapedFieldName(_Model, field, options) {
20
51
  let isString = Nife.instanceOf(field, 'string');
21
52
  let fieldName = (isString) ? field : field.fieldName;
@@ -30,6 +61,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
30
61
  return `"${field.Model.getModelName()}:${fieldName}"`;
31
62
  }
32
63
 
64
+ /// Get the escaped column name for the field provided.
65
+ ///
66
+ /// Given a field, return the full column name (including the table)
67
+ /// of that field, escaped for the underlying databases.
68
+ /// For example, given the field `User.fields.id`, return `"users"."id"`--assuming
69
+ /// the underlying database uses double quotes for escaping identifiers.
70
+ ///
71
+ /// This method will use the `columnName` defined on the field for the name
72
+ /// of the column, if defined, or will use `fieldName` as defined on the field
73
+ /// as the column name if no `columnName` is defined. To get the name of the table,
74
+ /// this method will use [getTableName](https://github.com/th317erd/mythix-orm/wiki/Model#method-static-getTableName)
75
+ /// on the model that owns the `field` provided.
76
+ ///
77
+ /// Arguments:
78
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
79
+ /// The model that owns the `field` provided.
80
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
81
+ /// The field to operate on.
82
+ /// options?: object
83
+ /// Options for the operation.
84
+ /// | Option | Type | Default Value | Description |
85
+ /// | ------ | ---- | ------------- | ----------- |
86
+ /// | `columnNameOnly` | `boolean` | `false` | If `true`, then escape only the column name, and don't include the name of the table. |
87
+ /// | `columnNamePrefix` | `string` | `''` | Prefix the column name with this value before escaping it. |
88
+ /// | `tableNamePrefix` | `string` | `''` | Only applicable if `columnNameOnly` is `false`. Used to prefix the table name before escaping it. |
89
+ ///
90
+ /// Return: string
91
+ /// The fully escaped column name, including the table the column exists on. i.e. `"users"."id"`.
33
92
  getEscapedColumnName(_Model, field, options) {
34
93
  let isString = Nife.instanceOf(field, 'string');
35
94
  let columnName = (isString) ? field : (field.columnName || field.fieldName);
@@ -47,6 +106,27 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
47
106
  return `${this.getEscapedTableName(Model)}.${this.escapeID(columnName)}`;
48
107
  }
49
108
 
109
+ /// Get the escaped table name for the model or field provided.
110
+ ///
111
+ /// Give a model or field, access the model to get the table name
112
+ /// for the model, and escape it for the underlying database.
113
+ /// If given a field, then the parent model for that field will be
114
+ /// retrieved via `field.Model`. Once a model is ascertained, then
115
+ /// call [getTableName](https://github.com/th317erd/mythix-orm/wiki/Model#method-static-getTableName)
116
+ /// on the model to get the table name in the underlying database for this
117
+ /// model, escaping it before it is returned.
118
+ ///
119
+ /// Arguments:
120
+ /// modelOrField: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model) | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
121
+ /// A model class, or a field to fetch the model class from.
122
+ /// options?: object
123
+ /// Options for the operation.
124
+ /// | Option | Type | Default Value | Description |
125
+ /// | ------ | ---- | ------------- | ----------- |
126
+ /// | `tableNamePrefix` | `string` | `''` | Used to prefix the table name before escaping it. |
127
+ ///
128
+ /// Return: string
129
+ /// The table name for the given model, escaped for the underlying database. i.e. `"users"`.
50
130
  getEscapedTableName(_modelOrField, options) {
51
131
  let Model = (_modelOrField.Model) ? _modelOrField.Model : _modelOrField;
52
132
  let tableName = Model.getTableName(this.connection);
@@ -57,6 +137,33 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
57
137
  return this.escapeID(tableName);
58
138
  }
59
139
 
140
+ /// Given a field, escape it for the projection,
141
+ /// optionally including an alias for the field.
142
+ ///
143
+ /// For example, given the field `User.fields.id`, return
144
+ /// the projected field with an alias: `"users"."id" AS "User:id"`.
145
+ /// By default, this will use <see>SQLQueryGeneratorBase.getEscapedFieldName</see>
146
+ /// for the alias, unless the `as` `options` is provided. If `as`
147
+ /// is provided to the `options`, then use that as the literal field
148
+ /// alias instead.
149
+ ///
150
+ /// Arguments:
151
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
152
+ /// The model that owns the `field` provided.
153
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
154
+ /// The field to operate on.
155
+ /// options?: object
156
+ /// Options for the operation.
157
+ /// | Option | Type | Default Value | Description |
158
+ /// | ------ | ---- | ------------- | ----------- |
159
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, then escape only the column name, and don't include the alias for the projection. |
160
+ /// | `columnNameOnly` | `boolean` | `false` | If `true`, then escape only the column name, and don't include the name of the table. |
161
+ /// | `columnNamePrefix` | `string` | `''` | Prefix the column name with this value before escaping it. |
162
+ /// | `tableNamePrefix` | `string` | `''` | Only applicable if `columnNameOnly` is `false`. Used to prefix the table name before escaping it. |
163
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
164
+ ///
165
+ /// Return: string
166
+ /// The fully escaped column name, for use in the projection. i.e. `"users"."id" AS "User:id"`.
60
167
  // eslint-disable-next-line no-unused-vars
61
168
  getEscapedProjectionName(Model, field, options) {
62
169
  if (options && options.noProjectionAliases)
@@ -65,8 +172,39 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
65
172
  return `${this.getEscapedColumnName(Model, field, options)} AS ${(options && options.as) ? this.escapeID(options.as) : this.getEscapedFieldName(Model, field, options)}`;
66
173
  }
67
174
 
175
+ /// Pass all non-virtual model fields through
176
+ /// <see>SQLQueryGeneratorBase.getEscapedFieldName</see>,
177
+ /// <see>SQLQueryGeneratorBase.getEscapedColumnName</see>,
178
+ /// or <see>SQLQueryGeneratorBase.getEscapedProjectionName</see>.
179
+ ///
180
+ /// This method is used to bulk-escape all model fields, using one
181
+ /// of the methods listed above. If the `asProjection` `options` is
182
+ /// used, then all fields will be escaped using <see>SQLQueryGeneratorBase.getEscapedProjectionName</see>.
183
+ /// If the `asColumn` `options` is used, then escape all fields using
184
+ /// <see>SQLQueryGeneratorBase.getEscapedColumnName</see>. Otherwise,
185
+ /// if no `options` are specified, then escape all model fields using
186
+ /// <see>SQLQueryGeneratorBase.getEscapedFieldName</see> instead.
187
+ ///
188
+ /// Arguments:
189
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
190
+ /// The model whose fields are to be escaped. Only non-virtual fields are operated upon.
191
+ /// options?: object
192
+ /// Options for the operation.
193
+ /// | Option | Type | Default Value | Description |
194
+ /// | ------ | ---- | ------------- | ----------- |
195
+ /// | `asProjection` | `boolean` | `false` | If `true`, then use <see>SQLQueryGeneratorBase.getEscapedProjectionName</see> to escape the model's fields. |
196
+ /// | `asColumn` | `boolean` | `false` | If `true`, then use <see>SQLQueryGeneratorBase.getEscapedColumnName</see> to escape the model's fields. |
197
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, then escape only the column name, and don't include the alias for the projection. |
198
+ /// | `columnNameOnly` | `boolean` | `false` | If `true`, then escape only the column name, and don't include the name of the table. |
199
+ /// | `columnNamePrefix` | `string` | `''` | Prefix the column name with this value before escaping it. |
200
+ /// | `tableNamePrefix` | `string` | `''` | Only applicable if `columnNameOnly` is `false`. Used to prefix the table name before escaping it. |
201
+ ///
202
+ /// Return: object
203
+ /// Return an object that maps all escaped model fields. Each key will be the fully qualified name of the field,
204
+ /// each value will be the escaped field as a string.
68
205
  // eslint-disable-next-line no-unused-vars
69
- getEscapedModelFields(Model, options) {
206
+ getEscapedModelFields(Model, _options) {
207
+ let options = Object.assign(_options || {}, { as: null });
70
208
  let fields = {};
71
209
  let modelName = Model.getModelName();
72
210
 
@@ -89,10 +227,21 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
89
227
  return fields;
90
228
  }
91
229
 
92
- isFieldIdentifier(str) {
93
- return (/^"[^"]+"."[^"]+"|"\w+:[\w.]+"/i).test(str);
94
- }
95
-
230
+ /// Get the `ORDER` clause from the provided [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine).
231
+ /// If none is found, then call [Connection.getDefaultOrder](https://github.com/th317erd/mythix-orm/wiki/ConnectionBase#method-getDefaultOrder)
232
+ /// to get the default order for the operation.
233
+ ///
234
+ /// Arguments:
235
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
236
+ /// The query engine to fetch the `ORDER` clause from.
237
+ /// options?: object
238
+ /// The options object provided to the operation that is taking place. This isn't used
239
+ /// by this method, but instead is passed off to [Connection.getDefaultOrder](https://github.com/th317erd/mythix-orm/wiki/ConnectionBase#method-getDefaultOrder)
240
+ /// in case the connection (or user) needs the options to produce a default ordering.
241
+ ///
242
+ /// Return: Map<string, { value: Field | Literal | string; direction?: '+' | '-'; ... }>
243
+ /// Return the field-set for the default ordering to apply to the operation taking place.
244
+ /// This `Map` should have the same format as is returned by [ModelScope.mergeFields](https://github.com/th317erd/mythix-orm/wiki/ModelScope#method-mergeFields).
96
245
  getQueryEngineOrder(queryEngine, _options) {
97
246
  let options = _options || {};
98
247
  let context = queryEngine.getOperationContext();
@@ -101,6 +250,35 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
101
250
  return (order && order.size) ? order : this.connection.getDefaultOrder(context.rootModel, options);
102
251
  }
103
252
 
253
+ /// Get the field projection for the operation taking place.
254
+ ///
255
+ /// This will prepare all fields in the projection, along with
256
+ /// any literals, and will also merge any `ORDER BY` clause fields
257
+ /// into the projection. The result will be either a `Map`, containing
258
+ /// all projected fields and literals, or will be an `Array` of just
259
+ /// the projected fields, escaped and prepared for the underlying database.
260
+ /// A `Map` of the fields will be returned instead of an array if the `asMap`
261
+ /// argument is set to `true`.
262
+ ///
263
+ /// Arguments:
264
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
265
+ /// The query engine to fetch the field projection (and order) from.
266
+ /// options?: object
267
+ /// Options for the operation. These options aren't used directly by this method,
268
+ /// but instead are passed off to the sub-methods that are called to complete the
269
+ /// operation, such as the `toString` method for literals, the <see>SQLQueryGeneratorBase.getQueryEngineOrder</see>
270
+ /// to get the field ordering, and to the <see>SQLQueryGeneratorBase.getEscapedProjectionName</see> to
271
+ /// project the fields.
272
+ /// asMap: boolean
273
+ /// If `true`, then return a `Map` of the projected fields, instead of an `Array`. This
274
+ /// is used for example internally by `SELECT` operations to know which fields were
275
+ /// projected. Each key in this `Map` is a fully qualified field name (to link the projection
276
+ /// back to its field), or a fully expanded literal string for literals. The value for each
277
+ /// property in this map in the projected field or literal value itself, as a string.
278
+ ///
279
+ /// Return: Map<string, string> | Array<string>
280
+ /// A `Map` of the projected fields if the `asMap` argument is `true`, or an `Array` of the
281
+ /// projected fields otherwise.
104
282
  getProjectedFields(queryEngine, _options, asMap) {
105
283
  let options = this.stackAssign(_options || {}, { isProjection: true });
106
284
  let context = queryEngine.getOperationContext();
@@ -146,6 +324,50 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
146
324
  return Array.from(allProjectionFields.values());
147
325
  }
148
326
 
327
+ /// Given two "operation contexts" from a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
328
+ /// instance, collection the information required to join two tables.
329
+ ///
330
+ /// Using the "left side" context of the operation being calculated, find the "root model"
331
+ /// of the query, and compile table-join information with the "right side" context of
332
+ /// the operation, joining the "root model" on the left-side with the model and field specified on
333
+ /// the right-side of the operation.
334
+ ///
335
+ /// A table-join looks like `User.where.id.EQ(Role.where.userID)`. In this context, the `User.where.id`
336
+ /// would be the "left side", and `Role.where.userID` would be the "right side".
337
+ ///
338
+ /// Interface:
339
+ /// interface TableJoinInformation {
340
+ /// operator: string; // The operator used for the table-join, i.e. "EQ"
341
+ /// joinType: string; // The database specific join-type for the table-join, i.e. "INNER JOIN"
342
+ /// rootModelName: string; // The name of the "root model" for the query
343
+ /// joinModel: class Model; // The the model being joined to the "root model"
344
+ /// joinModelName: string; // The name of the model being joined to the "root model"
345
+ /// leftSideModel: class Model; // The model on the left-side of the table-join
346
+ /// leftSideModelName: string; // The name of the model on the left-side of the table-join
347
+ /// leftQueryContext: object; // The "operation context" of the left-side of the operation
348
+ /// leftSideField: Field; // The field on the left side of the table-join
349
+ /// rightSideModel: class Model; // The model on the right-side of the table-join
350
+ /// rightSideModelName: string; // The model name of the model on the right-side of the table-join
351
+ /// rightQueryContext: object; // The "operation context" of the right-side of the operation
352
+ /// rightSideField: Field; // The field on the right-side of the table-join
353
+ /// }
354
+ ///
355
+ /// Arguments:
356
+ /// leftQueryContext: object
357
+ /// The left-side "operation context" of the query engine to compile the "root model" information
358
+ /// from.
359
+ /// rightQueryContext: object
360
+ /// The right-side "operation context" of the query engine to compile the "join model" information
361
+ /// from.
362
+ /// joinType: string
363
+ /// The join-type (LEFT, RIGHT, INNER, CROSS, etc...) for the join-table operation.
364
+ /// options?: object
365
+ /// The options for the operation. These aren't used by default by Mythix ORM, and instead are
366
+ /// provided for the user if the user should overload this method. These will be the options for
367
+ /// the operation taking place (i.e. a `SELECT` operation).
368
+ ///
369
+ /// Return: TableJoinInformation
370
+ /// Return an object containing all information needed to generate a table-join query.
149
371
  // eslint-disable-next-line no-unused-vars
150
372
  getJoinTableInfoFromQueryContexts(leftQueryContext, rightQueryContext, joinType, options) {
151
373
  let rootModel = leftQueryContext.rootModel;
@@ -194,6 +416,30 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
194
416
  };
195
417
  }
196
418
 
419
+ /// Get an `AS` projection alias for the literal being stringified,
420
+ /// but only if a projection alias is applicable.
421
+ ///
422
+ /// If the literal is a "sub-field" (i.e. the `FieldLiteral` in `new CountLiteral(new FieldLiteral(...))`)
423
+ /// then don't return a projection alias (i.e. the alias needs to be on `CountLiteral` in this example, not
424
+ /// on the sub-field `FieldLiteral`).
425
+ ///
426
+ /// Also don't return an alias if the `noProjectionAliases` `options` is set to `true`. This might
427
+ /// be the case for example if we are "projecting" the literal for an `ORDER BY` clause.
428
+ ///
429
+ /// Arguments:
430
+ /// literal: inherits from [LiteralBase](https://github.com/th317erd/mythix-orm/wiki/LiteralBase)
431
+ /// The literal that is being stringified.
432
+ /// options?: object
433
+ /// Options for the operation.
434
+ /// | Option | Type | Default Value | Description |
435
+ /// | ------ | ---- | ------------- | ----------- |
436
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
437
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
438
+ /// | `as` | `string` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
439
+ ///
440
+ /// Return: string
441
+ /// An empty string if a field alias isn't allowed, or an ` AS ...` postfix string
442
+ /// to apply to the stringified `literal` provided if an alias is allowed and requested.
197
443
  _getLiteralAlias(literal, options) {
198
444
  if (options && options.isSubField)
199
445
  return '';
@@ -208,6 +454,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
208
454
  return ` AS ${this.escapeID(as)}`;
209
455
  }
210
456
 
457
+ /// Convert an [AverageLiteral](https://github.com/th317erd/mythix-orm/wiki/AverageLiteral) to
458
+ /// a string for use in the underlying database.
459
+ ///
460
+ /// Arguments:
461
+ /// literal: [AverageLiteral](https://github.com/th317erd/mythix-orm/wiki/AverageLiteral)
462
+ /// The [AverageLiteral](https://github.com/th317erd/mythix-orm/wiki/AverageLiteral) to stringify.
463
+ /// options?: object
464
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
465
+ /// literal or connection specific options that can be supplied.
466
+ /// | Option | Type | Default Value | Description |
467
+ /// | ------ | ---- | ------------- | ----------- |
468
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
469
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
470
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
471
+ ///
472
+ /// Return: string
473
+ /// The literal provided, stringified for use in the underlying database.
211
474
  _averageLiteralToString(literal, options) {
212
475
  if (!literal || !LiteralBase.isLiteral(literal))
213
476
  return;
@@ -223,6 +486,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
223
486
  return `AVG(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
224
487
  }
225
488
 
489
+ /// Convert an [CountLiteral](https://github.com/th317erd/mythix-orm/wiki/CountLiteral) to
490
+ /// a string for use in the underlying database.
491
+ ///
492
+ /// Arguments:
493
+ /// literal: [CountLiteral](https://github.com/th317erd/mythix-orm/wiki/CountLiteral)
494
+ /// The [CountLiteral](https://github.com/th317erd/mythix-orm/wiki/CountLiteral) to stringify.
495
+ /// options?: object
496
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
497
+ /// literal or connection specific options that can be supplied.
498
+ /// | Option | Type | Default Value | Description |
499
+ /// | ------ | ---- | ------------- | ----------- |
500
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
501
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
502
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
503
+ ///
504
+ /// Return: string
505
+ /// The literal provided, stringified for use in the underlying database.
226
506
  _countLiteralToString(literal, options) {
227
507
  if (!literal || !LiteralBase.isLiteral(literal))
228
508
  return;
@@ -242,6 +522,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
242
522
  return `COUNT(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
243
523
  }
244
524
 
525
+ /// Convert an [DistinctLiteral](https://github.com/th317erd/mythix-orm/wiki/DistinctLiteral) to
526
+ /// a string for use in the underlying database.
527
+ ///
528
+ /// Arguments:
529
+ /// literal: [DistinctLiteral](https://github.com/th317erd/mythix-orm/wiki/DistinctLiteral)
530
+ /// The [DistinctLiteral](https://github.com/th317erd/mythix-orm/wiki/DistinctLiteral) to stringify.
531
+ /// options?: object
532
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
533
+ /// literal or connection specific options that can be supplied.
534
+ /// | Option | Type | Default Value | Description |
535
+ /// | ------ | ---- | ------------- | ----------- |
536
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
537
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
538
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
539
+ ///
540
+ /// Return: string
541
+ /// The literal provided, stringified for use in the underlying database.
245
542
  _distinctLiteralToString(literal, options) {
246
543
  if (!literal || !LiteralBase.isLiteral(literal))
247
544
  return;
@@ -271,6 +568,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
271
568
  return `DISTINCT ON(${escapedColumnName})`;
272
569
  }
273
570
 
571
+ /// Convert an [FieldLiteral](https://github.com/th317erd/mythix-orm/wiki/FieldLiteral) to
572
+ /// a string for use in the underlying database.
573
+ ///
574
+ /// Arguments:
575
+ /// literal: [FieldLiteral](https://github.com/th317erd/mythix-orm/wiki/FieldLiteral)
576
+ /// The [FieldLiteral](https://github.com/th317erd/mythix-orm/wiki/FieldLiteral) to stringify.
577
+ /// options?: object
578
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
579
+ /// literal or connection specific options that can be supplied.
580
+ /// | Option | Type | Default Value | Description |
581
+ /// | ------ | ---- | ------------- | ----------- |
582
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
583
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
584
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
585
+ ///
586
+ /// Return: string
587
+ /// The literal provided, stringified for use in the underlying database.
274
588
  _fieldLiteralToString(literal, options) {
275
589
  if (!literal || !LiteralBase.isLiteral(literal))
276
590
  return;
@@ -283,6 +597,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
283
597
  return this.getEscapedProjectionName(field.Model, field, this.stackAssign(options, { noProjectionAliases }, literal.options));
284
598
  }
285
599
 
600
+ /// Convert an [MaxLiteral](https://github.com/th317erd/mythix-orm/wiki/MaxLiteral) to
601
+ /// a string for use in the underlying database.
602
+ ///
603
+ /// Arguments:
604
+ /// literal: [MaxLiteral](https://github.com/th317erd/mythix-orm/wiki/MaxLiteral)
605
+ /// The [MaxLiteral](https://github.com/th317erd/mythix-orm/wiki/MaxLiteral) to stringify.
606
+ /// options?: object
607
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
608
+ /// literal or connection specific options that can be supplied.
609
+ /// | Option | Type | Default Value | Description |
610
+ /// | ------ | ---- | ------------- | ----------- |
611
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
612
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
613
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
614
+ ///
615
+ /// Return: string
616
+ /// The literal provided, stringified for use in the underlying database.
286
617
  _maxLiteralToString(literal, options) {
287
618
  if (!literal || !LiteralBase.isLiteral(literal))
288
619
  return;
@@ -298,6 +629,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
298
629
  return `MAX(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
299
630
  }
300
631
 
632
+ /// Convert an [MinLiteral](https://github.com/th317erd/mythix-orm/wiki/MinLiteral) to
633
+ /// a string for use in the underlying database.
634
+ ///
635
+ /// Arguments:
636
+ /// literal: [MinLiteral](https://github.com/th317erd/mythix-orm/wiki/MinLiteral)
637
+ /// The [MinLiteral](https://github.com/th317erd/mythix-orm/wiki/MinLiteral) to stringify.
638
+ /// options?: object
639
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
640
+ /// literal or connection specific options that can be supplied.
641
+ /// | Option | Type | Default Value | Description |
642
+ /// | ------ | ---- | ------------- | ----------- |
643
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
644
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
645
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
646
+ ///
647
+ /// Return: string
648
+ /// The literal provided, stringified for use in the underlying database.
301
649
  _minLiteralToString(literal, options) {
302
650
  if (!literal || !LiteralBase.isLiteral(literal))
303
651
  return;
@@ -313,6 +661,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
313
661
  return `MIN(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
314
662
  }
315
663
 
664
+ /// Convert an [SumLiteral](https://github.com/th317erd/mythix-orm/wiki/SumLiteral) to
665
+ /// a string for use in the underlying database.
666
+ ///
667
+ /// Arguments:
668
+ /// literal: [SumLiteral](https://github.com/th317erd/mythix-orm/wiki/SumLiteral)
669
+ /// The [SumLiteral](https://github.com/th317erd/mythix-orm/wiki/SumLiteral) to stringify.
670
+ /// options?: object
671
+ /// Options for the operation. Listed below are the common options for all literals. There may also be
672
+ /// literal or connection specific options that can be supplied.
673
+ /// | Option | Type | Default Value | Description |
674
+ /// | ------ | ---- | ------------- | ----------- |
675
+ /// | `isSubField` | `boolean` | `false` | If `true`, the engine is reporting that this is a "sub-field", or literal inside a literal... if this is the case, then don't return an `AS` field alias. |
676
+ /// | `noProjectionAliases` | `boolean` | `false` | If `true`, the engine is reporting that this is part of the query that shouldn't have field aliases, such as an `ORDER BY` clause. |
677
+ /// | `as` | `string` | `undefined` | If set to a valid string, then this will be used for the `AS` alias of the column instead of the default. |
678
+ ///
679
+ /// Return: string
680
+ /// The literal provided, stringified for use in the underlying database.
316
681
  _sumLiteralToString(literal, options) {
317
682
  if (!literal || !LiteralBase.isLiteral(literal))
318
683
  return;
@@ -328,106 +693,86 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
328
693
  return `SUM(${escapedColumnName})${this._getLiteralAlias(literal, options)}`;
329
694
  }
330
695
 
696
+ /// A convenience method that proxies to <see>SQLConnectionBase.prepareArrayValuesForSQL</see>.
697
+ ///
698
+ /// See: SQLConnectionBase.prepareArrayValuesForSQL
331
699
  prepareArrayValuesForSQL(array) {
332
700
  return this.connection.prepareArrayValuesForSQL(array);
333
701
  }
334
702
 
335
- parseFieldProjection = (str, getRawField) => {
336
- let modelName;
337
- let fieldName;
338
- let projectionField;
339
-
340
- str.replace(/(AS\s+)?"(\w+):([\w.]+)"/i, (m, as, _modelName, _fieldName) => {
341
- modelName = _modelName;
342
- fieldName = _fieldName;
343
- });
344
-
345
- if (!modelName || !fieldName) {
346
- // Reverse search model and field name
347
- // based on table and column name
348
- str.replace(/"([^"]+)"."([^"]+)"/i, (m, _tableName, _columnName) => {
349
- this.connection.findModelField(({ field, stop }) => {
350
- if (field.columnName !== _columnName)
351
- return;
352
-
353
- let tableName = field.Model.getTableName(this.connection);
354
- if (tableName !== _tableName)
355
- return;
356
-
357
- if (getRawField) {
358
- projectionField = field;
359
-
360
- stop();
361
-
362
- return;
363
- }
364
-
365
- modelName = field.Model.getModelName();
366
- fieldName = field.fieldName;
367
-
368
- stop();
369
- });
370
- });
371
-
372
- if (getRawField && projectionField)
373
- return projectionField;
374
- }
375
-
376
- if (getRawField && modelName && fieldName) {
377
- let field = this.connection.getField(fieldName, modelName);
378
- if (field)
379
- return field;
380
- } else if (!modelName || !fieldName) {
381
- return str;
382
- }
383
-
384
- return `${modelName}:${fieldName}`;
385
- };
386
-
387
- parseFieldProjectionToFieldMap(selectStatement) {
388
- let firstPart = selectStatement.replace(/[\r\n]/g, ' ').split(/\s+FROM\s+/i)[0].replace(/^SELECT\s+/i, '').trim();
389
- let fieldParts = firstPart.split(',');
390
- let projectionFieldMap = new Map();
391
-
392
- for (let i = 0, il = fieldParts.length; i < il; i++) {
393
- let fieldPart = fieldParts[i].trim();
394
- let field = this.parseFieldProjection(fieldPart, true);
395
-
396
- if (field !== fieldPart)
397
- projectionFieldMap.set(`${field.Model.getModelName()}:${field.fieldName}`, this.getEscapedProjectionName(field.Model, field));
398
- else
399
- projectionFieldMap.set(field, field);
400
-
401
- // If this isn't a field, then add it
402
- if (!this.isFieldIdentifier(fieldPart))
403
- projectionFieldMap.set(fieldPart, fieldPart);
404
- }
405
-
406
- return projectionFieldMap;
407
- }
408
-
409
- generateSelectQueryFieldProjection(queryEngine, options, asMap) {
410
- let projectedFields = this.getProjectedFields(queryEngine, options, asMap);
411
-
412
- if (asMap === true) {
413
- return projectedFields;
414
- } else {
415
- let projectedFieldList = Array.from(projectedFields.values()).join(',');
416
-
417
- let distinct = queryEngine.getOperationContext().distinct;
418
- if (distinct) {
419
- let result = distinct.toString(this.connection, { isProjection: true });
420
- return `${result} ${projectedFieldList}`;
421
- }
422
-
423
- return projectedFieldList;
703
+ /// Generate a field projection for a `SELECT` statement.
704
+ /// If a `DISTINCT` operation is in-use, then this will always
705
+ /// prefix any and all fields projected.
706
+ ///
707
+ /// Arguments:
708
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
709
+ /// The query engine to use to generate the field projection from.
710
+ /// options?: object
711
+ /// Options that are passed through the operation. These options are
712
+ /// simply passed to all other sub-methods used in this operation, including
713
+ /// when stringifying literals.
714
+ /// projectedFields?: Map<string, string>
715
+ /// Provide the projected fields if they are already known, instead of compiling them
716
+ /// again via a call to <see>SQLQueryGeneratorBase.getProjectedFields</see>.
717
+ ///
718
+ /// Return: string
719
+ /// Return a comma-separated list of projected fields. If any `DISTINCT`
720
+ /// clause is set on the `queryEngine`, then that will always come first
721
+ /// in the list of projected fields.
722
+ generateSelectQueryFieldProjection(queryEngine, options, _projectedFields) {
723
+ let projectedFields = (_projectedFields) ? _projectedFields : this.getProjectedFields(queryEngine, options, false);
724
+ let projectedFieldList = Array.from(projectedFields.values()).join(',');
725
+
726
+ let distinct = queryEngine.getOperationContext().distinct;
727
+ if (distinct) {
728
+ let result = distinct.toString(this.connection, { isProjection: true });
729
+ return `${result} ${projectedFieldList}`;
424
730
  }
425
- }
426
731
 
427
- // eslint-disable-next-line no-unused-vars
732
+ return projectedFieldList;
733
+ }
734
+
735
+ /// Convert a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) conditional
736
+ /// operator into the equivalent SQL conditional operator. For example, `EQ` will be converted
737
+ /// to `=` for most values, `IS` (in the case of `null`, `true`, and `false`), or an `IN` operator
738
+ /// if an array of values is provided.
739
+ ///
740
+ /// If a literal is provided, then it will be converted to a string using the literal's `toString`
741
+ /// method and returned.
742
+ ///
743
+ /// Standard conversion table for most SQL connections (results might differ based on the SQL
744
+ /// connection being used):
745
+ /// | `QueryEngine` Operator | `Array<any>` | `null` | `true` | `false` | Table Join | Other |
746
+ /// | -------------------- | ------------ | ------ | ------ | ------- | ---------- | ----- |
747
+ /// | `EQ` | `IN` | `IS NULL` | `IS TRUE` | `IS FALSE` | `=` | `=` |
748
+ /// | `NEQ` | `NOT IN` | `IS NOT NULL` | `IS NOT TRUE` | `IS NOT FALSE` | `!=` | `!=` |
749
+ /// | `GT` | `throw TypeError` | `>` | `>` | `>` | `>` | `>` |
750
+ /// | `GTE` | `throw TypeError` | `>=` | `>=` | `>=` | `>=` | `>=` |
751
+ /// | `LT` | `throw TypeError` | `<` | `<` | `<` | `<` | `<` |
752
+ /// | `LTE` | `throw TypeError` | `<=` | `<=` | `<=` | `<=` | `<=` |
753
+ /// | `LIKE` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `LIKE` |
754
+ /// | `NOT_LIKE` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `throw TypeError` | `NOT LIKE` |
755
+ ///
756
+ /// Arguments:
757
+ /// queryPart: object
758
+ /// The `QueryEngine` "operation frame" for this conditional operation.
759
+ /// operator: string | Literal
760
+ /// The `QueryEngine` operator to convert to SQL syntax. If a literal is provided,
761
+ /// then it will simply be stringified and returned (as the literal operator the user
762
+ /// specified... whatever that might be).
763
+ /// value: any
764
+ /// The right-hand-side value for this conditional operator.
765
+ /// valueIsReference: boolean
766
+ /// If `true`, then this operator is being converted for a table-join operation. If this is
767
+ /// the case, then `value` will be the "operation context" for the right-side of the table-join.
768
+ /// options?: object
769
+ /// Options for the operation. This is only used by this method for converting provided literals to strings.
770
+ ///
771
+ /// Return: string
772
+ /// Return SQL syntax for this context-specific conditional operator.
428
773
  generateSelectQueryOperatorFromQueryEngineOperator(queryPart, operator, value, valueIsReference, options) {
429
774
  if (LiteralBase.isLiteral(operator))
430
- return operator.toString(this.connection);
775
+ return operator.toString(this.connection, options);
431
776
 
432
777
  switch (operator) {
433
778
  case 'EQ':
@@ -451,12 +796,24 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
451
796
  else
452
797
  return '!=';
453
798
  case 'GT':
799
+ if (Array.isArray(value))
800
+ throw new TypeError(`${this.constructor.name}::generateSelectQueryOperatorFromQueryEngineOperator: Array of values provided to "GT" (greater than) operator.`);
801
+
454
802
  return '>';
455
803
  case 'GTE':
804
+ if (Array.isArray(value))
805
+ throw new TypeError(`${this.constructor.name}::generateSelectQueryOperatorFromQueryEngineOperator: Array of values provided to "GTE" (greater than or equal to) operator.`);
806
+
456
807
  return '>=';
457
808
  case 'LT':
809
+ if (Array.isArray(value))
810
+ throw new TypeError(`${this.constructor.name}::generateSelectQueryOperatorFromQueryEngineOperator: Array of values provided to "LT" (less than) operator.`);
811
+
458
812
  return '<';
459
813
  case 'LTE':
814
+ if (Array.isArray(value))
815
+ throw new TypeError(`${this.constructor.name}::generateSelectQueryOperatorFromQueryEngineOperator: Array of values provided to "LTE" (less than or equal to) operator.`);
816
+
460
817
  return '<=';
461
818
  case 'LIKE':
462
819
  if (valueIsReference)
@@ -479,15 +836,89 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
479
836
  }
480
837
  }
481
838
 
839
+ /// Format a `LIKE` or `NOT LIKE` value.
840
+ ///
841
+ /// Mythix ORM requires that `%` and `_` characters be used for
842
+ /// wildcard characters in `LIKE` and `NOT_LIKE` operations. This is to
843
+ /// create a unified standard for `LIKE` operations across databases.
844
+ /// If the underlying database uses different wildcard characters for
845
+ /// a `LIKE` operation, then it is required to convert them to its native
846
+ /// format here in this method.
847
+ ///
848
+ /// Interface:
849
+ /// interface QueryConditionContext {
850
+ /// queryPart: object; // The "operation frame" or "operation context" for this condition
851
+ /// field: Field; // The field for this conditional operator
852
+ /// sqlOperator: string; // The SQL operator that matches "operator" in the given context
853
+ /// operator: string; // The QueryEngine operator for this condition, i.e. "EQ"
854
+ /// value: any; // The conditional operator's value (right-hand-side value)
855
+ /// }
856
+ ///
857
+ /// Arguments:
858
+ /// context: QueryConditionContext
859
+ /// The "conditional operator" context for this `LIKE` or `NOT LIKE` condition.
860
+ ///
861
+ /// Return: string
862
+ /// By default simply return `context.value`. Other database drivers may
863
+ /// format `context.value` before returning it, in a format suitable for
864
+ /// the underlying database.
482
865
  formatLikeValue({ value }) {
483
866
  return value;
484
867
  }
485
868
 
869
+ /// Generate an optional postfix for any given conditional operator.
870
+ ///
871
+ /// This might be used by any given underlying database should it need
872
+ /// it. It is most commonly used by `LIKE` conditional operators to tag
873
+ /// on an `ESCAPE` clause, specifying the escape character for the `LIKE`
874
+ /// operator. However, it may be used for any operator where the database
875
+ /// needs a "postfix" for the condition.
876
+ ///
877
+ /// Interface:
878
+ /// interface QueryConditionContext {
879
+ /// queryPart: object; // The "operation frame" or "operation context" for this condition
880
+ /// field: Field; // The field for this conditional operator
881
+ /// sqlOperator: string; // The SQL operator that matches "operator" in the given context
882
+ /// operator: string; // The QueryEngine operator for this condition, i.e. "EQ"
883
+ /// value: any; // The conditional operator's value (right-hand-side value)
884
+ /// }
885
+ ///
886
+ /// Arguments:
887
+ /// context: QueryConditionContext
888
+ /// The "conditional operator" context for the current operator being generated.
889
+ ///
890
+ /// Return: string
891
+ /// By default, simply return an empty string, meaning that there is no postfix
892
+ /// for the condition being generated. Optionally, the database driver can return any postfix
893
+ /// for the conditional operator being generated.
486
894
  // eslint-disable-next-line no-unused-vars
487
895
  generateConditionPostfix(context) {
488
896
  return '';
489
897
  }
490
898
 
899
+ /// Generate a SQL "conditional operator" for a `WHERE` clause.
900
+ ///
901
+ /// Even though this is generally used for `SELECT` statements, it
902
+ /// may also be used for `UPDATE WHERE ...`, or `DELETE WHERE ...`
903
+ /// statements as well.
904
+ ///
905
+ /// This method should return a fully formatted "conditional" statement
906
+ /// for the query's `WHERE` clause.
907
+ ///
908
+ /// Arguments:
909
+ /// queryPart: object
910
+ /// The "operation frame" or "operation context" of the conditional
911
+ /// operation as found on the `QueryEngine` being used to generate the
912
+ /// query.
913
+ /// value: any
914
+ /// The value (right-hand-side) of the conditional operator being generated.
915
+ /// options?: object
916
+ /// Options for the current database operation.
917
+ ///
918
+ /// Return: string
919
+ /// A fully formatted SQL conditional statement. This may be an `EXISTS`,
920
+ /// an `ANY` or `ALL`, an `IN`, or some other valid condition for the query.
921
+ /// i.e. `"users"."id" = 1`.
491
922
  generateSelectQueryCondition(queryPart, _value, options) {
492
923
  let value = _value;
493
924
  let field = queryPart.Field;
@@ -566,7 +997,24 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
566
997
  return `${escapedColumnName} ${sqlOperator} ${this.escape(field, value)}${(conditionPostfix) ? ` ${conditionPostfix}` : ''}`;
567
998
  }
568
999
 
569
- // eslint-disable-next-line no-unused-vars
1000
+ /// Generate a table join statement, such as
1001
+ /// `INNER JOIN "table_name"`, or a `FROM` statement
1002
+ /// if `joinType` is falsy, such as `FROM "table_name"`.
1003
+ ///
1004
+ /// Arguments:
1005
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
1006
+ /// The model that defines the table for the operation.
1007
+ /// joinType: string | null
1008
+ /// If a falsy value, then generate a `FROM "table_name"` statement, using
1009
+ /// the provided `Model` to know the table name. If a string, then this will
1010
+ /// be a join-type, such as `INNER JOIN`, which will be used instead of `FROM`,
1011
+ /// i.e. `INNER JOIN "table_name"`.
1012
+ /// options?: object
1013
+ /// Options to provide to <see>SQLQueryGeneratorBase.getEscapedTableName</see> for the operation.
1014
+ ///
1015
+ /// Return: string
1016
+ /// A `FROM "table_name"` statement, or a table join statement, such as
1017
+ /// `INNER JOIN "table_name"`.
570
1018
  generateFromTableOrTableJoin(Model, _joinType, options) {
571
1019
  if (!Model)
572
1020
  throw new Error(`${this.constructor.name}::generateFromTableOrTableJoin: No valid model provided.`);
@@ -579,14 +1027,63 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
579
1027
  return (joinType) ? `${joinType} ${escapedTableName}` : `FROM ${escapedTableName}`;
580
1028
  }
581
1029
 
1030
+ /// Given a table-join query operation, such as `User.where.id.EQ(Role.where.userID)`,
1031
+ /// generate the table-join statement.
1032
+ ///
1033
+ /// Continuing with the example provided, this would generate the following SQL,
1034
+ /// `"users"."id" = "roles"."userID"`.
1035
+ ///
1036
+ /// Arguments:
1037
+ /// leftQueryPart: object
1038
+ /// The "operation frame" or "operation context" for the left-side of the statement.
1039
+ /// In this example this would be the `EQ` "operation context" of `User.where.id.EQ`.
1040
+ /// rightQueryPart: object
1041
+ /// The "operation frame" or "operation context" for the right-side of the statement.
1042
+ /// In this example this would be the `userID` "operation context" of `Role.where.userID`.
1043
+ /// leftField: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
1044
+ /// The left-hand side field for the condition. In this example this would be `User.fields.id`.
1045
+ /// rightField: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
1046
+ /// The right-hand side field for the condition. In this example this would be `Role.fields.userID`.
1047
+ /// operator: string
1048
+ /// The [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) operator to join the
1049
+ /// table columns on... i.e. `EQ`. This will be passed through <see>SQLQueryGeneratorBase.generateSelectQueryOperatorFromQueryEngineOperator</see>
1050
+ /// to get the related SQL operator for the condition.
1051
+ /// options?: object
1052
+ /// Options for the operation.
1053
+ ///
1054
+ /// Return: string
1055
+ /// Return the table-join condition, i.e. `"users"."id" = "roles"."userID"`.
582
1056
  generateSelectJoinOnTableQueryCondition(leftQueryPart, rightQueryPart, leftField, rightField, operator, options) {
583
1057
  let leftSideEscapedColumnName = this.getEscapedColumnName(leftField.Model, leftField, options);
584
1058
  let rightSideEscapedColumnName = this.getEscapedColumnName(rightField.Model, rightField, options);
585
- let sqlOperator = this.generateSelectQueryOperatorFromQueryEngineOperator(leftQueryPart, operator, undefined, true, options);
1059
+ let sqlOperator = this.generateSelectQueryOperatorFromQueryEngineOperator(leftQueryPart, operator, rightQueryPart, true, options);
586
1060
 
587
1061
  return `${leftSideEscapedColumnName} ${sqlOperator} ${rightSideEscapedColumnName}`;
588
1062
  }
589
1063
 
1064
+ /// Take the join table information from a call to
1065
+ /// <see>SQLQueryGeneratorBase.getJoinTableInfoFromQueryContexts</see>
1066
+ /// and generate all table-join conditions for the two
1067
+ /// tables being joined.
1068
+ ///
1069
+ /// The will generate one or more conditions for the table-join,
1070
+ /// using `AND` or `OR` to combine the conditions.
1071
+ /// For example, this might generate the following SQL code:
1072
+ /// `INNER JOIN "roles" ON "users"."id" = "roles"."userID" OR "users"."fullName" = "roles"."userFullName"`.
1073
+ ///
1074
+ /// Arguments:
1075
+ /// joinInfos: Array<TableJoinInformation>
1076
+ /// All collected table-join information for joining tables. The join table
1077
+ /// information at index zero `joinInfos[0]` is the table being joined against,
1078
+ /// which is commonly the "root model" of the query... but it doesn't have to be.
1079
+ /// options?: object
1080
+ /// Options for the operation.
1081
+ ///
1082
+ /// Return: string
1083
+ /// The full list of conditions for joining the tables together, i.e.
1084
+ /// `INNER JOIN "roles" ON "users"."id" = "roles"."userID" OR "users"."fullName" = "roles"."userFullName"`.
1085
+ ///
1086
+ /// See: SQLQueryGeneratorBase.getJoinTableInfoFromQueryContexts
590
1087
  generateJoinOnTableQueryConditions(joinInfos, options) {
591
1088
  if (Nife.isEmpty(joinInfos))
592
1089
  return '';
@@ -609,7 +1106,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
609
1106
  return sqlParts.join(' ');
610
1107
  }
611
1108
 
612
- // TODO: Needs to take a join type object
1109
+ /// Take a join type from a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1110
+ /// instance, and convert it to the proper join type for the underlying database.
1111
+ ///
1112
+ /// Arguments:
1113
+ /// joinType: string
1114
+ /// The join type, as specified by the [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine). This
1115
+ /// will generally be one of `'inner'`, `'left'`, `'right'`, `'full'`, or `'cross'`, but
1116
+ /// it might also be a user-defined table join type (in which case is is expected to already
1117
+ /// be in the correct format for the underlying database).
1118
+ /// outer: boolean
1119
+ /// If `true`, then this is an "outer join", instead of an "inner join". i.e. a `'left'` join
1120
+ /// type would be converted to a `LEFT OUTER JOIN` instead of a `LEFT INNER JOIN`.
1121
+ /// options?: object
1122
+ /// Options for the operation.
1123
+ ///
1124
+ /// Return: string
1125
+ /// The SQL join type, used to join tables. i.e. `LEFT JOIN`.
613
1126
  // eslint-disable-next-line no-unused-vars
614
1127
  generateSQLJoinTypeFromQueryEngineJoinType(joinType, outer, options) {
615
1128
  if (!joinType || joinType === 'inner')
@@ -626,6 +1139,32 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
626
1139
  return joinType;
627
1140
  }
628
1141
 
1142
+ /// Sort the table joins into a predictable order.
1143
+ ///
1144
+ /// Since table-join operations can exist anywhere in a query,
1145
+ /// this will take all table-join operations for the query, and
1146
+ /// sort them so that they are in a predictable order.
1147
+ /// By default the order will be in "dependency order", so if for
1148
+ /// example there is a join against the `Role` table, and the `Role`
1149
+ /// table "depends on" the `User` table, then the `Role` table will
1150
+ /// come first in the list of table joins.
1151
+ ///
1152
+ /// Why sort the table joins at all? Mythix ORM always does its best to
1153
+ /// keep all queries consistent, if for no other reason than for caching
1154
+ /// purposes (so the query itself can be used as a cache key).
1155
+ ///
1156
+ /// Arguments:
1157
+ /// joins: Map<string, Array<TableJoinInformation>>
1158
+ /// A map of all table-joins taking place. Each key in this map is the name
1159
+ /// of a model being joined. Each value is an array of `TableJoinInformation`
1160
+ /// that defines how two tables will be joined to each other. This array is
1161
+ /// what is sorted with this operation.
1162
+ ///
1163
+ /// Return: Array<string>
1164
+ /// An array of model names, sorted in order, used as "ordered keys"
1165
+ /// into the provided `Map` to generate the table-joins for the query.
1166
+ ///
1167
+ /// See: SQLQueryGeneratorBase.getJoinTableInfoFromQueryContexts
629
1168
  sortJoinRelationOrder(joins) {
630
1169
  let modelNames = Array.from(joins.keys());
631
1170
 
@@ -647,6 +1186,22 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
647
1186
  });
648
1187
  }
649
1188
 
1189
+ /// Generate the SQL syntax needed to join all tables in the operation.
1190
+ ///
1191
+ /// For example, this might generate the following SQL
1192
+ /// `INNER JOIN "roles" ON "users"."id" = "roles"."userID" RIGHT JOIN "organizations" ON "organization"."id" = "roles"."organizationID"`.
1193
+ ///
1194
+ /// Arguments:
1195
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1196
+ /// The query engine to pull table-join operations from.
1197
+ /// options?: object
1198
+ /// Options for the operation.
1199
+ ///
1200
+ /// Return: string
1201
+ /// A complete join of all tables in the operation that are being joined, including
1202
+ /// the conditions used to join each table.
1203
+ ///
1204
+ /// See: SQLQueryGeneratorBase.getJoinTableInfoFromQueryContexts
650
1205
  generateSelectQueryJoinTables(queryEngine, options) {
651
1206
  const addToJoins = (joinInfo) => {
652
1207
  let items = joins.get(joinInfo.joinModelName);
@@ -697,6 +1252,33 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
697
1252
  return sqlParts.join(' ');
698
1253
  }
699
1254
 
1255
+ /// Generate all `WHERE` conditions using the provided
1256
+ /// [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine).
1257
+ ///
1258
+ /// This will take all conditional operations defined by the query engine,
1259
+ /// and generate a full `WHERE` condition, `AND`ing or `OR`ing all conditions
1260
+ /// together, including grouping and sub-grouping conditions where needed.
1261
+ ///
1262
+ /// Note:
1263
+ /// This method can be recursively called if a sub-query is encountered that
1264
+ /// also has `WHERE` conditions.
1265
+ ///
1266
+ /// Note:
1267
+ /// Even though this method's name implies generating `WHERE` conditions for a `SELECT`
1268
+ /// statement, it is also used for non-SELECT statements, such as `UPDATE WHERE ...`,
1269
+ /// and `DELETE WHERE ...`.
1270
+ ///
1271
+ /// Arguments:
1272
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1273
+ /// The query engine to pull conditions from.
1274
+ /// options?: object
1275
+ /// Options for the operation.
1276
+ ///
1277
+ /// Return: string
1278
+ /// All SQL conditional operators for a `WHERE` statement, not including the
1279
+ /// `WHERE` prefix. i.e. `"users"."id" = 1 OR "users"."firstName" = 'Bob'`. An
1280
+ /// empty string will be returned if there are no conditional operators used in
1281
+ /// the provided query engine.
700
1282
  generateSelectWhereConditions(queryEngine, options) {
701
1283
  const logicalCondition = (sqlParts, queryPart) => {
702
1284
  if (sqlParts.length === 0)
@@ -771,6 +1353,27 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
771
1353
  return sqlParts.join(' ');
772
1354
  }
773
1355
 
1356
+ /// Generate an `ORDER BY` clause, listing all columns
1357
+ /// and their sort-direction.
1358
+ ///
1359
+ /// Arguments:
1360
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1361
+ /// The query engine to pull the "order by" fields from.
1362
+ /// options?: object
1363
+ /// Options for the operation. Though these might be database specific, there are some
1364
+ /// common options that can be supplied to this method:
1365
+ /// | Option | Type | Default Value | Description |
1366
+ /// | ------ | ---- | ------------- | ----------- |
1367
+ /// | `onlyProjectedFields` | `boolean` | `true` | If `true`, then only list fields that are also in the projection. |
1368
+ /// | `projectionFields` | `Map<string, object>` | Result of `getProjectedFields` | The fields that have been projected, to be used in combination with the `onlyProjectedFields` option. |
1369
+ /// | `rawOrder` | `boolean` | `false` | If `true`, then return the order fields (and literals) as a raw Array instead of a comma-separated list of fields. |
1370
+ /// | `reverseOrder` | `boolean` | `false` | If `true`, then reverse the sort order of all fields. |
1371
+ ///
1372
+ /// Return: string | Array<string>
1373
+ /// If the `rawOrder` `options` is `false`, then return a fully completed `ORDER BY` clause,
1374
+ /// listing all the fields and their sort order. If no order has been specified by the query engine
1375
+ /// (or the connection), then return an empty string instead. If the `rawOrder` `options` is `true`,
1376
+ /// then return an array of the ordered fields instead.
774
1377
  generateOrderClause(queryEngine, _options) {
775
1378
  if (!queryEngine || typeof queryEngine.getOperationContext !== 'function')
776
1379
  return (_options && _options.rawOrder) ? [] : '';
@@ -830,6 +1433,24 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
830
1433
  return (options.rawOrder) ? orderByParts : `ORDER BY ${orderByParts.join(',')}`;
831
1434
  }
832
1435
 
1436
+ /// Generate a `GROUP BY` clause, listing all columns
1437
+ /// to group by.
1438
+ ///
1439
+ /// Arguments:
1440
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1441
+ /// The query engine to pull the "group by" fields from.
1442
+ /// options?: object
1443
+ /// Options for the operation. Though these might be database specific, there are some
1444
+ /// common options that can be supplied to this method:
1445
+ /// | Option | Type | Default Value | Description |
1446
+ /// | ------ | ---- | ------------- | ----------- |
1447
+ /// | `rawGroupBy` | `boolean` | `false` | If `true`, then return the group by fields (and literals) as a raw Array instead of a comma-separated list of fields. |
1448
+ ///
1449
+ /// Return: string | Array<string>
1450
+ /// If the `rawGroupBy` `options` is `false`, then return a fully completed `GROUP BY` clause,
1451
+ /// listing all the columns and literals to group by. If no "group by" has been specified by the query engine
1452
+ /// then return an empty string instead. If the `rawGroupBy` `options` is `true`,
1453
+ /// then return an array of the "group by" fields instead.
833
1454
  generateGroupByClause(queryEngine, _options) {
834
1455
  if (!queryEngine || typeof queryEngine.getOperationContext !== 'function')
835
1456
  return (_options && _options.rawGroupBy) ? [] : '';
@@ -863,11 +1484,54 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
863
1484
  return (options.rawGroupBy) ? groupByParts : `GROUP BY ${groupByParts.join(',')}`;
864
1485
  }
865
1486
 
1487
+ /// Generate a `HAVING` clause to be used in combination with a
1488
+ /// `GROUP BY` clause.
1489
+ ///
1490
+ /// This simple calls <see>SQLQueryGeneratorBase.generateSelectWhereConditions</see>
1491
+ /// on the provided `queryEngine`, and if there is a result, wraps the conditions generated
1492
+ /// inside a `HAVING (...)` clause. If no conditions are generated, then an empty string will
1493
+ /// be returned instead.
1494
+ ///
1495
+ /// Arguments:
1496
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1497
+ /// The query engine to pull the "group by" conditions from.
1498
+ /// options?: object
1499
+ /// Options for the operation. These options are simply passed through to the
1500
+ /// <see>SQLQueryGeneratorBase.generateSelectWhereConditions</see> call.
1501
+ ///
1502
+ /// Return: string
1503
+ /// A `HAVING (...)` clause if conditions were found on the provided `queryEngine`, or
1504
+ /// an empty string if no conditions were found.
1505
+ ///
1506
+ /// See: SQLQueryGeneratorBase.generateSelectWhereConditions
866
1507
  generateHavingClause(queryEngine, options) {
867
1508
  let where = this.generateSelectWhereConditions(queryEngine, options);
868
1509
  return (where) ? `HAVING (${where})` : '';
869
1510
  }
870
1511
 
1512
+ /// Generate both a `GROUP BY` and `HAVING` clause
1513
+ /// together. If no `GROUP_BY` operation is set on the
1514
+ /// provided `queryEngine`, then nothing will be generated,
1515
+ /// and an empty string will be returned instead. The `HAVING`
1516
+ /// clause will be automatically skipped if there is no
1517
+ /// `GROUP_BY` operation to work off of.
1518
+ ///
1519
+ /// Arguments:
1520
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1521
+ /// The query engine to pull the "group by" clause, and "having" conditions from.
1522
+ /// options?: object
1523
+ /// Options for the operation. These options are simply passed through to the
1524
+ /// <see>SQLQueryGeneratorBase.generateGroupByClause</see> and <see>SQLQueryGeneratorBase.generateHavingClause</see>
1525
+ /// internal calls this method makes.
1526
+ ///
1527
+ /// Return: string
1528
+ /// A full `GROUP BY` clause, including any `HAVING` clause specified. An empty string
1529
+ /// will be returned if there is no `GROUP_BY` operation specified on the `queryEngine`
1530
+ /// provided.
1531
+ ///
1532
+ /// See: SQLQueryGeneratorBase.generateGroupByClause
1533
+ ///
1534
+ /// See: SQLQueryGeneratorBase.generateHavingClause
871
1535
  generateGroupByAndHavingClause(queryEngine, options) {
872
1536
  if (!queryEngine)
873
1537
  return '';
@@ -889,22 +1553,61 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
889
1553
  return sqlParts.join(' ');
890
1554
  }
891
1555
 
892
- // eslint-disable-next-line no-unused-vars
1556
+ /// Generate a `LIMIT` clause.
1557
+ ///
1558
+ /// Arguments:
1559
+ /// limit: number | Literal
1560
+ /// If `limit` is a literal, simply stringify and return it. Otherwise, if `limit`
1561
+ /// is a `number`, generate a `LIMIT` clause and return it, using the `limit` provided.
1562
+ /// options?: object
1563
+ /// Options for the operation. These are passed to `toString` for stringifying literals.
1564
+ ///
1565
+ /// Return: string
1566
+ /// A `LIMIT` clause to apply to the query.
893
1567
  generateLimitClause(limit, options) {
894
1568
  if (LiteralBase.isLiteral(limit))
895
- return limit.toString(this.connection);
1569
+ return limit.toString(this.connection, options);
896
1570
 
897
1571
  return `LIMIT ${limit}`;
898
1572
  }
899
1573
 
900
- // eslint-disable-next-line no-unused-vars
1574
+ /// Generate an `OFFSET` clause.
1575
+ ///
1576
+ /// Arguments:
1577
+ /// offset: number | Literal
1578
+ /// If `offset` is a literal, simply stringify and return it. Otherwise, if `offset`
1579
+ /// is a `number`, generate an `OFFSET` clause and return it, using the `offset` provided.
1580
+ /// options?: object
1581
+ /// Options for the operation. These are passed to `toString` for stringifying literals.
1582
+ ///
1583
+ /// Return: string
1584
+ /// An `OFFSET` clause to apply to the query.
901
1585
  generateOffsetClause(offset, options) {
902
1586
  if (LiteralBase.isLiteral(offset))
903
- return offset.toString(this.connection);
1587
+ return offset.toString(this.connection, options);
904
1588
 
905
1589
  return `OFFSET ${offset}`;
906
1590
  }
907
1591
 
1592
+ /// Generate the `ORDER`, `LIMIT`, and `OFFSET` clauses
1593
+ /// to apply to the query. If any one of these clauses is
1594
+ /// blank, then it will be skipped. If there is no order,
1595
+ /// limit, or offset applied to the query, then an empty
1596
+ /// string will be returned.
1597
+ ///
1598
+ /// Arguments:
1599
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1600
+ /// The query engine to pull the "order by", "limit", and "offset" clauses from.
1601
+ /// options?: object
1602
+ /// Options for the operation. These options are simply passed through to the
1603
+ /// the respective calls that generate the sub-parts of this operation.
1604
+ ///
1605
+ /// Return: string
1606
+ /// A combo `ORDER BY ... LIMIT ... OFFSET ...` clause to apply to the query.
1607
+ /// If any one of these sub-parts is empty or invalid, they will be skipped.
1608
+ /// i.e. if there is no `LIMIT` applied to the query, then the `LIMIT` and `OFFSET`
1609
+ /// will be skipped. If there is no output because all sub-parts (clauses) were skipped,
1610
+ /// then an empty string will be returned instead.
908
1611
  generateSelectOrderLimitOffset(queryEngine, _options) {
909
1612
  if (!queryEngine)
910
1613
  return '';
@@ -953,6 +1656,29 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
953
1656
  return sqlParts.join(' ');
954
1657
  }
955
1658
 
1659
+ /// Generate the `WHERE`, `ORDER`, `LIMIT`, and `OFFSET` clauses
1660
+ /// to apply to the query. If any one of these clauses is
1661
+ /// blank, then it will be skipped. If there is no output because
1662
+ /// all clauses were skipped, then an empty string will be returned instead.
1663
+ ///
1664
+ /// Arguments:
1665
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1666
+ /// The query engine to pull the conditions, "order by", "limit", and "offset" clauses from.
1667
+ /// options?: object
1668
+ /// Options for the operation. These options are simply passed through to the
1669
+ /// the respective calls that generate the sub-parts of this operation. One
1670
+ /// common option that can be used here for all connections is the `separateWhereAndOrder`
1671
+ /// option. If `true`, then an object with the following shape will be returned:
1672
+ /// `{ where: string; orderLimitOffset: string; }`, splitting the clauses apart and
1673
+ /// returning them separately.
1674
+ ///
1675
+ /// Return: string
1676
+ /// A combo `WHERE ... ORDER BY ... LIMIT ... OFFSET ...` clause to apply to the query.
1677
+ /// If any one of these sub-parts is empty or invalid, they will be skipped.
1678
+ /// i.e. if there is no `LIMIT` applied to the query, then the `LIMIT` and `OFFSET`
1679
+ /// will be skipped. If there are no conditions for the query, then the `WHERE` clause
1680
+ /// will be skipped. If there is no output because all sub-parts (clauses) were skipped,
1681
+ /// then an empty string will be returned instead.
956
1682
  generateWhereAndOrderLimitOffset(queryEngine, _options) {
957
1683
  let options = _options || {};
958
1684
  let sqlParts = [];
@@ -971,6 +1697,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
971
1697
  return sqlParts.join(' ');
972
1698
  }
973
1699
 
1700
+ /// Generate a full `SELECT` statement using the provided
1701
+ /// `queryEngine`.
1702
+ ///
1703
+ /// This will generate a `SELECT` statement for the underlying
1704
+ /// database that will include the field projection, table joins,
1705
+ /// any `GROUP BY` clause that is applied, all the `WHERE` conditions,
1706
+ /// any sub-queries involved, and an `ORDER BY`, `LIMIT`, and `OFFSET`
1707
+ /// if those are in-use in the query. This method will be recursively
1708
+ /// called for any sub-queries encountered.
1709
+ ///
1710
+ /// Arguments:
1711
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
1712
+ /// The query engine to use to generate the `SELECT` statement.
1713
+ /// options?: object
1714
+ /// Options for the operation. These options are passed through all generation calls
1715
+ /// invoked inside this method, and so impact all methods used to generate the statement,
1716
+ /// including methods used for column escaping, literal conversion, etc...
1717
+ /// Though these options are often connection-specific,
1718
+ /// the following options are available across all connections:
1719
+ /// | Option | Type | Default Value | Description |
1720
+ /// | ------ | ---- | ------------- | ----------- |
1721
+ /// | `includeRelations` | `boolean` | `false` | If `true`, then a `.PROJECT('*')` will be applied for you, including all tables used in the operation in the output. |
1722
+ /// | `isSubQuery` | `boolean` | `false` | Though often not used directly by the user, if this option is `true`, then it will alter how the `SELECT` statement is generated... for example, the `ORDER BY` clause might be skipped entirely, or the field projection might be altered. |
1723
+ /// | `returnFieldProjection` | `boolean` | `false` | If `true`, then return an object with the shape `{ sql, projectionFields }`, where `sql` is the `SELECT` statement, and `projectionFields` are the fields that were projected. |
1724
+ ///
1725
+ /// Return: string
1726
+ /// A fully generated `SELECT` statement that can be used directly in the underlying database
1727
+ /// to query data.
974
1728
  generateSelectStatement(_queryEngine, _options) {
975
1729
  let queryEngine = _queryEngine;
976
1730
  if (!QueryEngine.isQuery(queryEngine))
@@ -989,8 +1743,8 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
989
1743
 
990
1744
  options.selectStatement = true;
991
1745
 
992
- projectionFields = this.generateSelectQueryFieldProjection(queryEngine, options, true);
993
- sqlParts.push(this.generateSelectQueryFieldProjection(queryEngine, options));
1746
+ projectionFields = this.getProjectedFields(queryEngine, options, true);
1747
+ sqlParts.push(this.generateSelectQueryFieldProjection(queryEngine, options, projectionFields));
994
1748
 
995
1749
  sqlParts.push(this.generateFromTableOrTableJoin(rootModel, undefined, options));
996
1750
  sqlParts.push(this.generateSelectQueryJoinTables(queryEngine, options));
@@ -1011,6 +1765,51 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1011
1765
  return sql;
1012
1766
  }
1013
1767
 
1768
+ /// Get the default value for a field.
1769
+ ///
1770
+ /// This method is used for `CREATE TABLE`, `ALTER TABLE`, `UPDATE`, and `INSERT`
1771
+ /// statements. It uses the `defaultValue` of the `field` provided
1772
+ /// to fetch any `DEFAULT` that might be applicable to the field
1773
+ /// at the database level for a `CREATE TABLE` or `ALTER TABLE` statement. It might
1774
+ /// also be used for things like `created_at` and `updated_at` fields during `UPDATE`
1775
+ /// or `INSERT` statements.
1776
+ ///
1777
+ /// Most default values for fields are applied client-side by Mythix ORM
1778
+ /// immediately before an `INSERT` or `UPDATE` statement is executed.
1779
+ /// However, some of them are set directly at the database level for field
1780
+ /// defaults, such as `AUTOINCREMENT`, and some date and times types (i.e. `NOW`).
1781
+ /// This method will return a string representing the default value that
1782
+ /// should be applied to a column (i.e. `DEFAULT NOW()`), or it will return
1783
+ /// a literal defining the default value that might change the entire statement.
1784
+ /// For example, `AUTOINCREMENT` in PostgreSQL is actually handled by converting
1785
+ /// the data type of the field to a `SERIAL` type.
1786
+ ///
1787
+ /// This method is also used to fetch the default value for columns during an
1788
+ /// `UPDATE` or `INSERT` statement. For example, for `created_at` and `updated_at`
1789
+ /// fields, the default might be set during `INSERT` or always set on `UPDATE` operations.
1790
+ ///
1791
+ /// Arguments:
1792
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
1793
+ /// The field to fetch the `defaultValue` from, if any.
1794
+ /// fieldName: string
1795
+ /// The name of the field the default value is being fetched for. This should always be
1796
+ /// the same as `field.fieldName`.
1797
+ /// options?: object
1798
+ /// Options for the operation.
1799
+ /// | Option | Type | Default Value | Description |
1800
+ /// | ------ | ---- | ------------- | ----------- |
1801
+ /// | `escape` | `boolean` | `true` | If `true`, then Mythix ORM will escape the default value found (i.e. with single quotes) so it is treated as a "value" in the underlying database. |
1802
+ /// | `isInsertOperation` | `boolean` | `false` | If `true`, then Mythix ORM is reporting that this is for an `INSERT` operation. |
1803
+ /// | `isUpdateOperation` | `boolean` | `false` | If `true`, then Mythix ORM is reporting that this is for an `UPDATE` operation. |
1804
+ /// | `rawLiterals` | `boolean` | `false` | If `true`, then Mythix ORM will return the literal raw without stringifying it--if the default value is a literal. |
1805
+ /// | `remoteOnly` | `boolean` | `false` | If `true`, then Mythix ORM will only return a default for the field if it is flagged as a "remote" or "literal" default value (i.e. an `AUTOINCREMENT` default would be flagged "remote"). |
1806
+ /// | `useDefaultKeyword` | `boolean` | `true` | If `true`, then Mythix ORM will prefix any default value found with a `DEFAULT` statement (for use in `CREATE TABLE` statements). |
1807
+ ///
1808
+ /// Return: undefined | string | Literal
1809
+ /// Return a string representing the escaped default value, a Literal if the `rawLiterals` option is `true`,
1810
+ /// or `undefined` if the field has no default value. A default value will not always be returned from this
1811
+ /// method simply because the provided `field` has a `defaultValue` property. Only default values applicable
1812
+ /// at the database level, or applicable to the operation being carried out will be returned.
1014
1813
  getFieldDefaultValue(field, fieldName, _options) {
1015
1814
  let options = _options || {};
1016
1815
  let defaultValue = field.defaultValue;
@@ -1061,6 +1860,38 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1061
1860
  return `${defaultValue}`;
1062
1861
  }
1063
1862
 
1863
+ /// Generate an index name for creating an index on
1864
+ /// one or more columns.
1865
+ ///
1866
+ /// This will take one or more field names from the `Model`
1867
+ /// provided and generate an index name for creating an index
1868
+ /// in the underlying database. This method can generate an index
1869
+ /// name for indexing a single column (if a single field name is provided),
1870
+ /// or it can generate an index name for indexing multiple columns
1871
+ /// as a "combo index".
1872
+ ///
1873
+ /// Arguments:
1874
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
1875
+ /// The model to use for the table to index on. This model should also be
1876
+ /// the model that owns all fields to be indexed. It isn't possible to index
1877
+ /// columns across different tables, so the fields provided must all be from
1878
+ /// this same `Model`.
1879
+ /// indexFieldNames: Array<string>
1880
+ /// The field names that will be used to create the single or combo index. A
1881
+ /// single field name will generate an index name for a single column, whereas
1882
+ /// more than one field name will generate an index name for a combo-index that
1883
+ /// indexes all columns requested. These can be fully qualified field names, but
1884
+ /// they don't have to be, since the owning `Model` is already known. If fully qualified
1885
+ /// field names are used, then the model name for each field must match the `Model`
1886
+ /// provided (making it pointless to use fully qualified field names).
1887
+ /// options?: object
1888
+ /// Options for the operation. These are not used by this method, and instead are
1889
+ /// just provided for the user--should they overload this method and need the options.
1890
+ ///
1891
+ /// Return: string
1892
+ /// Return an index name, in the format `'idx_tableName_column1_column2_column3_...'`. If
1893
+ /// the `indexFieldNames` provided is empty--or result in an empty set of field names after
1894
+ /// filtering out invalid field names--then an empty string will be returned instead.
1064
1895
  // eslint-disable-next-line no-unused-vars
1065
1896
  generateIndexName(Model, _indexFieldNames, options) {
1066
1897
  let indexFieldNames = Nife.toArray(_indexFieldNames).filter((index) => {
@@ -1082,6 +1913,40 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1082
1913
  return this.escapeID(`idx_${tableName}_${columnNames.sort().join('_')}`);
1083
1914
  }
1084
1915
 
1916
+ /// Generate a `CREATE INDEX` statement.
1917
+ ///
1918
+ /// This will generate a `CREATE INDEX` statement, indexing
1919
+ /// all fields (columns) provided. If a single field name is
1920
+ /// provided, then a `CREATE INDEX` statement for a single column
1921
+ /// will be generated. If more than one field name is provided, then
1922
+ /// a `CREATE INDEX` statement for a combo-index (indexing across more
1923
+ /// than one column at once) will be generated instead.
1924
+ ///
1925
+ /// Arguments:
1926
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
1927
+ /// The model to use for the table to index on. This model should also be
1928
+ /// the model that owns all fields to be indexed. It isn't possible to index
1929
+ /// columns across different tables, so the fields provided must all be from
1930
+ /// this same `Model`.
1931
+ /// indexFieldNames: Array<string>
1932
+ /// The field names that will be used to create the single or combo index. A
1933
+ /// single field name will generate a statement for a single column, whereas
1934
+ /// more than one field name will generate a statement for a combo-index that
1935
+ /// indexes all columns requested. These can be fully qualified field names, but
1936
+ /// they don't have to be, since the owning `Model` is already known. If fully qualified
1937
+ /// field names are used, then the model name for each field must match the `Model`
1938
+ /// provided (making it pointless to use fully qualified field names).
1939
+ /// options?: object
1940
+ /// Options for the operation.
1941
+ /// | Option | Type | Default Value | Description |
1942
+ /// | ------ | ---- | ------------- | ----------- |
1943
+ /// | `concurrently` | `boolean` | `false` | If `true`, then add a `CONCURRENTLY` clause to the `CREATE INDEX` statement (if the database supports it). |
1944
+ /// | `ifNotExists` | `boolean` | `false` | If `true`, then add an `IF NOT EXISTS` clause to the `CREATE INDEX` statement. |
1945
+ ///
1946
+ /// Return: string
1947
+ /// Return a fully formatted `CREATE INDEX` statement for the fields (columns)
1948
+ /// requested. An empty string will be returned if `indexFieldNames` is empty,
1949
+ /// or contains no valid field names.
1085
1950
  generateCreateIndexStatement(Model, _indexFieldNames, _options) {
1086
1951
  let indexFieldNames = Nife.toArray(_indexFieldNames).filter((fieldName) => {
1087
1952
  if (Nife.isEmpty(fieldName))
@@ -1117,6 +1982,43 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1117
1982
  return `CREATE INDEX${(flags) ? ` ${flags}` : ''} ${indexName} ON ${escapedTableName} (${escapedColumnNames.join(',')})`;
1118
1983
  }
1119
1984
 
1985
+ /// Generate a `DROP INDEX` statement.
1986
+ ///
1987
+ /// This will generate a `DROP INDEX` statement, using the provided
1988
+ /// `indexFieldNames` to generate the name of the index to be dropped.
1989
+ /// The provided `indexFieldNames` are passed off to <see>SQLQueryGeneratorBase.generateIndexName</see>
1990
+ /// to get the name of the index to drop.
1991
+ ///
1992
+ /// Arguments:
1993
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
1994
+ /// The model to use for the table to index on. This model should also be
1995
+ /// the model that owns all fields to be indexed. It isn't possible to index
1996
+ /// columns across different tables, so the fields provided must all be from
1997
+ /// this same `Model`.
1998
+ /// indexFieldNames: Array<string>
1999
+ /// The field names that will be used to create the single or combo index. A
2000
+ /// single field name will generate a statement for a single column, whereas
2001
+ /// more than one field name will generate a statement for a combo-index that
2002
+ /// indexes all columns requested. These can be fully qualified field names, but
2003
+ /// they don't have to be, since the owning `Model` is already known. If fully qualified
2004
+ /// field names are used, then the model name for each field must match the `Model`
2005
+ /// provided (making it pointless to use fully qualified field names).
2006
+ /// options?: object
2007
+ /// Options for the operation.
2008
+ /// | Option | Type | Default Value | Description |
2009
+ /// | ------ | ---- | ------------- | ----------- |
2010
+ /// | `concurrently` | `boolean` | `false` | If `true`, then add a `CONCURRENTLY` clause to the `DROP INDEX` statement (if the database supports it). |
2011
+ /// | `ifExists` | `boolean` | `false` | If `true`, then add an `IF EXISTS` clause to the `DROP INDEX` statement. |
2012
+ /// | `cascade` | `boolean` | `true` | If `true`, then add a `CASCADE` clause to the `DROP INDEX` statement (if the database supports it). |
2013
+ ///
2014
+ /// Return: string
2015
+ /// Return a fully formatted `DROP INDEX` statement for the fields (columns)
2016
+ /// requested. An empty string will be returned if `indexFieldNames` is empty,
2017
+ /// or contains no valid field names. <see>SQLQueryGeneratorBase.generateIndexName</see> is called
2018
+ /// with the provided `indexFieldNames` to generate the name of the index that should
2019
+ /// be dropped.
2020
+ ///
2021
+ /// See: SQLQueryGeneratorBase.generateIndexName
1120
2022
  generateDropIndexStatement(Model, _indexFieldNames, _options) {
1121
2023
  let indexFieldNames = Nife.toArray(_indexFieldNames).filter((fieldName) => {
1122
2024
  if (Nife.isEmpty(fieldName))
@@ -1150,6 +2052,31 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1150
2052
  return `DROP INDEX${(flags) ? ` ${flags}` : ''} ${indexName}${(postFlags) ? ` ${postFlags}` : ''}`;
1151
2053
  }
1152
2054
 
2055
+ /// Generate zero or more `CREATE INDEX` statements,
2056
+ /// using `field.index` to generate the statements.
2057
+ ///
2058
+ /// A [Field](https://github.com/th317erd/mythix-orm/wiki/Field) in Mythix ORM
2059
+ /// can have an `index` property (see [Field.index](https://github.com/th317erd/mythix-orm/wiki/Field#property-index))
2060
+ /// that defines the indexes to be created for the field. A `true` value is short for
2061
+ /// "index this field". Other field names in the `index` array mean
2062
+ /// "index this field combined with the fields specified, creating a combined index".
2063
+ ///
2064
+ /// This method will turn the `index` property on the provided `field` into one or more `CREATE INDEX`
2065
+ /// statements. If the `index` property on the `field` is falsy, or empty, then an empty array
2066
+ /// will be returned instead.
2067
+ ///
2068
+ /// Arguments:
2069
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2070
+ /// The model that owns the `field` provided.
2071
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
2072
+ /// The field to generate indexes for, using the `index` property of this field.
2073
+ /// options?: object
2074
+ /// Options for the operation. These options are passed off to <see>SQLQueryGeneratorBase.generateCreateIndexStatement</see>
2075
+ /// to generate each `CREATE INDEX` statement.
2076
+ ///
2077
+ /// Return: Array<string>
2078
+ /// Return an array of `CREATE INDEX` statements. If the `index` property on the provided
2079
+ /// `field` is falsy or empty, then an empty array will be returned instead.
1153
2080
  generateColumnIndexes(Model, field, _options) {
1154
2081
  let indexes = Nife.toArray(field.index).filter((index) => {
1155
2082
  if (index === true)
@@ -1171,6 +2098,20 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1171
2098
  });
1172
2099
  }
1173
2100
 
2101
+ /// Generate a `DROP TABLE` statement.
2102
+ ///
2103
+ /// Arguments:
2104
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2105
+ /// The model that defines the table to be dropped.
2106
+ /// options?: object
2107
+ /// Options for the operation.
2108
+ /// | Option | Type | Default Value | Description |
2109
+ /// | ------ | ---- | ------------- | ----------- |
2110
+ /// | `ifExists` | `boolean` | `false` | If `true`, then add an `IF EXISTS` clause to the `DROP TABLE` statement. |
2111
+ ///
2112
+ /// Return: string
2113
+ /// Return a fully formatted `DROP TABLE` statement, using the `Model` provided
2114
+ /// to define which table should be dropped.
1174
2115
  generateDropTableStatement(Model, _options) {
1175
2116
  let options = _options || {};
1176
2117
  let escapedTableName = this.getEscapedTableName(Model, options);
@@ -1184,6 +2125,30 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1184
2125
  return `DROP TABLE ${flags} ${escapedTableName}${(options.cascade !== false) ? ' CASCADE' : ''}`;
1185
2126
  }
1186
2127
 
2128
+ /// Generate foreign key constraints for a column
2129
+ /// in a `CREATE TABLE` statement.
2130
+ ///
2131
+ /// This method will generate database specific syntax
2132
+ /// for foreign key constraints to apply to a column
2133
+ /// in a `CREATE TABLE` statement.
2134
+ ///
2135
+ /// Arguments:
2136
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
2137
+ /// The field to generate foreign key constraints for. This field should
2138
+ /// have a [ForeignKeyType](https://github.com/th317erd/mythix-orm/wiki/ForeignKeyType)
2139
+ /// `type`.
2140
+ /// type: [ForeignKeyType](https://github.com/th317erd/mythix-orm/wiki/ForeignKeyType)
2141
+ /// The `type` of the `field` provided, which should always be a [ForeignKeyType](https://github.com/th317erd/mythix-orm/wiki/ForeignKeyType).
2142
+ /// options?: object
2143
+ /// Options for the operation. These options are simply passed through to any
2144
+ /// <see>SQLQueryGeneratorBase.getEscapedColumnName</see>, or <see>SQLQueryGeneratorBase.getEscapedTableName</see>
2145
+ /// calls that are made internally by this method.
2146
+ ///
2147
+ /// Return: string
2148
+ /// A database specific string for defining foreign key constraints for a column.
2149
+ /// Any `ON DELETE` or `ON UPDATE` clauses will be generated from the `onDelete`
2150
+ /// and `onUpdate` properties set on the [ForeignKeyType](https://github.com/th317erd/mythix-orm/wiki/ForeignKeyType)
2151
+ /// `type` for the field.
1187
2152
  generateForeignKeyConstraint(field, type, options) {
1188
2153
  let typeOptions = type.getOptions();
1189
2154
  let targetModel = type.getTargetModel(this.connection);
@@ -1217,6 +2182,39 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1217
2182
  return sqlParts.join('');
1218
2183
  }
1219
2184
 
2185
+ /// Generate an "inner tail" for a `CREATE TABLE`
2186
+ /// statement.
2187
+ ///
2188
+ /// An "inner tail" is the trailing part of the `CREATE TABLE`
2189
+ /// statement that is still inside the parenthesis of the statement,
2190
+ /// after any columns have been defined. For example:
2191
+ /// ```sql
2192
+ /// CREATE TABLE table_name (
2193
+ /// column1 datatype(length) column_contraint,
2194
+ /// column2 datatype(length) column_contraint,
2195
+ /// ... // <---- this is the "inner tail"
2196
+ /// );
2197
+ ///
2198
+ /// ... // <---- this is the "outer tail"
2199
+ /// ```
2200
+ ///
2201
+ /// This is often database specific, and by default will be
2202
+ /// used to generate any foreign key constraints used by columns
2203
+ /// in the table.
2204
+ ///
2205
+ /// Arguments:
2206
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2207
+ /// The model that defines the table being created.
2208
+ /// options?: object
2209
+ /// Any options for the `CREATE TABLE` operation being carried out.
2210
+ ///
2211
+ /// Return: Array<string>
2212
+ /// Return an "inner tail" for the `CREATE TABLE` statement, or an empty
2213
+ /// array if there should be no "inner tail". An array of SQL statements
2214
+ /// is returned by this method, which will be added to the `CREATE TABLE`
2215
+ /// statement (inside its parenthesis).
2216
+ ///
2217
+ /// See: SQLQueryGeneratorBase.generateForeignKeyConstraint
1220
2218
  // eslint-disable-next-line no-unused-vars
1221
2219
  generateCreateTableStatementInnerTail(Model, options) {
1222
2220
  let fieldParts = [];
@@ -1237,6 +2235,40 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1237
2235
  return fieldParts;
1238
2236
  }
1239
2237
 
2238
+ /// Generate an "outer tail" for a `CREATE TABLE`
2239
+ /// statement.
2240
+ ///
2241
+ /// An "outer tail" is the trailing part of the `CREATE TABLE`
2242
+ /// statement that is outside the parenthesis of the statement,
2243
+ /// after the column list. For example:
2244
+ /// ```sql
2245
+ /// CREATE TABLE table_name (
2246
+ /// column1 datatype(length) column_contraint,
2247
+ /// column2 datatype(length) column_contraint,
2248
+ /// ... // <---- this is the "inner tail"
2249
+ /// );
2250
+ ///
2251
+ /// ... // <---- this is the "outer tail"
2252
+ /// ```
2253
+ ///
2254
+ /// This is often database specific, and by default will be
2255
+ /// used to generate any `CREATE INDEX` statements for columns
2256
+ /// that have indexes.
2257
+ ///
2258
+ /// Arguments:
2259
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2260
+ /// The model that defines the table being created.
2261
+ /// options?: object
2262
+ /// Any options for the `CREATE TABLE` operation being carried out.
2263
+ ///
2264
+ /// Return: Array<string>
2265
+ /// Return an "outer tail" for the `CREATE TABLE` statement, or an empty
2266
+ /// array if there should be no "outer tail". An array of SQL statements
2267
+ /// is returned by this method, which will either be added to the `CREATE TABLE`
2268
+ /// statement, or executed separately after the `CREATE TABLE` statement is
2269
+ /// executed--depending on the underlying database.
2270
+ ///
2271
+ /// See: SQLQueryGeneratorBase.generateColumnIndexes
1240
2272
  // eslint-disable-next-line no-unused-vars
1241
2273
  generateCreateTableStatementOuterTail(Model, options) {
1242
2274
  let fieldParts = [];
@@ -1258,6 +2290,27 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1258
2290
  return Nife.uniq(fieldParts);
1259
2291
  }
1260
2292
 
2293
+ /// Generate a column definition for use inside a `CREATE TABLE`
2294
+ /// statement, or an `ALTER TABLE` statement. The generated
2295
+ /// column definition will include any `DEFAULT` defined, as well
2296
+ /// as any constraints that should be applied to the column, and
2297
+ /// will define at least the column's name and type.
2298
+ ///
2299
+ /// Arguments:
2300
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2301
+ /// The model that defines the table the column definition is for.
2302
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
2303
+ /// The field that defines the column whose definition is being generated.
2304
+ /// options?: object
2305
+ /// Options for the operation. These are simply passed off to other generate
2306
+ /// methods that are used internally, such as <see>SQLQueryGeneratorBase.getEscapedColumnName</see>,
2307
+ /// and [Type.toConnectionType](https://github.com/th317erd/mythix-orm/wiki/Type#method-toConnectionType)
2308
+ /// for stringifying the column's type. There may be connection-specific options that
2309
+ /// can be supplied as well.
2310
+ ///
2311
+ /// Return: string
2312
+ /// A fully formatted "column definition" statement, for use in
2313
+ /// a `CREATE TABLE` or `ALTER TABLE` statement. i.e. `"id" BIGINT PRIMARY KEY AUTOINCREMENT`.
1261
2314
  generateColumnDeclarationStatement(Model, field, _options) {
1262
2315
  let options = _options || {};
1263
2316
  let constraintParts = [];
@@ -1293,6 +2346,26 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1293
2346
  return `${this.getEscapedColumnName(Model, field, { ...options, columnNameOnly: true })} ${field.type.toConnectionType(this.connection, { ...options, createTable: true, defaultValue })}${constraintParts}`;
1294
2347
  }
1295
2348
 
2349
+ /// Generate a full `CREATE TABLE` statement using
2350
+ /// the provided `Model` and its fields.
2351
+ ///
2352
+ /// Arguments:
2353
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2354
+ /// The model that defines the table being created.
2355
+ /// options?: object
2356
+ /// Options for the operation. Though these might contain connection-specific
2357
+ /// options, the following options are common across all connections:
2358
+ /// | Option | Type | Default Value | Description |
2359
+ /// | ------ | ---- | ------------- | ----------- |
2360
+ /// | `ifNotExists` | `boolean` | `false` | If `true`, then add an `IF NOT EXISTS` clause to the `CREATE TABLE` statement. |
2361
+ ///
2362
+ /// Return: string
2363
+ /// Return a fully formatted `CREATE TABLE` statement, to create
2364
+ /// the table defined by the provided `Model`.
2365
+ ///
2366
+ /// See: SQLQueryGeneratorBase.generateColumnDeclarationStatement
2367
+ ///
2368
+ /// See: SQLQueryGeneratorBase.generateCreateTableStatementInnerTail
1296
2369
  generateCreateTableStatement(Model, _options) {
1297
2370
  let options = _options || {};
1298
2371
  let fieldParts = [];
@@ -1316,6 +2389,42 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1316
2389
  return finalStatement;
1317
2390
  }
1318
2391
 
2392
+ /// Generate a comma-separated list of values for
2393
+ /// use in an `INSERT` statement.
2394
+ ///
2395
+ /// Only "dirty" fields are inserted into the table, which might seem
2396
+ /// odd at first. However, Mythix ORM model instances have all (or most) their
2397
+ /// fields set to dirty when they are first instantiated, except fields
2398
+ /// such as auto-incrementing ids. This makes sense, because we would want
2399
+ /// to insert all columns for a given model instance, but not columns such
2400
+ /// as an auto-incrementing "id" that we would want the database to provide
2401
+ /// a value for. Any field on a model that should be inserted should already
2402
+ /// be marked "dirty", and any field that isn't dirty should instead be provided
2403
+ /// the "default value" that is already defined for the column.
2404
+ ///
2405
+ /// The `dirtyFields` option that can be provided is to provide the fields that
2406
+ /// are marked as dirty across **all** model instances being inserted (for bulk-inserts).
2407
+ /// If provide, this option will override the "dirty" fields reported by each model
2408
+ /// instance, since the values for insertion must be aligned across all rows.
2409
+ ///
2410
+ /// Arguments:
2411
+ /// model: [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2412
+ /// The model instance to pull field values from. Only "dirty" values
2413
+ /// will be pulled from the model and compiled into output.
2414
+ /// options?: object
2415
+ /// Options for the operation. Though these might contain connection-specific
2416
+ /// options, the following options are common across all connections:
2417
+ /// | Option | Type | Default Value | Description |
2418
+ /// | ------ | ---- | ------------- | ----------- |
2419
+ /// | `dirtyFields` | `Array<Field>` | `undefined` | If more than one row is being inserted, then define all columns being inserted for the operation. |
2420
+ ///
2421
+ /// Return: undefined | { modelChanges: object; rowValues: string; }
2422
+ /// Return `undefined` if no model instance is provided, or if the model
2423
+ /// instance is marked as "clean" (meaning no insert needs to happen).
2424
+ /// Otherwise, return an object with the shape: `{ modelChanges: object; rowValues: string; }`,
2425
+ /// where `modelChanges` is an object, where each key is a field name, and each value is the
2426
+ /// value that will be inserted into the database. `rowValues` is a string, that is a
2427
+ /// comma-separated list of all values to be inserted for this row.
1319
2428
  generateInsertFieldValuesFromModel(model, _options) {
1320
2429
  if (!model)
1321
2430
  return;
@@ -1360,6 +2469,29 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1360
2469
  return { modelChanges, rowValues: sqlParts.join(',') };
1361
2470
  }
1362
2471
 
2472
+ /// Generate multiple rows of comma-separated values for
2473
+ /// a bulk `INSERT` operation.
2474
+ ///
2475
+ /// Arguments:
2476
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2477
+ /// The model class of all `models` being inserted.
2478
+ /// models: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)> | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2479
+ /// An array of model instances, or a single model instance to generate insertion
2480
+ /// values for. Each model (if dirty) will have a single row created for it in the
2481
+ /// underlying database table defined by the provided `Model`.
2482
+ /// options?: object
2483
+ /// Options for the operation. The only option that is really useful here is the
2484
+ /// `newlines` option. If `false`, then newlines won't be used to separate the rows
2485
+ /// of values. By default, newlines will be used to separate each row of values.
2486
+ ///
2487
+ /// Return: undefined | { modelChanges: Array<object>; values: string; }
2488
+ /// Return `undefined` if the provided `models` is empty. Otherwise,
2489
+ /// return an object with the shape `{ modelChanges: Array<object>; values: string; }`,
2490
+ /// where `modelChanges` are the fields being inserted for each model (key = field name, value = field value),
2491
+ /// and where `values` is the list of row-values to insert into the table, i.e.
2492
+ /// `(value1,value2,value3),(value1,value2,value3),...`.
2493
+ ///
2494
+ /// See: SQLQueryGeneratorBase.generateInsertFieldValuesFromModel
1363
2495
  generateInsertValuesFromModels(Model, _models, _options) {
1364
2496
  let options = _options || {};
1365
2497
  let preparedModels = this.connection.prepareAllModelsForOperation(Model, _models, options);
@@ -1389,10 +2521,55 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1389
2521
  };
1390
2522
  }
1391
2523
 
2524
+ /// Generate a "tail" for an `INSERT` statement.
2525
+ ///
2526
+ /// This method is provided to allow the connection
2527
+ /// itself for any given database to "tack on extra" to
2528
+ /// an `INSERT` statement. This is commonly used by databases
2529
+ /// to add on a `RETURNING` clause to the `INSERT` statement.
2530
+ /// However, its primary purpose is just to allow the engine
2531
+ /// (or the user via overloading) to add on any "extra" to
2532
+ /// an `INSERT` statement.
2533
+ ///
2534
+ /// Arguments:
2535
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2536
+ /// The model class of all `models` being inserted.
2537
+ /// models: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)> | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2538
+ /// An array of model instances, or a single model instance. These are the models
2539
+ /// that are being inserted into the database.
2540
+ /// options: object
2541
+ /// Options for the operation.
2542
+ /// context: object
2543
+ /// Useful information about the insert operation taking place. This is an
2544
+ /// object with the shape: `{ escapedTableName: string; modelChanges: Array<object>; dirtyFields: Array<Field>; }`.
2545
+ ///
2546
+ /// Return: string
2547
+ /// Any "extra" SQL to add onto the end of the `INSERT` statement.
2548
+ /// Most connection drivers will usually use this for a `RETURNING` clause.
1392
2549
  // eslint-disable-next-line no-unused-vars
1393
- generateInsertStatementTail(Model, model, options, context) {
1394
- }
1395
-
2550
+ generateInsertStatementTail(Model, models, options, context) {
2551
+ }
2552
+
2553
+ /// Generate an `INSERT` statement, for inserting
2554
+ /// one or more model instances into the database.
2555
+ ///
2556
+ /// Note:
2557
+ /// "clean" models will be skipped, and won't result
2558
+ /// in any output.
2559
+ ///
2560
+ /// Arguments:
2561
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2562
+ /// The model class of all `models` being inserted.
2563
+ /// models: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)> | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2564
+ /// An array of model instances, or a single model instance. These are the models
2565
+ /// that are being inserted into the database.
2566
+ /// options: object
2567
+ /// Options for the operation.
2568
+ ///
2569
+ /// Return: string
2570
+ /// If all models are clean, or no model instances are provided,
2571
+ /// then an empty string will be returned. Otherwise, a fully
2572
+ /// formatted `INSERT` statement will be returned.
1396
2573
  generateInsertStatement(Model, _models, _options) {
1397
2574
  let options = _options || {};
1398
2575
  let preparedModels = this.connection.prepareAllModelsForOperation(Model, _models, options);
@@ -1431,10 +2608,92 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1431
2608
  return `INSERT INTO ${escapedTableName} (${escapedFieldNames}) VALUES ${values}`;
1432
2609
  }
1433
2610
 
2611
+ /// Generate a "tail" for an `UPDATE` statement.
2612
+ ///
2613
+ /// This method is provided to allow the connection
2614
+ /// itself for any given database to "tack on extra" to
2615
+ /// an `UPDATE` statement. This is commonly used by databases
2616
+ /// to add on a `RETURNING` clause to the `UPDATE` statement.
2617
+ /// However, its primary purpose is just to allow the engine
2618
+ /// (or the user via overloading) to add on any "extra" to
2619
+ /// an `UPDATE` statement.
2620
+ ///
2621
+ /// Arguments:
2622
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2623
+ /// The model class of all `models` being updated.
2624
+ /// model: object | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2625
+ /// The model instance being updated. Bulk-updates aren't really supported
2626
+ /// well by any SQL database, so Mythix ORM takes the long route and
2627
+ /// updates model instances one-by-one. If `queryEngine` is a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance,
2628
+ /// then this should be a raw object listing the attributes (field names and values)
2629
+ /// that should be applied across all matching rows.
2630
+ /// queryEngine: null | [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2631
+ /// The query engine used for the update statement--if any. Updates have
2632
+ /// two primary paths: 1) update a single model instance, or 2) update across
2633
+ /// multiple rows at once. For the latter, a query engine will be used to
2634
+ /// select which rows to update. In this case, the `queryEngine` argument
2635
+ /// here will be a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance.
2636
+ /// In the case that we are updating a single model instance, then this
2637
+ /// `queryEngine` will be `null`.
2638
+ /// options: object
2639
+ /// Options for the operation.
2640
+ /// context: object
2641
+ /// Useful information about the update operation taking place. This is an
2642
+ /// object with the shape:
2643
+ /// ```javascript
2644
+ /// {
2645
+ /// queryEngine: QueryEngine | null;
2646
+ /// escapedTableName: string;
2647
+ /// modelChanges: Array<object>;
2648
+ /// dirtyFields: Array<Field>;
2649
+ /// where: string | null;
2650
+ /// }
2651
+ /// ```
2652
+ ///
2653
+ /// Return: string
2654
+ /// Any "extra" SQL to add onto the end of the `UPDATE` statement.
2655
+ /// Most connection drivers will usually use this for a `RETURNING` clause.
1434
2656
  // eslint-disable-next-line no-unused-vars
1435
2657
  generateUpdateStatementTail(Model, model, queryEngine, options, context) {
1436
2658
  }
1437
2659
 
2660
+ /// Generate an `UPDATE` statement.
2661
+ ///
2662
+ /// Update statements in Mythix ORM generally take one of
2663
+ /// two forms: 1) Update a single model instance, or 2) Update
2664
+ /// one or more columns across multiple rows at once.
2665
+ ///
2666
+ /// When updating a single model instance, the provided `model`
2667
+ /// argument should the model instance to update, and is expected
2668
+ /// to have dirty fields. If on the other hand the `queryEngine`
2669
+ /// argument is provided, and is a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance,
2670
+ /// then the provided `model` argument should be a raw object
2671
+ /// of attributes (field names) to update across all matching rows.
2672
+ ///
2673
+ /// Arguments:
2674
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2675
+ /// The model class of the model being updated.
2676
+ /// model: object | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2677
+ /// The model instance being updated. Bulk-updates aren't really supported
2678
+ /// well by any SQL database, so Mythix ORM takes the long route and
2679
+ /// updates model instances one-by-one. If `queryEngine` is a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance,
2680
+ /// then this should be a raw object listing the attributes (field names and values)
2681
+ /// that should be applied across all matching rows.
2682
+ /// queryEngine: null | [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2683
+ /// The query engine used for the update statement--if any. Updates have
2684
+ /// two primary paths: 1) update a single model instance, or 2) update across
2685
+ /// multiple rows at once. For the latter, a query engine will be used to
2686
+ /// select which rows to update. In this case, the `queryEngine` argument
2687
+ /// here will be a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance.
2688
+ /// In the case that we are updating a single model instance, then this
2689
+ /// `queryEngine` will be `null`.
2690
+ /// options: object
2691
+ /// Options for the operation.
2692
+ ///
2693
+ /// Return: string
2694
+ /// Return a fully formatted `UPDATE` statement, either for
2695
+ /// updating a single model instance, or for updating multiple
2696
+ /// rows at once using the provided attributes.
1438
2697
  generateUpdateStatement(Model, _model, _queryEngine, _options) {
1439
2698
  if (!_model)
1440
2699
  return '';
@@ -1519,6 +2778,30 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1519
2778
  return sqlParts.join('');
1520
2779
  }
1521
2780
 
2781
+ /// Generate a `RETURNING` clause for a `DELETE` statement.
2782
+ ///
2783
+ /// Arguments:
2784
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2785
+ /// The model class defining the table that is being deleted from.
2786
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2787
+ /// The query engine being used to specify which rows to delete.
2788
+ /// pkField: undefined | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
2789
+ /// The primary key field of the provided `Model`, if it has one. If the model has
2790
+ /// no primary key field, then this will be `undefined`.
2791
+ /// escapedColumnName: string
2792
+ /// The full column name (usually including the table name) of the column to use for the `RETURNING`
2793
+ /// clause. If the provided `Model` has a primary key field, then this should be that column name
2794
+ /// (though the name might be an alias of that column name, depending on how the `DELETE` statement
2795
+ /// is generated). If the provided `Model` has no primary key field, then this will be `*`.
2796
+ /// This column name might differ from the field's column name, because the `DELETE` statement
2797
+ /// might be constructed such that an alias name is needed for the column name.
2798
+ /// options?: object
2799
+ /// Options for the operation.
2800
+ ///
2801
+ /// Return: string
2802
+ /// Return a `RETURNING` clause to apply to the end of a `DELETE` statement. If
2803
+ /// `escapedColumnName` is empty, then an empty string will be returned.
2804
+ // eslint-disable-next-line no-unused-vars
1522
2805
  generateDeleteStatementReturningClause(Model, queryEngine, pkField, escapedColumnName, options) {
1523
2806
  if (!escapedColumnName)
1524
2807
  return '';
@@ -1526,8 +2809,43 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1526
2809
  return `RETURNING ${escapedColumnName}`;
1527
2810
  }
1528
2811
 
1529
- generateDeleteStatement(Model, _queryEngine, _options) {
1530
- let queryEngine = _queryEngine;
2812
+ /// Generate a `DELETE` statement to delete rows from
2813
+ /// the table defined by `Model`, either by using a
2814
+ /// query provided by the user, or by generating a query
2815
+ /// based on the provided model instances. If no query
2816
+ /// or model instances are provided, then generate a
2817
+ /// `DELETE` statement that will delete every row from
2818
+ /// the table, truncating the table.
2819
+ ///
2820
+ /// Arguments:
2821
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2822
+ /// The model class defining the table that is being deleted from.
2823
+ /// modelsOrQueryEngine?: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)> | [Model](https://github.com/th317erd/mythix-orm/wiki/Model) | [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2824
+ /// Model instance(s) to delete, or a query engine specifying which rows to delete.
2825
+ /// If model instances are provided, then their primary key field will be used to
2826
+ /// create a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2827
+ /// used to delete those specific primary keys from the database. If the `Model` provided
2828
+ /// has no primary key field, then an exception will be thrown, as deleting model instances
2829
+ /// this way requires the model have a primary key field. If your model has no primary key
2830
+ /// field, then it is required that the user generate their own query to select which rows
2831
+ /// to delete from the underlying table. Instead of provided model instances, the user
2832
+ /// can provide a raw [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine) instance
2833
+ /// that will be used to select which rows to delete instead. If neither is provided, then
2834
+ /// the entire table will be truncated.
2835
+ /// options?: object
2836
+ /// Options for the operation. These are simply passed through to any sub-calls
2837
+ /// this method makes internally.
2838
+ ///
2839
+ /// Return: string
2840
+ /// Return a fully formatted `DELETE` statement to delete rows from the
2841
+ /// table defined by the provided `Model`. If no model instances or [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2842
+ /// are provided, then a simple `DELETE FROM "table_name";` statement will be
2843
+ /// generated, truncating the entire table. If model instances or a [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
2844
+ /// are provided, then a `DELETE` statement in the form of
2845
+ /// `DELETE FROM "table_name" WHERE EXISTS(SELECT ...)` will be returned,
2846
+ /// selecting which rows to delete with the provided query.
2847
+ generateDeleteStatement(Model, _modelsOrQueryEngine, _options) {
2848
+ let queryEngine = _modelsOrQueryEngine;
1531
2849
  let options = _options;
1532
2850
 
1533
2851
  if (queryEngine) {
@@ -1589,7 +2907,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1589
2907
  }
1590
2908
  }
1591
2909
 
1592
- _collectRemoteReturningFields(Model) {
2910
+ /// Iterate all fields of the provided `Model`, and
2911
+ /// collect and return all fields that have a `defaultValue`
2912
+ /// with the `remote` flag set. The `remote` flag on
2913
+ /// the `defaultValue` of a field tells Mythix ORM that the
2914
+ /// default value is provided by the database itself. This
2915
+ /// would be the case for example for `AUTOINCREMENT` ids,
2916
+ /// and for `NOW()` date columns--among others.
2917
+ ///
2918
+ /// Any field that is marked with a `remote` default (a value
2919
+ /// provided by the database itself) should always be part of any
2920
+ /// `RETURNING` clause in-play, so this method is used to ensure
2921
+ /// all `remote` fields are part of the `RETURNING` clause.
2922
+ /// See [Helpers](https://github.com/th317erd/mythix-orm/wiki/Helpers) in
2923
+ /// the Mythix ORM documentation for a better explanation of "remote" fields
2924
+ /// and flags.
2925
+ ///
2926
+ /// Arguments:
2927
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2928
+ /// The model whose "remote" fields should be collected.
2929
+ /// options?: object
2930
+ /// Options for the operation.
2931
+ ///
2932
+ /// Return: Array<string>
2933
+ /// Return an array of escaped column names for direct use on
2934
+ /// a `RETURNING` clause. If the provided `Model` has no "remote"
2935
+ /// fields, then an empty array will be returned.
2936
+ // eslint-disable-next-line no-unused-vars
2937
+ _collectRemoteReturningFields(Model, options) {
1593
2938
  let remoteFieldNames = [];
1594
2939
 
1595
2940
  Model.iterateFields(({ field }) => {
@@ -1608,7 +2953,40 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1608
2953
  return remoteFieldNames;
1609
2954
  }
1610
2955
 
1611
- _collectReturningFields(Model, model, options, context) {
2956
+ /// Generate a `RETURNING` clause for `UPDATE`
2957
+ /// and `INSERT` statements.
2958
+ ///
2959
+ /// This will generate a `RETURNING` clause that
2960
+ /// will always includes all "remote" fields for the `Model`
2961
+ /// provided, will always include the primary key of the
2962
+ /// model (if the model has one), and will include all
2963
+ /// fields marked as "dirty" across all models being inserted
2964
+ /// or updated.
2965
+ ///
2966
+ /// Arguments:
2967
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2968
+ /// The model that defines the table for the insert or update operation.
2969
+ /// models: Array<[Model](https://github.com/th317erd/mythix-orm/wiki/Model)> | [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
2970
+ /// An array of model instances, or a single model instance. These are the models
2971
+ /// that are being inserted or updated.
2972
+ /// options: object
2973
+ /// Options for the operation.
2974
+ /// context: object
2975
+ /// The same `context` that is provided to <see>SQLQueryGeneratorBase.generateInsertStatementTail</see> or
2976
+ /// <see>SQLQueryGeneratorBase.generateUpdateStatementTail</see>, depending on if this is an
2977
+ /// insert or update operation.
2978
+ ///
2979
+ /// Return: string
2980
+ /// Return a `RETURNING` clause to apply to the end of an `UPDATE`
2981
+ /// or `INSERT` statement. If no fields are found for this clause,
2982
+ /// then an empty string will be returned.
2983
+ ///
2984
+ /// See: SQLQueryGeneratorBase.generateInsertStatementTail
2985
+ ///
2986
+ /// See: SQLQueryGeneratorBase.generateUpdateStatementTail
2987
+ ///
2988
+ /// See: SQLQueryGeneratorBase._collectRemoteReturningFields
2989
+ generateReturningClause(Model, models, options, context) {
1612
2990
  let {
1613
2991
  modelChanges,
1614
2992
  dirtyFields,
@@ -1632,7 +3010,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1632
3010
  if (!fieldValue.options.remote)
1633
3011
  continue;
1634
3012
 
1635
- let escapedColumnName = this.getEscapedColumnName(dirtyField.Model, dirtyField);
3013
+ let escapedColumnName = this.getEscapedColumnName(dirtyField.Model, dirtyField, options);
1636
3014
  returnFieldsMap[escapedColumnName] = true;
1637
3015
 
1638
3016
  break;
@@ -1652,17 +3030,41 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1652
3030
 
1653
3031
  let returnFields = Object.keys(returnFieldsMap);
1654
3032
  if (!returnFields.length)
1655
- return;
3033
+ return '';
1656
3034
 
1657
3035
  return `RETURNING ${returnFields.join(',')}`;
1658
3036
  }
1659
3037
 
3038
+ /// Generate a `TRUNCATE TABLE` statement.
3039
+ ///
3040
+ /// Arguments:
3041
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
3042
+ /// The model that defines the table being truncated.
3043
+ /// options?: object
3044
+ /// Options for the operation.
3045
+ ///
3046
+ /// Return: string
3047
+ /// Return a fully formatted `TRUNCATE TABLE` statement.
1660
3048
  // eslint-disable-next-line no-unused-vars
1661
3049
  generateTruncateTableStatement(Model, _options) {
1662
3050
  let escapedTableName = this.escapeID(Model.getTableName(this.connection));
1663
3051
  return `TRUNCATE TABLE ${escapedTableName}`;
1664
3052
  }
1665
3053
 
3054
+ /// Generate an `ALTER TABLE` statement
3055
+ /// to rename the table.
3056
+ ///
3057
+ /// Arguments:
3058
+ /// Model: class [Model](https://github.com/th317erd/mythix-orm/wiki/Model)
3059
+ /// The model that defines the table being altered.
3060
+ /// newModelAttributes: object
3061
+ /// An object that contains a `tableName` key for the new table name
3062
+ /// options?: object
3063
+ /// Options for the operation.
3064
+ ///
3065
+ /// Return: Array<string>
3066
+ /// An array of `ALTER TABLE` statements to change the table's name. For
3067
+ /// most databases this will probably only be a single statement.
1666
3068
  generateAlterTableStatement(Model, newModelAttributes, options) {
1667
3069
  if (Nife.isEmpty(newModelAttributes))
1668
3070
  return [];
@@ -1678,6 +3080,23 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1678
3080
  return statements;
1679
3081
  }
1680
3082
 
3083
+ /// Generate an `ALTER TABLE ... DROP COLUMN` statement.
3084
+ ///
3085
+ /// Arguments:
3086
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3087
+ /// The field to drop from the database.
3088
+ /// options?:
3089
+ /// Options for the operation. Though these might contain
3090
+ /// database specific options, generic options that should
3091
+ /// work for most databases are:
3092
+ /// | Option | Type | Default Value | Description |
3093
+ /// | ------ | ---- | ------------- | ----------- |
3094
+ /// | `ifExists` | `boolean` | `false` | If `true`, then add an `IF EXISTS` clause to the `ALTER TABLE` statement. |
3095
+ /// | `cascade` | `boolean` | `true` | If `true`, then add a `CASCADE` clause to the `ALTER TABLE` statement. |
3096
+ ///
3097
+ /// Return: string
3098
+ /// An `ALTER TABLE` statement to drop the column specified
3099
+ /// by the provided `field`.
1681
3100
  generateDropColumnStatement(field, _options) {
1682
3101
  let Model = field.Model;
1683
3102
  let options = _options || {};
@@ -1685,15 +3104,46 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1685
3104
  return `ALTER TABLE ${this.getEscapedTableName(Model, options)} DROP COLUMN${(options.ifExists) ? ' IF EXISTS' : ''} ${this.getEscapedColumnName(Model, field, { ...options, columnNameOnly: true })} ${(options.cascade !== false) ? 'CASCADE' : 'RESTRICT'}`;
1686
3105
  }
1687
3106
 
3107
+ /// Generate an `ALTER TABLE ... RENAME COLUMN` statement.
3108
+ ///
3109
+ /// Arguments:
3110
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3111
+ /// The field to rename in the database.
3112
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3113
+ /// A raw object containing a `columnName` or `fieldName` properties, or a `Field` instance
3114
+ /// containing a `columnName` or `fieldName`. This will be used as the new name
3115
+ /// of the column.
3116
+ /// options?:
3117
+ /// Options for the operation.
3118
+ ///
3119
+ /// Return: string
3120
+ /// An `ALTER TABLE` statement to rename the column specified
3121
+ /// by the provided `field`.
1688
3122
  generateAlterColumnRenameStatement(field, newField, _options) {
1689
3123
  let Model = field.Model;
1690
3124
  let options = _options || {};
1691
3125
  let prefix = `ALTER TABLE ${this.getEscapedTableName(Model, options)}`;
1692
3126
  let escapedColumnName = this.getEscapedColumnName(Model, field, { ...options, columnNameOnly: true });
1693
3127
 
1694
- return `${prefix} RENAME COLUMN ${escapedColumnName} TO ${this.escapeID(newField.columnName)}`;
1695
- }
1696
-
3128
+ return `${prefix} RENAME COLUMN ${escapedColumnName} TO ${this.escapeID(newField.columnName || newField.fieldName)}`;
3129
+ }
3130
+
3131
+ /// Generate an `ALTER TABLE` statement to add or remove
3132
+ /// a `NOT NULL` constraint on the specified `field`.
3133
+ ///
3134
+ /// Arguments:
3135
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3136
+ /// The field to add or remove the constraint from in the database.
3137
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3138
+ /// A raw object containing an `allowNull` property, or a `Field` instance
3139
+ /// containing a `allowNull` property. If `true`, then a `NOT NULL` constraint
3140
+ /// will be added. If `false`, then any `NOT NULL` constraint will be dropped.
3141
+ /// options?:
3142
+ /// Options for the operation.
3143
+ ///
3144
+ /// Return: string
3145
+ /// An `ALTER TABLE` statement to add or remove the `NOT NULL` constraint
3146
+ /// of the specified `field`.
1697
3147
  generateAlterColumnSetOrDropNullConstraintStatement(field, newField, _options) {
1698
3148
  let Model = field.Model;
1699
3149
  let options = _options || {};
@@ -1703,6 +3153,26 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1703
3153
  return `${prefix} ALTER COLUMN ${escapedColumnName} ${(newField.allowNull) ? 'DROP' : 'SET'} NOT NULL`;
1704
3154
  }
1705
3155
 
3156
+ /// Generate an `ALTER TABLE` statement to add or remove
3157
+ /// a `DEFAULT` value for the specified `field`.
3158
+ ///
3159
+ /// Arguments:
3160
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3161
+ /// The field to add or remove the `DEFAULT` value from in the database.
3162
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3163
+ /// A raw object or a `Field` instance defining how the field is being altered.
3164
+ /// This argument is not used by this method, but is passed through when calling
3165
+ /// other alter table generators.
3166
+ /// newDefaultValue: any
3167
+ /// The new `DEFAULT` value to apply to the column. If this argument is `undefined`,
3168
+ /// then any `DEFAULT` value applied to the column will be dropped. This value must
3169
+ /// already be escaped and ready for the underlying database to consume.
3170
+ /// options?:
3171
+ /// Options for the operation.
3172
+ ///
3173
+ /// Return: string
3174
+ /// An `ALTER TABLE ... ALTER COLUMN ... DROP | SET DEFAULT` statement to add or set
3175
+ /// a `DEFAULT` value for this column.
1706
3176
  generateAlterColumnSetDefaultStatement(field, newField, newDefaultValue, _options) {
1707
3177
  let Model = field.Model;
1708
3178
  let options = _options || {};
@@ -1715,6 +3185,25 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1715
3185
  return `${prefix} ALTER COLUMN ${escapedColumnName} SET DEFAULT ${newDefaultValue}`;
1716
3186
  }
1717
3187
 
3188
+ /// Generate an `ALTER TABLE` statement to change the
3189
+ /// type of the `field` specified.
3190
+ ///
3191
+ /// Arguments:
3192
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3193
+ /// The field to change the type on in the database.
3194
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3195
+ /// A raw object or a `Field` instance defining how the field is being altered.
3196
+ /// This argument is not used by this method, but is passed through when calling
3197
+ /// other alter table generators.
3198
+ /// newFieldType: string
3199
+ /// The new type to change the field/column to. This must be a raw type that
3200
+ /// the underlying database supports, in database format.
3201
+ /// options?:
3202
+ /// Options for the operation.
3203
+ ///
3204
+ /// Return: string
3205
+ /// An `ALTER TABLE ... ALTER COLUMN ... SET DATA TYPE` statement to alter
3206
+ /// the column's data type.
1718
3207
  generateAlterColumnChangeTypeStatement(field, newField, newFieldType, _options) {
1719
3208
  let Model = field.Model;
1720
3209
  let options = _options || {};
@@ -1724,6 +3213,22 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1724
3213
  return `${prefix} ALTER COLUMN ${escapedColumnName} SET DATA TYPE ${newFieldType}`;
1725
3214
  }
1726
3215
 
3216
+ /// Generate an `ALTER TABLE` statement to add or remove
3217
+ /// a `PRIMARY KEY` constraint on the specified `field`.
3218
+ ///
3219
+ /// Arguments:
3220
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3221
+ /// The field to add or remove the constraint from in the database.
3222
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3223
+ /// A raw object containing an `primaryKey` property, or a `Field` instance
3224
+ /// containing a `primaryKey` property. If `true`, then a `PRIMARY KEY` constraint
3225
+ /// will be added. If `false`, then any `PRIMARY KEY` constraint will be dropped.
3226
+ /// options?:
3227
+ /// Options for the operation.
3228
+ ///
3229
+ /// Return: string
3230
+ /// An `ALTER TABLE` statement to add or remove the `PRIMARY KEY` constraint
3231
+ /// of the specified `field`.
1727
3232
  generateAlterColumnChangePrimaryKeyConstraintStatement(field, newField, _options) {
1728
3233
  let Model = field.Model;
1729
3234
  let options = _options || {};
@@ -1736,6 +3241,22 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1736
3241
  return `${prefix} ALTER COLUMN ${escapedColumnName} DROP CONSTRAINT PRIMARY KEY`;
1737
3242
  }
1738
3243
 
3244
+ /// Generate an `ALTER TABLE` statement to add or remove
3245
+ /// a `UNIQUE` constraint on the specified `field`.
3246
+ ///
3247
+ /// Arguments:
3248
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3249
+ /// The field to add or remove the constraint from in the database.
3250
+ /// newField: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3251
+ /// A raw object containing an `unique` property, or a `Field` instance
3252
+ /// containing a `unique` property. If `true`, then a `UNIQUE` constraint
3253
+ /// will be added. If `false`, then any `UNIQUE` constraint will be dropped.
3254
+ /// options?:
3255
+ /// Options for the operation.
3256
+ ///
3257
+ /// Return: string
3258
+ /// An `ALTER TABLE` statement to add or remove a `UNIQUE` constraint
3259
+ /// of the specified `field`.
1739
3260
  generateAlterColumnChangeUniqueConstraintStatement(field, newField, _options) {
1740
3261
  let Model = field.Model;
1741
3262
  let options = _options || {};
@@ -1748,6 +3269,28 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1748
3269
  return `${prefix} ALTER COLUMN ${escapedColumnName} DROP CONSTRAINT UNIQUE`;
1749
3270
  }
1750
3271
 
3272
+ /// Generate multiple `ALTER TABLE` statements to change the
3273
+ /// `field` provided to match the `newFieldAttributes` provided.
3274
+ ///
3275
+ /// Arguments:
3276
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3277
+ /// The field to alter in the database.
3278
+ /// newFieldAttributes: object | [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3279
+ /// A raw object containing field properties, or a `Field` instance. Any differing
3280
+ /// properties between these two "fields" will generate an `ALTER TABLE` statement
3281
+ /// for that difference. For example, if the `columnName` between both "fields" is
3282
+ /// different, then an `ALTER TABLE` statement will be generated to change the column's
3283
+ /// name. Properties that are checked for differences are: `primaryKey`, `unique`, `index`,
3284
+ /// `columnName` & `fieldName`, `allowNull`, `type`, and `defaultValue`. If any of these
3285
+ /// differ between the two provided "fields", then an `ALTER TABLE` statement will be generated
3286
+ /// to update the column to match the new properties of `newFieldAttributes`.
3287
+ /// options?:
3288
+ /// Options for the operation.
3289
+ ///
3290
+ /// Return: Array<string>
3291
+ /// Multiple `ALTER TABLE` statements to alter the specified `field`
3292
+ /// to match the new properties defined by `newFieldAttributes`. If
3293
+ /// no changes are detected, then an empty array will be returned instead.
1751
3294
  generateAlterColumnStatements(field, _newFieldAttributes, _options) {
1752
3295
  if (Nife.isEmpty(_newFieldAttributes))
1753
3296
  return [];
@@ -1843,6 +3386,22 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1843
3386
  return statements.filter(Boolean);
1844
3387
  }
1845
3388
 
3389
+ /// Generate an `ALTER TABLE ... ADD COLUMN` statement.
3390
+ ///
3391
+ /// Arguments:
3392
+ /// field: [Field](https://github.com/th317erd/mythix-orm/wiki/Field)
3393
+ /// The field to add to the database.
3394
+ /// options?:
3395
+ /// Options for the operation. Though these might contain
3396
+ /// database specific options, generic options that should
3397
+ /// work for most databases are:
3398
+ /// | Option | Type | Default Value | Description |
3399
+ /// | ------ | ---- | ------------- | ----------- |
3400
+ /// | `ifNotExists` | `boolean` | `false` | If `true`, then add an `IF NOT EXISTS` clause to the `ALTER TABLE` statement. |
3401
+ ///
3402
+ /// Return: string
3403
+ /// An `ALTER TABLE ... ADD COLUMN` statement to add the column specified
3404
+ /// by the provided `field`.
1846
3405
  generateAddColumnStatement(field, _options) {
1847
3406
  let Model = field.Model;
1848
3407
  let options = _options || {};
@@ -1851,6 +3410,22 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1851
3410
  return `${prefix} ADD COLUMN${(options.ifNotExists) ? ' IF NOT EXISTS' : ''} ${this.generateColumnDeclarationStatement(Model, field, options)}`;
1852
3411
  }
1853
3412
 
3413
+ /// Convert the provided `queryEngine` into
3414
+ /// a `SELECT` statement.
3415
+ ///
3416
+ /// This is similar to a `toSQL` method in other ORMs.
3417
+ /// It is usually called directly from the `queryEngine` itself,
3418
+ /// i.e. `queryEngine.toString()` will call this method.
3419
+ ///
3420
+ /// Arguments:
3421
+ /// queryEngine: [QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)
3422
+ /// The query engine to use to generate a `SELECT` statement.
3423
+ /// options?: object
3424
+ /// Options for the operation. These are simply passed off to <see>SQLQueryGeneratorBase.generateSelectStatement</see>.
3425
+ ///
3426
+ /// Return: string
3427
+ /// A fully formatted `SELECT` statement, that was generated from
3428
+ /// the provided `queryEngine`.
1854
3429
  toConnectionString(queryEngine, options) {
1855
3430
  return this.generateSelectStatement(queryEngine, options);
1856
3431
  }