From 40631aa7c4bbfec2a7dfcdf1e4a731934424059b Mon Sep 17 00:00:00 2001
From: Johannes Hoermann <j.hoermann@adito.de>
Date: Tue, 8 Oct 2019 09:47:35 +0200
Subject: [PATCH] improve sql builder, add some tests

---
 .../children/sqltests/onActionProcess.js      | 106 +++++++++---------
 process/Sql_lib/process.js                    |   9 +-
 2 files changed, 64 insertions(+), 51 deletions(-)

diff --git a/entity/Person_entity/entityfields/campaignactiongroup/children/sqltests/onActionProcess.js b/entity/Person_entity/entityfields/campaignactiongroup/children/sqltests/onActionProcess.js
index 1e272ce959..1faa5af6eb 100644
--- a/entity/Person_entity/entityfields/campaignactiongroup/children/sqltests/onActionProcess.js
+++ b/entity/Person_entity/entityfields/campaignactiongroup/children/sqltests/onActionProcess.js
@@ -93,7 +93,7 @@ var validAndUsageTests = new TestSuite([
                                 .from("PERSON")
                                 .where("PERSON.FIRSTNAME", "Tim")
                                 .and("PERSON.LASTNAME", "Admin"),
-                                "exists ?")
+                                "exists ?")  // Note: you can use SqlBuilder.EXISTS() instead of  "exists ?"
         
         pTester.assert("exists  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ", actual._where._sqlStorage, "prepared sql");
         pTester.assert(2, actual._where.preparedValues.length, "number of params");
