foxhound 2.0.26 → 2.0.28

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.
@@ -0,0 +1,1218 @@
1
+ /**
2
+ * FoxHound Oracle Dialect
3
+ *
4
+ * @license MIT
5
+ *
6
+ * For an Oracle query override:
7
+ // An underscore template with the following values:
8
+ // <%= DataElements %> = Field1, Field2, Field3, Field4
9
+ // <%= Begin %> = 0
10
+ // <%= Cap %> = 10
11
+ // <%= Filter %> = WHERE StartDate > :MyStartDate
12
+ // <%= Sort %> = ORDER BY Field1
13
+ // The values are empty strings if they aren't set.
14
+ *
15
+ * Oracle notes:
16
+ * - Identifiers are emitted unquoted by default (Oracle folds them to
17
+ * UPPERCASE). Set pParameters.quoteIdentifiers to wrap them in double
18
+ * quotes and preserve the original PascalCase (case-sensitive matching).
19
+ * - Bind parameters use the :name style consumed natively by oracledb.
20
+ * - Pagination uses the 12c+ OFFSET/FETCH clause by default and a
21
+ * ROWNUM double-subquery wrapper when pParameters.legacyPagination is set
22
+ * (for 11g and earlier, which have no OFFSET/FETCH).
23
+ * - INSERT appends a RETURNING <IDColumn> INTO :RETURNING_ID clause when the
24
+ * table has an AutoIdentity column (Oracle has no SCOPE_IDENTITY()).
25
+ * - Generated statements carry no trailing semicolon — oracledb rejects the
26
+ * terminator on non-PL/SQL statements.
27
+ *
28
+ * @author Steven Velozo <steven@velozo.com>
29
+ * @class FoxHoundDialectOracle
30
+ */
31
+
32
+ var FoxHoundDialectOracle = function(pFable)
33
+ {
34
+ // True UTC timestamp regardless of the database server's session time zone.
35
+ const SQL_NOW = "SYS_EXTRACT_UTC(SYSTIMESTAMP)";
36
+
37
+ let _Fable = pFable;
38
+
39
+ // Whether to wrap identifiers in double quotes (preserving case) or emit
40
+ // them bare (folded to UPPERCASE by Oracle). Set from
41
+ // pParameters.quoteIdentifiers at the top of each public dialect method;
42
+ // query building is synchronous so this closure value is safe to reuse.
43
+ let _QuoteIdentifiers = false;
44
+
45
+ /**
46
+ * Generate a table name from the scope
47
+ *
48
+ * @method: generateTableName
49
+ * @param: {Object} pParameters SQL Query Parameters
50
+ * @return: {String} Returns the table name clause
51
+ */
52
+ var generateTableName = function(pParameters)
53
+ {
54
+ // Every Foxhound query has a table name; this lazily creates the
55
+ // parameterTypes map even for column-less queries (e.g. COUNT(*)).
56
+ if (!pParameters.query.hasOwnProperty('parameterTypes'))
57
+ {
58
+ pParameters.query.parameterTypes = {};
59
+ }
60
+
61
+ if (_QuoteIdentifiers)
62
+ {
63
+ if (pParameters.scope && pParameters.scope.indexOf('"') >= 0)
64
+ {
65
+ return ' '+pParameters.scope;
66
+ }
67
+ return ' "'+pParameters.scope+'"';
68
+ }
69
+
70
+ return ' '+pParameters.scope;
71
+ };
72
+
73
+ /**
74
+ * Record the oracledb bind type for a parameter, keyed by parameter name.
75
+ *
76
+ * These are driver-free strings ('NUMBER', 'STRING', 'CLOB', 'DATE') so the
77
+ * dialect stays browser-safe; the Meadow Oracle provider translates them to
78
+ * oracledb type descriptors at execution time (it is the only layer that may
79
+ * require the driver).
80
+ *
81
+ * @method: generateOracleParameterTypeEntry
82
+ * @param: {Object} pParameters SQL Query Parameters
83
+ * @param: {String} pColumnParameterName The bind parameter name
84
+ * @param: {Object|String} pColumn A schema column object, or a column name to look up
85
+ * @return: {Boolean} True if a known type was mapped
86
+ */
87
+ var generateOracleParameterTypeEntry = function(pParameters, pColumnParameterName, pColumn)
88
+ {
89
+ if (!pParameters.query.hasOwnProperty('parameterTypes'))
90
+ {
91
+ pParameters.query.parameterTypes = {};
92
+ }
93
+
94
+ let tmpColumnParameterTypeString = 'String';
95
+ if (typeof(pColumn) == 'object')
96
+ {
97
+ tmpColumnParameterTypeString = pColumn.Type;
98
+ }
99
+ else if (typeof(pColumn) == 'string')
100
+ {
101
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
102
+ for (let i = 0; i < tmpSchema.length; i++)
103
+ {
104
+ if (tmpSchema[i].Column == pColumn)
105
+ {
106
+ tmpColumnParameterTypeString = tmpSchema[i].Type;
107
+ break;
108
+ }
109
+ }
110
+ }
111
+ else
112
+ {
113
+ _Fable.log.warn(`Meadow Oracle query attempted to add a parameter type but no valid column schema entry object or column name was passed; parameter name ${pColumnParameterName}.`);
114
+ }
115
+
116
+ if ((tmpColumnParameterTypeString == null) || (tmpColumnParameterTypeString == undefined))
117
+ {
118
+ return false;
119
+ }
120
+
121
+ switch (tmpColumnParameterTypeString)
122
+ {
123
+ case 'AutoIdentity':
124
+ case 'CreateIDUser':
125
+ case 'UpdateIDUser':
126
+ case 'DeleteIDUser':
127
+ case 'ForeignKey':
128
+ case 'Numeric':
129
+ case 'Integer':
130
+ case 'Deleted':
131
+ case 'Boolean':
132
+ case 'Decimal':
133
+ pParameters.query.parameterTypes[pColumnParameterName] = 'NUMBER';
134
+ break;
135
+
136
+ case 'String':
137
+ case 'AutoGUID':
138
+ pParameters.query.parameterTypes[pColumnParameterName] = 'STRING';
139
+ break;
140
+
141
+ case 'Text':
142
+ case 'JSON':
143
+ case 'JSONProxy':
144
+ pParameters.query.parameterTypes[pColumnParameterName] = 'CLOB';
145
+ break;
146
+
147
+ case 'CreateDate':
148
+ case 'UpdateDate':
149
+ case 'DeleteDate':
150
+ case 'DateTime':
151
+ pParameters.query.parameterTypes[pColumnParameterName] = 'DATE';
152
+ break;
153
+
154
+ default:
155
+ pParameters.query.parameterTypes[pColumnParameterName] = 'STRING';
156
+ return false;
157
+ }
158
+
159
+ return true;
160
+ };
161
+
162
+ /**
163
+ * Generate a field list from the array of dataElements
164
+ *
165
+ * Each entry in the dataElements is a simple string
166
+ *
167
+ * @method: generateFieldList
168
+ * @param: {Object} pParameters SQL Query Parameters
169
+ * @param {Boolean} pIsForCountClause (optional) If true, generate fields for use within a count clause.
170
+ * @return: {String} Returns the field list clause, or empty string if explicit fields are requested but cannot be fulfilled
171
+ * due to missing schema.
172
+ */
173
+ var generateFieldList = function(pParameters, pIsForCountClause)
174
+ {
175
+ var tmpDataElements = pParameters.dataElements;
176
+ if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1)
177
+ {
178
+ const tmpTableName = generateTableName(pParameters);
179
+ if (!pIsForCountClause)
180
+ {
181
+ return tmpTableName + '.*';
182
+ }
183
+ // we need to list all of the table fields explicitly; get them from the schema
184
+ const tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
185
+ if (tmpSchema.length < 1)
186
+ {
187
+ // this means we have no schema; returning an empty string here signals the calling code to handle this case
188
+ return '';
189
+ }
190
+ const idColumn = tmpSchema.find((entry) => entry.Type === 'AutoIdentity');
191
+ if (!idColumn)
192
+ {
193
+ // this means there is no autoincrementing unique ID column; treat as above
194
+ return '';
195
+ }
196
+ return ` ${generateSafeFieldName(idColumn.Column)}`;
197
+ }
198
+
199
+ var tmpFieldList = ' ';
200
+ for (var i = 0; i < tmpDataElements.length; i++)
201
+ {
202
+ if (i > 0)
203
+ {
204
+ tmpFieldList += ', ';
205
+ }
206
+ if (Array.isArray(tmpDataElements[i]))
207
+ {
208
+ tmpFieldList += generateSafeFieldName(tmpDataElements[i][0]);
209
+ if (tmpDataElements[i].length > 1 && tmpDataElements[i][1])
210
+ {
211
+ tmpFieldList += " AS " + generateSafeFieldName(tmpDataElements[i][1]);
212
+ }
213
+ }
214
+ else
215
+ {
216
+ tmpFieldList += generateSafeFieldName(tmpDataElements[i]);
217
+ }
218
+ }
219
+ return tmpFieldList;
220
+ };
221
+
222
+ /**
223
+ * Generate a field list for the outer SELECT of the legacy pagination
224
+ * wrapper. The outer FROM is a ROWNUM subquery, so the default
225
+ * "Table.*" qualifier can't resolve there — we need either an explicit
226
+ * column list from the schema or a bare "*".
227
+ *
228
+ * If the caller set explicit dataElements, reuse them (they reference bare
229
+ * column names, which work fine against the subquery). Otherwise emit an
230
+ * explicit list from the schema to keep "_RowNum" from leaking. As a last
231
+ * resort, fall back to "*".
232
+ *
233
+ * @param: {Object} pParameters SQL Query Parameters
234
+ * @return: {String} Field list (prefixed with a single leading space)
235
+ */
236
+ var generateOuterFieldListForLegacyPagination = function(pParameters)
237
+ {
238
+ var tmpDataElements = pParameters.dataElements;
239
+ if (Array.isArray(tmpDataElements) && tmpDataElements.length > 0)
240
+ {
241
+ return generateFieldList(pParameters);
242
+ }
243
+
244
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
245
+ if (tmpSchema.length > 0)
246
+ {
247
+ var tmpList = ' ';
248
+ for (var i = 0; i < tmpSchema.length; i++)
249
+ {
250
+ if (i > 0) tmpList += ', ';
251
+ tmpList += generateSafeFieldName(tmpSchema[i].Column);
252
+ }
253
+ return tmpList;
254
+ }
255
+
256
+ // No schema, no explicit dataElements — "*" is the best we can do.
257
+ // "_RowNum" will surface on marshalled records; downstream code can
258
+ // ignore it. Schemas are the norm via Meadow so this is rare.
259
+ return ' *';
260
+ };
261
+
262
+ const SURROUNDING_QUOTES_AND_WHITESPACE_REGEX = /^[" ]+|[" ]+$/g;
263
+
264
+ const cleanseQuoting = (str) =>
265
+ {
266
+ return str.replace(SURROUNDING_QUOTES_AND_WHITESPACE_REGEX, '');
267
+ };
268
+
269
+ /**
270
+ * Ensure a field name is emitted correctly for the current quoting mode.
271
+ *
272
+ * Unquoted (default): bare identifiers (Oracle folds to UPPERCASE).
273
+ * Quoted: wrapped in double quotes, preserving case.
274
+ */
275
+ var generateSafeFieldName = function(pFieldName)
276
+ {
277
+ let pFieldNames = pFieldName.split('.');
278
+ if (pFieldNames.length > 1)
279
+ {
280
+ const cleansedTable = cleanseQuoting(pFieldNames[0]);
281
+ const cleansedFieldName = cleanseQuoting(pFieldNames[1]);
282
+ if (cleansedFieldName === '*')
283
+ {
284
+ return _QuoteIdentifiers ? '"'+cleansedTable+'".*' : cleansedTable+'.*';
285
+ }
286
+ return _QuoteIdentifiers
287
+ ? '"'+cleansedTable+'"."'+cleansedFieldName+'"'
288
+ : cleansedTable+'.'+cleansedFieldName;
289
+ }
290
+ const cleansedFieldName = cleanseQuoting(pFieldNames[0]);
291
+ if (cleansedFieldName === '*')
292
+ {
293
+ return '*';
294
+ }
295
+ return _QuoteIdentifiers ? '"'+cleansedFieldName+'"' : cleansedFieldName;
296
+ };
297
+
298
+ var resolveJsonColumnPath = function(pColumnName, pSchema)
299
+ {
300
+ if (!Array.isArray(pSchema) || pSchema.length < 1) return null;
301
+ var tmpParts = pColumnName.replace(/`/g, '').replace(/"/g, '').split('.');
302
+ for (var tmpStartIdx = 0; tmpStartIdx < Math.min(tmpParts.length - 1, 2); tmpStartIdx++)
303
+ {
304
+ var tmpBaseColumn = tmpParts[tmpStartIdx];
305
+ for (var s = 0; s < pSchema.length; s++)
306
+ {
307
+ if (pSchema[s].Column === tmpBaseColumn &&
308
+ (pSchema[s].Type === 'JSON' || pSchema[s].Type === 'JSONProxy'))
309
+ {
310
+ var tmpActualColumn = (pSchema[s].Type === 'JSONProxy') ? pSchema[s].StorageColumn : tmpBaseColumn;
311
+ var tmpJsonPath = '$.' + tmpParts.slice(tmpStartIdx + 1).join('.');
312
+ return { column: tmpActualColumn, path: tmpJsonPath };
313
+ }
314
+ }
315
+ }
316
+ return null;
317
+ };
318
+
319
+ /**
320
+ * Generate a query from the array of where clauses
321
+ *
322
+ * Each clause is an object like:
323
+ {
324
+ Column:'Name',
325
+ Operator:'EQ',
326
+ Value:'John',
327
+ Connector:'And',
328
+ Parameter:'Name'
329
+ }
330
+ *
331
+ * @method: generateWhere
332
+ * @param: {Object} pParameters SQL Query Parameters
333
+ * @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
334
+ */
335
+ var generateWhere = function(pParameters)
336
+ {
337
+ var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
338
+
339
+ if (!pParameters.query.disableDeleteTracking)
340
+ {
341
+ // Check if there is a Deleted column on the Schema. If so, we add this to the filters automatically (if not already present)
342
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
343
+ for (var i = 0; i < tmpSchema.length; i++)
344
+ {
345
+ var tmpSchemaEntry = tmpSchema[i];
346
+
347
+ if (tmpSchemaEntry.Type === 'Deleted')
348
+ {
349
+ var tmpHasDeletedParameter = false;
350
+
351
+ if (tmpFilter.length > 0)
352
+ {
353
+ for (var x = 0; x < tmpFilter.length; x++)
354
+ {
355
+ if (tmpFilter[x].Column === tmpSchemaEntry.Column)
356
+ {
357
+ tmpHasDeletedParameter = true;
358
+ break;
359
+ }
360
+ }
361
+ }
362
+ if (!tmpHasDeletedParameter)
363
+ {
364
+ tmpFilter.push(
365
+ {
366
+ Column: tmpSchemaEntry.Column,
367
+ Operator: '=',
368
+ Value: 0,
369
+ Connector: 'AND',
370
+ Parameter: 'Deleted'
371
+ });
372
+ }
373
+ break;
374
+ }
375
+ }
376
+ }
377
+
378
+ if (tmpFilter.length < 1)
379
+ {
380
+ return '';
381
+ }
382
+
383
+ var tmpWhere = ' WHERE';
384
+
385
+ // This is used to disable the connectors for subsequent queries.
386
+ // Only the open parenthesis operator uses this, currently.
387
+ var tmpLastOperatorNoConnector = false;
388
+
389
+ for (var i = 0; i < tmpFilter.length; i++)
390
+ {
391
+ if ((tmpFilter[i].Connector != 'NONE') && (tmpFilter[i].Operator != ')') && (tmpWhere != ' WHERE') && (tmpLastOperatorNoConnector == false))
392
+ {
393
+ tmpWhere += ' '+tmpFilter[i].Connector;
394
+ }
395
+
396
+ tmpLastOperatorNoConnector = false;
397
+
398
+ var tmpColumnParameter;
399
+
400
+ if (tmpFilter[i].Operator === '(')
401
+ {
402
+ tmpWhere += ' (';
403
+ tmpLastOperatorNoConnector = true;
404
+ }
405
+ else if (tmpFilter[i].Operator === ')')
406
+ {
407
+ tmpWhere += ' )';
408
+ }
409
+ else if (tmpFilter[i].Operator === 'IN' || tmpFilter[i].Operator === "NOT IN")
410
+ {
411
+ // oracledb will not expand a single bound array into an IN list,
412
+ // so expand the value list into discrete :name binds here.
413
+ var tmpInValues = Array.isArray(tmpFilter[i].Value)
414
+ ? tmpFilter[i].Value
415
+ : String(tmpFilter[i].Value).split(',');
416
+ var tmpInPlaceholders = [];
417
+ for (var v = 0; v < tmpInValues.length; v++)
418
+ {
419
+ var tmpInParameter = tmpFilter[i].Parameter+'_w'+i+'_'+v;
420
+ tmpInPlaceholders.push(':'+tmpInParameter);
421
+ pParameters.query.parameters[tmpInParameter] = tmpInValues[v];
422
+ generateOracleParameterTypeEntry(pParameters, tmpInParameter, tmpFilter[i].Parameter);
423
+ }
424
+ tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator+' ('+tmpInPlaceholders.join(', ')+')';
425
+ }
426
+ else if (tmpFilter[i].Operator === 'IS NULL')
427
+ {
428
+ tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator;
429
+ }
430
+ else if (tmpFilter[i].Operator === 'IS NOT NULL')
431
+ {
432
+ tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator;
433
+ }
434
+ else
435
+ {
436
+ tmpColumnParameter = tmpFilter[i].Parameter+'_w'+i;
437
+ var tmpSchemaForJson = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
438
+ var tmpJsonRef = resolveJsonColumnPath(tmpFilter[i].Column, tmpSchemaForJson);
439
+ if (tmpJsonRef)
440
+ {
441
+ tmpWhere += ' JSON_VALUE('+generateSafeFieldName(tmpJsonRef.column)+", '"+tmpJsonRef.path+"') "+tmpFilter[i].Operator+' :'+tmpColumnParameter;
442
+ }
443
+ else
444
+ {
445
+ tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator+' :'+tmpColumnParameter;
446
+ }
447
+ pParameters.query.parameters[tmpColumnParameter] = tmpFilter[i].Value;
448
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpFilter[i].Parameter);
449
+ }
450
+ }
451
+
452
+ return tmpWhere;
453
+ };
454
+
455
+ /**
456
+ * Find the table's AutoIdentity primary-key column from the schema, if any.
457
+ * Used as a deterministic default ORDER BY when the caller didn't set a
458
+ * sort, and as the RETURNING target for INSERT.
459
+ *
460
+ * @param: {Object} pParameters SQL Query Parameters
461
+ * @return: {String|null} The column name, or null if none found
462
+ */
463
+ var findPrimaryKeyColumn = function(pParameters)
464
+ {
465
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
466
+ for (var i = 0; i < tmpSchema.length; i++)
467
+ {
468
+ if (tmpSchema[i].Type === 'AutoIdentity')
469
+ {
470
+ return tmpSchema[i].Column;
471
+ }
472
+ }
473
+ return null;
474
+ };
475
+
476
+ /**
477
+ * Generate an ORDER BY clause from the sort array
478
+ *
479
+ * When no sort is specified but the query has a cap (pagination is active),
480
+ * inject a default ORDER BY on the primary key so paging is deterministic.
481
+ * Unlike MSSQL, Oracle permits OFFSET/FETCH and ROWNUM paging without an
482
+ * ORDER BY, so when no PK can be inferred we simply omit the clause rather
483
+ * than emitting an invalid "ORDER BY (SELECT 1)".
484
+ *
485
+ * @method: generateOrderBy
486
+ * @param: {Object} pParameters SQL Query Parameters
487
+ * @return: {String} Returns the order by clause
488
+ */
489
+ var generateOrderBy = function(pParameters)
490
+ {
491
+ var tmpOrderBy = pParameters.sort;
492
+ if (!Array.isArray(tmpOrderBy) || tmpOrderBy.length < 1)
493
+ {
494
+ if (pParameters.cap)
495
+ {
496
+ var tmpPK = findPrimaryKeyColumn(pParameters);
497
+ if (tmpPK)
498
+ {
499
+ return ' ORDER BY '+generateSafeFieldName(tmpPK);
500
+ }
501
+ }
502
+ return '';
503
+ }
504
+
505
+ var tmpOrderClause = ' ORDER BY';
506
+ for (var i = 0; i < tmpOrderBy.length; i++)
507
+ {
508
+ if (i > 0)
509
+ {
510
+ tmpOrderClause += ',';
511
+ }
512
+ tmpOrderClause += ' '+generateSafeFieldName(tmpOrderBy[i].Column);
513
+
514
+ if (tmpOrderBy[i].Direction == 'Descending')
515
+ {
516
+ tmpOrderClause += ' DESC';
517
+ }
518
+ }
519
+ return tmpOrderClause;
520
+ };
521
+
522
+ /**
523
+ * Generate the limit clause using the 12c+ OFFSET/FETCH syntax.
524
+ *
525
+ * When pParameters.legacyPagination is set the Read function wraps the
526
+ * query in a ROWNUM subquery instead (11g and earlier have no OFFSET/FETCH),
527
+ * so this returns an empty string in that case.
528
+ *
529
+ * @method: generateLimit
530
+ * @param: {Object} pParameters SQL Query Parameters
531
+ * @return: {String} Returns the table limit clause
532
+ */
533
+ var generateLimit = function(pParameters)
534
+ {
535
+ if (!pParameters.cap)
536
+ {
537
+ return '';
538
+ }
539
+
540
+ if (pParameters.legacyPagination)
541
+ {
542
+ return '';
543
+ }
544
+
545
+ var tmpLimit = ' OFFSET ';
546
+ if (pParameters.begin !== false)
547
+ {
548
+ tmpLimit += pParameters.begin;
549
+ }
550
+ else
551
+ {
552
+ tmpLimit += '0';
553
+ }
554
+ tmpLimit += ` ROWS FETCH NEXT ${pParameters.cap} ROWS ONLY`;
555
+
556
+ return tmpLimit;
557
+ };
558
+
559
+ /**
560
+ * Generate the join clause
561
+ *
562
+ * @method: generateJoins
563
+ * @param: {Object} pParameters SQL Query Parameters
564
+ * @return: {String} Returns the join clause
565
+ */
566
+ var generateJoins = function(pParameters)
567
+ {
568
+ var tmpJoins = pParameters.join;
569
+ if (!Array.isArray(tmpJoins) || tmpJoins.length < 1)
570
+ {
571
+ return '';
572
+ }
573
+
574
+ var tmpJoinClause = '';
575
+ for (var i = 0; i < tmpJoins.length; i++)
576
+ {
577
+ var join = tmpJoins[i];
578
+ if (join.Type && join.Table && join.From && join.To)
579
+ {
580
+ var tmpJoinTable = _QuoteIdentifiers ? '"'+join.Table+'"' : join.Table;
581
+ tmpJoinClause += ` ${join.Type} ${tmpJoinTable} ON ${join.From} = ${join.To}`;
582
+ }
583
+ }
584
+
585
+ return tmpJoinClause;
586
+ };
587
+
588
+ /**
589
+ * Generate the update SET clause
590
+ *
591
+ * @method: generateUpdateSetters
592
+ * @param: {Object} pParameters SQL Query Parameters
593
+ * @return: {String} Returns the update set clause
594
+ */
595
+ var generateUpdateSetters = function(pParameters)
596
+ {
597
+ var tmpRecords = pParameters.query.records;
598
+ if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
599
+ {
600
+ return false;
601
+ }
602
+
603
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
604
+
605
+ var tmpUpdate = '';
606
+ var tmpCurrentColumn = 0;
607
+ for(var tmpColumn in tmpRecords[0])
608
+ {
609
+ var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
610
+ for (var i = 0; i < tmpSchema.length; i++)
611
+ {
612
+ if (tmpColumn == tmpSchema[i].Column)
613
+ {
614
+ tmpSchemaEntry = tmpSchema[i];
615
+ break;
616
+ }
617
+ }
618
+
619
+ if (pParameters.query.disableAutoUserStamp &&
620
+ tmpSchemaEntry.Type === 'UpdateIDUser')
621
+ {
622
+ continue;
623
+ }
624
+
625
+ switch (tmpSchemaEntry.Type)
626
+ {
627
+ case 'AutoIdentity':
628
+ case 'CreateDate':
629
+ case 'CreateIDUser':
630
+ case 'DeleteDate':
631
+ case 'DeleteIDUser':
632
+ continue;
633
+ }
634
+ if (tmpCurrentColumn > 0)
635
+ {
636
+ tmpUpdate += ',';
637
+ }
638
+ switch (tmpSchemaEntry.Type)
639
+ {
640
+ case 'UpdateDate':
641
+ if (pParameters.query.disableAutoDateStamp)
642
+ {
643
+ var tmpColumnParameter = 'MANUAL_UpdateDate';
644
+ tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :MANUAL_UpdateDate';
645
+ pParameters.query.parameters[tmpColumnParameter] = tmpRecords[0][tmpColumn];
646
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
647
+ }
648
+ else
649
+ {
650
+ tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = ' + SQL_NOW;
651
+ }
652
+ break;
653
+ case 'UpdateIDUser':
654
+ var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
655
+ tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :'+tmpColumnParameter;
656
+ pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
657
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpColumn);
658
+ break;
659
+ case 'JSON':
660
+ var tmpJSONUpdateParam = tmpColumn+'_'+tmpCurrentColumn;
661
+ tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :'+tmpJSONUpdateParam;
662
+ pParameters.query.parameters[tmpJSONUpdateParam] = (typeof tmpRecords[0][tmpColumn] === 'string')
663
+ ? tmpRecords[0][tmpColumn]
664
+ : JSON.stringify(tmpRecords[0][tmpColumn] || {});
665
+ generateOracleParameterTypeEntry(pParameters, tmpJSONUpdateParam, {Type:'Text'});
666
+ break;
667
+ case 'JSONProxy':
668
+ var tmpProxyUpdateParam = tmpSchemaEntry.StorageColumn+'_'+tmpCurrentColumn;
669
+ tmpUpdate += ' '+generateSafeFieldName(tmpSchemaEntry.StorageColumn)+' = :'+tmpProxyUpdateParam;
670
+ pParameters.query.parameters[tmpProxyUpdateParam] = (typeof tmpRecords[0][tmpColumn] === 'string')
671
+ ? tmpRecords[0][tmpColumn]
672
+ : JSON.stringify(tmpRecords[0][tmpColumn] || {});
673
+ generateOracleParameterTypeEntry(pParameters, tmpProxyUpdateParam, {Type:'Text'});
674
+ break;
675
+ default:
676
+ var tmpColumnDefaultParameter = tmpColumn+'_'+tmpCurrentColumn;
677
+ tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :'+tmpColumnDefaultParameter;
678
+ pParameters.query.parameters[tmpColumnDefaultParameter] = tmpRecords[0][tmpColumn];
679
+ generateOracleParameterTypeEntry(pParameters, tmpColumnDefaultParameter, tmpSchemaEntry);
680
+ break;
681
+ }
682
+
683
+ tmpCurrentColumn++;
684
+ }
685
+
686
+ if (tmpUpdate === '')
687
+ {
688
+ return false;
689
+ }
690
+
691
+ return tmpUpdate;
692
+ };
693
+
694
+ /**
695
+ * Generate the update-delete SET clause
696
+ *
697
+ * @method: generateUpdateDeleteSetters
698
+ * @param: {Object} pParameters SQL Query Parameters
699
+ * @return: {String} Returns the soft-delete set clause
700
+ */
701
+ var generateUpdateDeleteSetters = function(pParameters)
702
+ {
703
+ if (pParameters.query.disableDeleteTracking)
704
+ {
705
+ return false;
706
+ }
707
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
708
+
709
+ var tmpCurrentColumn = 0;
710
+ var tmpHasDeletedField = false;
711
+ var tmpUpdate = '';
712
+ var tmpSchemaEntry = {Type:'Default'};
713
+ for (var i = 0; i < tmpSchema.length; i++)
714
+ {
715
+ tmpSchemaEntry = tmpSchema[i];
716
+
717
+ var tmpUpdateSql = null;
718
+
719
+ switch (tmpSchemaEntry.Type)
720
+ {
721
+ case 'Deleted':
722
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = 1';
723
+ tmpHasDeletedField = true;
724
+ break;
725
+ case 'DeleteDate':
726
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
727
+ break;
728
+ case 'UpdateDate':
729
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
730
+ break;
731
+ case 'DeleteIDUser':
732
+ var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
733
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = :'+tmpColumnParameter;
734
+ pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
735
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
736
+ break;
737
+ default:
738
+ continue;
739
+ }
740
+
741
+ if (tmpCurrentColumn > 0)
742
+ {
743
+ tmpUpdate += ',';
744
+ }
745
+
746
+ tmpUpdate += tmpUpdateSql;
747
+
748
+ tmpCurrentColumn++;
749
+ }
750
+
751
+ if (!tmpHasDeletedField ||
752
+ tmpUpdate === '')
753
+ {
754
+ return false;
755
+ }
756
+
757
+ return tmpUpdate;
758
+ };
759
+
760
+ /**
761
+ * Generate the update-undelete SET clause
762
+ *
763
+ * @method: generateUpdateUndeleteSetters
764
+ * @param: {Object} pParameters SQL Query Parameters
765
+ * @return: {String} Returns the soft-undelete set clause
766
+ */
767
+ var generateUpdateUndeleteSetters = function(pParameters)
768
+ {
769
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
770
+
771
+ var tmpCurrentColumn = 0;
772
+ var tmpHasDeletedField = false;
773
+ var tmpUpdate = '';
774
+ var tmpSchemaEntry = {Type:'Default'};
775
+ for (var i = 0; i < tmpSchema.length; i++)
776
+ {
777
+ tmpSchemaEntry = tmpSchema[i];
778
+
779
+ var tmpUpdateSql = null;
780
+
781
+ switch (tmpSchemaEntry.Type)
782
+ {
783
+ case 'Deleted':
784
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = 0';
785
+ tmpHasDeletedField = true;
786
+ break;
787
+ case 'UpdateDate':
788
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
789
+ break;
790
+ case 'UpdateIDUser':
791
+ var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
792
+ tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = :'+tmpColumnParameter;
793
+ pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
794
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
795
+ break;
796
+ default:
797
+ continue;
798
+ }
799
+
800
+ if (tmpCurrentColumn > 0)
801
+ {
802
+ tmpUpdate += ',';
803
+ }
804
+
805
+ tmpUpdate += tmpUpdateSql;
806
+
807
+ tmpCurrentColumn++;
808
+ }
809
+
810
+ if (!tmpHasDeletedField ||
811
+ tmpUpdate === '')
812
+ {
813
+ return false;
814
+ }
815
+
816
+ return tmpUpdate;
817
+ };
818
+
819
+ /**
820
+ * Generate the create SET clause values
821
+ *
822
+ * @method: generateCreateSetValues
823
+ * @param: {Object} pParameters SQL Query Parameters
824
+ * @return: {String} Returns the insert values clause
825
+ */
826
+ var generateCreateSetValues = function(pParameters)
827
+ {
828
+ var tmpRecords = pParameters.query.records;
829
+ if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
830
+ {
831
+ return false;
832
+ }
833
+
834
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
835
+
836
+ var tmpCreateSet = '';
837
+ var tmpCurrentColumn = 0;
838
+ for(var tmpColumn in tmpRecords[0])
839
+ {
840
+ var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
841
+ for (var i = 0; i < tmpSchema.length; i++)
842
+ {
843
+ if (tmpColumn == tmpSchema[i].Column)
844
+ {
845
+ tmpSchemaEntry = tmpSchema[i];
846
+ break;
847
+ }
848
+ }
849
+
850
+ if (!pParameters.query.disableDeleteTracking)
851
+ {
852
+ if (tmpSchemaEntry.Type === 'DeleteDate' ||
853
+ tmpSchemaEntry.Type === 'DeleteIDUser')
854
+ {
855
+ continue;
856
+ }
857
+ }
858
+
859
+ // AutoIdentity is omitted entirely from the INSERT (the IDENTITY
860
+ // column or the BEFORE-INSERT sequence trigger supplies it); skip
861
+ // adding a separator for it unless the consumer is overriding it.
862
+ if (tmpSchemaEntry.Type === 'AutoIdentity' && !pParameters.query.disableAutoIdentity)
863
+ {
864
+ continue;
865
+ }
866
+
867
+ if ((tmpCurrentColumn > 0) && (tmpCreateSet != ''))
868
+ {
869
+ tmpCreateSet += ',';
870
+ }
871
+
872
+ var buildDefaultDefinition = function()
873
+ {
874
+ var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
875
+ tmpCreateSet += ' :'+tmpColumnParameter;
876
+ pParameters.query.parameters[tmpColumnParameter] = tmpRecords[0][tmpColumn];
877
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
878
+ };
879
+
880
+ var tmpColumnParameter;
881
+ switch (tmpSchemaEntry.Type)
882
+ {
883
+ case 'AutoIdentity':
884
+ // Only reached when disableAutoIdentity is set (overriding
885
+ // the generated key with an explicit value).
886
+ buildDefaultDefinition();
887
+ break;
888
+ case 'AutoGUID':
889
+ if (pParameters.query.disableAutoIdentity)
890
+ {
891
+ buildDefaultDefinition();
892
+ }
893
+ else if (tmpRecords[0][tmpColumn] &&
894
+ tmpRecords[0][tmpColumn].length >= 5 &&
895
+ tmpRecords[0][tmpColumn] !== '0x0000000000000000') //stricture default
896
+ {
897
+ buildDefaultDefinition();
898
+ }
899
+ else
900
+ {
901
+ tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
902
+ tmpCreateSet += ' :'+tmpColumnParameter;
903
+ pParameters.query.parameters[tmpColumnParameter] = pParameters.query.UUID;
904
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
905
+ }
906
+ break;
907
+ case 'UpdateDate':
908
+ case 'CreateDate':
909
+ case 'DeleteDate':
910
+ if (pParameters.query.disableAutoDateStamp)
911
+ {
912
+ buildDefaultDefinition();
913
+ }
914
+ else
915
+ {
916
+ tmpCreateSet += ' ' + SQL_NOW;
917
+ }
918
+ break;
919
+ case 'DeleteIDUser':
920
+ case 'UpdateIDUser':
921
+ case 'CreateIDUser':
922
+ if (pParameters.query.disableAutoUserStamp)
923
+ {
924
+ buildDefaultDefinition();
925
+ }
926
+ else
927
+ {
928
+ tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
929
+ tmpCreateSet += ' :'+tmpColumnParameter;
930
+ pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
931
+ generateOracleParameterTypeEntry(pParameters, tmpColumnParameter, tmpSchemaEntry);
932
+ }
933
+ break;
934
+ case 'JSON':
935
+ var tmpJSONCreateParam = tmpColumn+'_'+tmpCurrentColumn;
936
+ tmpCreateSet += ' :'+tmpJSONCreateParam;
937
+ pParameters.query.parameters[tmpJSONCreateParam] = (typeof tmpRecords[0][tmpColumn] === 'string')
938
+ ? tmpRecords[0][tmpColumn]
939
+ : JSON.stringify(tmpRecords[0][tmpColumn] || {});
940
+ generateOracleParameterTypeEntry(pParameters, tmpJSONCreateParam, {Type:'Text'});
941
+ break;
942
+ case 'JSONProxy':
943
+ var tmpProxyCreateParam = tmpColumn+'_'+tmpCurrentColumn;
944
+ tmpCreateSet += ' :'+tmpProxyCreateParam;
945
+ pParameters.query.parameters[tmpProxyCreateParam] = (typeof tmpRecords[0][tmpColumn] === 'string')
946
+ ? tmpRecords[0][tmpColumn]
947
+ : JSON.stringify(tmpRecords[0][tmpColumn] || {});
948
+ generateOracleParameterTypeEntry(pParameters, tmpProxyCreateParam, {Type:'Text'});
949
+ break;
950
+ default:
951
+ buildDefaultDefinition();
952
+ break;
953
+ }
954
+
955
+ tmpCurrentColumn++;
956
+ }
957
+
958
+ if (tmpCreateSet === '')
959
+ {
960
+ return false;
961
+ }
962
+
963
+ return tmpCreateSet;
964
+ };
965
+
966
+ /**
967
+ * Generate the create SET clause column list
968
+ *
969
+ * @method: generateCreateSetList
970
+ * @param: {Object} pParameters SQL Query Parameters
971
+ * @return: {String} Returns the insert column list
972
+ */
973
+ var generateCreateSetList = function(pParameters)
974
+ {
975
+ var tmpRecords = pParameters.query.records;
976
+
977
+ var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
978
+
979
+ var tmpCreateSet = '';
980
+ for(var tmpColumn in tmpRecords[0])
981
+ {
982
+ var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
983
+ for (var i = 0; i < tmpSchema.length; i++)
984
+ {
985
+ if (tmpColumn == tmpSchema[i].Column)
986
+ {
987
+ tmpSchemaEntry = tmpSchema[i];
988
+ break;
989
+ }
990
+ }
991
+ if (!pParameters.query.disableDeleteTracking)
992
+ {
993
+ if (tmpSchemaEntry.Type === 'DeleteDate' ||
994
+ tmpSchemaEntry.Type === 'DeleteIDUser')
995
+ {
996
+ continue;
997
+ }
998
+ }
999
+ switch (tmpSchemaEntry.Type)
1000
+ {
1001
+ case 'AutoIdentity':
1002
+ // Skipped on INSERT (IDENTITY/sequence trigger supplies it)
1003
+ // unless the consumer is overriding the value.
1004
+ if (pParameters.query.disableAutoIdentity)
1005
+ {
1006
+ if (tmpCreateSet != '')
1007
+ {
1008
+ tmpCreateSet += ',';
1009
+ }
1010
+ tmpCreateSet += ' '+generateSafeFieldName(tmpColumn);
1011
+ }
1012
+ continue;
1013
+ case 'JSONProxy':
1014
+ if (tmpCreateSet != '')
1015
+ {
1016
+ tmpCreateSet += ',';
1017
+ }
1018
+ tmpCreateSet += ' '+generateSafeFieldName(tmpSchemaEntry.StorageColumn);
1019
+ break;
1020
+ default:
1021
+ if (tmpCreateSet != '')
1022
+ {
1023
+ tmpCreateSet += ',';
1024
+ }
1025
+ tmpCreateSet += ' '+generateSafeFieldName(tmpColumn);
1026
+ break;
1027
+ }
1028
+ }
1029
+
1030
+ return tmpCreateSet;
1031
+ };
1032
+
1033
+
1034
+ var Create = function(pParameters)
1035
+ {
1036
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1037
+ var tmpTableName = generateTableName(pParameters);
1038
+ var tmpCreateSetList = generateCreateSetList(pParameters);
1039
+ var tmpCreateSetValues = generateCreateSetValues(pParameters);
1040
+
1041
+ if (!tmpCreateSetValues)
1042
+ {
1043
+ return false;
1044
+ }
1045
+
1046
+ // Oracle has no SCOPE_IDENTITY(); return the generated key via a
1047
+ // RETURNING ... INTO out-bind that the Meadow Oracle provider reads.
1048
+ var tmpReturning = '';
1049
+ if (!pParameters.query.disableAutoIdentity)
1050
+ {
1051
+ var tmpIDColumn = findPrimaryKeyColumn(pParameters);
1052
+ if (tmpIDColumn)
1053
+ {
1054
+ tmpReturning = ' RETURNING '+generateSafeFieldName(tmpIDColumn)+' INTO :RETURNING_ID';
1055
+ }
1056
+ }
1057
+
1058
+ return 'INSERT INTO'+tmpTableName+' ('+tmpCreateSetList+') VALUES ('+tmpCreateSetValues+')'+tmpReturning;
1059
+ };
1060
+
1061
+
1062
+ /**
1063
+ * Read one or many records
1064
+ *
1065
+ * @method Read
1066
+ * @param {Object} pParameters SQL Query parameters
1067
+ * @return {String} Returns the generated query.
1068
+ */
1069
+ var Read = function(pParameters)
1070
+ {
1071
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1072
+ var tmpFieldList = generateFieldList(pParameters);
1073
+ var tmpTableName = generateTableName(pParameters);
1074
+ var tmpWhere = generateWhere(pParameters);
1075
+ var tmpJoin = generateJoins(pParameters);
1076
+ var tmpOrderBy = generateOrderBy(pParameters);
1077
+ var tmpLimit = generateLimit(pParameters);
1078
+ const tmpOptDistinct = pParameters.distinct ? ' DISTINCT' : '';
1079
+
1080
+ if (pParameters.queryOverride)
1081
+ {
1082
+ try
1083
+ {
1084
+ var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
1085
+ return tmpQueryTemplate({FieldList:tmpFieldList, TableName:tmpTableName, Where:tmpWhere, Join:tmpJoin, OrderBy:tmpOrderBy, Limit:tmpLimit, Distinct: tmpOptDistinct, _Params: pParameters});
1086
+ }
1087
+ catch (pError)
1088
+ {
1089
+ console.log('Error with custom Read Query ['+pParameters.queryOverride+']: '+pError);
1090
+ return false;
1091
+ }
1092
+ }
1093
+
1094
+ // Legacy pagination path — wrap in a ROWNUM double-subquery for 11g
1095
+ // and earlier, which have no OFFSET/FETCH. The innermost query keeps
1096
+ // the ORDER BY; the middle query captures ROWNUM with an upper-bound
1097
+ // predicate (enabling a COUNT STOPKEY short-circuit); the outer query
1098
+ // applies the lower bound. Enabled via pParameters.legacyPagination
1099
+ // (forwarded from the meadow-connection-oracle LegacyPagination config).
1100
+ if (pParameters.legacyPagination && pParameters.cap)
1101
+ {
1102
+ var tmpBegin = (pParameters.begin !== false) ? pParameters.begin : 0;
1103
+ var tmpEnd = tmpBegin + pParameters.cap;
1104
+ var tmpOuterFieldList = generateOuterFieldListForLegacyPagination(pParameters);
1105
+ return `SELECT${tmpOptDistinct}${tmpOuterFieldList} FROM (SELECT meadow_inner.*, ROWNUM AS "_RowNum" FROM (SELECT${tmpFieldList} FROM${tmpTableName}${tmpJoin}${tmpWhere}${tmpOrderBy}) meadow_inner WHERE ROWNUM <= ${tmpEnd}) WHERE "_RowNum" > ${tmpBegin}`;
1106
+ }
1107
+
1108
+ return `SELECT${tmpOptDistinct}${tmpFieldList} FROM${tmpTableName}${tmpJoin}${tmpWhere}${tmpOrderBy}${tmpLimit}`;
1109
+ };
1110
+
1111
+ var Update = function(pParameters)
1112
+ {
1113
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1114
+ var tmpTableName = generateTableName(pParameters);
1115
+ var tmpUpdateSetters = generateUpdateSetters(pParameters);
1116
+ var tmpWhere = generateWhere(pParameters);
1117
+
1118
+ if (!tmpUpdateSetters)
1119
+ {
1120
+ return false;
1121
+ }
1122
+
1123
+ return 'UPDATE'+tmpTableName+' SET'+tmpUpdateSetters+tmpWhere;
1124
+ };
1125
+
1126
+ var Delete = function(pParameters)
1127
+ {
1128
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1129
+ var tmpTableName = generateTableName(pParameters);
1130
+ var tmpUpdateDeleteSetters = generateUpdateDeleteSetters(pParameters);
1131
+ var tmpWhere = generateWhere(pParameters);
1132
+
1133
+ if (tmpUpdateDeleteSetters)
1134
+ {
1135
+ return 'UPDATE'+tmpTableName+' SET'+tmpUpdateDeleteSetters+tmpWhere;
1136
+ }
1137
+ else
1138
+ {
1139
+ return 'DELETE FROM'+tmpTableName+tmpWhere;
1140
+ }
1141
+ };
1142
+
1143
+ var Undelete = function(pParameters)
1144
+ {
1145
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1146
+ var tmpTableName = generateTableName(pParameters);
1147
+ let tmpDeleteTrackingState = pParameters.query.disableDeleteTracking;
1148
+ pParameters.query.disableDeleteTracking = true;
1149
+ var tmpUpdateUndeleteSetters = generateUpdateUndeleteSetters(pParameters);
1150
+ var tmpWhere = generateWhere(pParameters);
1151
+ pParameters.query.disableDeleteTracking = tmpDeleteTrackingState;
1152
+
1153
+ if (tmpUpdateUndeleteSetters)
1154
+ {
1155
+ return 'UPDATE'+tmpTableName+' SET'+tmpUpdateUndeleteSetters+tmpWhere;
1156
+ }
1157
+ else
1158
+ {
1159
+ // No-op — the record can't be undeleted. Oracle requires a FROM.
1160
+ return 'SELECT NULL FROM DUAL';
1161
+ }
1162
+ };
1163
+
1164
+ var Count = function(pParameters)
1165
+ {
1166
+ _QuoteIdentifiers = !!pParameters.quoteIdentifiers;
1167
+ var tmpFieldList = pParameters.distinct ? generateFieldList(pParameters, true) : '*';
1168
+ var tmpTableName = generateTableName(pParameters);
1169
+ var tmpJoin = generateJoins(pParameters);
1170
+ var tmpWhere = generateWhere(pParameters);
1171
+ if (pParameters.distinct && tmpFieldList.length < 1)
1172
+ {
1173
+ console.warn('Distinct requested but no field list or schema are available, so not honoring distinct for count query.');
1174
+ }
1175
+ const tmpOptDistinct = pParameters.distinct && tmpFieldList.length > 0 ? 'DISTINCT' : '';
1176
+
1177
+ if (pParameters.queryOverride)
1178
+ {
1179
+ try
1180
+ {
1181
+ var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
1182
+ return tmpQueryTemplate({FieldList:[], TableName:tmpTableName, Where:tmpWhere, OrderBy:'', Limit:'', Distinct: tmpOptDistinct, _Params: pParameters});
1183
+ }
1184
+ catch (pError)
1185
+ {
1186
+ console.log('Error with custom Count Query ['+pParameters.queryOverride+']: '+pError);
1187
+ return false;
1188
+ }
1189
+ }
1190
+
1191
+ return `SELECT COUNT(${tmpOptDistinct}${tmpFieldList || '*'}) AS RowCount FROM${tmpTableName}${tmpJoin}${tmpWhere}`;
1192
+ };
1193
+
1194
+ var tmpDialect = ({
1195
+ Create: Create,
1196
+ Read: Read,
1197
+ Update: Update,
1198
+ Delete: Delete,
1199
+ Undelete: Undelete,
1200
+ Count: Count
1201
+ });
1202
+
1203
+ /**
1204
+ * Dialect Name
1205
+ *
1206
+ * @property name
1207
+ * @type string
1208
+ */
1209
+ Object.defineProperty(tmpDialect, 'name',
1210
+ {
1211
+ get: function() { return 'Oracle'; },
1212
+ enumerable: true
1213
+ });
1214
+
1215
+ return tmpDialect;
1216
+ };
1217
+
1218
+ module.exports = FoxHoundDialectOracle;