foxhound 1.0.37 → 1.0.38
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/.config/configstore/update-notifier-npm.json +1 -1
- package/Dockerfile +14 -0
- package/debug/Harness.js +15 -2
- package/package.json +1 -1
- package/source/FoxHound.js +8 -0
- package/source/dialects/ALASQL/FoxHound-Dialect-ALASQL.js +166 -74
- package/source/dialects/English/FoxHound-Dialect-English.js +8 -0
- package/source/dialects/MySQL/FoxHound-Dialect-MySQL.js +171 -79
- package/test/FoxHound-Dialect-ALASQL_tests.js +37 -0
- package/test/FoxHound-Dialect-English_tests.js +14 -0
- package/test/FoxHound-Dialect-MySQL_tests.js +37 -0
package/Dockerfile
CHANGED
|
@@ -17,6 +17,20 @@ RUN echo "...mapping library specific volumes..."
|
|
|
17
17
|
VOLUME /home/coder/foxhound
|
|
18
18
|
# VOLUME /home/coder/foxhound/node_modules
|
|
19
19
|
|
|
20
|
+
RUN echo "...installing vscode extensions..."
|
|
21
|
+
RUN code-server --install-extension hbenl.vscode-mocha-test-adapter \
|
|
22
|
+
code-server --install-extension hbenl.vscode-test-explorer \
|
|
23
|
+
code-server --install-extension hbenl.test-adapter-converter \
|
|
24
|
+
code-server --install-extension cweijan.vscode-mysql-client2 \
|
|
25
|
+
code-server --install-extension daylerees.rainglow \
|
|
26
|
+
code-server --install-extension oderwat.indent-rainbow \
|
|
27
|
+
code-server --install-extension evan-buss.font-switcher \
|
|
28
|
+
code-server --install-extension vscode-icons-team.vscode-icons \
|
|
29
|
+
code-server --install-extension bengreenier.vscode-node-readme \
|
|
30
|
+
code-server --install-extension bierner.color-info \
|
|
31
|
+
code-server --install-extension dbaeumer.vscode-eslint \
|
|
32
|
+
code-server --install-extension PKief.material-icon-theme
|
|
33
|
+
|
|
20
34
|
SHELL ["/bin/bash", "-c"]
|
|
21
35
|
USER coder
|
|
22
36
|
|
package/debug/Harness.js
CHANGED
|
@@ -24,8 +24,10 @@ var _AnimalSchemaWithoutDeleted = (
|
|
|
24
24
|
{ Column: "UpdatingIDUser", Type:"UpdateIDUser" }
|
|
25
25
|
]);
|
|
26
26
|
|
|
27
|
+
/*
|
|
27
28
|
var tmpQuery = libFoxHound.new(libFable)
|
|
28
|
-
|
|
29
|
+
//.setDialect('MeadowEndpoints')
|
|
30
|
+
.setDialect('MySQL')
|
|
29
31
|
.setScope('Animal')
|
|
30
32
|
.setDataElements(['Name', 'Age', 'Cost'])
|
|
31
33
|
.setCap(100)
|
|
@@ -39,7 +41,18 @@ var tmpQuery = libFoxHound.new(libFable)
|
|
|
39
41
|
tmpQuery.setLogLevel(3).addSort('Age');
|
|
40
42
|
// Build the query
|
|
41
43
|
tmpQuery.buildReadQuery();
|
|
42
|
-
// This is the query generated by the
|
|
44
|
+
// This is the query generated by the set dialect
|
|
43
45
|
libFable.log.trace('Select Query', tmpQuery.query);
|
|
46
|
+
*/
|
|
47
|
+
var tmpQuery = libFoxHound.new(libFable)
|
|
48
|
+
//.setDialect('MeadowEndpoints')
|
|
49
|
+
.setDialect('MySQL')
|
|
50
|
+
.setScope('Animal')
|
|
51
|
+
.addFilter('IDAnimal', 10);
|
|
52
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
53
|
+
// Build the query
|
|
54
|
+
tmpQuery.buildUndeleteQuery();
|
|
55
|
+
// This is the query generated by the set dialect
|
|
56
|
+
libFable.log.trace('Query: ', tmpQuery.query);
|
|
44
57
|
|
|
45
58
|
console.log(tmpQuery.query.body);
|
package/package.json
CHANGED
package/source/FoxHound.js
CHANGED
|
@@ -794,6 +794,13 @@ var FoxHound = function()
|
|
|
794
794
|
return this;
|
|
795
795
|
};
|
|
796
796
|
|
|
797
|
+
var buildUndeleteQuery = function()
|
|
798
|
+
{
|
|
799
|
+
checkDialect();
|
|
800
|
+
_Parameters.query.body = _Dialect.Undelete(_Parameters);
|
|
801
|
+
return this;
|
|
802
|
+
};
|
|
803
|
+
|
|
797
804
|
var buildCountQuery = function()
|
|
798
805
|
{
|
|
799
806
|
checkDialect();
|
|
@@ -836,6 +843,7 @@ var FoxHound = function()
|
|
|
836
843
|
buildReadQuery: buildReadQuery,
|
|
837
844
|
buildUpdateQuery: buildUpdateQuery,
|
|
838
845
|
buildDeleteQuery: buildDeleteQuery,
|
|
846
|
+
buildUndeleteQuery: buildUndeleteQuery,
|
|
839
847
|
buildCountQuery: buildCountQuery,
|
|
840
848
|
|
|
841
849
|
clone: clone,
|
|
@@ -20,24 +20,24 @@ var libUnderscore = require('underscore');
|
|
|
20
20
|
var FoxHoundDialectALASQL = function()
|
|
21
21
|
{
|
|
22
22
|
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
* Generate a table name from the scope.
|
|
24
|
+
*
|
|
25
|
+
* Because ALASQL is all in-memory, and can be run in two modes (anonymous
|
|
26
|
+
* working on arrays or table-based) we are going to make this a programmable
|
|
27
|
+
* value. Then we can share the code across both providers.
|
|
28
|
+
*
|
|
29
|
+
* @method: generateTableName
|
|
30
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
31
|
+
* @return: {String} Returns the table name clause
|
|
32
|
+
*/
|
|
33
33
|
var generateTableName = function(pParameters)
|
|
34
34
|
{
|
|
35
35
|
return ' '+pParameters.scope;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
* Escape columns, because ALASQL has more reserved KWs than most SQL dialects
|
|
40
|
+
*/
|
|
41
41
|
var escapeColumn = (pColumn, pParameters) =>
|
|
42
42
|
{
|
|
43
43
|
if (pColumn.indexOf('.') < 0)
|
|
@@ -61,16 +61,16 @@ var FoxHoundDialectALASQL = function()
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
* Generate a field list from the array of dataElements
|
|
65
|
+
*
|
|
66
|
+
* Each entry in the dataElements is a simple string
|
|
67
|
+
*
|
|
68
|
+
* @method: generateFieldList
|
|
69
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
70
|
+
* @param {Boolean} pIsForCountClause (optional) If true, generate fields for use within a count clause.
|
|
71
|
+
* @return: {String} Returns the field list clause, or empty string if explicit fields are requested but cannot be fulfilled
|
|
72
|
+
* due to missing schema.
|
|
73
|
+
*/
|
|
74
74
|
var generateFieldList = function(pParameters, pIsForCountClause)
|
|
75
75
|
{
|
|
76
76
|
var tmpDataElements = pParameters.dataElements;
|
|
@@ -109,9 +109,9 @@ var FoxHoundDialectALASQL = function()
|
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
* Generate a query from the array of where clauses
|
|
113
|
+
*
|
|
114
|
+
* Each clause is an object like:
|
|
115
115
|
{
|
|
116
116
|
Column:'Name',
|
|
117
117
|
Operator:'EQ',
|
|
@@ -119,11 +119,11 @@ var FoxHoundDialectALASQL = function()
|
|
|
119
119
|
Connector:'And',
|
|
120
120
|
Parameter:'Name'
|
|
121
121
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
*
|
|
123
|
+
* @method: generateWhere
|
|
124
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
125
|
+
* @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
|
|
126
|
+
*/
|
|
127
127
|
var generateWhere = function(pParameters)
|
|
128
128
|
{
|
|
129
129
|
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
|
|
@@ -229,15 +229,15 @@ var FoxHoundDialectALASQL = function()
|
|
|
229
229
|
};
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
* Generate an ORDER BY clause from the sort array
|
|
233
|
+
*
|
|
234
|
+
* Each entry in the sort is an object like:
|
|
235
|
+
* {Column:'Color',Direction:'Descending'}
|
|
236
|
+
*
|
|
237
|
+
* @method: generateOrderBy
|
|
238
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
239
|
+
* @return: {String} Returns the field list clause
|
|
240
|
+
*/
|
|
241
241
|
var generateOrderBy = function(pParameters)
|
|
242
242
|
{
|
|
243
243
|
var tmpOrderBy = pParameters.sort;
|
|
@@ -264,12 +264,12 @@ var FoxHoundDialectALASQL = function()
|
|
|
264
264
|
};
|
|
265
265
|
|
|
266
266
|
/**
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
* Generate the limit clause
|
|
268
|
+
*
|
|
269
|
+
* @method: generateLimit
|
|
270
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
271
|
+
* @return: {String} Returns the table name clause
|
|
272
|
+
*/
|
|
273
273
|
var generateLimit = function(pParameters)
|
|
274
274
|
{
|
|
275
275
|
if (!pParameters.cap)
|
|
@@ -291,12 +291,12 @@ var FoxHoundDialectALASQL = function()
|
|
|
291
291
|
};
|
|
292
292
|
|
|
293
293
|
/**
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
* Generate the update SET clause
|
|
295
|
+
*
|
|
296
|
+
* @method: generateUpdateSetters
|
|
297
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
298
|
+
* @return: {String} Returns the table name clause
|
|
299
|
+
*/
|
|
300
300
|
var generateUpdateSetters = function(pParameters)
|
|
301
301
|
{
|
|
302
302
|
var tmpRecords = pParameters.query.records;
|
|
@@ -391,12 +391,12 @@ var FoxHoundDialectALASQL = function()
|
|
|
391
391
|
};
|
|
392
392
|
|
|
393
393
|
/**
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
394
|
+
* Generate the update-delete SET clause
|
|
395
|
+
*
|
|
396
|
+
* @method: generateUpdateDeleteSetters
|
|
397
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
398
|
+
* @return: {String} Returns the table name clause
|
|
399
|
+
*/
|
|
400
400
|
var generateUpdateDeleteSetters = function(pParameters)
|
|
401
401
|
{
|
|
402
402
|
if (pParameters.query.disableDeleteTracking)
|
|
@@ -468,12 +468,86 @@ var FoxHoundDialectALASQL = function()
|
|
|
468
468
|
};
|
|
469
469
|
|
|
470
470
|
/**
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
471
|
+
* Generate the update-delete SET clause
|
|
472
|
+
*
|
|
473
|
+
* @method: generateUpdateDeleteSetters
|
|
474
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
475
|
+
* @return: {String} Returns the table name clause
|
|
476
|
+
*/
|
|
477
|
+
var generateUpdateUndeleteSetters = function(pParameters)
|
|
478
|
+
{
|
|
479
|
+
if (pParameters.query.disableDeleteTracking)
|
|
480
|
+
{
|
|
481
|
+
//Don't generate an UPDATE query if Delete tracking is disabled
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
|
|
485
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
486
|
+
|
|
487
|
+
var tmpCurrentColumn = 0;
|
|
488
|
+
var tmpHasDeletedField = false;
|
|
489
|
+
var tmpUpdate = '';
|
|
490
|
+
// No hash table yet, so, we will just linear search it for now.
|
|
491
|
+
// This uses the schema to decide if we want to treat a column differently on insert
|
|
492
|
+
var tmpSchemaEntry = {Type:'Default'};
|
|
493
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
494
|
+
{
|
|
495
|
+
// There is a schema entry for it. Process it accordingly.
|
|
496
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
497
|
+
|
|
498
|
+
var tmpUpdateSql = null;
|
|
499
|
+
|
|
500
|
+
switch (tmpSchemaEntry.Type)
|
|
501
|
+
{
|
|
502
|
+
case 'Deleted':
|
|
503
|
+
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = 0';
|
|
504
|
+
tmpHasDeletedField = true; //this field is required in order for query to be built
|
|
505
|
+
break;
|
|
506
|
+
case 'UpdateDate':
|
|
507
|
+
// Delete operation is an Update, so we should stamp the update time
|
|
508
|
+
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = NOW()';
|
|
509
|
+
break;
|
|
510
|
+
case 'UpdateIDUser':
|
|
511
|
+
// This is the user ID, which we hope is in the query.
|
|
512
|
+
// This is how to deal with a normal column
|
|
513
|
+
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
|
|
514
|
+
tmpUpdateSql = ' '+escapeColumn(tmpSchemaEntry.Column, pParameters)+' = :'+tmpColumnParameter;
|
|
515
|
+
// Set the query parameter
|
|
516
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
517
|
+
break;
|
|
518
|
+
default:
|
|
519
|
+
//DON'T allow update of other fields in this query
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (tmpCurrentColumn > 0)
|
|
524
|
+
{
|
|
525
|
+
tmpUpdate += ',';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
tmpUpdate += tmpUpdateSql;
|
|
529
|
+
|
|
530
|
+
// We use a number to make sure parameters are unique.
|
|
531
|
+
tmpCurrentColumn++;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// We need to tell the query not to generate improperly if there are no values set.
|
|
535
|
+
if (!tmpHasDeletedField ||
|
|
536
|
+
tmpUpdate === '')
|
|
537
|
+
{
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return tmpUpdate;
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Generate the create SET clause
|
|
546
|
+
*
|
|
547
|
+
* @method: generateCreateSetList
|
|
548
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
549
|
+
* @return: {String} Returns the table name clause
|
|
550
|
+
*/
|
|
477
551
|
var generateCreateSetValues = function(pParameters)
|
|
478
552
|
{
|
|
479
553
|
var tmpRecords = pParameters.query.records;
|
|
@@ -613,12 +687,12 @@ var FoxHoundDialectALASQL = function()
|
|
|
613
687
|
};
|
|
614
688
|
|
|
615
689
|
/**
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
690
|
+
* Generate the create SET clause
|
|
691
|
+
*
|
|
692
|
+
* @method: generateCreateSetList
|
|
693
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
694
|
+
* @return: {String} Returns the table name clause
|
|
695
|
+
*/
|
|
622
696
|
var generateCreateSetList = function(pParameters)
|
|
623
697
|
{
|
|
624
698
|
// The records were already validated by generateCreateSetValues
|
|
@@ -755,6 +829,23 @@ var FoxHoundDialectALASQL = function()
|
|
|
755
829
|
}
|
|
756
830
|
};
|
|
757
831
|
|
|
832
|
+
var Undelete = function(pParameters)
|
|
833
|
+
{
|
|
834
|
+
var tmpTableName = generateTableName(pParameters);
|
|
835
|
+
var tmpWhere = generateWhere(pParameters);
|
|
836
|
+
var tmpUpdateUndeleteSetters = generateUpdateUndeleteSetters(pParameters);
|
|
837
|
+
|
|
838
|
+
if (tmpUpdateUndeleteSetters)
|
|
839
|
+
{
|
|
840
|
+
//If it has a deleted bit, update it instead of actually deleting the record
|
|
841
|
+
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateUndeleteSetters+tmpWhere+';';
|
|
842
|
+
}
|
|
843
|
+
else
|
|
844
|
+
{
|
|
845
|
+
return 'SELECT NULL;';
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
758
849
|
var Count = function(pParameters)
|
|
759
850
|
{
|
|
760
851
|
var tmpTableName = generateTableName(pParameters);
|
|
@@ -790,15 +881,16 @@ var FoxHoundDialectALASQL = function()
|
|
|
790
881
|
Read: Read,
|
|
791
882
|
Update: Update,
|
|
792
883
|
Delete: Delete,
|
|
884
|
+
Undelete: Undelete,
|
|
793
885
|
Count: Count
|
|
794
886
|
});
|
|
795
887
|
|
|
796
888
|
/**
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
889
|
+
* Dialect Name
|
|
890
|
+
*
|
|
891
|
+
* @property name
|
|
892
|
+
* @type string
|
|
893
|
+
*/
|
|
802
894
|
Object.defineProperty(tmpDialect, 'name',
|
|
803
895
|
{
|
|
804
896
|
get: function() { return 'ALASQL'; },
|
|
@@ -54,6 +54,13 @@ var FoxHoundDialectEnglish = function()
|
|
|
54
54
|
return 'I am deleting your '+tmpScope+'.';
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
+
var Undelete = function(pParameters)
|
|
58
|
+
{
|
|
59
|
+
var tmpScope = pParameters.scope;
|
|
60
|
+
|
|
61
|
+
return 'I am undeleting your '+tmpScope+'.';
|
|
62
|
+
};
|
|
63
|
+
|
|
57
64
|
var Count = function(pParameters)
|
|
58
65
|
{
|
|
59
66
|
var tmpScope = pParameters.scope;
|
|
@@ -67,6 +74,7 @@ var FoxHoundDialectEnglish = function()
|
|
|
67
74
|
Read: Read,
|
|
68
75
|
Update: Update,
|
|
69
76
|
Delete: Delete,
|
|
77
|
+
Undelete: Undelete,
|
|
70
78
|
Count: Count
|
|
71
79
|
});
|
|
72
80
|
|
|
@@ -23,12 +23,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
23
23
|
const SQL_NOW = "NOW(3)";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
32
|
var generateTableName = function(pParameters)
|
|
33
33
|
{
|
|
34
34
|
if (pParameters.scope && pParameters.scope.indexOf('`') >= 0)
|
|
@@ -38,16 +38,16 @@ var FoxHoundDialectMySQL = function()
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
51
|
var generateFieldList = function(pParameters, pIsForCountClause)
|
|
52
52
|
{
|
|
53
53
|
var tmpDataElements = pParameters.dataElements;
|
|
@@ -106,8 +106,8 @@ var FoxHoundDialectMySQL = function()
|
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
* Ensure a field name is properly escaped.
|
|
110
|
+
*/
|
|
111
111
|
var generateSafeFieldName = function(pFieldName)
|
|
112
112
|
{
|
|
113
113
|
let pFieldNames = pFieldName.split('.');
|
|
@@ -122,9 +122,9 @@ var FoxHoundDialectMySQL = function()
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
* Generate a query from the array of where clauses
|
|
126
|
+
*
|
|
127
|
+
* Each clause is an object like:
|
|
128
128
|
{
|
|
129
129
|
Column:'Name',
|
|
130
130
|
Operator:'EQ',
|
|
@@ -132,11 +132,11 @@ var FoxHoundDialectMySQL = function()
|
|
|
132
132
|
Connector:'And',
|
|
133
133
|
Parameter:'Name'
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
*
|
|
136
|
+
* @method: generateWhere
|
|
137
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
138
|
+
* @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary
|
|
139
|
+
*/
|
|
140
140
|
var generateWhere = function(pParameters)
|
|
141
141
|
{
|
|
142
142
|
var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : [];
|
|
@@ -247,15 +247,15 @@ var FoxHoundDialectMySQL = function()
|
|
|
247
247
|
};
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
250
|
+
* Generate an ORDER BY clause from the sort array
|
|
251
|
+
*
|
|
252
|
+
* Each entry in the sort is an object like:
|
|
253
|
+
* {Column:'Color',Direction:'Descending'}
|
|
254
|
+
*
|
|
255
|
+
* @method: generateOrderBy
|
|
256
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
257
|
+
* @return: {String} Returns the field list clause
|
|
258
|
+
*/
|
|
259
259
|
var generateOrderBy = function(pParameters)
|
|
260
260
|
{
|
|
261
261
|
var tmpOrderBy = pParameters.sort;
|
|
@@ -282,12 +282,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
282
282
|
};
|
|
283
283
|
|
|
284
284
|
/**
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
* Generate the limit clause
|
|
286
|
+
*
|
|
287
|
+
* @method: generateLimit
|
|
288
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
289
|
+
* @return: {String} Returns the table name clause
|
|
290
|
+
*/
|
|
291
291
|
var generateLimit = function(pParameters)
|
|
292
292
|
{
|
|
293
293
|
if (!pParameters.cap)
|
|
@@ -308,12 +308,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
308
308
|
};
|
|
309
309
|
|
|
310
310
|
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
* Generate the join clause
|
|
312
|
+
*
|
|
313
|
+
* @method: generateJoins
|
|
314
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
315
|
+
* @return: {String} Returns the join clause
|
|
316
|
+
*/
|
|
317
317
|
var generateJoins = function(pParameters)
|
|
318
318
|
{
|
|
319
319
|
var tmpJoins = pParameters.join;
|
|
@@ -337,12 +337,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
340
|
+
* Generate the update SET clause
|
|
341
|
+
*
|
|
342
|
+
* @method: generateUpdateSetters
|
|
343
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
344
|
+
* @return: {String} Returns the table name clause
|
|
345
|
+
*/
|
|
346
346
|
var generateUpdateSetters = function(pParameters)
|
|
347
347
|
{
|
|
348
348
|
var tmpRecords = pParameters.query.records;
|
|
@@ -437,12 +437,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
437
437
|
};
|
|
438
438
|
|
|
439
439
|
/**
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
440
|
+
* Generate the update-delete SET clause
|
|
441
|
+
*
|
|
442
|
+
* @method: generateUpdateDeleteSetters
|
|
443
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
444
|
+
* @return: {String} Returns the table name clause
|
|
445
|
+
*/
|
|
446
446
|
var generateUpdateDeleteSetters = function(pParameters)
|
|
447
447
|
{
|
|
448
448
|
if (pParameters.query.disableDeleteTracking)
|
|
@@ -452,7 +452,7 @@ var FoxHoundDialectMySQL = function()
|
|
|
452
452
|
}
|
|
453
453
|
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
|
|
454
454
|
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
455
|
-
|
|
455
|
+
|
|
456
456
|
var tmpCurrentColumn = 0;
|
|
457
457
|
var tmpHasDeletedField = false;
|
|
458
458
|
var tmpUpdate = '';
|
|
@@ -463,9 +463,9 @@ var FoxHoundDialectMySQL = function()
|
|
|
463
463
|
{
|
|
464
464
|
// There is a schema entry for it. Process it accordingly.
|
|
465
465
|
tmpSchemaEntry = tmpSchema[i];
|
|
466
|
-
|
|
466
|
+
|
|
467
467
|
var tmpUpdateSql = null;
|
|
468
|
-
|
|
468
|
+
|
|
469
469
|
switch (tmpSchemaEntry.Type)
|
|
470
470
|
{
|
|
471
471
|
case 'Deleted':
|
|
@@ -491,6 +491,78 @@ var FoxHoundDialectMySQL = function()
|
|
|
491
491
|
//DON'T allow update of other fields in this query
|
|
492
492
|
continue;
|
|
493
493
|
}
|
|
494
|
+
|
|
495
|
+
if (tmpCurrentColumn > 0)
|
|
496
|
+
{
|
|
497
|
+
tmpUpdate += ',';
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
tmpUpdate += tmpUpdateSql;
|
|
501
|
+
|
|
502
|
+
// We use a number to make sure parameters are unique.
|
|
503
|
+
tmpCurrentColumn++;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// We need to tell the query not to generate improperly if there are no values set.
|
|
507
|
+
if (!tmpHasDeletedField ||
|
|
508
|
+
tmpUpdate === '')
|
|
509
|
+
{
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return tmpUpdate;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Generate the update-undelete SET clause
|
|
518
|
+
*
|
|
519
|
+
* @method: generateUpdateUndeleteSetters
|
|
520
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
521
|
+
* @return: {String} Returns the table name clause
|
|
522
|
+
*/
|
|
523
|
+
var generateUpdateUndeleteSetters = function(pParameters)
|
|
524
|
+
{
|
|
525
|
+
// TODO: Eliminate this disableDeleteTracking stuff that was added
|
|
526
|
+
if (pParameters.query.disableDeleteTracking)
|
|
527
|
+
{
|
|
528
|
+
//Don't generate an UPDATE query if Delete tracking is disabled
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
// Check if there is a schema. If so, we will use it to decide if these are parameterized or not.
|
|
532
|
+
var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : [];
|
|
533
|
+
|
|
534
|
+
var tmpCurrentColumn = 0;
|
|
535
|
+
var tmpHasDeletedField = false;
|
|
536
|
+
var tmpUpdate = '';
|
|
537
|
+
// No hash table yet, so, we will just linear search it for now.
|
|
538
|
+
// This uses the schema to decide if we want to treat a column differently on insert
|
|
539
|
+
var tmpSchemaEntry = {Type:'Default'};
|
|
540
|
+
for (var i = 0; i < tmpSchema.length; i++)
|
|
541
|
+
{
|
|
542
|
+
// There is a schema entry for it. Process it accordingly.
|
|
543
|
+
tmpSchemaEntry = tmpSchema[i];
|
|
544
|
+
|
|
545
|
+
var tmpUpdateSql = null;
|
|
546
|
+
|
|
547
|
+
switch (tmpSchemaEntry.Type)
|
|
548
|
+
{
|
|
549
|
+
case 'Deleted':
|
|
550
|
+
tmpUpdateSql = ' '+tmpSchemaEntry.Column+' = 0';
|
|
551
|
+
tmpHasDeletedField = true; //this field is required in order for query to be built
|
|
552
|
+
break;
|
|
553
|
+
case 'UpdateDate':
|
|
554
|
+
// The undelete operation is an Update, so we should stamp the update time
|
|
555
|
+
tmpUpdateSql = ' '+tmpSchemaEntry.Column+' = ' + SQL_NOW;
|
|
556
|
+
break;
|
|
557
|
+
case 'UpdateIDUser':
|
|
558
|
+
var tmpColumnParameter = tmpSchemaEntry.Column+'_'+tmpCurrentColumn;
|
|
559
|
+
tmpUpdateSql = ' '+tmpSchemaEntry.Column+' = :'+tmpColumnParameter;
|
|
560
|
+
pParameters.query.parameters[tmpColumnParameter] = pParameters.query.IDUser;
|
|
561
|
+
break;
|
|
562
|
+
default:
|
|
563
|
+
//DON'T allow update of other fields in this query
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
494
566
|
|
|
495
567
|
if (tmpCurrentColumn > 0)
|
|
496
568
|
{
|
|
@@ -514,12 +586,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
514
586
|
};
|
|
515
587
|
|
|
516
588
|
/**
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
589
|
+
* Generate the create SET clause
|
|
590
|
+
*
|
|
591
|
+
* @method: generateCreateSetList
|
|
592
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
593
|
+
* @return: {String} Returns the table name clause
|
|
594
|
+
*/
|
|
523
595
|
var generateCreateSetValues = function(pParameters)
|
|
524
596
|
{
|
|
525
597
|
var tmpRecords = pParameters.query.records;
|
|
@@ -659,12 +731,12 @@ var FoxHoundDialectMySQL = function()
|
|
|
659
731
|
};
|
|
660
732
|
|
|
661
733
|
/**
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
734
|
+
* Generate the create SET clause
|
|
735
|
+
*
|
|
736
|
+
* @method: generateCreateSetList
|
|
737
|
+
* @param: {Object} pParameters SQL Query Parameters
|
|
738
|
+
* @return: {String} Returns the table name clause
|
|
739
|
+
*/
|
|
668
740
|
var generateCreateSetList = function(pParameters)
|
|
669
741
|
{
|
|
670
742
|
// The records were already validated by generateCreateSetValues
|
|
@@ -802,6 +874,25 @@ var FoxHoundDialectMySQL = function()
|
|
|
802
874
|
}
|
|
803
875
|
};
|
|
804
876
|
|
|
877
|
+
var Undelete = function(pParameters)
|
|
878
|
+
{
|
|
879
|
+
var tmpTableName = generateTableName(pParameters);
|
|
880
|
+
var tmpWhere = generateWhere(pParameters);
|
|
881
|
+
var tmpUpdateUndeleteSetters = generateUpdateUndeleteSetters(pParameters);
|
|
882
|
+
|
|
883
|
+
if (tmpUpdateUndeleteSetters)
|
|
884
|
+
{
|
|
885
|
+
//If the table has a deleted bit, go forward with the update to change things.
|
|
886
|
+
return 'UPDATE'+tmpTableName+' SET'+tmpUpdateUndeleteSetters+tmpWhere+';';
|
|
887
|
+
}
|
|
888
|
+
else
|
|
889
|
+
{
|
|
890
|
+
// This is a no-op because the record can't be undeleted.
|
|
891
|
+
// TODO: Should it throw instead?
|
|
892
|
+
return 'SELECT NULL;';
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
|
|
805
896
|
var Count = function(pParameters)
|
|
806
897
|
{
|
|
807
898
|
var tmpFieldList = pParameters.distinct ? generateFieldList(pParameters, true) : '*';
|
|
@@ -838,15 +929,16 @@ var FoxHoundDialectMySQL = function()
|
|
|
838
929
|
Read: Read,
|
|
839
930
|
Update: Update,
|
|
840
931
|
Delete: Delete,
|
|
932
|
+
Undelete: Undelete,
|
|
841
933
|
Count: Count
|
|
842
934
|
});
|
|
843
935
|
|
|
844
936
|
/**
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
937
|
+
* Dialect Name
|
|
938
|
+
*
|
|
939
|
+
* @property name
|
|
940
|
+
* @type string
|
|
941
|
+
*/
|
|
850
942
|
Object.defineProperty(tmpDialect, 'name',
|
|
851
943
|
{
|
|
852
944
|
get: function() { return 'MySQL'; },
|
|
@@ -659,6 +659,43 @@ suite
|
|
|
659
659
|
}
|
|
660
660
|
);
|
|
661
661
|
test
|
|
662
|
+
(
|
|
663
|
+
'Undelete Query with Deleted Bit',
|
|
664
|
+
function()
|
|
665
|
+
{
|
|
666
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('ALASQL')
|
|
667
|
+
.setScope('Animal')
|
|
668
|
+
.addFilter('IDAnimal', 10);
|
|
669
|
+
|
|
670
|
+
//Use a schema that already defines a deleted bit
|
|
671
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
672
|
+
|
|
673
|
+
// Build the query
|
|
674
|
+
tmpQuery.buildUndeleteQuery();
|
|
675
|
+
// This is the query generated by the ALASQL dialect
|
|
676
|
+
libFable.log.trace('Undelete Query', tmpQuery.query);
|
|
677
|
+
Expect(tmpQuery.query.body)
|
|
678
|
+
.to.equal('UPDATE Animal SET `UpdateDate` = NOW(), `UpdatingIDUser` = :UpdatingIDUser_1, `Deleted` = 0 WHERE `IDAnimal` = :IDAnimal_w0 AND `Deleted` = :Deleted_w1;');
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
test
|
|
682
|
+
(
|
|
683
|
+
'Undelete Query without Deleted Bit',
|
|
684
|
+
function()
|
|
685
|
+
{
|
|
686
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('ALASQL')
|
|
687
|
+
.setScope('Animal')
|
|
688
|
+
.addFilter('IDAnimal', 10);
|
|
689
|
+
|
|
690
|
+
// Build the query
|
|
691
|
+
tmpQuery.buildUndeleteQuery();
|
|
692
|
+
// This is the query generated by the ALASQL dialect
|
|
693
|
+
libFable.log.trace('Undelete Query', tmpQuery.query);
|
|
694
|
+
Expect(tmpQuery.query.body)
|
|
695
|
+
.to.equal('SELECT NULL;');
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
test
|
|
662
699
|
(
|
|
663
700
|
'Update Query -- without Deleted',
|
|
664
701
|
function()
|
|
@@ -205,6 +205,20 @@ suite
|
|
|
205
205
|
}
|
|
206
206
|
);
|
|
207
207
|
test
|
|
208
|
+
(
|
|
209
|
+
'Undelete Query',
|
|
210
|
+
function()
|
|
211
|
+
{
|
|
212
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('English').setScope('Animal');
|
|
213
|
+
|
|
214
|
+
// Build the query
|
|
215
|
+
tmpQuery.buildUndeleteQuery();
|
|
216
|
+
// This is the query generated by the English dialect
|
|
217
|
+
Expect(tmpQuery.query.body)
|
|
218
|
+
.to.equal('I am undeleting your Animal.');
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
test
|
|
208
222
|
(
|
|
209
223
|
'Count Query',
|
|
210
224
|
function()
|
|
@@ -772,6 +772,43 @@ suite
|
|
|
772
772
|
.to.equal('DELETE FROM `Animal` WHERE IDAnimal = :IDAnimal_w0;');
|
|
773
773
|
}
|
|
774
774
|
);
|
|
775
|
+
test
|
|
776
|
+
(
|
|
777
|
+
'Undelete Query with Deleted Bit',
|
|
778
|
+
function()
|
|
779
|
+
{
|
|
780
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MySQL')
|
|
781
|
+
.setScope('Animal')
|
|
782
|
+
.addFilter('IDAnimal', 10);
|
|
783
|
+
|
|
784
|
+
//Use a schema that already defines a deleted bit
|
|
785
|
+
tmpQuery.query.schema = _AnimalSchema;
|
|
786
|
+
|
|
787
|
+
// Build the query
|
|
788
|
+
tmpQuery.buildUndeleteQuery();
|
|
789
|
+
// This is the query generated by the MySQL dialect
|
|
790
|
+
libFable.log.trace('Undelete Query', tmpQuery.query);
|
|
791
|
+
Expect(tmpQuery.query.body)
|
|
792
|
+
.to.equal('UPDATE `Animal` SET UpdateDate = NOW(3), UpdatingIDUser = :UpdatingIDUser_1, Deleted = 0 WHERE IDAnimal = :IDAnimal_w0 AND `Animal`.Deleted = :Deleted_w1;');
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
test
|
|
796
|
+
(
|
|
797
|
+
'Undelete Query without Deleted Bit',
|
|
798
|
+
function()
|
|
799
|
+
{
|
|
800
|
+
var tmpQuery = libFoxHound.new(libFable).setDialect('MySQL')
|
|
801
|
+
.setScope('Animal')
|
|
802
|
+
.addFilter('IDAnimal', 10);
|
|
803
|
+
|
|
804
|
+
// Build the query
|
|
805
|
+
tmpQuery.buildUndeleteQuery();
|
|
806
|
+
// This is the query generated by the MySQL dialect
|
|
807
|
+
libFable.log.trace('Undelete Query', tmpQuery.query);
|
|
808
|
+
Expect(tmpQuery.query.body)
|
|
809
|
+
.to.equal('SELECT NULL;');
|
|
810
|
+
}
|
|
811
|
+
);
|
|
775
812
|
}
|
|
776
813
|
);
|
|
777
814
|
|