@@ -582,155 +582,143 @@ var mandatoryErrorTests = new TestSuite([
 // and
     ["and without parameter should error", function(pTester)
     {
-        new SqlBuilder().where();
+        new SqlBuilder().where().or();
     }, SqlBuilder.ERROR_NO_PARAMETER_PROVIDED()],
 
     ["and with '' as value should error", function(pTester)
     {
-        new SqlBuilder().where("PERSON.FIRSTNAME", "");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["and with null as value should error", function(pTester)
     {
-        new SqlBuilder().where("PERSON.FIRSTNAME", null);
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", null);
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["and with a jdito-var containing null should error", function(pTester)
     {
         vars.set("$global.TestingVarNull", null);
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarNull");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarNull");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["and with a jdito-var containing '' should error", function(pTester)
     {
         vars.set("$global.TestingVarEmptyString", "");
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["and with a jdito-var containing '' should error", function(pTester)
     {
         vars.set("$global.TestingVarEmptyString", "");
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["and with an empty sql-builder as subquery should error", function(pTester)
     {        
-        new SqlBuilder().where("PERSON.FIRSTNAME", new SqlBuilder());
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", new SqlBuilder());
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["and with an empty prepared statement as subquery should error", function(pTester)
     {        
-        new SqlBuilder().where("PERSON.FIRSTNAME", ["", []]);
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", ["", []]);
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
 
 // or
     ["or without parameter should error", function(pTester)
     {
-        new SqlBuilder().where();
+        new SqlBuilder().where().or();
     }, SqlBuilder.ERROR_NO_PARAMETER_PROVIDED()],
 
     ["or with '' as value should error", function(pTester)
     {
-        new SqlBuilder().where("PERSON.FIRSTNAME", "");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["or with null as value should error", function(pTester)
     {
-        new SqlBuilder().where("PERSON.FIRSTNAME", null);
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", null);
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["or with a jdito-var containing null should error", function(pTester)
     {
         vars.set("$global.TestingVarNull", null);
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarNull");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarNull");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["or with a jdito-var containing '' should error", function(pTester)
     {
         vars.set("$global.TestingVarEmptyString", "");
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["or with a jdito-var containing '' should error", function(pTester)
     {
         vars.set("$global.TestingVarEmptyString", "");
         
-        new SqlBuilder().where("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", "$global.TestingVarEmptyString");
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR()],
 
     ["or with an empty sql-builder as subquery should error", function(pTester)
     {        
-        new SqlBuilder().where("PERSON.FIRSTNAME", new SqlBuilder());
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", new SqlBuilder());
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
 
     ["or with an empty prepared statement as subquery should error", function(pTester)
     {        
-        new SqlBuilder().where("PERSON.FIRSTNAME", ["", []]);
+        new SqlBuilder().where().or("PERSON.FIRSTNAME", ["", []]);
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
-])
-
+]);
 
 var inStatementTests = new TestSuite([
-// and
     ["simple and in", function(pTester)
     {
         var actual = new SqlBuilder()
-                            .where("PERSON.LASTNAME", ["Franz", "Fritz"]);
+                            .where("PERSON.LASTNAME", ["Franz", "Fritz"]) // Note: you can use SqlBuilder.IN() or "# in ?" as 3rd parameter
+                            .or("PERSON.LASTNAME", ["Peter", "Mayer"]);
                             
-        pTester.assert(" ( PERSON.LASTNAME in  (?, ?)  ) ", actual._where._sqlStorage, "prepared sql");
-        pTester.assert(2, actual._where.preparedValues.length, "number of params");
+        pTester.assert(" ( PERSON.LASTNAME in  (?, ?)  )  or  ( PERSON.LASTNAME in  (?, ?)  ) ", actual._where._sqlStorage, "prepared sql");
+        pTester.assert(4, actual._where.preparedValues.length, "number of params");
     }],
 
     ["simple and not in", function(pTester)
     {
         var actual = new SqlBuilder()
-                            .where("PERSON.LASTNAME", ["Franz", "Fritz"], "# not in ?");
+                            .where("PERSON.LASTNAME", ["Franz", "Fritz"], "# not in ?"); // Note: you can use SqlBuilder.NOT_IN() instead of "# not in ?"
                             
         pTester.assert(" ( PERSON.LASTNAME not in  (?, ?)  ) ", actual._where._sqlStorage, "prepared sql");
         pTester.assert(2, actual._where.preparedValues.length, "number of params");
     }],
 
-    ["andIfSet should ignore empty array", function(pTester)
+    ["in with subquery", function(pTester)
     {
         var actual = new SqlBuilder()
-                            .whereIfSet("PERSON.LASTNAME", []);
+                        .where("PERSON.FIRSTNAME", new SqlBuilder()
+                                                        .select("PERSON.FIRSTNAME")
+                                                        .from("PERSON")
+                                                        .where("PERSON.LASTNAME", "Fritz")
+                                                , "# in ?"); // Note: you can use SqlBuilder.IN() instead of "# in ?"
                             
-        pTester.assert("", actual._where._sqlStorage, "prepared sql should be empty");
-        pTester.assert(0, actual._where.preparedValues.length, "number of params should be 0");
-    }],
-
-    ["and should error on an empty array", function(pTester)
-    {
-        new SqlBuilder()
-                .where("PERSON.LASTNAME", []);
-    }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()],
-
-// or
-    ["simple or in", function(pTester)
-    {
-        var actual = new SqlBuilder()
-                            .where("PERSON.LASTNAME", ["Franz", "Fritz"]);
-                            
-        pTester.assert(" ( PERSON.LASTNAME in  (?, ?)  ) ", actual._where._sqlStorage, "prepared sql");
-        pTester.assert(2, actual._where.preparedValues.length, "number of params");
+        pTester.assert("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ", actual._where._sqlStorage, "prepared sql");
+        pTester.assert(1, actual._where.preparedValues.length, "number of params");
     }],
 
-    ["simple or not in", function(pTester)
+    ["in with prepared statement-array", function(pTester)
     {
         var actual = new SqlBuilder()
-                            .where("PERSON.LASTNAME", ["Franz", "Fritz"], "# not in ?");
+                        .where("PERSON.FIRSTNAME", ["select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ?", [["Fritz", SQLTYPES.VARCHAR]]]
+                                                , "# in ?"); // Note: you can use SqlBuilder.IN() instead of "# in ?"
                             
-        pTester.assert(" ( PERSON.LASTNAME not in  (?, ?)  ) ", actual._where._sqlStorage, "prepared sql");
-        pTester.assert(2, actual._where.preparedValues.length, "number of params");
+        pTester.assert("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ", actual._where._sqlStorage, "prepared sql");
+        pTester.assert(1, actual._where.preparedValues.length, "number of params");
     }],
 
-    ["orIfSet should ignore empty array", function(pTester)
+    ["andIfSet should ignore empty array", function(pTester)
     {
         var actual = new SqlBuilder()
                             .whereIfSet("PERSON.LASTNAME", []);
@@ -739,13 +727,30 @@ var inStatementTests = new TestSuite([
         pTester.assert(0, actual._where.preparedValues.length, "number of params should be 0");
     }],
 
-    ["or should error on an empty array", function(pTester)
+    ["and should error on an empty array", function(pTester)
     {
         new SqlBuilder()
                 .where("PERSON.LASTNAME", []);
     }, SqlBuilder.ERROR_VALUE_IS_MANDATORY()]
 ]);
 
+var testConstantFunctions = new TestSuite([
+    ["SqlBuilder.IN()", function(pTester)
+    {
+        pTester.assert("# in ?", SqlBuilder.IN());
+    }],
+    
+    ["SqlBuilder.NOT_IN()", function(pTester)
+    {
+        pTester.assert("# not in ?", SqlBuilder.NOT_IN());
+    }],
+
+    ["SqlBuilder.EXISTS()", function(pTester)
+    {
+        pTester.assert("exists ?", SqlBuilder.EXISTS());
+    }]
+]);
+
 var tester = new Tester("Test SqlBuilder");
 tester.test(validAndUsageTests);
 tester.test(validOrUsageTests);
@@ -754,6 +759,7 @@ tester.test(ifSetTests);
 tester.test(dbWrapperTests);
 tester.test(mandatoryErrorTests);
 tester.test(inStatementTests);
+tester.test(testConstantFunctions);
 
 logging.log("-------------------------");
 tester.printResults();
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index e24a5f5ea0..54d0a299df 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -1190,7 +1190,6 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon
         if (typeofValue == "string" && pValue[0] == "$")
         {
             pValue = this._resolveJditoVariable(pValue)
-            // TODO: mandatory
             if (pMandatory && !pValue)
                 throw SqlBuilder.ERROR_VALUE_IS_MANDATORY_JDITO_VAR();
             
@@ -1202,7 +1201,10 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon
 
         // ... everything else -> just pass it
         if (pValue === false || pValue === 0 || pValue)
+        {
             this._whereCondition(this._prepare(field, pValue, pCond, pFieldType), undefined, pAddPreparedConditionCallback);
+        }
+            
 
         return this;
     }
@@ -1283,6 +1285,11 @@ SqlBuilder.IN = function()
     return "# in ?";
 }
 
+SqlBuilder.EXISTS = function()
+{
+    return "exists ?";
+}
+
 //TODO: more detailed comments with examples
 
 /**
-- 
GitLab