foxhound 2.0.18 → 2.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/docs/README.md +32 -11
- package/docs/_cover.md +3 -2
- package/docs/_sidebar.md +21 -1
- package/docs/_topbar.md +7 -0
- package/docs/api/README.md +73 -0
- package/docs/api/addFilter.md +170 -0
- package/docs/api/addJoin.md +128 -0
- package/docs/api/addRecord.md +108 -0
- package/docs/api/addSort.md +109 -0
- package/docs/api/behaviorFlags.md +123 -0
- package/docs/api/buildQuery.md +146 -0
- package/docs/api/clone.md +115 -0
- package/docs/api/queryOverrides.md +82 -0
- package/docs/api/setCap.md +115 -0
- package/docs/api/setDataElements.md +95 -0
- package/docs/api/setDialect.md +78 -0
- package/docs/api/setDistinct.md +76 -0
- package/docs/api/setIDUser.md +80 -0
- package/docs/api/setScope.md +70 -0
- package/docs/architecture.md +134 -42
- package/docs/dialects/README.md +2 -0
- package/docs/dialects/postgresql.md +126 -0
- package/docs/quickstart.md +196 -0
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +7996 -2630
- package/package.json +2 -2
- package/source/Foxhound-Dialects.js +4 -0
- package/source/dialects/DGraph/FoxHound-Dialect-DGraph.js +954 -0
- package/source/dialects/MongoDB/FoxHound-Dialect-MongoDB.js +902 -0
- package/source/dialects/PostgreSQL/FoxHound-Dialect-PostgreSQL.js +865 -0
- package/source/dialects/Solr/FoxHound-Dialect-Solr.js +895 -0
- package/test/FoxHound-Dialect-DGraph_tests.js +547 -0
- package/test/FoxHound-Dialect-MongoDB_tests.js +485 -0
- package/test/FoxHound-Dialect-PostgreSQL_tests.js +342 -0
- package/test/FoxHound-Dialect-Solr_tests.js +551 -0
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FoxHound PostgreSQL Dialect
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* For a PostgreSQL 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 > $1
|
|
12
|
+
// <%= Sort %> = ORDER BY Field1
|
|
13
|
+
// The values are empty strings if they aren't set.
|
|
14
|
+
*
|
|
15
|
+
* @author Steven Velozo <steven@velozo.com>
|
|
16
|
+
* @class FoxHoundDialectPostgreSQL
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var FoxHoundDialectPostgreSQL = function(pFable)
|
|
20
|
+
{
|
|
21
|
+
const SQL_NOW = "NOW()";
|
|
22
|
+
|
|
23
|
+
_Fable = pFable;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate a table name from the scope
|
|
27
|
+
*
|
|
28
|
+
* @method: generateTableName
|
|
29
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
30
|
+
* @return: {String} Returns the table name clause
|
|
31
|
+
*/
|
|
32
|
+
var generateTableName = function(pParameters)
|
|
33
|
+
{
|
|
34
|
+
if (pParameters.scope && pParameters.scope.indexOf('"') >= 0)
|
|
35
|
+
return ' '+pParameters.scope+'';
|
|
36
|
+
else
|
|
37
|
+
return ' "'+pParameters.scope+'"';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate a field list from the array of dataElements
|
|
42
|
+
*
|
|
43
|
+
* Each entry in the dataElements is a simple string
|
|
44
|
+
*
|
|
45
|
+
* @method: generateFieldList
|
|
46
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
47
|
+
* @param {Boolean} pIsForCountClause (optional) If true, generate fields for use within a count clause.
|
|
48
|
+
* @return: {String} Returns the field list clause, or empty string if explicit fields are requested but cannot be fulfilled
|
|
49
|
+
* due to missing schema.
|
|
50
|
+
*/
|
|
51
|
+
var generateFieldList = function(pParameters, pIsForCountClause)
|
|
52
|
+
{
|
|
53
|
+
var tmpDataElements = pParameters.dataElements;
|
|
54
|
+
if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1)
|
|
55
|
+
{
|
|
56
|
+
const tmpTableName = generateTableName(pParameters);
|
|
57
|
+
if (!pIsForCountClause)
|
|
58
|
+
{
|
|
59
|
+
return tmpTableName + '.*';
|
|
60
|
+
}
|
|
61
|
+
// we need to list all of the table fields explicitly; get them from the schema
|
|
62
|
+
const tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
63
|
+
if (tmpSchema.length < 1)
|
|
64
|
+
{
|
|
65
|
+
// this means we have no schema; returning an empty string here signals the calling code to handle this case
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
const idColumn = tmpSchema.find((entry) => entry.Type === 'AutoIdentity');
|
|
69
|
+
if (!idColumn)
|
|
70
|
+
{
|
|
71
|
+
// this means there is no autoincrementing unique ID column; treat as above
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
const qualifiedIDColumn = `${tmpTableName}.${idColumn.Column}`;
|
|
75
|
+
return ` ${generateSafeFieldName(qualifiedIDColumn)}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var tmpFieldList = ' ';
|
|
79
|
+
for (var i = 0; i < tmpDataElements.length; i++)
|
|
80
|
+
{
|
|
81
|
+
if (i > 0)
|
|
82
|
+
{
|
|
83
|
+
tmpFieldList += ', ';
|
|
84
|
+
}
|
|
85
|
+
if (Array.isArray(tmpDataElements[i]))
|
|
86
|
+
{
|
|
87
|
+
tmpFieldList += generateSafeFieldName(tmpDataElements[i][0]);
|
|
88
|
+
if (tmpDataElements[i].length > 1 && tmpDataElements[i][1])
|
|
89
|
+
{
|
|
90
|
+
tmpFieldList += " AS " + generateSafeFieldName(tmpDataElements[i][1]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else
|
|
94
|
+
{
|
|
95
|
+
tmpFieldList += generateSafeFieldName(tmpDataElements[i]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return tmpFieldList;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const SURROUNDING_QUOTES_AND_WHITESPACE_REGEX = /^[" ]+|[" ]+$/g;
|
|
102
|
+
|
|
103
|
+
const cleanseQuoting = (str) =>
|
|
104
|
+
{
|
|
105
|
+
return str.replace(SURROUNDING_QUOTES_AND_WHITESPACE_REGEX, '');
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Ensure a field name is properly escaped with double quotes.
|
|
110
|
+
*/
|
|
111
|
+
var generateSafeFieldName = function(pFieldName)
|
|
112
|
+
{
|
|
113
|
+
let pFieldNames = pFieldName.split('.');
|
|
114
|
+
if (pFieldNames.length > 1)
|
|
115
|
+
{
|
|
116
|
+
const cleansedFieldName = cleanseQuoting(pFieldNames[1]);
|
|
117
|
+
if (cleansedFieldName === '*')
|
|
118
|
+
{
|
|
119
|
+
return '"' + cleanseQuoting(pFieldNames[0]) + '".*';
|
|
120
|
+
}
|
|
121
|
+
return '"' + cleanseQuoting(pFieldNames[0]) + '"."' + cleansedFieldName + '"';
|
|
122
|
+
}
|
|
123
|
+
const cleansedFieldName = cleanseQuoting(pFieldNames[0]);
|
|
124
|
+
if (cleansedFieldName === '*')
|
|
125
|
+
{
|
|
126
|
+
return '*';
|
|
127
|
+
}
|
|
128
|
+
return '"' + cleanseQuoting(pFieldNames[0]) + '"';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate a query from the array of where clauses
|
|
133
|
+
*
|
|
134
|
+
* Each clause is an object like:
|
|
135
|
+
{
|
|
136
|
+
Column:'Name',
|
|
137
|
+
Operator:'EQ',
|
|
138
|
+
Value:'John',
|
|
139
|
+
Connector:'And',
|
|
140
|
+
Parameter:'Name'
|
|
141
|
+
}
|
|
142
|
+
*
|
|
143
|
+
* @method: generateWhere
|
|
144
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
145
|
+
* @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
|
|
146
|
+
*/
|
|
147
|
+
var generateWhere = function(pParameters)
|
|
148
|
+
{
|
|
149
|
+
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
|
|
150
|
+
var tmpTableName = generateTableName(pParameters);
|
|
151
|
+
|
|
152
|
+
if (!pParameters.query.disableDeleteTracking)
|
|
153
|
+
{
|
|
154
|
+
// Check if there is a Deleted column on the Schema. If so, we add this to the filters automatically (if not already present)
|
|
155
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
156
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
157
|
+
{
|
|
158
|
+
var tmpSchemaEntry = tmpSchema[i];
|
|
159
|
+
|
|
160
|
+
if (tmpSchemaEntry.Type === 'Deleted')
|
|
161
|
+
{
|
|
162
|
+
var tmpHasDeletedParameter = false;
|
|
163
|
+
|
|
164
|
+
if (tmpFilter.length > 0)
|
|
165
|
+
{
|
|
166
|
+
for (var x = 0; x < tmpFilter.length; x++)
|
|
167
|
+
{
|
|
168
|
+
if (tmpFilter[x].Column === tmpSchemaEntry.Column)
|
|
169
|
+
{
|
|
170
|
+
tmpHasDeletedParameter = true;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!tmpHasDeletedParameter)
|
|
176
|
+
{
|
|
177
|
+
tmpFilter.push(
|
|
178
|
+
{
|
|
179
|
+
Column: generateSafeFieldName(pParameters.scope + '.' + tmpSchemaEntry.Column),
|
|
180
|
+
Operator: '=',
|
|
181
|
+
Value: 0,
|
|
182
|
+
Connector: 'AND',
|
|
183
|
+
Parameter: 'Deleted'
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (tmpFilter.length < 1)
|
|
192
|
+
{
|
|
193
|
+
return '';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
var tmpWhere = ' WHERE';
|
|
197
|
+
|
|
198
|
+
var tmpLastOperatorNoConnector = false;
|
|
199
|
+
|
|
200
|
+
for (var i = 0; i < tmpFilter.length; i++)
|
|
201
|
+
{
|
|
202
|
+
if ((tmpFilter[i].Connector != 'NONE') && (tmpFilter[i].Operator != ')') && (tmpWhere != ' WHERE') && (tmpLastOperatorNoConnector == false))
|
|
203
|
+
{
|
|
204
|
+
tmpWhere += ' '+tmpFilter[i].Connector;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
tmpLastOperatorNoConnector = false;
|
|
208
|
+
|
|
209
|
+
var tmpColumnParameter;
|
|
210
|
+
|
|
211
|
+
if (tmpFilter[i].Operator === '(')
|
|
212
|
+
{
|
|
213
|
+
tmpWhere += ' (';
|
|
214
|
+
tmpLastOperatorNoConnector = true;
|
|
215
|
+
}
|
|
216
|
+
else if (tmpFilter[i].Operator === ')')
|
|
217
|
+
{
|
|
218
|
+
tmpWhere += ' )';
|
|
219
|
+
}
|
|
220
|
+
else if (tmpFilter[i].Operator === 'IN' || tmpFilter[i].Operator === "NOT IN")
|
|
221
|
+
{
|
|
222
|
+
tmpColumnParameter = tmpFilter[i].Parameter+'_w'+i;
|
|
223
|
+
tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator+' ( :'+tmpColumnParameter+' )';
|
|
224
|
+
pParameters.query.parameters[tmpColumnParameter] = tmpFilter[i].Value;
|
|
225
|
+
}
|
|
226
|
+
else if (tmpFilter[i].Operator === 'IS NULL')
|
|
227
|
+
{
|
|
228
|
+
tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator;
|
|
229
|
+
}
|
|
230
|
+
else if (tmpFilter[i].Operator === 'IS NOT NULL')
|
|
231
|
+
{
|
|
232
|
+
tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator;
|
|
233
|
+
}
|
|
234
|
+
else
|
|
235
|
+
{
|
|
236
|
+
tmpColumnParameter = tmpFilter[i].Parameter+'_w'+i;
|
|
237
|
+
tmpWhere += ' '+generateSafeFieldName(tmpFilter[i].Column)+' '+tmpFilter[i].Operator+' :'+tmpColumnParameter;
|
|
238
|
+
pParameters.query.parameters[tmpColumnParameter] = tmpFilter[i].Value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return tmpWhere;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Generate an ORDER BY clause from the sort array
|
|
247
|
+
*
|
|
248
|
+
* Each entry in the sort is an object like:
|
|
249
|
+
* {Column:'Color',Direction:'Descending'}
|
|
250
|
+
*
|
|
251
|
+
* @method: generateOrderBy
|
|
252
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
253
|
+
* @return: {String} Returns the field list clause
|
|
254
|
+
*/
|
|
255
|
+
var generateOrderBy = function(pParameters)
|
|
256
|
+
{
|
|
257
|
+
var tmpOrderBy = pParameters.sort;
|
|
258
|
+
if (!Array.isArray(tmpOrderBy) || tmpOrderBy.length < 1)
|
|
259
|
+
{
|
|
260
|
+
return '';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
var tmpOrderClause = ' ORDER BY';
|
|
264
|
+
for (var i = 0; i < tmpOrderBy.length; i++)
|
|
265
|
+
{
|
|
266
|
+
if (i > 0)
|
|
267
|
+
{
|
|
268
|
+
tmpOrderClause += ',';
|
|
269
|
+
}
|
|
270
|
+
tmpOrderClause += ' '+generateSafeFieldName(tmpOrderBy[i].Column);
|
|
271
|
+
|
|
272
|
+
if (tmpOrderBy[i].Direction == 'Descending')
|
|
273
|
+
{
|
|
274
|
+
tmpOrderClause += ' DESC';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return tmpOrderClause;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Generate the limit clause using PostgreSQL LIMIT/OFFSET syntax
|
|
282
|
+
*
|
|
283
|
+
* @method: generateLimit
|
|
284
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
285
|
+
* @return: {String} Returns the table limit clause
|
|
286
|
+
*/
|
|
287
|
+
var generateLimit = function(pParameters)
|
|
288
|
+
{
|
|
289
|
+
if (!pParameters.cap)
|
|
290
|
+
{
|
|
291
|
+
return '';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
var tmpLimit = ' LIMIT ' + pParameters.cap;
|
|
295
|
+
|
|
296
|
+
if (pParameters.begin !== false)
|
|
297
|
+
{
|
|
298
|
+
tmpLimit += ' OFFSET ' + pParameters.begin;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return tmpLimit;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Generate the join clause
|
|
306
|
+
*
|
|
307
|
+
* @method: generateJoins
|
|
308
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
309
|
+
* @return: {String} Returns the join clause
|
|
310
|
+
*/
|
|
311
|
+
var generateJoins = function(pParameters)
|
|
312
|
+
{
|
|
313
|
+
var tmpJoins = pParameters.join;
|
|
314
|
+
if (!Array.isArray(tmpJoins) || tmpJoins.length < 1)
|
|
315
|
+
{
|
|
316
|
+
return '';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
var tmpJoinClause = '';
|
|
320
|
+
for (var i = 0; i < tmpJoins.length; i++)
|
|
321
|
+
{
|
|
322
|
+
var join = tmpJoins[i];
|
|
323
|
+
if (join.Type && join.Table && join.From && join.To)
|
|
324
|
+
{
|
|
325
|
+
tmpJoinClause += ` ${join.Type} ${join.Table} ON ${join.From} = ${join.To}`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return tmpJoinClause;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Generate the update SET clause
|
|
334
|
+
*
|
|
335
|
+
* @method: generateUpdateSetters
|
|
336
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
337
|
+
* @return: {String} Returns the table name clause
|
|
338
|
+
*/
|
|
339
|
+
var generateUpdateSetters = function(pParameters)
|
|
340
|
+
{
|
|
341
|
+
var tmpRecords = pParameters.query.records;
|
|
342
|
+
if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
|
|
343
|
+
{
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
348
|
+
|
|
349
|
+
var tmpUpdate = '';
|
|
350
|
+
var tmpCurrentColumn = 0;
|
|
351
|
+
for(var tmpColumn in tmpRecords[0])
|
|
352
|
+
{
|
|
353
|
+
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
|
|
354
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
355
|
+
{
|
|
356
|
+
if (tmpColumn == tmpSchema[i].Column)
|
|
357
|
+
{
|
|
358
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (pParameters.query.disableAutoDateStamp &&
|
|
364
|
+
tmpSchemaEntry.Type === 'UpdateDate')
|
|
365
|
+
{
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (pParameters.query.disableAutoUserStamp &&
|
|
369
|
+
tmpSchemaEntry.Type === 'UpdateIDUser')
|
|
370
|
+
{
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
switch (tmpSchemaEntry.Type)
|
|
375
|
+
{
|
|
376
|
+
case 'AutoIdentity':
|
|
377
|
+
case 'CreateDate':
|
|
378
|
+
case 'CreateIDUser':
|
|
379
|
+
case 'DeleteDate':
|
|
380
|
+
case 'DeleteIDUser':
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (tmpCurrentColumn > 0)
|
|
384
|
+
{
|
|
385
|
+
tmpUpdate += ',';
|
|
386
|
+
}
|
|
387
|
+
switch (tmpSchemaEntry.Type)
|
|
388
|
+
{
|
|
389
|
+
case 'UpdateDate':
|
|
390
|
+
tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = ' + SQL_NOW;
|
|
391
|
+
break;
|
|
392
|
+
case 'UpdateIDUser':
|
|
393
|
+
var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
|
|
394
|
+
tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :'+tmpColumnParameter;
|
|
395
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
396
|
+
break;
|
|
397
|
+
default:
|
|
398
|
+
var tmpColumnDefaultParameter = tmpColumn+'_'+tmpCurrentColumn;
|
|
399
|
+
tmpUpdate += ' '+generateSafeFieldName(tmpColumn)+' = :'+tmpColumnDefaultParameter;
|
|
400
|
+
pParameters.query.parameters[tmpColumnDefaultParameter] = tmpRecords[0][tmpColumn];
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
tmpCurrentColumn++;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (tmpUpdate === '')
|
|
408
|
+
{
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return tmpUpdate;
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Generate the update-delete SET clause
|
|
417
|
+
*
|
|
418
|
+
* @method: generateUpdateDeleteSetters
|
|
419
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
420
|
+
* @return: {String} Returns the table name clause
|
|
421
|
+
*/
|
|
422
|
+
var generateUpdateDeleteSetters = function(pParameters)
|
|
423
|
+
{
|
|
424
|
+
if (pParameters.query.disableDeleteTracking)
|
|
425
|
+
{
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
429
|
+
|
|
430
|
+
var tmpCurrentColumn = 0;
|
|
431
|
+
var tmpHasDeletedField = false;
|
|
432
|
+
var tmpUpdate = '';
|
|
433
|
+
var tmpSchemaEntry = {Type:'Default'};
|
|
434
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
435
|
+
{
|
|
436
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
437
|
+
|
|
438
|
+
var tmpUpdateSql = null;
|
|
439
|
+
|
|
440
|
+
switch (tmpSchemaEntry.Type)
|
|
441
|
+
{
|
|
442
|
+
case 'Deleted':
|
|
443
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = 1';
|
|
444
|
+
tmpHasDeletedField = true;
|
|
445
|
+
break;
|
|
446
|
+
case 'DeleteDate':
|
|
447
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
|
|
448
|
+
break;
|
|
449
|
+
case 'UpdateDate':
|
|
450
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
|
|
451
|
+
break;
|
|
452
|
+
case 'DeleteIDUser':
|
|
453
|
+
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
|
|
454
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = :'+tmpColumnParameter;
|
|
455
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
456
|
+
break;
|
|
457
|
+
default:
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (tmpCurrentColumn > 0)
|
|
462
|
+
{
|
|
463
|
+
tmpUpdate += ',';
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
tmpUpdate += tmpUpdateSql;
|
|
467
|
+
|
|
468
|
+
tmpCurrentColumn++;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!tmpHasDeletedField ||
|
|
472
|
+
tmpUpdate === '')
|
|
473
|
+
{
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return tmpUpdate;
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Generate the update-undelete SET clause
|
|
482
|
+
*
|
|
483
|
+
* @method: generateUpdateUndeleteSetters
|
|
484
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
485
|
+
* @return: {String} Returns the table name clause
|
|
486
|
+
*/
|
|
487
|
+
var generateUpdateUndeleteSetters = function(pParameters)
|
|
488
|
+
{
|
|
489
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
490
|
+
|
|
491
|
+
var tmpCurrentColumn = 0;
|
|
492
|
+
var tmpHasDeletedField = false;
|
|
493
|
+
var tmpUpdate = '';
|
|
494
|
+
var tmpSchemaEntry = {Type:'Default'};
|
|
495
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
496
|
+
{
|
|
497
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
498
|
+
|
|
499
|
+
var tmpUpdateSql = null;
|
|
500
|
+
|
|
501
|
+
switch (tmpSchemaEntry.Type)
|
|
502
|
+
{
|
|
503
|
+
case 'Deleted':
|
|
504
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = 0';
|
|
505
|
+
tmpHasDeletedField = true;
|
|
506
|
+
break;
|
|
507
|
+
case 'UpdateDate':
|
|
508
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = ' + SQL_NOW;
|
|
509
|
+
break;
|
|
510
|
+
case 'UpdateIDUser':
|
|
511
|
+
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
|
|
512
|
+
tmpUpdateSql = ' '+generateSafeFieldName(tmpSchemaEntry.Column)+' = :'+tmpColumnParameter;
|
|
513
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
514
|
+
break;
|
|
515
|
+
default:
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (tmpCurrentColumn > 0)
|
|
520
|
+
{
|
|
521
|
+
tmpUpdate += ',';
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
tmpUpdate += tmpUpdateSql;
|
|
525
|
+
|
|
526
|
+
tmpCurrentColumn++;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (!tmpHasDeletedField ||
|
|
530
|
+
tmpUpdate === '')
|
|
531
|
+
{
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return tmpUpdate;
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Generate the create SET clause values
|
|
540
|
+
*
|
|
541
|
+
* @method: generateCreateSetValues
|
|
542
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
543
|
+
* @return: {String} Returns the table name clause
|
|
544
|
+
*/
|
|
545
|
+
var generateCreateSetValues = function(pParameters)
|
|
546
|
+
{
|
|
547
|
+
var tmpRecords = pParameters.query.records;
|
|
548
|
+
if (!Array.isArray(tmpRecords) || tmpRecords.length < 1)
|
|
549
|
+
{
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
var tmpCreateSet = '';
|
|
557
|
+
var tmpCurrentColumn = 0;
|
|
558
|
+
for(var tmpColumn in tmpRecords[0])
|
|
559
|
+
{
|
|
560
|
+
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
|
|
561
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
562
|
+
{
|
|
563
|
+
if (tmpColumn == tmpSchema[i].Column)
|
|
564
|
+
{
|
|
565
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (!pParameters.query.disableDeleteTracking)
|
|
571
|
+
{
|
|
572
|
+
if (tmpSchemaEntry.Type === 'DeleteDate' ||
|
|
573
|
+
tmpSchemaEntry.Type === 'DeleteIDUser')
|
|
574
|
+
{
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (tmpCurrentColumn > 0)
|
|
580
|
+
{
|
|
581
|
+
tmpCreateSet += ',';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
var buildDefaultDefinition = function()
|
|
585
|
+
{
|
|
586
|
+
var tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
|
|
587
|
+
tmpCreateSet += ' :'+tmpColumnParameter;
|
|
588
|
+
pParameters.query.parameters[tmpColumnParameter] = tmpRecords[0][tmpColumn];
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
var tmpColumnParameter;
|
|
592
|
+
switch (tmpSchemaEntry.Type)
|
|
593
|
+
{
|
|
594
|
+
case 'AutoIdentity':
|
|
595
|
+
if (pParameters.query.disableAutoIdentity)
|
|
596
|
+
{
|
|
597
|
+
buildDefaultDefinition();
|
|
598
|
+
}
|
|
599
|
+
else
|
|
600
|
+
{
|
|
601
|
+
// PostgreSQL SERIAL columns use DEFAULT rather than NULL
|
|
602
|
+
tmpCreateSet += ' DEFAULT';
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
case 'AutoGUID':
|
|
606
|
+
if (pParameters.query.disableAutoIdentity)
|
|
607
|
+
{
|
|
608
|
+
buildDefaultDefinition();
|
|
609
|
+
}
|
|
610
|
+
else if (tmpRecords[0][tmpColumn] &&
|
|
611
|
+
tmpRecords[0][tmpColumn].length >= 5 &&
|
|
612
|
+
tmpRecords[0][tmpColumn] !== '0x0000000000000000') //stricture default
|
|
613
|
+
{
|
|
614
|
+
buildDefaultDefinition();
|
|
615
|
+
}
|
|
616
|
+
else
|
|
617
|
+
{
|
|
618
|
+
tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
|
|
619
|
+
tmpCreateSet += ' :'+tmpColumnParameter;
|
|
620
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.UUID;
|
|
621
|
+
}
|
|
622
|
+
break;
|
|
623
|
+
case 'UpdateDate':
|
|
624
|
+
case 'CreateDate':
|
|
625
|
+
case 'DeleteDate':
|
|
626
|
+
if (pParameters.query.disableAutoDateStamp)
|
|
627
|
+
{
|
|
628
|
+
buildDefaultDefinition();
|
|
629
|
+
}
|
|
630
|
+
else
|
|
631
|
+
{
|
|
632
|
+
tmpCreateSet += ' ' + SQL_NOW;
|
|
633
|
+
}
|
|
634
|
+
break;
|
|
635
|
+
case 'DeleteIDUser':
|
|
636
|
+
case 'UpdateIDUser':
|
|
637
|
+
case 'CreateIDUser':
|
|
638
|
+
if (pParameters.query.disableAutoUserStamp)
|
|
639
|
+
{
|
|
640
|
+
buildDefaultDefinition();
|
|
641
|
+
}
|
|
642
|
+
else
|
|
643
|
+
{
|
|
644
|
+
tmpColumnParameter = tmpColumn+'_'+tmpCurrentColumn;
|
|
645
|
+
tmpCreateSet += ' :'+tmpColumnParameter;
|
|
646
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
default:
|
|
650
|
+
buildDefaultDefinition();
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
tmpCurrentColumn++;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (tmpCreateSet === '')
|
|
658
|
+
{
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return tmpCreateSet;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Generate the create SET clause column list
|
|
667
|
+
*
|
|
668
|
+
* @method: generateCreateSetList
|
|
669
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
670
|
+
* @return: {String} Returns the table name clause
|
|
671
|
+
*/
|
|
672
|
+
var generateCreateSetList = function(pParameters)
|
|
673
|
+
{
|
|
674
|
+
var tmpRecords = pParameters.query.records;
|
|
675
|
+
|
|
676
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
677
|
+
|
|
678
|
+
var tmpCreateSet = '';
|
|
679
|
+
for(var tmpColumn in tmpRecords[0])
|
|
680
|
+
{
|
|
681
|
+
var tmpSchemaEntry = {Column:tmpColumn, Type:'Default'};
|
|
682
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
683
|
+
{
|
|
684
|
+
if (tmpColumn == tmpSchema[i].Column)
|
|
685
|
+
{
|
|
686
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (!pParameters.query.disableDeleteTracking)
|
|
691
|
+
{
|
|
692
|
+
if (tmpSchemaEntry.Type === 'DeleteDate' ||
|
|
693
|
+
tmpSchemaEntry.Type === 'DeleteIDUser')
|
|
694
|
+
{
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
switch (tmpSchemaEntry.Type)
|
|
699
|
+
{
|
|
700
|
+
default:
|
|
701
|
+
if (tmpCreateSet != '')
|
|
702
|
+
{
|
|
703
|
+
tmpCreateSet += ',';
|
|
704
|
+
}
|
|
705
|
+
tmpCreateSet += ' '+generateSafeFieldName(tmpColumn);
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return tmpCreateSet;
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
var Create = function(pParameters)
|
|
715
|
+
{
|
|
716
|
+
var tmpTableName = generateTableName(pParameters);
|
|
717
|
+
var tmpCreateSetList = generateCreateSetList(pParameters);
|
|
718
|
+
var tmpCreateSetValues = generateCreateSetValues(pParameters);
|
|
719
|
+
|
|
720
|
+
if (!tmpCreateSetValues)
|
|
721
|
+
{
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return 'INSERT INTO'+tmpTableName+' ('+tmpCreateSetList+') VALUES ('+tmpCreateSetValues+') RETURNING *;';
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Read one or many records
|
|
731
|
+
*
|
|
732
|
+
* @method Read
|
|
733
|
+
* @param {Object} pParameters SQL Query parameters
|
|
734
|
+
* @return {String} Returns the current Query for chaining.
|
|
735
|
+
*/
|
|
736
|
+
var Read = function(pParameters)
|
|
737
|
+
{
|
|
738
|
+
var tmpFieldList = generateFieldList(pParameters);
|
|
739
|
+
var tmpTableName = generateTableName(pParameters);
|
|
740
|
+
var tmpWhere = generateWhere(pParameters);
|
|
741
|
+
var tmpJoin = generateJoins(pParameters);
|
|
742
|
+
var tmpOrderBy = generateOrderBy(pParameters);
|
|
743
|
+
var tmpLimit = generateLimit(pParameters);
|
|
744
|
+
const tmpOptDistinct = pParameters.distinct ? ' DISTINCT' : '';
|
|
745
|
+
|
|
746
|
+
if (pParameters.queryOverride)
|
|
747
|
+
{
|
|
748
|
+
try
|
|
749
|
+
{
|
|
750
|
+
var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
|
|
751
|
+
return tmpQueryTemplate({FieldList:tmpFieldList, TableName:tmpTableName, Where:tmpWhere, Join:tmpJoin, OrderBy:tmpOrderBy, Limit:tmpLimit, Distinct: tmpOptDistinct, _Params: pParameters});
|
|
752
|
+
}
|
|
753
|
+
catch (pError)
|
|
754
|
+
{
|
|
755
|
+
console.log('Error with custom Read Query ['+pParameters.queryOverride+']: '+pError);
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
return `SELECT${tmpOptDistinct}${tmpFieldList} FROM${tmpTableName}${tmpJoin}${tmpWhere}${tmpOrderBy}${tmpLimit};`;
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
var Update = function(pParameters)
|
|
764
|
+
{
|
|
765
|
+
var tmpTableName = generateTableName(pParameters);
|
|
766
|
+
var tmpWhere = generateWhere(pParameters);
|
|
767
|
+
var tmpUpdateSetters = generateUpdateSetters(pParameters);
|
|
768
|
+
|
|
769
|
+
if (!tmpUpdateSetters)
|
|
770
|
+
{
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateSetters+tmpWhere+';';
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
var Delete = function(pParameters)
|
|
778
|
+
{
|
|
779
|
+
var tmpTableName = generateTableName(pParameters);
|
|
780
|
+
var tmpWhere = generateWhere(pParameters);
|
|
781
|
+
var tmpUpdateDeleteSetters = generateUpdateDeleteSetters(pParameters);
|
|
782
|
+
|
|
783
|
+
if (tmpUpdateDeleteSetters)
|
|
784
|
+
{
|
|
785
|
+
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateDeleteSetters+tmpWhere+';';
|
|
786
|
+
}
|
|
787
|
+
else
|
|
788
|
+
{
|
|
789
|
+
return 'DELETE FROM'+tmpTableName+tmpWhere+';';
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
var Undelete = function(pParameters)
|
|
794
|
+
{
|
|
795
|
+
var tmpTableName = generateTableName(pParameters);
|
|
796
|
+
let tmpDeleteTrackingState = pParameters.query.disableDeleteTracking;
|
|
797
|
+
pParameters.query.disableDeleteTracking = true;
|
|
798
|
+
var tmpWhere = generateWhere(pParameters);
|
|
799
|
+
var tmpUpdateUndeleteSetters = generateUpdateUndeleteSetters(pParameters);
|
|
800
|
+
pParameters.query.disableDeleteTracking = tmpDeleteTrackingState;
|
|
801
|
+
|
|
802
|
+
if (tmpUpdateUndeleteSetters)
|
|
803
|
+
{
|
|
804
|
+
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateUndeleteSetters+tmpWhere+';';
|
|
805
|
+
}
|
|
806
|
+
else
|
|
807
|
+
{
|
|
808
|
+
return 'SELECT NULL;';
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
var Count = function(pParameters)
|
|
813
|
+
{
|
|
814
|
+
var tmpFieldList = pParameters.distinct ? generateFieldList(pParameters, true) : '*';
|
|
815
|
+
var tmpTableName = generateTableName(pParameters);
|
|
816
|
+
var tmpJoin = generateJoins(pParameters);
|
|
817
|
+
var tmpWhere = generateWhere(pParameters);
|
|
818
|
+
if (pParameters.distinct && tmpFieldList.length < 1)
|
|
819
|
+
{
|
|
820
|
+
console.warn('Distinct requested but no field list or schema are available, so not honoring distinct for count query.');
|
|
821
|
+
}
|
|
822
|
+
const tmpOptDistinct = pParameters.distinct && tmpFieldList.length > 0 ? 'DISTINCT' : '';
|
|
823
|
+
|
|
824
|
+
if (pParameters.queryOverride)
|
|
825
|
+
{
|
|
826
|
+
try
|
|
827
|
+
{
|
|
828
|
+
var tmpQueryTemplate = _Fable.Utility.template(pParameters.queryOverride);
|
|
829
|
+
return tmpQueryTemplate({FieldList:[], TableName:tmpTableName, Where:tmpWhere, OrderBy:'', Limit:'', Distinct: tmpOptDistinct, _Params: pParameters});
|
|
830
|
+
}
|
|
831
|
+
catch (pError)
|
|
832
|
+
{
|
|
833
|
+
console.log('Error with custom Count Query ['+pParameters.queryOverride+']: '+pError);
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return `SELECT COUNT(${tmpOptDistinct}${tmpFieldList || '*'}) AS RowCount FROM${tmpTableName}${tmpJoin}${tmpWhere};`;
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
var tmpDialect = ({
|
|
842
|
+
Create: Create,
|
|
843
|
+
Read: Read,
|
|
844
|
+
Update: Update,
|
|
845
|
+
Delete: Delete,
|
|
846
|
+
Undelete: Undelete,
|
|
847
|
+
Count: Count
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Dialect Name
|
|
852
|
+
*
|
|
853
|
+
* @property name
|
|
854
|
+
* @type string
|
|
855
|
+
*/
|
|
856
|
+
Object.defineProperty(tmpDialect, 'name',
|
|
857
|
+
{
|
|
858
|
+
get: function() { return 'PostgreSQL'; },
|
|
859
|
+
enumerable: true
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
return tmpDialect;
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
module.exports = FoxHoundDialectPostgreSQL;
|