From 4fc6c4dc02a627b583dd4faf45f7307674b530ef Mon Sep 17 00:00:00 2001
From: Sebastian Listl <s.listl@adito.de>
Date: Mon, 25 Oct 2021 19:27:09 +0200
Subject: [PATCH] 1086863 SqlBuilder IN condition with over 1000 values

---
 process/ArrayUtils_test/ArrayUtils_test.aod |   12 +
 process/ArrayUtils_test/process.js          |   52 +
 process/JditoFilter_lib/process.js          |   97 +-
 process/SqlBuilder_test/process.js          |  154 +-
 process/Sql_lib/process.js                  | 7569 ++++++++++---------
 process/Util_lib/process.js                 |   27 +
 6 files changed, 4042 insertions(+), 3869 deletions(-)
 create mode 100644 process/ArrayUtils_test/ArrayUtils_test.aod
 create mode 100644 process/ArrayUtils_test/process.js

diff --git a/process/ArrayUtils_test/ArrayUtils_test.aod b/process/ArrayUtils_test/ArrayUtils_test.aod
new file mode 100644
index 0000000000..ccb19f2cc7
--- /dev/null
+++ b/process/ArrayUtils_test/ArrayUtils_test.aod
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
+  <name>ArrayUtils_test</name>
+  <title>[TEST] Util_lib - ArrayUtils</title>
+  <majorModelMode>DISTRIBUTED</majorModelMode>
+  <icon>VAADIN:CHECK_CIRCLE</icon>
+  <process>%aditoprj%/process/ArrayUtils_test/process.js</process>
+  <alias>Data_alias</alias>
+  <variants>
+    <element>EXECUTABLE</element>
+  </variants>
+</process>
diff --git a/process/ArrayUtils_test/process.js b/process/ArrayUtils_test/process.js
new file mode 100644
index 0000000000..04c1f4e13e
--- /dev/null
+++ b/process/ArrayUtils_test/process.js
@@ -0,0 +1,52 @@
+import("Util_lib");
+import("system.result");
+import("system.vars");
+import("UnitTest_lib");
+
+var chunk = new TestSuite("ArrayUtils.chunk", [
+    new Test("should test if an arrays are split up into the correct amount of chunks",
+        function (pTester, pDataProvider) 
+        {
+            var chunkSize = pDataProvider[1];
+            var chunkedArray = ArrayUtils.chunk(pDataProvider[0], chunkSize);
+            var expectedChunkCount = pDataProvider[2];
+            
+            pTester.expectThat(chunkedArray).hasLength(expectedChunkCount).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [["a","b","c","d","e","f","g","h","i","j"], 2, 5],  //exactly divisible
+                [["k","o","n","o","d","i","o","d","a","o"], 3, 4],  //last chunk not full
+                [["a","b","c","d","e","f","g","h","i","j"], 16, 1], //first chunk not full
+                [["a","b","c","d","e","n","a","n","i","o"], 0, 0]   //chunksize 0 => empty array
+            ];
+        }
+    ),
+    new Test("should test if the size of the chunks is correct",
+        function (pTester, pDataProvider) 
+        {
+            var chunkSize = pDataProvider[1];
+            var chunkedArray = ArrayUtils.chunk(pDataProvider[0], chunkSize);
+            
+            pTester.expectThat(chunkedArray).elementAt(0).hasLength(chunkSize).assert();
+            pTester.expectThat(chunkedArray).elementAt(-1).hasMaxLength(chunkSize).assert();
+        },
+        function dataProvider() 
+        {
+            return [
+                [["a","b","a","k","a","y","a","r","o","u"], 2],  //exactly divisible
+                [["a","b","c","d","e","f","g","h","i","j"], 3]  //last chunk not full
+            ];
+        }
+    )
+]);
+
+
+var tester = new Tester("Test ArrayUtils");
+tester.initCoverage(ArrayUtils);
+tester.test(chunk);
+
+tester.summary();
+    
+result.object(tester.getResults());
diff --git a/process/JditoFilter_lib/process.js b/process/JditoFilter_lib/process.js
index 29b6ee9e6a..d6292dc2df 100644
--- a/process/JditoFilter_lib/process.js
+++ b/process/JditoFilter_lib/process.js
@@ -5,6 +5,7 @@ import("system.logging");
 import("Sql_lib");
 import("Util_lib");
 import("system.datetime");
+import("system.neonFilter");
 
 //@TODO: add support for permissions to the lib
 
@@ -18,7 +19,7 @@ function FilterConditionGroup (pGroup)
     if (pGroup && "filter" in pGroup)
         pGroup = pGroup.filter;
         
-    var operator = pGroup ? pGroup.operator : "AND";
+    var operator = pGroup ? pGroup.operator : neonFilter.MERGE_OPERATOR_AND;
     var childs = pGroup ? pGroup.childs : [];
     this.type = "group";
     this.operator = operator;
@@ -229,7 +230,7 @@ JditoFilter.prototype.lookupFilterFields = function (pFields)
     this._lookupFields = pFields;
     var lookupFilterFn = function (pRecordValue, pFilterValue, pOperator, pRow)
     {
-        if (pOperator == "CONTAINS")
+        if (pOperator == neonFilter.SEARCH_OPERATOR_CONTAINS)
         {
             var filterValues = pFilterValue.split(" ").filter(function (val) {return val.trim();});
             return filterValues.every(function (filterValue)
@@ -290,7 +291,7 @@ JditoFilter.prototype.checkRecord = function (pRow)
         }
         else if (pCondition.type == "group")
         {
-            if (pCondition.operator == "AND")
+            if (pCondition.operator == neonFilter.MERGE_OPERATOR_AND)
                 return pCondition.childs.every(_testRecord, this);
             return pCondition.childs.some(_testRecord, this);
         }
@@ -304,34 +305,34 @@ JditoFilter.prototype.checkRecord = function (pRow)
     {
         switch (pOperator)
         {
-            case "CONTAINS":
+            case neonFilter.SEARCH_OPERATOR_CONTAINS:
                 return (new RegExp(RegExpUtils.escapePatternStr(pFilterValue), regexFlags)).test(pRowValue);
-            case "CONTAINSNOT":
+            case neonFilter.SEARCH_OPERATOR_CONTAINSNOT:
                 return !(new RegExp(RegExpUtils.escapePatternStr(pFilterValue), regexFlags)).test(pRowValue);
-            case "STARTSWITH":
+            case neonFilter.SEARCH_OPERATOR_STARTSWITH:
                 return (new RegExp("^" + RegExpUtils.escapePatternStr(pFilterValue), regexFlags)).test(pRowValue);
-            case "ENDSWITH":
+            case neonFilter.SEARCH_OPERATOR_ENDSWITH:
                 return (new RegExp(RegExpUtils.escapePatternStr(pFilterValue) + "$", regexFlags)).test(pRowValue);
-            case "EQUAL":
+            case neonFilter.SEARCH_OPERATOR_EQUAL:
                 return (new RegExp("^" + RegExpUtils.escapePatternStr(pFilterValue) + "$", regexFlags)).test(pRowValue);
-            case "NOT_EQUAL":
+            case neonFilter.SEARCH_OPERATOR_NOT_EQUAL:
                 return !(new RegExp("^" + RegExpUtils.escapePatternStr(pFilterValue) + "$", regexFlags)).test(pRowValue);
             //String-comparison returns false values < <= > >= are just relevant for numbervalues
-            case "LESS":
+            case neonFilter.SEARCH_OPERATOR_LESS:
                 return new Number(pRowValue) < new Number(pFilterValue);
-            case "LESS_OR_EQUAL":
+            case neonFilter.SEARCH_OPERATOR_LESS_OR_EQUAL:
                 return new Number(pRowValue) <= new Number(pFilterValue);
-            case "GREATER":
+            case neonFilter.SEARCH_OPERATOR_GREATER:
                 return new Number(pRowValue) > new Number(pFilterValue);
-            case "GREATER_OR_EQUAL":
+            case neonFilter.SEARCH_OPERATOR_GREATER_OR_EQUAL:
                 return new Number(pRowValue) >= new Number(pFilterValue);
-            case "ISNULL":
+            case neonFilter.SEARCH_OPERATOR_ISNULL:
                 return pRowValue == "";
-            case "ISNOTNULL":
+            case neonFilter.SEARCH_OPERATOR_ISNOTNULL:
                 return pRowValue != "";
-            case "TIMEFRAME_EQUAL":
-            case "TIMEFRAME_COMING":
-            case "TIMEFRAME_PAST":
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_EQUAL:
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_COMING:
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_PAST:
                 var [start, end] = datetime.resolveRelativeDateExpression(pFilterValue, new Date().getTime(), vars.get("$sys.timezone"));
                 return pRowValue >= start && pRowValue <= end;
         }
@@ -506,7 +507,7 @@ JditoFilterUtils.getEmptyFilter = function (pEntity)
         entity: pEntity, 
         filter: {
             type: "group", 
-            operator: "AND", 
+            operator: neonFilter.MERGE_OPERATOR_AND, 
             childs: []
         }
     };
@@ -698,9 +699,9 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
                 var conditionFn = fieldConditionFns[pFilter.name];
                 
                 condition = conditionFn.call(null, filterValue, pFilter.operator);
-                if (pOperator == "AND")
+                if (pOperator == neonFilter.MERGE_OPERATOR_AND)
                     this.andIfSet(condition);
-                else if (pOperator == "OR")
+                else if (pOperator == neonFilter.MERGE_OPERATOR_OR)
                     this.orIfSet(condition);
                 
                 return;
@@ -722,23 +723,23 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
             var generatedCondition = _getCondition(filterValue, pFilter.operator, sqlField, pFilter.contenttype);
             if (generatedCondition instanceof SqlBuilder || Utils.isString(generatedCondition))
             {
-                if (pOperator == "AND")
+                if (pOperator == neonFilter.MERGE_OPERATOR_AND)
                     this.andIfSet(generatedCondition);
-                else if (pOperator == "OR")
+                else if (pOperator == neonFilter.MERGE_OPERATOR_OR)
                     this.orIfSet(generatedCondition);
             }
             else
             {
-                var isStringType = pFilter.contenttype != "NUMBER" 
-                    && pFilter.contenttype != "DATE" 
-                    && pFilter.contenttype != "BOOLEAN";
+                var isStringType = pFilter.contenttype != neonFilter.CONTENT_TYPE_NUMBER 
+                    && pFilter.contenttype != neonFilter.CONTENT_TYPE_DATE 
+                    && pFilter.contenttype != neonFilter.CONTENT_TYPE_BOOLEAN;
                 [condition, filterValue] = generatedCondition;
                 if (isStringType && ignoreCase)
                     condition = condition.replace("#", "UPPER(#)").replace("?", "UPPER(?)");
 
-                if (pOperator == "AND")
+                if (pOperator == neonFilter.MERGE_OPERATOR_AND)
                     this.andIfSet(sqlField, filterValue, condition);
-                else if (pOperator == "OR")
+                else if (pOperator == neonFilter.MERGE_OPERATOR_OR)
                     this.orIfSet(sqlField, filterValue, condition);
             }
         }
@@ -750,16 +751,16 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
             {
                 _addCondition.call(subCondition, cond, operator);
             });
-            if (pOperator == "AND")
+            if (pOperator == neonFilter.MERGE_OPERATOR_AND)
                 this.andIfSet(subCondition);
-            else if (pOperator == "OR")
+            else if (pOperator == neonFilter.MERGE_OPERATOR_OR)
                 this.orIfSet(subCondition);
         }
     }
     
     function _handleDate(pValue, pType)
     {
-        if(pValue && pType == "DATE")
+        if(pValue && pType == neonFilter.CONTENT_TYPE_DATE)
         {
             pValue = datetime.switchTimezone(pValue, null, vars.get("$sys.timezone"));
         }
@@ -772,16 +773,16 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
     {
         switch (pOperator)
         {
-            case "CONTAINS":
+            case neonFilter.SEARCH_OPERATOR_CONTAINS:
                 return [SqlBuilder.LIKE(), "%" + pValue + "%"];
-            case "CONTAINSNOT":
+            case neonFilter.SEARCH_OPERATOR_CONTAINSNOT:
                 return [SqlBuilder.NOT_LIKE(), "%" + pValue + "%"];
-            case "STARTSWITH":
+            case neonFilter.SEARCH_OPERATOR_STARTSWITH:
                 return [SqlBuilder.LIKE(), pValue + "%"];
-            case "ENDSWITH":
+            case neonFilter.SEARCH_OPERATOR_ENDSWITH:
                 return [SqlBuilder.LIKE(), "%" + pValue];
-            case "EQUAL":
-                if(pType == "DATE")
+            case neonFilter.SEARCH_OPERATOR_EQUAL:
+                if(pType == neonFilter.CONTENT_TYPE_DATE)
                 {
                     let start = _handleDate(pValue, pType);
                     let end = new Date(start);
@@ -795,8 +796,8 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
                 {
                     return [SqlBuilder.EQUAL(), pValue];
                 }
-            case "NOT_EQUAL":
-                if(pType == "DATE")
+            case neonFilter.SEARCH_OPERATOR_NOT_EQUAL:
+                if(pType == neonFilter.CONTENT_TYPE_DATE)
                 {
                     let start = _handleDate(pValue, pType);
                     let end = new Date(start);
@@ -810,25 +811,25 @@ FilterSqlTranslator.prototype.getSqlCondition = function ()
                 {
                     return [SqlBuilder.NOT_EQUAL(), pValue];
                 }
-            case "LESS":
+            case neonFilter.SEARCH_OPERATOR_LESS:
                 pValue = _handleDate(pValue, pType);
                 return [SqlBuilder.LESS(), pValue];
-            case "LESS_OR_EQUAL":
+            case neonFilter.SEARCH_OPERATOR_LESS_OR_EQUAL:
                 pValue = _handleDate(pValue, pType);
                 return [SqlBuilder.LESS_OR_EQUAL(), pValue];
-            case "GREATER":
+            case neonFilter.SEARCH_OPERATOR_GREATER:
                 pValue = _handleDate(pValue, pType);
                 return [SqlBuilder.GREATER(), pValue];
-            case "GREATER_OR_EQUAL":
+            case neonFilter.SEARCH_OPERATOR_GREATER_OR_EQUAL:
                 pValue = _handleDate(pValue, pType);
                 return [SqlBuilder.GREATER_OR_EQUAL(), pValue];
-            case "ISNULL":
+            case neonFilter.SEARCH_OPERATOR_ISNULL:
                 return pField + " is null";
-            case "ISNOTNULL":
+            case neonFilter.SEARCH_OPERATOR_ISNOTNULL:
                 return pField + " is not null";
-            case "TIMEFRAME_EQUAL":
-            case "TIMEFRAME_COMING":
-            case "TIMEFRAME_PAST":
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_EQUAL:
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_COMING:
+            case neonFilter.SEARCH_OPERATOR_TIMEFRAME_PAST:
                 var [start, end] = datetime.resolveRelativeDateExpression(pValue, new Date().getTime(), vars.get("$sys.timezone"));
                 return newWhere(pField, start, SqlBuilder.GREATER_OR_EQUAL())
                     .and(pField, end, SqlBuilder.LESS_OR_EQUAL());
diff --git a/process/SqlBuilder_test/process.js b/process/SqlBuilder_test/process.js
index 3ec9ec86dd..af79070862 100644
--- a/process/SqlBuilder_test/process.js
+++ b/process/SqlBuilder_test/process.js
@@ -14,7 +14,7 @@ var newSelectTests = new TestSuite("SqlLib.newSelect", [
         {
             var actualValue = newSelect("MySuper, Field, String")
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select MySuper, Field, String").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage").equals("select MySuper, Field, String").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -24,7 +24,7 @@ var newSelectTests = new TestSuite("SqlLib.newSelect", [
         {
             var actualValue = newSelect(["MySuper", "Field", "String"])
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select MySuper, Field, String").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage").equals("select MySuper, Field, String").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -34,7 +34,7 @@ var newSelectTests = new TestSuite("SqlLib.newSelect", [
         {
             var actualValue = newSelect(new SqlBuilder().select("PERSONID").from("PERSON").where("PERSON.FIRSTNAME", "Fritz"))
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select (select PERSONID from PERSON where PERSON.FIRSTNAME = ?)").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage").equals("select (select PERSONID from PERSON where PERSON.FIRSTNAME = ?)").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -44,7 +44,7 @@ var newSelectTests = new TestSuite("SqlLib.newSelect", [
         {
             var actualValue = newSelect(["MySuper", "Field", "String", new SqlBuilder().select("PERSONID").from("PERSON").where("PERSON.FIRSTNAME", "Fritz")])
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select MySuper, Field, String, (select PERSONID from PERSON where PERSON.FIRSTNAME = ?)").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage").equals("select MySuper, Field, String, (select PERSONID from PERSON where PERSON.FIRSTNAME = ?)").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -59,7 +59,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where("PERSON.FIRSTNAME = 'Tim'") // NOTE: you should not do this as this does not add a real prepared statement with "?"
                                 .and("PERSON.LASTNAME = 'Admin'")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = 'Tim' and PERSON.LASTNAME = 'Admin'").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = 'Tim' and PERSON.LASTNAME = 'Admin'").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -71,7 +71,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where("PERSON.FIRSTNAME", "Tim")
                                 .and("PERSON.LASTNAME", "Admin")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -83,7 +83,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where("PERSON.FIRSTNAME", "")
                                 .and("PERSON.LASTNAME", "")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -117,7 +117,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where("PERSON.FIRSTNAME", "$global.TestingVarEmptyString")
                                 .and("PERSON.LASTNAME", "$global.TestingVarEmptyString")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -129,7 +129,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where("PERSON.FIRSTNAME", "Tim", "# <> ?")
                                 .and("PERSON.LASTNAME", "Admin")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME <> ? and PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME <> ? and PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -142,7 +142,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .and("PERSON.LASTNAME", 7, undefined, SQLTYPES.INTEGER)
                                 .and("PERSON.LASTNAME", 8, "# <> ?", SQLTYPES.INTEGER)
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and PERSON.LASTNAME <> ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and PERSON.LASTNAME <> ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(3).assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").elementAt(0).elementAt(1).equals(SQLTYPES.INTEGER).assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").elementAt(1).elementAt(1).equals(SQLTYPES.INTEGER).assert();
@@ -161,7 +161,8 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                     "exists (select * FROM CONTACT where PERSON_ID = PERSONID)", []
                                 ])
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.FIRSTNAME = ? )  and  ( exists (select * FROM CONTACT where PERSON_ID = PERSONID) ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals(" ( PERSON.FIRSTNAME = ? )  and  ( exists (select * FROM CONTACT where PERSON_ID = PERSONID) ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -176,7 +177,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                     .where("PERSON.FIRSTNAME", "Tim")
                                     .and("PERSON.LASTNAME", "Admin"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals(" ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -192,7 +193,8 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                     .and("PERSON.LASTNAME", "Admin"),
                                     "exists ?")  // Note: you can use SqlBuilder.EXISTS() instead of  "exists ?"
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("exists  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("exists  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -207,7 +209,8 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                     .where("PERSON.FIRSTNAME", "Tim")
                                     .and("PERSON.LASTNAME", "Admin"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -219,7 +222,8 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
                                 .where(null, ["select FIRSTNAME from PERSON.FIRSTNAME = ?", [["Peter", 12]]], "exists ?")
                                 .and(null, ["exists (select FIRSTNAME from PERSON.FIRSTNAME = ?)", [["Peter", 12]]]) // also without pCond it should work as the condition could be included in the prep statement
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("exists  ( select FIRSTNAME from PERSON.FIRSTNAME = ? )  and  ( exists (select FIRSTNAME from PERSON.FIRSTNAME = ?) ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("exists  ( select FIRSTNAME from PERSON.FIRSTNAME = ? )  and  ( exists (select FIRSTNAME from PERSON.FIRSTNAME = ?) ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -230,7 +234,7 @@ var validAndUsageTests = new TestSuite("SqlLib.validAndUsage", [
             var actualValue = new SqlBuilder()
                                 .where("PERSON.FIRSTNAME", ["select FIRSTNAME from PERSON.FIRSTNAME = ?", [["Peter", 12]]])
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON.FIRSTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON.FIRSTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -245,7 +249,7 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                 .where("PERSON.FIRSTNAME = 'Tim'") // NOTE: you should not do this as this does not add a real prepared statement with "?"
                                 .or("PERSON.LASTNAME = 'Admin'")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = 'Tim' or PERSON.LASTNAME = 'Admin'").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = 'Tim' or PERSON.LASTNAME = 'Admin'").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -257,7 +261,7 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                 .where("PERSON.FIRSTNAME", "Tim")
                                 .or("PERSON.LASTNAME", "Admin")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -269,7 +273,7 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                 .where("PERSON.FIRSTNAME", "Tim", "# <> ?")
                                 .or("PERSON.LASTNAME", "Admin")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME <> ? or PERSON.LASTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME <> ? or PERSON.LASTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -282,7 +286,7 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                 .or("PERSON.LASTNAME", 7, undefined, SQLTYPES.INTEGER)
                                 .or("PERSON.LASTNAME", 8, "# <> ?", SQLTYPES.INTEGER)
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? or PERSON.LASTNAME <> ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? or PERSON.LASTNAME <> ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(3).assert();
         }
     ),
@@ -298,7 +302,8 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                     "exists (select * FROM CONTACT where PERSON_ID = PERSONID)", []
                                 ])
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.FIRSTNAME = ? )  or  ( exists (select * FROM CONTACT where PERSON_ID = PERSONID) ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals(" ( PERSON.FIRSTNAME = ? )  or  ( exists (select * FROM CONTACT where PERSON_ID = PERSONID) ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -311,7 +316,7 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                     .where("PERSON.FIRSTNAME", "Tim")
                                     .or("PERSON.LASTNAME", "Admin"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals(" ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -327,7 +332,8 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                     .or("PERSON.LASTNAME", "Admin"),
                                     "exists ?")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("exists  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("exists  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -342,7 +348,8 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                     .where("PERSON.FIRSTNAME", "Tim")
                                     .or("PERSON.LASTNAME", "Admin"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("PERSON.FIRSTNAME =  ( select FIRSTNAME from PERSON where PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -354,7 +361,8 @@ var validOrUsageTests = new TestSuite("SqlLib.validOrUsage", [
                                 .where(null, ["select FIRSTNAME from PERSON.FIRSTNAME = ?", [["Peter", 12]]], "exists ?")
                                 .or(null, ["exists (select FIRSTNAME from PERSON.FIRSTNAME = ?)", [["Peter", 12]]]) // also without pCond it should work as the condition could be included in the prep statement
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("exists  ( select FIRSTNAME from PERSON.FIRSTNAME = ? )  or  ( exists (select FIRSTNAME from PERSON.FIRSTNAME = ?) ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("exists  ( select FIRSTNAME from PERSON.FIRSTNAME = ? )  or  ( exists (select FIRSTNAME from PERSON.FIRSTNAME = ?) ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -371,7 +379,8 @@ var combinedAndOrTests = new TestSuite("SqlLib.combinedAndOr", [
                                     .where("PERSON.FIRSTNAME", "Peter")
                                     .and("PERSON.LASTNAME", "Müller"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("(PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?) or  ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("(PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ?) or  ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(4).assert();
         }
     ),
@@ -387,7 +396,8 @@ var combinedAndOrTests = new TestSuite("SqlLib.combinedAndOr", [
                                     .where("PERSON.FIRSTNAME", "Peter")
                                     .or("PERSON.LASTNAME", "Müller"))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals(" ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(4).assert();
         }
     ),
@@ -411,7 +421,8 @@ var combinedAndOrTests = new TestSuite("SqlLib.combinedAndOr", [
                                             .where("PERSON.FIRSTNAME", "Peter")
                                             .or("PERSON.LASTNAME", "Müller")))
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME = ? or PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  or (PERSON.FIRSTNAME = ?) and PERSON.FIRSTNAME = ? or  ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("PERSON.FIRSTNAME = ? or PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  or (PERSON.FIRSTNAME = ?) and PERSON.FIRSTNAME = ? or  ( PERSON.FIRSTNAME = ? and PERSON.LASTNAME = ? and  ( PERSON.FIRSTNAME = ? or PERSON.LASTNAME = ? )  ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(11).assert();
         }
     ),
@@ -426,7 +437,7 @@ var ifSetTests = new TestSuite("SqlLib.ifSet", [
                             .whereIfSet("PERSON.LASTNAME", null)
                             .andIfSet("PERSON.LASTNAME", undefined)
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("", "no sql should be added").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("", "no sql should be added").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0, "no params should be added").assert();
         }
     ),
@@ -439,7 +450,7 @@ var ifSetTests = new TestSuite("SqlLib.ifSet", [
             var actualValue = new SqlBuilder()
                             .whereIfSet("PERSON.FIRSTNAME", "$global.TestingVarNull")
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("", "no sql should be added").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("", "no sql should be added").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0, "no params should be added").assert();
         }
     ),
@@ -452,7 +463,7 @@ var ifSetTests = new TestSuite("SqlLib.ifSet", [
                             .andIfSet(["", []])
                             .andIfSet(new SqlBuilder())
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("", "no sql should be added").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("", "no sql should be added").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0, "no params should be added").assert();
         }
     ),
@@ -464,7 +475,7 @@ var ifSetTests = new TestSuite("SqlLib.ifSet", [
                             .whereIfSet("PERSON.FIRSTNAME", ["", []])
                             .andIfSet("PERSON.LASTNAME", new SqlBuilder())
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("", "no sql should be added").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("", "no sql should be added").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0, "no params should be added").assert();
         }
     ),
@@ -885,7 +896,8 @@ var inStatementTests = new TestSuite("SqlLib.inStatement", [
                                 .where("PERSON.LASTNAME", ["Franz", "Fritz"], SqlBuilder.IN()) // Note: you can use SqlBuilder.IN(), SqlBuilder.NOT_IN(), "# in ?", etc. as 3rd parameter
                                 .or("PERSON.LASTNAME", ["Peter", "Mayer"], SqlBuilder.IN());
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.LASTNAME in  (?, ?)  )  or  ( PERSON.LASTNAME in  (?, ?)  ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals(" ( PERSON.LASTNAME in  (?, ?)  )  or  ( PERSON.LASTNAME in  (?, ?)  ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(4).assert();
         }
     ),
@@ -896,7 +908,7 @@ var inStatementTests = new TestSuite("SqlLib.inStatement", [
             var actualValue = new SqlBuilder()
                                 .where("PERSON.LASTNAME", ["Franz", "Fritz"], "# not in ?"); // Note: you can use SqlBuilder.IN(), SqlBuilder.NOT_IN(), "# in ?", etc. as 3rd parameter
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( PERSON.LASTNAME not in  (?, ?)  ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals(" ( PERSON.LASTNAME not in  (?, ?)  ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(2).assert();
         }
     ),
@@ -911,7 +923,8 @@ var inStatementTests = new TestSuite("SqlLib.inStatement", [
                                                             .where("PERSON.LASTNAME", "Fritz")
                                                     , "# in ?"); // Note: you can use SqlBuilder.IN() instead of "# in ?"
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -923,7 +936,8 @@ var inStatementTests = new TestSuite("SqlLib.inStatement", [
                             .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.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals("PERSON.FIRSTNAME in  ( select PERSON.FIRSTNAME from PERSON where PERSON.LASTNAME = ? ) ").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -934,7 +948,7 @@ var inStatementTests = new TestSuite("SqlLib.inStatement", [
             var actualValue = new SqlBuilder()
                                 .whereIfSet("PERSON.LASTNAME", []);
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals("").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage").equals("").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -990,7 +1004,8 @@ var selectTests = new TestSuite("SqlLib.select", [
                 .select(["AB_ATTRIBUTEID", "AB_ATTRIBUTEUSAGEID", countSubQuery])
                 .from("AB_ATTRIBUTE")
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select AB_ATTRIBUTEID, AB_ATTRIBUTEUSAGEID, (select count(*) from AB_ATTRIBUTEUSAGE where AB_ATTRIBUTEUSAGE.OBJECT_TYPE = ? and AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID)").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage")
+                .equals("select AB_ATTRIBUTEID, AB_ATTRIBUTEUSAGEID, (select count(*) from AB_ATTRIBUTEUSAGE where AB_ATTRIBUTEUSAGE.OBJECT_TYPE = ? and AB_ATTRIBUTEUSAGE.AB_ATTRIBUTE_ID = AB_ATTRIBUTE.AB_ATTRIBUTEID)").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1006,7 +1021,7 @@ var selectTests = new TestSuite("SqlLib.select", [
                 .select("*")
                 .from(subQuery)
 
-            pTester.expectThat(actualValue).elementAt("_from").elementAt("_sqlStorage").equals("from (select FIRSTNAME from PERSON where PERSON.LASTNAME = ?)").assert();
+            pTester.expectThat(actualValue).elementAt("_from").elementAt("sqlStorage").equals("(select FIRSTNAME from PERSON where PERSON.LASTNAME = ?)").assert();
             pTester.expectThat(actualValue).elementAt("_from").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1026,7 +1041,7 @@ var joinTests = new TestSuite("SqlLib.join", [
                 .from("PERSON")
                 .join("ORGANISATION", subQuery)
 
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("_sqlStorage").equals("join ORGANISATION on ORGANISATION.NAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("sqlStorage").equals("join ORGANISATION on ORGANISATION.NAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1043,7 +1058,8 @@ var joinTests = new TestSuite("SqlLib.join", [
                 .from("PERSON")
                 .join(subQuery, "orgname.NAME = TABLE2.NAME", "orgname")
 
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("_sqlStorage").equals("join (select NAME from ORGANISATION where ORGANISATION.NAME = ?) orgname on orgname.NAME = TABLE2.NAME").assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("sqlStorage")
+                .equals("join (select NAME from ORGANISATION where ORGANISATION.NAME = ?) orgname on orgname.NAME = TABLE2.NAME").assert();
             pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1056,7 +1072,7 @@ var joinTests = new TestSuite("SqlLib.join", [
                 .from("PERSON")
                 .join("TABLE1 on TABLE1.NAME = TABLE2.NAME")
 
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("_sqlStorage").equals("join TABLE1 on TABLE1.NAME = TABLE2.NAME").assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("sqlStorage").equals("join TABLE1 on TABLE1.NAME = TABLE2.NAME").assert();
             pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("preparedValues").hasLength(0).assert();
         }
     ),
@@ -1076,7 +1092,8 @@ var subqueryAsFieldTests = new TestSuite("SqlLib.subqueryAsField", [
                                          .and("PERSON.FIRSTNAME", "val3");
 
 
-            pTester.expectThat(actualValue).elementAt("_where").elementAt("_sqlStorage").equals(" ( ( select NAME from ORGANISATION where ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID and PERSON.FIRSTNAME = ? ) = ? )  and PERSON.FIRSTNAME = ?").assert();
+            pTester.expectThat(actualValue).elementAt("_where").elementAt("sqlStorage")
+                .equals(" ( ( select NAME from ORGANISATION where ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID and PERSON.FIRSTNAME = ? ) = ? )  and PERSON.FIRSTNAME = ?").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").hasLength(3).assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").elementAt(0).elementAt(0).equals("val1").assert();
             pTester.expectThat(actualValue).elementAt("_where").elementAt("preparedValues").elementAt(1).elementAt(0).equals("val2").assert();
@@ -1185,7 +1202,8 @@ var subqueryAliasTests = new TestSuite("SqlLib.subqueryAlias", [
                 .select([subQuery, "FIRSTNAME"])
                 .from("PERSON")
 
-            pTester.expectThat(actualValue).elementAt("_select").elementAt("_sqlStorage").equals("select (select NAME from ORGANISATION where ORGANISATION.NAME = ?) testAlias, FIRSTNAME").assert();
+            pTester.expectThat(actualValue).elementAt("_select").elementAt("sqlStorage")
+                .equals("select (select NAME from ORGANISATION where ORGANISATION.NAME = ?) testAlias, FIRSTNAME").assert();
             pTester.expectThat(actualValue).elementAt("_select").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1201,7 +1219,7 @@ var subqueryAliasTests = new TestSuite("SqlLib.subqueryAlias", [
             var actualValue = new SqlBuilder()
                 .from(subQuery)
 
-            pTester.expectThat(actualValue).elementAt("_from").elementAt("_sqlStorage").equals("from (select NAME from ORGANISATION where ORGANISATION.NAME = ?) testAlias").assert();
+            pTester.expectThat(actualValue).elementAt("_from").elementAt("sqlStorage").equals("(select NAME from ORGANISATION where ORGANISATION.NAME = ?) testAlias").assert();
             pTester.expectThat(actualValue).elementAt("_from").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1217,7 +1235,7 @@ var subqueryAliasTests = new TestSuite("SqlLib.subqueryAlias", [
             var actualValue = new SqlBuilder()
                 .from(subQuery, "overwriteAlias")
 
-            pTester.expectThat(actualValue).elementAt("_from").elementAt("_sqlStorage").equals("from (select NAME from ORGANISATION where ORGANISATION.NAME = ?) overwriteAlias").assert();
+            pTester.expectThat(actualValue).elementAt("_from").elementAt("sqlStorage").equals("(select NAME from ORGANISATION where ORGANISATION.NAME = ?) overwriteAlias").assert();
             pTester.expectThat(actualValue).elementAt("_from").elementAt("preparedValues").hasLength(1).assert();
         }
     ),
@@ -1235,15 +1253,49 @@ var subqueryAliasTests = new TestSuite("SqlLib.subqueryAlias", [
                 .join(subQuery, "testAlias.ORGANISATIONID = ORGANISATION_ID")
                 .join(subQuery, "testAlias.ORGANISATIONID = ORGANISATION_ID", "overwriteAlias")
 
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("_sqlStorage").equals("join (select NAME, ORGANISATIONID from ORGANISATION where ORGANISATION.NAME = ?) testAlias on testAlias.ORGANISATIONID = ORGANISATION_ID").assert();
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("preparedValues").hasLength(1).assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("sqlStorage")
+                .equals("join (select NAME, ORGANISATIONID from ORGANISATION where ORGANISATION.NAME = ?) testAlias on testAlias.ORGANISATIONID = ORGANISATION_ID").assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(0).elementAt("preparedValues")
+                .hasLength(1).assert();
 
-            pTester.expectThat(actualValue).elementAt("_joins").elementAt(1).elementAt("_sqlStorage").equals("join (select NAME, ORGANISATIONID from ORGANISATION where ORGANISATION.NAME = ?) overwriteAlias on testAlias.ORGANISATIONID = ORGANISATION_ID").assert();
+            pTester.expectThat(actualValue).elementAt("_joins").elementAt(1).elementAt("sqlStorage")
+                .equals("join (select NAME, ORGANISATIONID from ORGANISATION where ORGANISATION.NAME = ?) overwriteAlias on testAlias.ORGANISATIONID = ORGANISATION_ID").assert();
             pTester.expectThat(actualValue).elementAt("_joins").elementAt(1).elementAt("preparedValues").hasLength(1).assert();
         }
-    ),
+    )
 ]);
 
+var setTableName = new TestSuite("SqlLib.setTableName", [
+    new Test("tablename should be set by using .where",
+        function (pTester)
+        {
+            var sqlQuery = newWhere("ORGANISATION.NAME", "Adito");
+            var actualValue = sqlQuery._tableName;
+            pTester.expectThat(actualValue).equals("ORGANISATION").assert();
+        }
+    )
+]);
+
+var conditionIn = new TestSuite("SqlLib.conditionIn", [
+    new Test("in condition shouldn't break with >1000 ids",
+        function (pTester)
+        {
+            var ids = new Array(1042);
+            for (let i = 0; i < ids.length; i++)
+            {
+                ids[i] = i.toString();
+            }
+            var sqlQuery = newSelect("CONTACT.CONTACTID")
+                .from("CONTACT")
+                .where("CONTACT.CONTACTID", ids, SqlBuilder.IN());
+                
+            pTester.expectThat(function ()
+            {
+                sqlQuery.table();
+            }).not().throwsException();
+        }
+    )
+]);
 
 var tester = new Tester("Test SqlBuilder");
 tester.test(newSelectTests);
@@ -1260,6 +1312,8 @@ tester.test(joinTests);
 tester.test(subqueryAsFieldTests);
 tester.test(conditionFormatTests);
 tester.test(subqueryAliasTests);
+tester.test(setTableName);
+tester.test(conditionIn);
 
 tester.summary();
 
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index 4ec5fbabe2..851b094fcf 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -9,1167 +9,1363 @@ import("system.SQLTYPES");
 import("system.text");
 import("Util_lib");
 
-/**
- * object for easier handling of conditions;
- * With this object you do not have to check if the string is empty or not;
- * you don't need to append a "1=1" condition or similar;
- * this objects gains most benefit if you have a lot of conditions that are added (or not) depending on tons of JDito-conditions
+// see Documentation property of this lib for further explanation
+
+/** 
+ * Creates a new SqlBuilder object and sets the select clause of the sql.
  * 
- * You can also use SqlCondition.begin(alias) for simpler object creation without new and without the need for an extra variable to save the object.
+ * @param {String|Array|SqlBuilder} pFields You can pass:<br/>
+ *                                          - A String is just used AS IT IS. (e.g. "FIRSTNAME, LASTNAME")<br/>
+ *                                          - SqlBuilder is used as Subquery<br/>
+ *                                          - The array can also contain Strings, SqlBuilder which are just concatenated (e.g. ["FIRSTNAME", "LASTNAME", someSqlBuilderContainingFullSelect])<br/>
+ *                                          Please see .select() for more information and examples.
+ * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
+ * @return {SqlBuilder} A new SqlBuilder object already containing the provided fields
  * 
- * @class 
- * @param {String} [alias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
- * @example 
- * see others/guide/HowToSqlConditionLib.adoc
+ * @example
+ * var lastname = "Huber";
  * 
- * @deprecated The SqlCondition will be removed in version >= 2020.x
- *             Use the SqlBuilder instead.
- *             For SqlBuilder usage see the documentation-property of the Sql_lib.
+ * var persons = newSelect("FIRSTNAME")
+ *                  .from("PERSON")
+ *                  .where("PERSON.LASTNAME", lastname)
+ *                  .arrayColumn();
  */
-function SqlCondition(alias) {
-    // setting null is only needed to provide autocomplete for the ADITO-designer
-    this.preparedValues = null;
-    this._init(); // the properties are initalized in an extra function because init is nearly the same as resetting (clearing) the SqlConditions
-    this.alias = alias;
-    
-    // save, if the last condition was an OR. For better bracket-placement
-    this._lastWasOr = false;
+function newSelect(pFields, pAlias)
+{
+    return new SqlBuilder(pAlias).select(pFields);
 }
 
-/**
- * Alternative possibility to crate a new condition.
- * With this you don't need new SqlCondition and you can use the object directly after it's creation
- * --> cleaner code
+/** 
+ * Creates a new SqlBuilder object and calls .where on it.<br/>
+ * This is very useful if you just need a condition as you can pass the first condition directly.<br/>
+ * Note: Even if you ommit all parameters, you do not have to call .where again. You can directly write .and / .or after newWhere().<br/>
  * 
- * It is very usefull for the orSqlCondition() and andSqlCondition() because now you can create a new condition inline.
- * You can also use it for simple selects without the need to save the conditionObject in an extra variable.
- * See Examples!
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
+ *                                                                     else it is used as Field. <br/>
+ *                                                                     Please see .where() for more information and examples.
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       Please see .where() for more information and examples.
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
+ *                               Please see .where() for more information and examples.
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                Please see .where() for more information and examples.
+ * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
+ * @return {SqlBuilder} A new SqlBuilder object which already called .where
  * 
- * @param {String} [alias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
- * @return {SqlCondition} the new SqlCondition-object
+ * @example
+ * // examples, how where / whereIfSet could be used in a conditionProcess.
+ * //////Example 1/////
+ * var cond = newWhereIfSet("CONTACT.PERSON_ID", "$param.PersonId_param")
+ *                  .andIfSet("CONTACT.PERSON_ID", JSON.parse(vars.getString("$param.BlacklistPersons_param")), SqlBuilder.NOT_IN())
+ *                  
+ * result.string(cond.toString());
  * 
- * @example 
- * vars mySelect = SqlCondition.begin(alias)
- *                             .and("MYID = '123'")
- *                             .and(SqlCondition.begin()
- *                                              .or("NAME = 'Max'")
- *                                              .or("NAME = 'Bob'")
- *                              )
- *                             .buildSql("select * from MYTABLE");
- *                             
- * // Or use it for simple selects:
- * var sum = db.cell(SqlCondition.begin()
- *                               .andPrepared("STOCK.PRODUCT_ID", pid)
- *                               .buildSql("select sum(QUANTITY * IN_OUT) from STOCK"));
- *                               
- * @deprecated The SqlCondition will be removed in version >= 2020.x
- *             Use the SqlBuilder instead.
- *             For SqlBuilder usage see the documentation-property of the Sql_lib."
+ * //////Example 2/////
+ * var cond = newWhere();
+ * 
+ * // note: we can use .and* now without an extra .where
+ * if (SOMECHECKS)
+ *      cond.andIfSet(...)
+ *      
+ * if (SOME_MORE_CHECKS)
+ *      cond.and(...)
+ *                  
+ * result.string(cond.toString());
  */
-SqlCondition.begin = function(alias) {
-    return new SqlCondition(alias);
+function newWhere(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
+{
+    return new SqlBuilder(pAlias).where(pFieldOrCond, pValue, pCondition, pFieldType);
 }
 
-/**
- * checks if conditions have been added to the object
- * @return {Boolean} true if conditions have been added, false when not
+/** 
+ * Creates a new SqlBuilder object and calls .whereIfSet on it.<br/>
+ * This is very useful if you just need a condition as you can pass the first condition directly.<br/>
+ * Note: Even if you ommit all parameters, you do not have to call .where again. You can directly write .and / .or after newWhere().
+ * 
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
+ *                                                                     else it is used as Field. <br/>
+ *                                                                     Please see .whereIfSet() for more information and examples.
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       Please see .whereIfSet() for more information and examples.
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
+ *                               Please see .whereIfSet() for more information and examples.
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                Please see .whereIfSet() for more information and examples.
+ * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
+ * @return {SqlBuilder} A new SqlBuilder object which already called .whereIfSet
+ * 
+ * @example
+ * // examples, how where / whereIfSet could be used in a conditionProcess.
+ * //////Example 1/////
+ * var cond = newWhereIfSet("CONTACT.PERSON_ID", "$param.PersonId_param")
+ *                  .andIfSet("CONTACT.PERSON_ID", JSON.parse(vars.getString("$param.BlacklistPersons_param")), SqlBuilder.NOT_IN())
+ *                  
+ * result.string(cond.toString());
+ * 
+ * //////Example 2/////
+ * var cond = newWhere();
+ * 
+ * // note: we can use .and* now without an extra .where
+ * if (SOMECHECKS)
+ *      cond.andIfSet(...)
+ *      
+ * if (SOME_MORE_CHECKS)
+ *      cond.and(...)
+ *                  
+ * result.string(cond.toString());
  */
-SqlCondition.prototype.isSet = function() {
-    if (this._sqlStorage)
-        return true;
-    return false;
+function newWhereIfSet(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
+{
+    return new SqlBuilder(pAlias).whereIfSet(pFieldOrCond, pValue, pCondition, pFieldType);
 }
 
-
 /**
- * append with SQL-and; no paranthesize of existing conditions is done
- * @param {String} cond the condition string which shall be appended
- * @return {SqlCondition} current SqlCondition-object
+ * Object for building sqls. The main purpose of this is to make it easy to use prepared statements.<br/>
+ * You should ALWAYS use prepared statemnts for Security and maybe also for performance reasons.<br/>
+ * If you are not used to use prepared statements all the time you WILL forget to use it, when it's really needed. (eg. direct user input, not just ids)<br/>
+ * <br/>
+ * This can also be useful to build complex sqls where parts should be added<br/>
+ * dynamically while keeping the code clean.<br/>
+ * <br/>
+ * There exist some shortcut funtions<br/>
+ * - if you need a full select use newSelect(...)<br/>
+ * - if you need only a condition use newWhere(...) or newWhereIfSet(...) 
+ * 
+ * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
+ * @class
  */
-SqlCondition.prototype.and = function(cond) {
-    if (!cond)
-        return this;
-    if (this.isSet())
-        this._sqlStorage += " and ";
-    this._sqlStorage += cond;
-    return this;
+function SqlBuilder (pAlias)
+{
+    if(!(this instanceof SqlBuilder)) 
+        throw SqlBuilder._ERROR_INSTANCIATE_WITH_NEW();
+    this._select = null;
+    this._from = null;
+    this._tableName = null; //for insert/update/delete
+    this._joins = [];
+    this._groupBy = null;
+    this._having = null;
+    this._orderBy = null;
+    this._unions = [];
+    this.alias = pAlias;
+    
+    //for paging
+    this._startRow = null;
+    this._pageSize = null;
+    this._hasMoreRows = true;
+    
+    this._subselectAlias = null;
+    
+    this._where = {};
+    this._initWhere();
 }
 
 /**
- * append with SQL-or; Also paranthesize the existing conditions
- * @param {String} cond the condition string which shall be appended
- * @return {SqlCondition} current SqlCondition-object
+ * @return {Symbol}
  */
-SqlCondition.prototype.or = function(cond) {
-    if (!cond)
-        return this;
-    
-    if (this.isSet() && !this._lastWasOr) {
-        this._sqlStorage = "(" + this._sqlStorage + ") or (" + cond + ")";
-        this._lastWasOr = true;
-        
-    } else if (this.isSet() && this._lastWasOr) {
-        this._sqlStorage = this._sqlStorage + " or (" + cond + ")";
-        this._lastWasOr = true;
-        
-    } else {
-        this._sqlStorage = cond;
-    }
-    return this;
+SqlBuilder.getCanBuildSqlSymbol = function ()
+{
+    return Symbol["for"]("canBuildSql");
 }
 
-/**
- * append a prepared-array to this sql condition with SQL-and
- * @param {Array} preparedObj a prepared condition-array
- * @return {SqlCondition} current SqlCondition-object
- */
-SqlCondition.prototype.andAttachPrepared = function(preparedObj) {
-    if (preparedObj)
-    {
-        this.preparedValues = this.preparedValues.concat(preparedObj[1]);
-        return this.and(preparedObj[0]);
-    }
-    
-    return this;
+SqlBuilder.defineCanBuildSql = function (pObject)
+{
+    pObject[SqlBuilder.getCanBuildSqlSymbol()] = true;
+}
+
+SqlBuilder.checkCanBuildSql = function (pObject)
+{
+    return pObject[SqlBuilder.getCanBuildSqlSymbol()];
 }
 
+SqlBuilder.defineCanBuildSql(SqlBuilder.prototype);
+
 /**
- * append a prepared-array to this sql condition with SQL-or
- * @param {Array} preparedObj a prepared condition-array
- * @return {SqlCondition} current SqlCondition-object
+ * Deep copies the SqlBuilder object and returns a new one.<br/>
+ * Use this if you want to add for example add additional parameters without modifying the current builder.
+ * @return a full copy of the current SqlBuilder
  */
-SqlCondition.prototype.orAttachPrepared = function(preparedObj) {
-    if (preparedObj)
+SqlBuilder.prototype.copy = function()
+{
+    return Utils.clone(this);
+}
+
+// errors which are thrown by the SqlBuilder
+SqlBuilder._ERROR_INSTANCIATE_WITH_NEW = function() 
+{
+    return new Error(translate.text("SqlBuilder must be instanciated with 'new' or one of the factory methods (newSelect, newWhere, newWhereIfSet)"));
+}
+
+SqlBuilder._ERROR_INVALID_CONDITION_VALUE_TYPE = function() 
+{
+    return new Error(translate.text("SqlBuilder: invalid value-type for pCondition"));
+}
+
+SqlBuilder._ERROR_NO_CONDITION = function() 
+{
+    return new Error(translate.text("SqlBuilder: if you use a subQuery (e.g. SqlBuilder) you have to provide pCondition (e.g. \"exists ?\")"));
+}
+
+SqlBuilder._ERROR_INVALID_SUBQUERY_TYPE = function() 
+{
+    return new Error(translate.text("SqlBuilder: invalid value-type for pFieldOrCond. It can be a fully qualified SqlBuilder (e.g. select, from, ... have to be set) or an jdito-prepared-statement array"));
+}
+
+SqlBuilder._ERROR_VALUE_IS_MANDATORY = function() 
+{
+    return new Error(translate.text("SqlBuilder: pValue (or pFieldOrCond if only one param) is not allowed to be null, undefined or []. (use *IfSet functions if you need optional conditions which are just ignored if value is null or undefined)"));
+}
+
+SqlBuilder._ERROR_VALUE_IS_MANDATORY_JDITO_VAR = function() 
+{
+    return new Error(translate.text("SqlBuilder: pValue has to be a jdito variable which returns something different than null. (use *IfSet functions if you need optional conditions which are just ignored if value is null or undefined)"));
+}
+
+SqlBuilder._ERROR_UNSUPPORTED_PARAMETER_COMBINATION = function() 
+{
+    return new Error(translate.text("SqlBuilder: unsupportet parameter combination"));
+}
+
+SqlBuilder._ERROR_NO_TABLE = function()
+{
+    return new Error(translate.text("SqlBuilder.deleteDat/updateData: You have to specify a tablename"));
+}
+
+SqlBuilder._ERROR_NO_PARAMETER_PROVIDED = function()
+{
+    return new Error(translate.text("SqlBuilder: You have to specify at least one parameter"));
+}
+
+SqlBuilder._ERROR_WHERE_NOT_FIRST = function()
+{
+    return new Error(translate.text("SqlBuilder: .where has to be called before following and/or."));
+}
+
+SqlBuilder._ERROR_ONLY_ONE_WHERE = function()
+{
+    return new Error(translate.text("SqlBuilder: .where has to be called only one time. Use and/or for further conditions."));
+}
+
+SqlBuilder._ERROR_INCOMPLETE_SELECT = function ()
+{
+    return new Error(translate.text("SqlBuilder: select and from were expected, but not provided."));
+}
+
+SqlBuilder._ERROR_CONDITION_IS_MANDATORY = function ()
+{
+    return new Error(translate.text("SqlBuilder: You have to provide a subquery as SqlBuilder, prepared-array or string"));
+}
+
+SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NOT_COMPLETE = function ()
+{
+    return new Error(translate.text("SqlBuilder: If pFieldOrCond is a SqlBuilder & pValue is provided, pFieldOrCond has to be a full SqlBuilder which will be used as subselect"));
+}
+
+SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NO_FIELD_TYPE = function ()
+{
+    return new Error(translate.text("SqlBuilder: If pFieldOrCond is a SqlBuilder & pValue is provided, you have to provide also pFieldType, as the type cannot be calculated from pFieldOrCond because it is a subselect"));
+}
+
+SqlBuilder._ERROR_CONDITION_WRONG_FORMAT = function ()
+{
+    return new Error(translate.text("SqlBuilder: The '#' in pCondition has to occur before the '?' and '?' has to occur 1 time, '#' has to occur 1 or 0 times."));
+}
+
+SqlBuilder._ERROR_NOT_BOOLEAN = function ()
+{
+    return new Error(translate.text("pExecuteOnlyIfConditionExists has to be of type boolean. This parameter controls what happens if the condition is empty (select / delete all or nothing)"));
+}
+
+SqlBuilder._ERROR_UPDATE_VALUES_INVALID = function ()
+{
+    return new Error(translate.text("SqlBuilder: The provided values object for updateFields is invalid or is not an object."));
+}
+
+SqlBuilder._ERROR_PAGESIZE_INVALID = function ()
+{
+    return new Error(translate.text("SqlBuilder: The pagesize is not set or is not a number."));
+}
+
+SqlBuilder._ERROR_NOT_A_FUNCTION = function ()
+{
+    return new Error(translate.text("SqlBuilder: The provided callback function is not a function."));
+}
+/**
+ * Alternative way of creating a new SqlBuilder object that allows to use
+ * methods on it directly without having to put brackets around it
+ * 
+ * @return {SqlBuilder} a new SqlBuilder object
+ * 
+ * @example 
+ * var query = SqlBuilder.begin()
+ *   .select("ORGANISATION.NAME, FIRSTNAME, LASTNAME")
+ *   .from("PERSON")
+ *   .join("CONTACT", "CONTACT.PERSON_ID = PERSON.PERSONID")
+ *   .leftJoin("ORGANISATION", SqlCondition.begin()
+ *       .and("CONTACT.ORGANISATION_ID = ORGANISATION.ORGANISATIONID")
+ *       .andPrepare("ORGANISATION.NAME", "S%", "# like ?")
+ *       .build("1=2"))
+ *   .where(SqlCondition.begin()
+ *       .andPrepare("CONTACT.STATUS", $KeywordRegistry.contactStatus$active())
+ *       .build("1=2"));
+ * 
+ * if (getCountry) //changing and adding parts
+ * {
+ *   query.select("ORGANISATION.NAME, FIRSTNAME, LASTNAME, COUNTRY");
+ *   query.leftJoin("ADDRESS", "CONTACT.ADDRESS_ID = ADDRESS.ADDRESSID");
+ * }
+ *   
+ * var data = db.table(query.build());
+ * 
+ * @deprecated using .begin is deprecated as it's now possible to write "new SqlBuilder().select(...).from(...)....
+               You can now use "newSelect(...)", "newWhere(...)", "newWhereIfSet(...)" or "new SqlBuilder()" to create a new SqlBuilder instance.
+               For further SqlBuilder usage see the documentation-property of the Sql_lib.
+ */
+SqlBuilder.begin = function ()
+{
+    return new SqlBuilder();
+}
+
+
+/**
+ * Builds the sql and uses SqlUtils.translateXXXWithQuotes to make a string out of it.
+ * @param {String} [pDefaultConditionIfNone=""] the default condition string to use if the SqlBuilder contains no condition. In most cases you won't need this
+ * @param {Boolean} [pForceAsStatement=false] forces the use of SqlUtils.translateStatementWithQuotes even if it's no full statement. This is needed for example if you do not want brakets around the generated statement
+ * @return {String} the sql as string
+ */
+SqlBuilder.prototype.toString = function(pDefaultConditionIfNone, pForceAsStatement)
+{
+    var built = this.build(pDefaultConditionIfNone)
+    
+    if (built[0] !== "")
     {
-        this.preparedValues = this.preparedValues.concat(preparedObj[1]);
-        return this.or(preparedObj[0]);
+        if (!pForceAsStatement && !this.isFullSelect() && (this.hasCondition() || pDefaultConditionIfNone))
+            return SqlUtils.translateConditionWithQuotes(built, this.alias);
+        else
+            return SqlUtils.translateStatementWithQuotes(built, this.alias);
     }
     
-    return this;
+    return "";
 }
 
 /**
- * append another condition with SQL-and
+ * Sets the select clause of the sql.
+ * @param {String|Array|SqlBuilder} pFields You can pass:<br/>
+ *                                          - A String is just used AS IT IS. (e.g. "FIRSTNAME, LASTNAME")<br/>
+ *                                          - SqlBuilder is used as Subquery<br/>
+ *                                          - The array can also contain Strings, SqlBuilder which are just concatenated (e.g. ["FIRSTNAME", "LASTNAME", someSqlBuilderContainingFullSelect])
  * 
- * @param {SqlCondition} cond the condition which shall be appended
- * @param {String} [alternativeCond=""] condition if the given SqlCondition has none
- * @return {SqlCondition} current SqlCondition-object
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.andSqlCondition = function(cond, alternativeCond) {
-    if (!cond)
-        return this
-        
-    var otherCondition = cond.toString(alternativeCond);
-    if (otherCondition.trim() != "")
-    {
-        this.and(" ( " + cond.toString(alternativeCond) + " ) ");
-        if (cond.preparedValues) {
-            this.preparedValues = this.preparedValues.concat(cond.preparedValues);
-        }
-    }
+SqlBuilder.prototype.select = function(pFields)
+{
+    this._select = SqlBuilder._getStatement(pFields, "select", undefined, true, true);
     return this;
 }
 
 /**
- * append another condition with SQL-or; Also paranthesize the existing conditions
- * 
- * @param {SqlCondition} cond the condition which shall be appended
- * @param {String} [alternativeCond=""] condition if the given SqlCondition has none
- * @return {SqlCondition} current SqlCondition-object
+ * Sets the select clause of the sql with distinct.
+ * @param {String|String[]} pFields
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.orSqlCondition = function(cond, alternativeCond) {
-    var otherCondition = cond.toString(alternativeCond);
-    if (otherCondition.trim() != "")
-    {
-        this.or(" ( " + cond.toString(alternativeCond) + " ) ");
-        if (cond.preparedValues) {
-            this.preparedValues = this.preparedValues.concat(cond.preparedValues);
-        }
-    }
+SqlBuilder.prototype.selectDistinct = function (pFields)
+{
+    this._select = SqlBuilder._getStatement(pFields, "select distinct", undefined, true, true);
     return this;
 }
 
 /**
- * append an condition that uses a subQuery with SQL-and
- * 
- * @param {SqlBuilder} subQuery the SqlBuilder object that will be used as a subquery
- * @param {String} [cond="exists"] condition that is used (e. g. exists, not exists, COLUMN = any, COLUMN in, ...)
- * @return {SqlCondition} current SqlCondition-object
+ * Sets the select clause to "select count(...)"
+ * @param {String} [pField=*]   sql column to count, if omitted "count(*)" will be used
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.andSqlBuilder = function(subQuery, cond) {
-    if (!cond)
-        cond = "exists";
-    
-    var preparedObj = subQuery.build();
-    preparedObj[0] = cond + " ( " + preparedObj[0] + " ) ";
-    this.andAttachPrepared(preparedObj);
-    
+SqlBuilder.prototype.selectCount = function (pField)
+{
+    if (pField == undefined)
+    {
+        pField = "*";
+    }
+    this._select = SqlBuilder._getStatement(pField, "select count(", ")", true, true);
     return this;
 }
 
+
 /**
- * append an condition that uses a subQuery with SQL-or
+ * sets an alias-name which is added at some places if this SqlBuilder is used as subselect (e.g. in .select(), .join(), .from(), ...)
+ * @param {String} pSubselectAlias
  * 
- * @param {SqlBuilder} subQuery the SqlBuilder object that will be used as a subquery
- * @param {String} [cond="exists"] condition that is used (e. g. exists, not exists, COLUMN = any, COLUMN in, ...)
- * @return {SqlCondition} current SqlCondition-object
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.orSqlBuilder = function(subQuery, cond) {
-    if (!cond)
-        cond = "exists";
-    
-    var preparedObj = subQuery.build();
-    preparedObj[0] = cond + " ( " + preparedObj[0] + " ) ";
-    this.orAttachPrepared(preparedObj);
-    
+SqlBuilder.prototype.subselectAlias = function(pSubselectAlias)
+{
+    this._subselectAlias = pSubselectAlias;
     return this;
 }
 
 /**
- * same as the "and"-function but with preparedStatement functionality
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} value the value that shall be set into the prepared statement
- * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
- */
-SqlCondition.prototype.andPrepare = function(field, value, cond, fieldType) {
-    cond = this._prepare(field, value, cond, fieldType);
-    return this.and(cond);
-}
-
-/**
- * same as the "or"-function but with preparedStatement functionality
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} value the value that shall be set into the prepared statement
- * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
- */
-SqlCondition.prototype.orPrepare = function(field, value, cond, fieldType) {
-    cond = this._prepare(field, value, cond, fieldType);
-    return this.or(cond);
-}
-
-/**
- * same as the "andPrepare"-function but only applied if the passed "value" is truely
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} value the value that shall be set into the prepared statement
- * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
+ * Sets the table that is used for insert/update/delete functions.
+ * 
+ * @param {String} pTable
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.andPrepareIfSet = function(field, value, cond, fieldType) {
-    if (value)
-        return this.andPrepare(field, value, cond, fieldType);
+SqlBuilder.prototype.tableName = function (pTable)
+{
+    this._tableName = pTable;
     return this;
 }
 
 /**
- * same as the "orPrepare"-function but only applied if the passed "value" is truely
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} value the value that shall be set into the prepared statement
- * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
+ * Sets the from clause of the sql.<br/>
+ * <br/>
+ * Note: It is recommended to add joins via the .join functions.<br/>
+ *       But in some cases you may already get a full from clause including the joins. In this case it is also possible to include them in the from-string.<br/>
+ * 
+ * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
+ *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from (select FIRSTNAME from PERSON)
+ * @param {String} [pTableAlias] table alias
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.orPrepareIfSet = function(field, value, cond, fieldType) {
-    if (value)
-        return this.orPrepare(field, value, cond, fieldType);
+SqlBuilder.prototype.from = function(pTable, pTableAlias)
+{
+    this._from = SqlBuilder._getStatement(pTable, "", pTableAlias, false, (pTableAlias ? false : true));
+    if (typeof(pTable) === "string")
+        this._tableName = pTable;
     return this;
 }
 
 /**
- * same as the "andPrepare"-function but with validation of adito-variables functionality
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} variable the adito-variable that shall be set into the prepared statement
- * @param {String} [cond = "# = ?" ] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
+ * Adds a join clause to the sql.
+ * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
+ *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
+ * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
+ *                                                - a string (without the where keyword)<br/>
+ *                                                - a SqlBuilder NOTE: only the condition is used from it
+ *
+ * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
+ * @param {String} [pPrefix] string before the join, for example "left", "right"
+ * @param {String} [pReplacementForWordJoin] if this is set, this is used instead of the word "join". Needed for e.g. OUTER APPLY in MSSQL
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.andPrepareVars = function(field, variable, cond, fieldType) {
-    variable = this._checkVars(variable)
-    if (variable) {
-        return this.andPrepare(field, variable, cond, fieldType);
+SqlBuilder.prototype.join = function(pTable, pCondition, pTableAlias, pPrefix, pReplacementForWordJoin)
+{
+    // support for deprecated SqlCondition
+    if (pCondition instanceof SqlCondition)
+    {
+        pCondition = newWhere(pCondition);
+        logging.log("Warning: using .where with a SqlCondition as pFieldOrCond is deprecated. The SqlCondition will be removed in version >= 2020.x\n"
+                    + "For SqlBuilder usage see the documentation-property of the Sql_lib.");
     }
-    return this;
-}
+    
+    var prefix = (pReplacementForWordJoin ? pReplacementForWordJoin : "join");
+    if (pPrefix)
+        prefix = pPrefix + " " + prefix;
+    
+    var postfix = pCondition ? "on" : "";
+    
+    if (pTableAlias)
+        postfix = pTableAlias + " " + postfix;
+    else if (pTable instanceof SqlBuilder && pTable._subselectAlias)
+        postfix = pTable._subselectAlias + " " + postfix;
+    
+    var joinPart = SqlBuilder._getStatement(pTable, prefix, postfix.trim());
+    
+    if (pCondition)
+    {
+        if (pCondition instanceof SqlBuilder)
+            pCondition = [pCondition._where.sqlStorage, pCondition._where.preparedValues]
 
-/**
- * same as the "orPrepare"-function but with validation of adito-variables functionality
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} variable the adito-variable that shall be set into the prepared statement
- * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {SqlCondition} current SqlCondition-object
- */
-SqlCondition.prototype.orPrepareVars = function(field, variable, cond, fieldType) {
-    variable = this._checkVars(variable)
-    if (variable) {
-        return this.orPrepare(field, variable, cond, fieldType);
+        var conditionPart = SqlBuilder._getStatement(pCondition);
+
+        joinPart.sqlStorage += " " + conditionPart.sqlStorage;
+        joinPart.preparedValues = joinPart.preparedValues.concat(conditionPart.preparedValues);
     }
+    
+    this._joins.push(joinPart)
     return this;
 }
 
 /**
- * creates a IN-statement out of a field and an array of values.
- * Be carefull with a big number of values. This may have a bad performance.
+ * Adds a left join clause to the sql.
  * 
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String[]} values the value that shall be set into the prepared statement
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @param {Boolean} [not = undefined] if true, add not before in
- * @return {SqlCondition} current SqlCondition-object
+ * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
+ *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
+ * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
+ *                                                - a string (without the where keyword)<br/>
+ *                                                - a SqlBuilder NOTE: only the condition is used from it
+ *
+ * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.andIn = function(field, values, fieldType, not) {
-    return this.andAttachPrepared(this._in(field, values, fieldType, not));
+SqlBuilder.prototype.leftJoin = function(pTable, pCondition, pTableAlias)
+{
+    return this.join(pTable, pCondition, pTableAlias, "left");
 }
 
 /**
- * creates a IN-statement out of a field and an array of values.
- * Be carefull with a big number of values. This may have a bad performance.
+ * Adds a right join clause to the sql.
  * 
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String[]} values the value that shall be set into the prepared statement
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @param {Boolean} [not = undefined] if true, add not before in
- * @return {SqlCondition} current SqlCondition-object
+ * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
+ *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
+ * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
+ *                                                - a string (without the where keyword)<br/>
+ *                                                - a SqlBuilder NOTE: only the condition is used from it
+ *
+ * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.orIn = function(field, values, fieldType, not) {
-    return this.orAttachPrepared(this._in(field, values, fieldType, not));
+SqlBuilder.prototype.rightJoin = function(pTable, pCondition, pTableAlias)
+{
+    return this.join(pTable, pCondition, pTableAlias, "right");
 }
 
 /**
- * creates a IN-statement out of a field and an array of values.
- * Be carefull with a big number of values. This may have a bad performance.
+ * Throws an error if pValue is null, undefined or a SqlBuilder without condition (or if pValue is a $-variable: error if the result of it is null or undefined)<br/>
+ * Also throws an error if pFieldOrCond is the only parameter and it is null<br/>
+ * <br/>
+ * Starts the where clause of the SQL. You may pass the first condition with it.<br/>
+ * But you can also call this function without any parameter and add the conditions with subsequent .and / .or<br/>
+ * <br/>
+ * This method exists mainly for semantic reasons and can only be callled once.<br/>
+ * As shourtcut you could use the newWhere(...) function.<br/>
  * 
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String[]} values the value that shall be set into the prepared statement
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @param {Boolean} [not = undefined] if true, add not before in
- * @return {SqlCondition} current SqlCondition-object
- */
-SqlCondition.prototype._in = function(field, values, fieldType, not) {
-    if (values && values.length > 0)
-    {
-        if (fieldType == undefined)
-            fieldType = SqlUtils.getSingleColumnType(field, undefined, this.alias);
-        
-        preparedStatement = SqlUtils.getSqlInStatement(field, values, undefined, true, fieldType);
-        if (not)
-            preparedStatement[0] = " not " + preparedStatement[0];
-        return preparedStatement;
-    }
-    
-    return null;
-}
-
-/**
- * ready to use string; does not contain a where keyword at the beginning
- * @param {String} [alternativeCond=""] condition that is returned when nothing has been appended.
- * @return {String} concatenated SQL-condition; empty string if nothing has been appended or - if passed - the alternativeCond
- */
-SqlCondition.prototype.toString = function(alternativeCond) {
-    if (!this.isSet() && alternativeCond)
-        return alternativeCond
-    else
-        return this._sqlStorage;
-}
-
-/**
- * ready to use string; does contain a where keyword at the beginning
- * @param {String} [alternativeCond=""] condition that is returned when nothing has been appended.
- * @return {SqlCondition} concatenated SQL-condition; empty string if nothing has been appended or - if passed - the alternativeCond
- */
-SqlCondition.prototype.toWhereString = function(alternativeCond) {
-    var cond = this.toString(alternativeCond);
-    if (cond)
-        return " where " + cond;
-    else 
-        return cond;
-}
-
-/**
- * ready to use prepared condition; does not contain a where keyword at the beginning
- * @param {String} [alternativeCond=""] Condition that is returned when nothing has been appended.
- * @return {Array[][][]} Prepared condition with [condition, [[field1, type1], [field2, type2]]]
- */
-SqlCondition.prototype.build = function(alternativeCond) {
-    return [this.toString(alternativeCond), this.preparedValues];
-}
-
-/**
- * ready to use prepared select
- * @param {String} pBeforeCondition Part of the sql before the condition without where (e.g. "select FIRSTNAME from PERSON")
- * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
- * @param {String} [pAfterCondition=""] Part of the sql after the condition (e.g. "order by FIRSTNAME").
- * @param {Boolean} [pWithWere=true] true if where should be added to the bginning
- * @return {Array[][][]} Prepared condition with [condition, [[field1, type1], [field2, type2]]]
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
+ *                                                                          else it is used as Field.<br/>
+ *                                                                     <br/>
+ *                                                                          If you use it as Subselect (the only param), it can be:<br/>
+ *                                                                           - a string: just added as it is<br/>
+ *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
+ *                                                                                                 the sql is just used as it is.<br/>
+ *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
+ *                                                                           <br/>
+ *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
+ *                                                                          load the SQLTYPE for this condition. <br/>
+ *                                                                          It can be provided in the following ways:<br/>
+ *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
+ *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
+ *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
+ *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
+ *                                                                          - a SqlBuilder: the full select is used as subselect and compared with pValue. <br/>
+ *                                                                               (e.g. select * from PERSON where (select "NAME" from ORGANISATION where ... ) = ?)<br/>
+ *                                                                                Note: for this you have to provide pFieldType as the type cannot be calculated from the subselect!<br/>
+ *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
+ *                                                                     
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
+ *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
+ *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
+ *                                                                           
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong>
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
+ *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
+ *                                                                  
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.buildSql = function(pBeforeCondition, pAlternativeCond, pAfterCondition, pWithWere) {
-    if (pAfterCondition == undefined)
-        pAfterCondition = "";
-    
-    if (pWithWere == undefined) 
-        pWithWere = true;
+SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldType)
+{    
+    // support for deprecated SqlCondition
+    if (pFieldOrCond instanceof SqlCondition && pValue === undefined && pCondition === undefined && pFieldType === undefined)
+    {
+        let copiedCondition = newWhere();
+        
+        copiedCondition._where.preparedValues = pFieldOrCond.preparedValues;
+        copiedCondition._where._lastWasOr = pFieldOrCond._lastWasOr;
+        copiedCondition._where.sqlStorage = pFieldOrCond.sqlStorage;
+        
+        pFieldOrCond = copiedCondition;
+        
+        logging.log("Warning: using .where with a SqlCondition as pFieldOrCond is deprecated. The SqlCondition will be removed in version >= 2020.x\n"
+                    + "For SqlBuilder usage see the documentation-property of the Sql_lib.");
+    }
     
-    return [pBeforeCondition  + " " + 
-            (pWithWere ? this.toWhereString(pAlternativeCond) : this.toString(pAlternativeCond)) +
-            " " + pAfterCondition, this.preparedValues];
+    return this._setWhere(pFieldOrCond, pValue, pCondition, pFieldType, this.or);
 }
 
 /**
- * translates SqlCondition to plain SQL. Use this if prepared statements are not supported.
- * It resolves all prepared values.
- * @param {String} pAlternativeCond used if the SqlCondition does not contain any condition.
- * @return {String} plain SQL condition
+ * Difference to where(): where throws errors on invalid values, whereIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
+ * <br/>
+ * Starts the whereIfSet clause of the SQL. You may pass the first condition with it.<br/>
+ * But you can also call this function without any parameter and add the conditions with subsequent .and / .or<br/>
+ * <br/>
+ * This method exists mainly for semantic reasons and can only be callled once.<br/>
+ * As shourtcut you could use the newWhereIfSet(...) function.
+ * 
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
+ *                                                                          else it is used as Field.<br/>
+ *                                                                     <br/>
+ *                                                                          If you use it as Subselect (the only param), it can be:<br/>
+ *                                                                           - a string: just added as it is<br/>
+ *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
+ *                                                                                                 the sql is just used as it is.<br/>
+ *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
+ *                                                                           <br/>
+ *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
+ *                                                                          load the SQLTYPE for this condition. <br/>
+ *                                                                          It can be provided in the following ways:<br/>
+ *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
+ *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
+ *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
+ *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
+ *                                                                          - a SqlBuilder: the full select is used as subselect and compared with pValue. <br/>
+ *                                                                               (e.g. select * from PERSON where (select "NAME" from ORGANISATION where ... ) = ?)<br/>
+ *                                                                                Note: for this you have to provide pFieldType as the type cannot be calculated from the subselect!<br/>
+ *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
+ *                                                                     
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
+ *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
+ *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
+ *                                                                           
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong>
+ *                               
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
+ *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
+ *
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlCondition.prototype.translate = function(pAlternativeCond) 
+SqlBuilder.prototype.whereIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    return SqlUtils.translateConditionWithQuotes(this.build(pAlternativeCond, this.alias));
+    return this._setWhere(pFieldOrCond, pValue, pCondition, pFieldType, this.orIfSet);
 }
 
 /**
- * Check if (adito-)variable exists and vars.getString is not empty
- * @param {String} variable the variable name (e.g. "$field.CONTACT_ID")
- * @return {String | Boolean} The value of the field as string OR false if it doesn't exist.
+ * helper function for .where and .whereIfSet because they do almost the same<br/>
+ * See .where() for further explanations
+ * 
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] 
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] 
+ * @param {String} [pCondition="# = ?"] <strong>IMPORTANT: the # has to be before the ?</strong><br/>
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] 
+ * @param {SQLTYPES|Numeric} pAddCondFn=AutomaticallyLoadedType This is a callback which is called if a condition should be added (needs to have same parameters as .or()
  * 
+ * @return {SqlBuilder} current SqlBuilder object
  * @ignore
  */
-SqlCondition.prototype._checkVars = function(variable) {
-    if (vars.exists(variable)) {
-        var value = vars.getString(variable);
-        if (value) {
-            return value;
-        }
+SqlBuilder.prototype._setWhere = function (pFieldOrCond, pValue, pCondition, pFieldType, pAddCondFn)
+{
+    // allow where-call without parameter to just enable where mode
+    if (pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
+    {
+        this._where._whereWasCalled = true;
+        return this;
     }
-    return false;
+    
+    // where has to be called before all other and / or
+    if (this.hasCondition())
+        throw SqlBuilder._ERROR_WHERE_NOT_FIRST();
+    
+    // only one where call is allowed
+    if (this._where._whereWasCalled)
+        throw SqlBuilder._ERROR_ONLY_ONE_WHERE();
+    
+    this._where._whereWasCalled = true;
+    return pAddCondFn.call(this, pFieldOrCond, pValue, pCondition, pFieldType);
 }
 
 /**
- * hidden function for composing preparedStatements
- * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} value the value that shall be set into the prepared statement
- * @param {String} cond the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
- *                 Default is "# = ?" 
- * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
- *                              e.g.
- *                              for (...) {
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
- *                              }
- * @return {String} the replaced SQL-condition string (replace # by the fieldname)
+ * helper function which adds a condition
+ * 
+ * @param {String|SqlBuilder|PreparedSqlArray} pCondition the condition to add
+ * @param {Boolean} [pMandatory=true] if true: throws error on SqlBuilder without conditon or PreparedSqlArray with empty string. Else: just does nothing
+ * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
+ * @param {Boolean} pBrackets if true, Brackets are added in some cases
+ *
+ * @return {SqlBuilder} current SqlBuilder object
  * @ignore
  */
-SqlCondition.prototype._prepare = function(field, value, cond, fieldType) {
-    if (value == undefined)
-    {
-        throw new Error(translate.withArguments("${SQL_LIB_UNDEFINED_VALUE} field: %0", [field]));
-    }
+SqlBuilder.prototype._addWhereCondition = function(pCondition, pMandatory, pAddPreparedConditionCallback, pBrackets)
+{
+    if (pCondition === undefined)
+        return this;
     
-    if (cond == undefined) {
-        cond = "# = ?"
-    }
-
-    var alias;
+    if (pMandatory === undefined)
+        pMandatory = true;
     
-    if (typeof field === 'string')
+    var sql = pCondition;
+
+    // the field is a simple string -> just add the string, no prepared statement
+    if (Utils.isString(sql))
     {
-        var pointPos = field.indexOf(".");
-        
-        if (pointPos > 0 && pointPos < field.length-1)
-        {
-            alias = field;
-        }
-        else
-        {
-            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + field + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [field]));
-        }
+        pAddPreparedConditionCallback.call(this, [sql, []]);
+        return this;
     }
-    else
+
+    // the field is an array -> it is a prepared condition
+    if (Array.isArray(sql))
     {
-        if (field.length == 3)
-        {
-            alias = field[2] + "." + field[1];
-            field = field[0] + "." + field[1];
-        }
-        else
+        if (sql[0])
         {
-            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + field.toSource() + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [field.toSource()]));
-        }
-    }
-    
-    var type;
-    
-    if (fieldType == undefined)
-        fieldType = SqlUtils.getSingleColumnType(field, undefined, this.alias);
+            this._where.preparedValues = this._where.preparedValues.concat(sql[1]);
 
-    //this function looks more complex (and slower) than it actually is
-    /* the following regex looks like this after javascript-escaping of the backslash: (?<!\\)((?:\\\\)*)#
-    the regexp searches for the unescaped character and these characters are replaced by the field name
+            // add only brackets if needed
+            if (pBrackets)
+                sql[0] = " ( " + sql[0] + " ) ";
 
-    examples:
-    ---------------------
-    | # --match         |
-    | \# --no-match     |
-    | \\# --match       |
-    | \\\# --no-match   |
-    | \\\\# --match     |
-    ---------------------
-    */
-    //use replaceAll because it's faster and supports negative lookbehinds
-    cond = text.replaceAll(cond, {
-        //manually readd the replaced backslashes by using a group reference, because they a part of the match and therefore replaced by "replaceAll"
-        //since the field COULD contain already a group reference (I think this is extremely uncommon; 
-        //probably that never happens but better stay save): escape that references within the fieldname
-        "(?<!\\\\)((?:\\\\\\\\)*)#": "$1" + text.replaceAll(alias, {
-            "$1": "\\$1"
-        }),
-        //now that we've replaced the correct field placeholder let's replace the escaped number sign "\#" to a normal number sign "#"
-        "\\\\#": "#"
-    });
-    
-    
-    
-    type = fieldType
-    this.preparedValues.push([value.toString(), type]);
-    return cond;
-}
+            pAddPreparedConditionCallback.call(this, [sql[0], []], pBrackets)
+            return this;
+        }
+        else if (pMandatory)
+            throw SqlBuilder._ERROR_CONDITION_IS_MANDATORY();
+        
+        return this;
+    }
 
+    // the field is a SqlBuilder -> it is a SqlBuilder which contains a condition -> the condition of the SqlBuilder is added.
+    if (sql instanceof SqlBuilder)
+    {
+        // add only brackets if needed
+        var sqlString = sql._where.sqlStorage;
+        
+        
+        var condString = sqlString;
+        if (condString.trim() != "")
+        {
+            if (pBrackets)
+                condString = " ( " + condString + " ) ";
+            
+            pAddPreparedConditionCallback.call(this, [condString, sql._where.preparedValues], pBrackets);
+            return this;
+        }
+        else if (pMandatory)
+            throw SqlBuilder._ERROR_CONDITION_IS_MANDATORY();
+        
+        return this;
+    }
 
-/**
- * function that resets the current SqlCondition as if no conditions would have been added
- * this is usefull if you want to reuse the same object over and over
- * @return {null} 
- */
-SqlCondition.prototype.clear = function() {
-    this._sqlStorage = "";
-    this.preparedValues = [];
-    return this;
+    throw SqlBuilder._ERROR_INVALID_CONDITION_VALUE_TYPE();
 }
 
 /**
- * hidden function for initializing all properties for the sql conditions
- * @return {null} 
+ * helper function which adds a Subquery-condition
  * 
+ * @param {SqlBuilder|PreparedSqlArray} pSubquery the subquery to add
+ * @param {Boolean} [pMandatory=true] if true: throws error on SqlBuilder without conditon or PreparedSqlArray with empty string. Else: just does nothing
+ * @param {Boolean} pCondition the condition to be used: e.g. "exists(?)" the ? is replaced by the subquery
+ * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
+ *
+ * @return {SqlBuilder} current SqlBuilder object
  * @ignore
  */
-SqlCondition.prototype._init = function() {
-    //init only wraps the clear function to avoid confusion in the constructor (and provide better extensibility)
-    return this.clear();
-}
-
-// some static functions for often used tasks. They are only provided for very simple tasks.
+SqlBuilder.prototype._addWhereSubquery = function(pSubquery, pMandatory, pCondition, pAddPreparedConditionCallback)
+{
+    if (pSubquery === undefined)
+        return this;
 
-/**
- * pField = pValue
- * @param {String} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME"
- * @param {String} pValue the value that shall be set into the prepared statement
- * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
- * @param {String} [pAlias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
- * 
- * @return {Array[][][]} Prepared condition with [condition, [[field, type]]]
- * 
- * @deprecated
- */
-SqlCondition.equals = function(pField, pValue, pAlternativeCond, pAlias) {
-    return SqlCondition["begin"](pAlias).andPrepare(pField, pValue).build(pAlternativeCond);
-}
+    if (pMandatory === undefined)
+        pMandatory = true;
+    
+    var sql = pSubquery;
 
-/**
- * pField <> pValue
- * @param {String} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME"
- * @param {String} pValue the value that shall be set into the prepared statement
- * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
- * @param {String} [pAlias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
- * 
- * @return {Array[][][]} Prepared condition with [condition, [[field, type]]]
- * 
- * @deprecated
- */
-SqlCondition.equalsNot = function(pField, pValue, pAlternativeCond, pAlias) {
-    return SqlCondition["begin"](pAlias).andPrepare(pField, pValue, "# <> ?").build(pAlternativeCond);
-}
+    // the field is an array -> it is a prepared statement which already SHOULD contain exists or another condition
+    // Both can be handled by _prepare
+    if (Array.isArray(sql))
+    {        
+        if (sql[0])
+            pAddPreparedConditionCallback.call(this, this._prepare(undefined, sql, pCondition));
+        else if (pMandatory)
+            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
+        
+        return this;
+    }
 
-// see Documentation property of this lib for further explanation
+    // the field is a SqlBuilder -> it is a SqlBuilder which contains a condition -> the condition of the SqlBuilder is added.
+    if (sql instanceof SqlBuilder)
+    {
+         var subQuery = pSubquery;
 
-/** 
- * Creates a new SqlBuilder object and sets the select clause of the sql.
- * 
- * @param {String|Array|SqlBuilder} pFields You can pass:<br/>
- *                                          - A String is just used AS IT IS. (e.g. "FIRSTNAME, LASTNAME")<br/>
- *                                          - SqlBuilder is used as Subquery<br/>
- *                                          - The array can also contain Strings, SqlBuilder which are just concatenated (e.g. ["FIRSTNAME", "LASTNAME", someSqlBuilderContainingFullSelect])<br/>
- *                                          Please see .select() for more information and examples.
- * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
- * @return {SqlBuilder} A new SqlBuilder object already containing the provided fields
- * 
- * @example
- * var lastname = "Huber";
- * 
- * var persons = newSelect("FIRSTNAME")
- *                  .from("PERSON")
- *                  .where("PERSON.LASTNAME", lastname)
- *                  .arrayColumn();
- */
-function newSelect(pFields, pAlias)
-{
-    return new SqlBuilder(pAlias).select(pFields);
+        // Without condition this function cannot be used with SqlBuilder object as it cannot contain a condition
+        if (!pCondition)
+            throw SqlBuilder._ERROR_NO_CONDITION();
+        
+        if (subQuery.isFullSelect() || subQuery.hasCondition()) //can also be only an condition if SqlBuilder.NOT() is used
+        {
+            var preparedObj = subQuery.build();
+            pAddPreparedConditionCallback.call(this, this._prepare(undefined, preparedObj, pCondition));
+        } 
+        else if (pMandatory)
+            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
+        
+        return this;
+    }
+    
+    throw SqlBuilder._ERROR_INVALID_SUBQUERY_TYPE();
 }
 
-/** 
- * Creates a new SqlBuilder object and calls .where on it.<br/>
- * This is very useful if you just need a condition as you can pass the first condition directly.<br/>
- * Note: Even if you ommit all parameters, you do not have to call .where again. You can directly write .and / .or after newWhere().<br/>
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
- *                                                                     else it is used as Field. <br/>
- *                                                                     Please see .where() for more information and examples.
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       Please see .where() for more information and examples.
- * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
- *                               Please see .where() for more information and examples.
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                Please see .where() for more information and examples.
- * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
- * @return {SqlBuilder} A new SqlBuilder object which already called .where
- * 
- * @example
- * // examples, how where / whereIfSet could be used in a conditionProcess.
- * //////Example 1/////
- * var cond = newWhereIfSet("CONTACT.PERSON_ID", "$param.PersonId_param")
- *                  .andIfSet("CONTACT.PERSON_ID", JSON.parse(vars.getString("$param.BlacklistPersons_param")), SqlBuilder.NOT_IN())
- *                  
- * result.string(cond.toString());
+/**
+ * helper function which adds a condition to the where
  * 
- * //////Example 2/////
- * var cond = newWhere();
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
+ * @param {Boolean} pMandatory if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
+ * @param {String} [pCondition="# = ?"] see .where()
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
+ * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
  * 
- * // note: we can use .and* now without an extra .where
- * if (SOMECHECKS)
- *      cond.andIfSet(...)
- *      
- * if (SOME_MORE_CHECKS)
- *      cond.and(...)
- *                  
- * result.string(cond.toString());
+ * @return {SqlBuilder} current SqlBuilder object
+ * @ignore
  */
-function newWhere(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
+SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, pAddPreparedConditionCallback)
 {
-    return new SqlBuilder(pAlias).where(pFieldOrCond, pValue, pCondition, pFieldType);
-}
+    pCondition = this._verifyConditionFormat(pCondition);
 
-/** 
- * Creates a new SqlBuilder object and calls .whereIfSet on it.<br/>
- * This is very useful if you just need a condition as you can pass the first condition directly.<br/>
- * Note: Even if you ommit all parameters, you do not have to call .where again. You can directly write .and / .or after newWhere().
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
- *                                                                     else it is used as Field. <br/>
- *                                                                     Please see .whereIfSet() for more information and examples.
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       Please see .whereIfSet() for more information and examples.
- * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
- *                               Please see .whereIfSet() for more information and examples.
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                Please see .whereIfSet() for more information and examples.
- * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
- * @return {SqlBuilder} A new SqlBuilder object which already called .whereIfSet
- * 
- * @example
- * // examples, how where / whereIfSet could be used in a conditionProcess.
- * //////Example 1/////
- * var cond = newWhereIfSet("CONTACT.PERSON_ID", "$param.PersonId_param")
- *                  .andIfSet("CONTACT.PERSON_ID", JSON.parse(vars.getString("$param.BlacklistPersons_param")), SqlBuilder.NOT_IN())
- *                  
- * result.string(cond.toString());
- * 
- * //////Example 2/////
- * var cond = newWhere();
- * 
- * // note: we can use .and* now without an extra .where
- * if (SOMECHECKS)
- *      cond.andIfSet(...)
- *      
- * if (SOME_MORE_CHECKS)
- *      cond.and(...)
- *                  
- * result.string(cond.toString());
- */
-function newWhereIfSet(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
-{
-    return new SqlBuilder(pAlias).whereIfSet(pFieldOrCond, pValue, pCondition, pFieldType);
-}
+    if (!this._where._whereWasCalled)
+        throw SqlBuilder._ERROR_WHERE_NOT_FIRST();
+    
+    //skip if no values are provided and mandatory is false
+    if (!pMandatory && pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
+        return this;
 
-/**
- * Object for building sqls. The main purpose of this is to make it easy to use prepared statements.<br/>
- * You should ALWAYS use prepared statemnts for Security and maybe also for performance reasons.<br/>
- * If you are not used to use prepared statements all the time you WILL forget to use it, when it's really needed. (eg. direct user input, not just ids)<br/>
- * <br/>
- * This can also be useful to build complex sqls where parts should be added<br/>
- * dynamically while keeping the code clean.<br/>
- * <br/>
- * There exist some shortcut funtions<br/>
- * - if you need a full select use newSelect(...)<br/>
- * - if you need only a condition use newWhere(...) or newWhereIfSet(...) 
- * 
- * @param {String} [pAlias=currentAlias] This alias is used for fetching the ColumnTypes and also for the .table, .cell, .updateData, ... -functions
- * @class
- */
-function SqlBuilder (pAlias)
-{
-    if(!(this instanceof SqlBuilder)) 
-        throw SqlBuilder._ERROR_INSTANCIATE_WITH_NEW();
-    this._select = null;
-    this._from = null;
-    this._tableName = null; //for insert/update/delete
-    this._joins = [];
-    this._groupBy = null;
-    this._having = null;
-    this._orderBy = null;
-    this._unions = [];
-    this.alias = pAlias;
+    if (pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
+        throw SqlBuilder._ERROR_NO_PARAMETER_PROVIDED();
+ 
+    // Special case: if only pFieldOrCond is set and we can identify it as a valid field-string (e.g. "Table.Field") we assume that it is not just a condition string.
+    // --> we can check pValue for undefined and also allow simple string-conditions
+    // --> this only works if isFullFieldQualifier() can detect if the supplied string is a valid field-string or if it is some sql. 
+    //     currently it checks for some special characters which should not exist in any field-string but in conditions. 
+    //     If there is a special case missing -> add it to the regexp in isFullFieldQualifier()
+    if (pValue === undefined && pCondition === undefined && pFieldType === undefined && typeof pFieldOrCond == "string" && SqlUtils.isFullFieldQualifier(pFieldOrCond))
+    {
+        if (pMandatory)
+            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
+        
+        return this;
+    }
+  
+    // just call the andCondition function if it is only a Condition
+    if (pFieldOrCond !== undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
+    {
+        return this._addWhereCondition(pFieldOrCond, pMandatory, pAddPreparedConditionCallback, true);
+    }
     
-    //for paging
-    this._startRow = null;
-    this._pageSize = null;
-    this._hasMoreRows = true;
+    // Subselects containing full select can be used as field, if pValue and pFieldType are provided.
+    if (pFieldOrCond instanceof SqlBuilder)
+    {
+        if (!pFieldOrCond.isFullSelect())
+        {
+            throw SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NOT_COMPLETE();
+        }
+        if (!pFieldType)
+        {
+            throw SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NO_FIELD_TYPE();
+        }
+        
+        var tmpCond = newWhere(this.alias)
+            ._addWhere("SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN", pValue, pMandatory, pCondition, pFieldType, pAddPreparedConditionCallback);
+                
+        var subSqlPrepared = pFieldOrCond.build();
+        
+        tmpCond._where.sqlStorage = SqlUtils.replaceConditionTemplate(tmpCond._where.sqlStorage, 'SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN', "( " + subSqlPrepared[0] + " )");
+        tmpCond._where.preparedValues = subSqlPrepared[1].concat(tmpCond._where.preparedValues)
+        
+        this._addWhereCondition(tmpCond, pMandatory, pAddPreparedConditionCallback, true)
+        return this;
+    }
     
-    this._subselectAlias = null;
+    // first check the default-mandatory-cases: null or undefined. everything else such as checking $-variables is done later
+    if (pMandatory && (pValue === null || pValue === undefined))
+        throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
     
-    this._where = {};
-    this._initWhere();
-}
+    // a field is string or array -> normal case
+    // OR !pFieldOrCond and pValue and pCondition is given -> preparedSQL/SqlBuilder can be used without field if pCondition is set (e.g. with "exists ?")
+    if ((typeof pFieldOrCond == "string" || Array.isArray(pFieldOrCond) || (!pFieldOrCond && pFieldType))
+        || (!pFieldOrCond && (pCondition && pValue instanceof SqlBuilder || !(pValue instanceof SqlBuilder))))
+    {
+        var field = pFieldOrCond;
+        var typeofValue = typeof pValue;
 
-/**
- * @return {Symbol}
- */
-SqlBuilder.getCanBuildSqlSymbol = function ()
-{
-    return Symbol["for"]("canBuildSql");
-}
+        // ... a string starting with $ -> jdito varable which has to be resolved
+        if (typeofValue == "string" && pValue.length >= 2 && pValue[0] == "$" && pValue[1] != "$") // escape $ if using two $
+        {
+            //important: just overwrite the value because some $local variables may contain an array and then the default handling of arrays (which
+            //is generating an IN-statement) should apply
+            pValue = vars.get(pValue);
+            if (pMandatory && pValue === null)
+                throw SqlBuilder._ERROR_VALUE_IS_MANDATORY_JDITO_VAR();
+            typeofValue = typeof pValue;
+        }
 
-SqlBuilder.defineCanBuildSql = function (pObject)
-{
-    pObject[SqlBuilder.getCanBuildSqlSymbol()] = true;
-}
+        // remove the first $ if there are two $
+        if (typeofValue == "string" && pValue.length >= 2 && pValue[0] == "$" && pValue[1] == "$")
+            pValue = pValue.slice(1);
+        
+        //support for Set by converting to Array
+        if (pValue instanceof Set)
+            pValue = Array.from(pValue);
+        
+        // pValue can be...
+        // ... a SqlBuilder / Prepared statement array -> it is a SqlBuilder containing a complete subquery or an simple array (in statement)
+        if (pValue instanceof SqlBuilder || Array.isArray(pValue) || (typeofValue == "string" && (pFieldOrCond == undefined || pFieldOrCond == null)))
+        {            
+            // check if the array is really a value-array for an in and not a prepared statement
+            if (Array.isArray(pValue) && !SqlUtils.isPreparedSqlArray(pValue))
+            {
+                if (pValue.length == 0)
+                {
+                    if (pMandatory)
+                        throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
+                    
+                    return this;
+                }
+                               
+                // if it is null -> ignore it. -> the pCondition should not contain a # in this case
+                if (field != null)
+                {
+                    var [alias, parsedField] = SqlUtils.parseField(field);
+                    if (pFieldType === undefined || pFieldType === null)
+                        pFieldType = SqlUtils.getSingleColumnType(parsedField, undefined, this.alias);
+                }
+                //overwrite condition to set a default behaviour
+                if (pCondition == undefined)
+                    pCondition = SqlBuilder.IN();
+                // value-array -> convert it to a prepared statement ["(?, ?, ?)", [[val1, type1], [val2, type2], [val3, type3]]]
+                
+                var mergeOp = pCondition == SqlBuilder.NOT_IN() ? " and " : " or ";
+                var inCondition = ArrayUtils.chunk(pValue, 1000)
+                    .map(function (values)
+                    {
+                        return this._prepare(field, SqlUtils.getSqlInStatement(undefined, values, undefined, true, pFieldType), pCondition, pFieldType, false);
+                    }, this)
+                    .reduce(function (fullCondition, nextCondition)
+                    {
+                        return [
+                            fullCondition[0] + mergeOp + nextCondition[0],
+                            fullCondition[1].concat(nextCondition[1])
+                        ];
+                    });
+                
+                this._addWhereCondition(inCondition, undefined, pAddPreparedConditionCallback, true);
+                return this;
+            }
+            
+            if (pFieldOrCond !== null && pFieldOrCond !== undefined)
+            {
+                if (!pCondition)
+                    pCondition = SqlBuilder.EQUAL();
+                
+                pCondition = SqlUtils.replaceConditionTemplate(pCondition, '#', SqlUtils.parseField(pFieldOrCond)[0]);
+            }
+            else if (!pCondition)
+            {
+                pCondition = "?";
+            }
+            
+            // _addWhereSubquery can handle SqlBuilder and prepared statements as value
+            return this._addWhereSubquery(pValue, pMandatory, pCondition, pAddPreparedConditionCallback);
+        }
 
-SqlBuilder.checkCanBuildSql = function (pObject)
-{
-    return pObject[SqlBuilder.getCanBuildSqlSymbol()];
-}
+        if (!pCondition)
+            pCondition = SqlBuilder.EQUAL();
 
-SqlBuilder.defineCanBuildSql(SqlBuilder.prototype);
+        // ... everything else -> just pass it
+        if (pValue === false || pValue === 0 || pValue === "" || pValue)
+        {
+            let prep = this._prepare(field, pValue, pCondition, pFieldType)
+            this._addWhereCondition(prep, undefined, pAddPreparedConditionCallback);
+        }
+        return this;
+    }
+
+    throw SqlBuilder._ERROR_UNSUPPORTED_PARAMETER_COMBINATION();
+}
 
 /**
- * Deep copies the SqlBuilder object and returns a new one.<br/>
- * Use this if you want to add for example add additional parameters without modifying the current builder.
- * @return a full copy of the current SqlBuilder
+ * helper function that checks if the format of a condition is valid
+ * 
+ * @param {String} pCondition   condition
+ * @return {String} the given condition, if the format is correct
+ * @throws when the format is invalid
  */
-SqlBuilder.prototype.copy = function()
+SqlBuilder.prototype._verifyConditionFormat = function (pCondition)
 {
-    return Utils.clone(this);
-}
+    if (!pCondition)
+    {
+        return pCondition;
+    }
+    
+    //In a special case, pCondition can be a function. It will be called with the alias as argument and
+    //must return an array of the condition string and (optionally) the required sql field type.
+    //alternatively the function may return a string only to make the usage more bulletproof and convenient, so both SqlBuilder.EQUAL() 
+    //and SqlBuilder.EQUAL work equally 
+    if (typeof pCondition === "function")
+    {
+        var resCond = pCondition(this.alias);
+        if (Array.isArray(resCond))
+        {
+            pCondition = resCond[0];
+            pFieldType = pFieldType || resCond[1];
+        }
+        else if(Utils.isString(pCondition))
+        {
+            pCondition = resCond;
+        }
+    }
+    
+    // replace by {@NUMBERSIGN@} / {@QUESTIONSIGN@} as the js-regexp cannot do lookbehind (needed by the regexp used in replaceConditionTemplate to check escapes)
+    // so we just use replaceConditionTemplate to replace by something which never should occur anywhere (it uses text.replaceAll which supports lookbehind because it uses java)
+    var conditionFormat = SqlUtils.replaceConditionTemplate(pCondition, "#", "{@NUMBERSIGN@}")
+    conditionFormat = SqlUtils.replaceConditionTemplate(conditionFormat, "\\?", "{@QUESTIONSIGN@}")
 
-// errors which are thrown by the SqlBuilder
-SqlBuilder._ERROR_INSTANCIATE_WITH_NEW = function() 
-{
-    return new Error(translate.text("SqlBuilder must be instanciated with 'new' or one of the factory methods (newSelect, newWhere, newWhereIfSet)"));
+    var indexOfNumberSign = conditionFormat.indexOf("{@NUMBERSIGN@}");
+    var indexOfQuestionSign = conditionFormat.indexOf("{@QUESTIONSIGN@}");
+    
+    var isFormatValid = !(indexOfQuestionSign == -1 
+        || indexOfNumberSign > indexOfQuestionSign 
+        || indexOfNumberSign != conditionFormat.lastIndexOf("{@NUMBERSIGN@}") 
+        || indexOfQuestionSign != conditionFormat.lastIndexOf("{@QUESTIONSIGN@}"));
+    
+    if (!isFormatValid)
+    {
+        throw SqlBuilder._ERROR_CONDITION_WRONG_FORMAT();
+    }
+    
+    return pCondition;
 }
 
-SqlBuilder._ERROR_INVALID_CONDITION_VALUE_TYPE = function() 
+/**
+ * helper function to add a condition via "and"
+ * 
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
+ * @param {Boolean} [pMandatory=true] if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
+ * @param {String} [pCondition="# = ?"] see .where()
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
+ * 
+ * @ignore
+ */
+SqlBuilder.prototype._and = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType)
 {
-    return new Error(translate.text("SqlBuilder: invalid value-type for pCondition"));
+    return this._addWhere(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, function(pPreparedCondition) 
+    {
+        this._where._previouslyOnlyOr = false;
+        if (SqlUtils.isNonEmptyPreparedSqlArray(pPreparedCondition))
+        {
+            if (this.hasCondition())
+                this._where.sqlStorage += " and ";
+            
+            this._where.sqlStorage += pPreparedCondition[0];
+            this._where.preparedValues = this._where.preparedValues.concat(pPreparedCondition[1]);
+        }
+    });
 }
 
-SqlBuilder._ERROR_NO_CONDITION = function() 
-{
-    return new Error(translate.text("SqlBuilder: if you use a subQuery (e.g. SqlBuilder) you have to provide pCondition (e.g. \"exists ?\")"));
-}
-
-SqlBuilder._ERROR_INVALID_SUBQUERY_TYPE = function() 
-{
-    return new Error(translate.text("SqlBuilder: invalid value-type for pFieldOrCond. It can be a fully qualified SqlBuilder (e.g. select, from, ... have to be set) or an jdito-prepared-statement array"));
-}
-
-SqlBuilder._ERROR_VALUE_IS_MANDATORY = function() 
-{
-    return new Error(translate.text("SqlBuilder: pValue (or pFieldOrCond if only one param) is not allowed to be null, undefined or []. (use *IfSet functions if you need optional conditions which are just ignored if value is null or undefined)"));
+/**
+ * helper function to add a condition via "or"
+ * The callback inside of this function adds brackets where needed.
+ * 
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
+ * @param {Boolean} [pMandatory=true] if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
+ * @param {String} [pCondition="# = ?"] see .where()
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
+ * 
+ * @ignore
+ */
+SqlBuilder.prototype._or = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType)
+{    
+    return this._addWhere(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, function(pPreparedCondition, pAlreadySurroundedByBrackets) 
+    {
+        if (SqlUtils.isNonEmptyPreparedSqlArray(pPreparedCondition))
+        {   
+            if (this._where._previouslyOnlyOr)
+            {
+                this._where.sqlStorage = this._where.sqlStorage + " or " + pPreparedCondition[0];
+                this._where._lastWasOr = true;
+            }
+            else if (this.hasCondition())
+            {
+                let cond = pPreparedCondition[0];
+                
+                if (!pAlreadySurroundedByBrackets)
+                    cond = "(" + cond + ")";
+                
+                if (this._where._lastWasOr)
+                    this._where.sqlStorage = this._where.sqlStorage + " or " + cond;
+                else
+                    this._where.sqlStorage = "(" + this._where.sqlStorage + ") or " + cond;
+                
+                this._where._lastWasOr = true;
+            } 
+            else
+            {
+                if (!this.hasCondition())
+                    this._where._previouslyOnlyOr = true;
+                
+                this._where.sqlStorage = pPreparedCondition[0];
+            }
+            this._where.preparedValues = this._where.preparedValues.concat(pPreparedCondition[1]);
+        }
+    });
 }
 
-SqlBuilder._ERROR_VALUE_IS_MANDATORY_JDITO_VAR = function() 
+/**
+ * Constant-like function which provides a value for pCondition if you need a "not" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere(null, someCondition, SqlBuilder.NOT())
+ */
+SqlBuilder.NOT = function()
 {
-    return new Error(translate.text("SqlBuilder: pValue has to be a jdito variable which returns something different than null. (use *IfSet functions if you need optional conditions which are just ignored if value is null or undefined)"));
+    return "not ?";
 }
 
-SqlBuilder._ERROR_UNSUPPORTED_PARAMETER_COMBINATION = function() 
+/**
+ * Constant-like function which provides a value for pCondition if you need a "not in" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", ["Fritz"], SqlBuilder.NOT_IN())
+ */
+SqlBuilder.NOT_IN = function()
 {
-    return new Error(translate.text("SqlBuilder: unsupportet parameter combination"));
+    return "# not in ?";
 }
 
-SqlBuilder._ERROR_NO_TABLE = function()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "in" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", ["Fritz"], SqlBuilder.IN())
+ */
+SqlBuilder.IN = function()
 {
-    return new Error(translate.text("SqlBuilder.deleteDat/updateData: You have to specify a tablename"));
+    return "# in ?";
 }
 
-SqlBuilder._ERROR_NO_PARAMETER_PROVIDED = function()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "exists" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere(null, mySubSqlBuilder, SqlBuilder.EXISTS())
+ */
+SqlBuilder.EXISTS = function()
 {
-    return new Error(translate.text("SqlBuilder: You have to specify at least one parameter"));
+    return "exists ?";
 }
 
-SqlBuilder._ERROR_WHERE_NOT_FIRST = function()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "not exists" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere(null, mySubSqlBuilder, SqlBuilder.NOT_EXISTS())
+ */
+SqlBuilder.NOT_EXISTS = function()
 {
-    return new Error(translate.text("SqlBuilder: .where has to be called before following and/or."));
+    return "not exists ?";
 }
 
-SqlBuilder._ERROR_ONLY_ONE_WHERE = function()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "year(#) = ?" statement.
+ * If you use this, the default pFieldType will be SQLTYPES.INTEGER.
+ * 
+ * @return {Function} 
+ * 
+ * @example
+ * var cond = newWhere("FORECAST.DATE_START", DateUtils.getCurrentYear(), SqlBuilder.YEAR_EQUALS());
+ */
+SqlBuilder.YEAR_EQUALS = function ()
 {
-    return new Error(translate.text("SqlBuilder: .where has to be called only one time. Use and/or for further conditions."));
+    //function will be called later so it can use the alias of the SqlBuilder
+    return function (pAlias) {return [(new SqlMaskingUtils(pAlias).yearFromDate("#")) + " = ?", SQLTYPES.INTEGER];};
 }
 
-SqlBuilder._ERROR_INCOMPLETE_SELECT = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# = ?" statement.
+ * This is the default for the pCondition parameter, so it can be omitted.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", "Fritz", SqlBuilder.EQUAL())
+ */
+SqlBuilder.EQUAL = function ()
 {
-    return new Error(translate.text("SqlBuilder: select and from were expected, but not provided."));
+    return "# = ?";
 }
 
-SqlBuilder._ERROR_CONDITION_IS_MANDATORY = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# &lt;&gt; ?" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", "Fritz", SqlBuilder.NOT_EQUALS())
+ */
+SqlBuilder.NOT_EQUAL = function ()
 {
-    return new Error(translate.text("SqlBuilder: You have to provide a subquery as SqlBuilder, prepared-array or string"));
+    return "# <> ?";
 }
 
-SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NOT_COMPLETE = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# like ?" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", "F%", SqlBuilder.LIKE())
+ */
+SqlBuilder.LIKE = function ()
 {
-    return new Error(translate.text("SqlBuilder: If pFieldOrCond is a SqlBuilder & pValue is provided, pFieldOrCond has to be a full SqlBuilder which will be used as subselect"));
+    return "# like ?";
 }
 
-SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NO_FIELD_TYPE = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# like ?" statement.
+ * 
+ * @return {String} 
+ * 
+ * @example
+ * var cond = newWhere("PERSON.FIRSTNAME", "F%", SqlBuilder.NOT_LIKE())
+ */
+SqlBuilder.NOT_LIKE = function ()
 {
-    return new Error(translate.text("SqlBuilder: If pFieldOrCond is a SqlBuilder & pValue is provided, you have to provide also pFieldType, as the type cannot be calculated from pFieldOrCond because it is a subselect"));
+    return "# not like ?";
 }
 
-SqlBuilder._ERROR_CONDITION_WRONG_FORMAT = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# &gt; ?" statement.
+ * 
+ * @return {String} 
+ */
+SqlBuilder.GREATER = function ()
 {
-    return new Error(translate.text("SqlBuilder: The '#' in pCondition has to occur before the '?' and '?' has to occur 1 time, '#' has to occur 1 or 0 times."));
+    return "# > ?";
 }
 
-SqlBuilder._ERROR_NOT_BOOLEAN = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# &lt; ?" statement.
+ * 
+ * @return {String} 
+ */
+SqlBuilder.LESS = function ()
 {
-    return new Error(translate.text("pExecuteOnlyIfConditionExists has to be of type boolean. This parameter controls what happens if the condition is empty (select / delete all or nothing)"));
+    return "# < ?";
 }
 
-SqlBuilder._ERROR_UPDATE_VALUES_INVALID = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# &gt;= ?" statement.
+ * 
+ * @return {String} 
+ */
+SqlBuilder.GREATER_OR_EQUAL = function ()
 {
-    return new Error(translate.text("SqlBuilder: The provided values object for updateFields is invalid or is not an object."));
+    return "# >= ?";
 }
 
-SqlBuilder._ERROR_PAGESIZE_INVALID = function ()
+/**
+ * Constant-like function which provides a value for pCondition if you need a "# &lt;= ?" statement.
+ * 
+ * @return {String}
+ */
+SqlBuilder.LESS_OR_EQUAL = function ()
 {
-    return new Error(translate.text("SqlBuilder: The pagesize is not set or is not a number."));
+    return "# <= ?";
 }
 
-SqlBuilder._ERROR_NOT_A_FUNCTION = function ()
-{
-    return new Error(translate.text("SqlBuilder: The provided callback function is not a function."));
-}
 /**
- * Alternative way of creating a new SqlBuilder object that allows to use
- * methods on it directly without having to put brackets around it
- * 
- * @return {SqlBuilder} a new SqlBuilder object
- * 
- * @example 
- * var query = SqlBuilder.begin()
- *   .select("ORGANISATION.NAME, FIRSTNAME, LASTNAME")
- *   .from("PERSON")
- *   .join("CONTACT", "CONTACT.PERSON_ID = PERSON.PERSONID")
- *   .leftJoin("ORGANISATION", SqlCondition.begin()
- *       .and("CONTACT.ORGANISATION_ID = ORGANISATION.ORGANISATIONID")
- *       .andPrepare("ORGANISATION.NAME", "S%", "# like ?")
- *       .build("1=2"))
- *   .where(SqlCondition.begin()
- *       .andPrepare("CONTACT.STATUS", $KeywordRegistry.contactStatus$active())
- *       .build("1=2"));
- * 
- * if (getCountry) //changing and adding parts
- * {
- *   query.select("ORGANISATION.NAME, FIRSTNAME, LASTNAME, COUNTRY");
- *   query.leftJoin("ADDRESS", "CONTACT.ADDRESS_ID = ADDRESS.ADDRESSID");
- * }
- *   
- * var data = db.table(query.build());
+ * Constant-like function which returns an impossible condition ("1 = 2").
  * 
- * @deprecated using .begin is deprecated as it's now possible to write "new SqlBuilder().select(...).from(...)....
-               You can now use "newSelect(...)", "newWhere(...)", "newWhereIfSet(...)" or "new SqlBuilder()" to create a new SqlBuilder instance.
-               For further SqlBuilder usage see the documentation-property of the Sql_lib.
+ * @return {String}
  */
-SqlBuilder.begin = function ()
+SqlBuilder.NORESULT_CONDITION = function ()
 {
-    return new SqlBuilder();
+    return "1 = 2";
 }
 
-
 /**
- * Builds the sql and uses SqlUtils.translateXXXWithQuotes to make a string out of it.
- * @param {String} [pDefaultConditionIfNone=""] the default condition string to use if the SqlBuilder contains no condition. In most cases you won't need this
- * @param {Boolean} [pForceAsStatement=false] forces the use of SqlUtils.translateStatementWithQuotes even if it's no full statement. This is needed for example if you do not want brakets around the generated statement
- * @return {String} the sql as string
+ * Object providing constant-like functions for sql-any-conditions.
  */
-SqlBuilder.prototype.toString = function(pDefaultConditionIfNone, pForceAsStatement)
-{
-    var built = this.build(pDefaultConditionIfNone)
-    
-    if (built[0] !== "")
-    {
-        if (!pForceAsStatement && !this.isFullSelect() && (this.hasCondition() || pDefaultConditionIfNone))
-            return SqlUtils.translateConditionWithQuotes(built, this.alias);
-        else
-            return SqlUtils.translateStatementWithQuotes(built, this.alias);
-    }
-    
-    return "";
-}
-
-/**
- * Sets the select clause of the sql.
- * @param {String|Array|SqlBuilder} pFields You can pass:<br/>
- *                                          - A String is just used AS IT IS. (e.g. "FIRSTNAME, LASTNAME")<br/>
- *                                          - SqlBuilder is used as Subquery<br/>
- *                                          - The array can also contain Strings, SqlBuilder which are just concatenated (e.g. ["FIRSTNAME", "LASTNAME", someSqlBuilderContainingFullSelect])
- * 
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.select = function(pFields)
-{
-    this._select = SqlBuilder._getStatement(pFields, "select", undefined, true, true);
-    return this;
-}
-
-/**
- * Sets the select clause of the sql with distinct.
- * @param {String|String[]} pFields
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.selectDistinct = function (pFields)
-{
-    this._select = SqlBuilder._getStatement(pFields, "select distinct", undefined, true, true);
-    return this;
-}
-
-/**
- * Sets the select clause to "select count(...)"
- * @param {String} [pField=*]   sql column to count, if omitted "count(*)" will be used
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.selectCount = function (pField)
-{
-    if (pField == undefined)
-    {
-        pField = "*";
-    }
-    this._select = SqlBuilder._getStatement(pField, "select count(", ")", true, true);
-    return this;
-}
-
-
-/**
- * sets an alias-name which is added at some places if this SqlBuilder is used as subselect (e.g. in .select(), .join(), .from(), ...)
- * @param {String} pSubselectAlias
- * 
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.subselectAlias = function(pSubselectAlias)
-{
-    this._subselectAlias = pSubselectAlias;
-    return this;
-}
-
-/**
- * Sets the table that is used for insert/update/delete functions.
- * 
- * @param {String} pTable
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.tableName = function (pTable)
-{
-    this._tableName = pTable;
-    return this;
-}
-
-/**
- * Sets the from clause of the sql.<br/>
- * <br/>
- * Note: It is recommended to add joins via the .join functions.<br/>
- *       But in some cases you may already get a full from clause including the joins. In this case it is also possible to include them in the from-string.<br/>
- * 
- * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
- *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from (select FIRSTNAME from PERSON)
- * @param {String} [pTableAlias] table alias
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.from = function(pTable, pTableAlias)
-{
-    this._from = SqlBuilder._getStatement(pTable, "", pTableAlias, false, (pTableAlias ? false : true));
-    if (typeof(pTable) === "string")
-        this._tableName = pTable;
-    return this;
-}
-
-/**
- * Adds a join clause to the sql.
- * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
- *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
- * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
- *                                                - a string (without the where keyword)<br/>
- *                                                - a SqlBuilder NOTE: only the condition is used from it
- *
- * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
- * @param {String} [pPrefix] string before the join, for example "left", "right"
- * @param {String} [pReplacementForWordJoin] if this is set, this is used instead of the word "join". Needed for e.g. OUTER APPLY in MSSQL
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.join = function(pTable, pCondition, pTableAlias, pPrefix, pReplacementForWordJoin)
-{
-    // support for deprecated SqlCondition
-    if (pCondition instanceof SqlCondition)
-    {
-        pCondition = newWhere(pCondition);
-        logging.log("Warning: using .where with a SqlCondition as pFieldOrCond is deprecated. The SqlCondition will be removed in version >= 2020.x\n"
-                    + "For SqlBuilder usage see the documentation-property of the Sql_lib.");
-    }
-    
-    var prefix = (pReplacementForWordJoin ? pReplacementForWordJoin : "join");
-    if (pPrefix)
-        prefix = pPrefix + " " + prefix;
-    
-    var postfix = pCondition ? "on" : "";
-    
-    if (pTableAlias)
-        postfix = pTableAlias + " " + postfix;
-    else if (pTable instanceof SqlBuilder && pTable._subselectAlias)
-        postfix = pTable._subselectAlias + " " + postfix;
-    
-    var joinPart = SqlBuilder._getStatement(pTable, prefix, postfix.trim());
-    
-    if (pCondition)
-    {
-        if (pCondition instanceof SqlBuilder)
-            pCondition = [pCondition._where.sqlStorage, pCondition._where.preparedValues]
-
-        var conditionPart = SqlBuilder._getStatement(pCondition);
-
-        joinPart.sqlStorage += " " + conditionPart.sqlStorage;
-        joinPart.preparedValues = joinPart.preparedValues.concat(conditionPart.preparedValues);
-    }
-    
-    this._joins.push(joinPart)
-    return this;
-}
-
-/**
- * Adds a left join clause to the sql.
- * 
- * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
- *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
- * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
- *                                                - a string (without the where keyword)<br/>
- *                                                - a SqlBuilder NOTE: only the condition is used from it
- *
- * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.leftJoin = function(pTable, pCondition, pTableAlias)
-{
-    return this.join(pTable, pCondition, pTableAlias, "left");
+SqlBuilder.ANY = {
+    /**
+     * Constant-like function that returns a "# = any ?" statement.
+     */
+    EQUAL : function () {return "# = any ?";},
+    /**
+     * Constant-like function that returns a "# &lt;&gt; any ?" statement.
+     */
+    NOT_EQUAL : function () {return "# <> any ?";},
+    /**
+     * Constant-like function that returns a "# &gt; any ?" statement.
+     */
+    GREATER : function () {return "# > any ?";},
+    /**
+     * Constant-like function that returns a "# &gt;= any ?" statement.
+     */
+    GREATER_OR_EQUAL : function () {return "# >= any ?";},
+    /**
+     * Constant-like function that returns a "# &lt; any ?" statement.
+     */
+    LESS : function () {return "# < any ?";},
+    /**
+     * Constant-like function that returns a "# &lt;= any ?" statement.
+     */
+    LESS_OR_EQUAL : function () {return "# <= any ?";}
 }
 
 /**
- * Adds a right join clause to the sql.
- * 
- * @param {String|SqlBuilder} pTable if it is a String, it is used as it is as table<br/>
- *                                   if it is a SqlBuilder, it is used as subselect: e.g. select * from Table1 join (select FIRSTNAME from PERSON) on ...
- * @param {String|SqlBuilder} [pCondition] The where condition. This can be<br/>
- *                                                - a string (without the where keyword)<br/>
- *                                                - a SqlBuilder NOTE: only the condition is used from it
- *
- * @param {String} [pTableAlias] This alias is used to add an alias to the tablename 
- * @return {SqlBuilder} current SqlBuilder object
+ * Object providing constant-like functions for sql-all-conditions.
  */
-SqlBuilder.prototype.rightJoin = function(pTable, pCondition, pTableAlias)
-{
-    return this.join(pTable, pCondition, pTableAlias, "right");
+SqlBuilder.ALL = {
+    /**
+     * Constant-like function that returns a "# = all ?" statement.
+     */
+    EQUAL : function () {return "# = all ?";},
+    /**
+     * Constant-like function that returns a "# &lt;&gt; all ?" statement.
+     */
+    NOT_EQUAL : function () {return "# <> all ?";},
+    /**
+     * Constant-like function that returns a "# &gt; all ?" statement.
+     */
+    GREATER : function () {return "# > all ?";},
+    /**
+     * Constant-like function that returns a "# &gt;= all ?" statement.
+     */
+    GREATER_OR_EQUAL : function () {return "# >= all ?";},
+    /**
+     * Constant-like function that returns a "# &lt; all ?" statement.
+     */
+    LESS : function () {return "# < all ?";},
+    /**
+     * Constant-like function that returns a "# &lt;= all ?" statement.
+     */
+    LESS_OR_EQUAL : function () {return "# <= all ?";}
 }
 
 /**
  * Throws an error if pValue is null, undefined or a SqlBuilder without condition (or if pValue is a $-variable: error if the result of it is null or undefined)<br/>
  * Also throws an error if pFieldOrCond is the only parameter and it is null<br/>
  * <br/>
- * Starts the where clause of the SQL. You may pass the first condition with it.<br/>
- * But you can also call this function without any parameter and add the conditions with subsequent .and / .or<br/>
- * <br/>
- * This method exists mainly for semantic reasons and can only be callled once.<br/>
- * As shourtcut you could use the newWhere(...) function.<br/>
+ * Adds a condition by using "or" to the Sql.<br/>
+ * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
  * 
  * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
  *                                                                          else it is used as Field.<br/>
@@ -1187,9 +1383,6 @@ SqlBuilder.prototype.rightJoin = function(pTable, pCondition, pTableAlias)
  *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
  *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
  *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          - a SqlBuilder: the full select is used as subselect and compared with pValue. <br/>
- *                                                                               (e.g. select * from PERSON where (select "NAME" from ORGANISATION where ... ) = ?)<br/>
- *                                                                                Note: for this you have to provide pFieldType as the type cannot be calculated from the subselect!<br/>
  *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
  *                                                                     
  * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
@@ -1200,8 +1393,8 @@ SqlBuilder.prototype.rightJoin = function(pTable, pCondition, pTableAlias)
  *                                                                           
  * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
  *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong>
+ *                               ? will be replaced by pValue
+ *                               
  * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
  *                                                                In most cases you don't need this.<br/>
  *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
@@ -1209,34 +1402,16 @@ SqlBuilder.prototype.rightJoin = function(pTable, pCondition, pTableAlias)
  *                                                                  
  * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldType)
-{    
-    // support for deprecated SqlCondition
-    if (pFieldOrCond instanceof SqlCondition && pValue === undefined && pCondition === undefined && pFieldType === undefined)
-    {
-        let copiedCondition = newWhere();
-        
-        copiedCondition._where.preparedValues = pFieldOrCond.preparedValues;
-        copiedCondition._where._lastWasOr = pFieldOrCond._lastWasOr;
-        copiedCondition._where.sqlStorage = pFieldOrCond.sqlStorage;
-        
-        pFieldOrCond = copiedCondition;
-        
-        logging.log("Warning: using .where with a SqlCondition as pFieldOrCond is deprecated. The SqlCondition will be removed in version >= 2020.x\n"
-                    + "For SqlBuilder usage see the documentation-property of the Sql_lib.");
-    }
-    
-    return this._setWhere(pFieldOrCond, pValue, pCondition, pFieldType, this.or);
-}
-
+SqlBuilder.prototype.or = function(pFieldOrCond, pValue, pCondition, pFieldType)
+{
+    return this._or(pFieldOrCond, pValue, true, pCondition, pFieldType);
+}
+
 /**
- * Difference to where(): where throws errors on invalid values, whereIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
- * <br/>
- * Starts the whereIfSet clause of the SQL. You may pass the first condition with it.<br/>
- * But you can also call this function without any parameter and add the conditions with subsequent .and / .or<br/>
+ * Difference to or(): or() throws errors on invalid values, orIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
  * <br/>
- * This method exists mainly for semantic reasons and can only be callled once.<br/>
- * As shourtcut you could use the newWhereIfSet(...) function.
+ * Adds a condition by using "or" to the Sql.<br/>
+ * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
  * 
  * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
  *                                                                          else it is used as Field.<br/>
@@ -1254,9 +1429,6 @@ SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldTy
  *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
  *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
  *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          - a SqlBuilder: the full select is used as subselect and compared with pValue. <br/>
- *                                                                               (e.g. select * from PERSON where (select "NAME" from ORGANISATION where ... ) = ?)<br/>
- *                                                                                Note: for this you have to provide pFieldType as the type cannot be calculated from the subselect!<br/>
  *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
  *                                                                     
  * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
@@ -1267,8 +1439,7 @@ SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldTy
  *                                                                           
  * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
  *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong>
+ *                               ? will be replaced by pValue
  *                               
  * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
  *                                                                In most cases you don't need this.<br/>
@@ -1277,3006 +1448,2201 @@ SqlBuilder.prototype.where = function(pFieldOrCond, pValue, pCondition, pFieldTy
  *
  * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.prototype.whereIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
+SqlBuilder.prototype.orIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    return this._setWhere(pFieldOrCond, pValue, pCondition, pFieldType, this.orIfSet);
+    return this._or(pFieldOrCond, pValue, false, pCondition, pFieldType);
 }
 
 /**
- * helper function for .where and .whereIfSet because they do almost the same<br/>
- * See .where() for further explanations
+ * Throws an error if pValue is null, undefined or a SqlBuilder without condition (or if pValue is a $-variable: error if the result of it is null or undefined)<br/>
+ * Also throws an error if pFieldOrCond is the only parameter and it is null<br/>
+ * <br/>
+ * Adds a condition by using "and" to the Sql.<br/>
+ * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
  * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] 
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] 
- * @param {String} [pCondition="# = ?"] <strong>IMPORTANT: the # has to be before the ?</strong><br/>
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] 
- * @param {SQLTYPES|Numeric} pAddCondFn=AutomaticallyLoadedType This is a callback which is called if a condition should be added (needs to have same parameters as .or()
- *
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
+ *                                                                          else it is used as Field.<br/>
+ *                                                                     <br/>
+ *                                                                          If you use it as Subselect (the only param), it can be:<br/>
+ *                                                                           - a string: just added as it is<br/>
+ *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
+ *                                                                                                 the sql is just used as it is.<br/>
+ *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
+ *                                                                           <br/>
+ *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
+ *                                                                          load the SQLTYPE for this condition. <br/>
+ *                                                                          It can be provided in the following ways:<br/>
+ *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
+ *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
+ *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
+ *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
+ *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
+ *                                                                     
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
+ *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
+ *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
+ *                                                                           
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue
+ *                               
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
+ *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
+ *                                                                  
  * @return {SqlBuilder} current SqlBuilder object
- * @ignore
  */
-SqlBuilder.prototype._setWhere = function (pFieldOrCond, pValue, pCondition, pFieldType, pAddCondFn)
+SqlBuilder.prototype.and = function(pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    // allow where-call without parameter to just enable where mode
-    if (pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
-    {
-        this._where._whereWasCalled = true;
-        return this;
-    }
-    
-    // where has to be called before all other and / or
-    if (this.hasCondition())
-        throw SqlBuilder._ERROR_WHERE_NOT_FIRST();
-    
-    // only one where call is allowed
-    if (this._where._whereWasCalled)
-        throw SqlBuilder._ERROR_ONLY_ONE_WHERE();
-    
-    this._where._whereWasCalled = true;
-    return pAddCondFn.call(this, pFieldOrCond, pValue, pCondition, pFieldType);
+    return this._and(pFieldOrCond, pValue, true, pCondition, pFieldType);
 }
 
 /**
- * helper function which adds a condition
+ * Difference to and(): and() throws errors on invalid values, andIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
+ * <br/>
+ * Adds a condition by using "and" to the Sql.<br/>
+ * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
  * 
- * @param {String|SqlBuilder|PreparedSqlArray} pCondition the condition to add
- * @param {Boolean} [pMandatory=true] if true: throws error on SqlBuilder without conditon or PreparedSqlArray with empty string. Else: just does nothing
- * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
- * @param {Boolean} pBrackets if true, Brackets are added in some cases
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
+ *                                                                          else it is used as Field.<br/>
+ *                                                                     <br/>
+ *                                                                          If you use it as Subselect (the only param), it can be:<br/>
+ *                                                                           - a string: just added as it is<br/>
+ *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
+ *                                                                                                 the sql is just used as it is.<br/>
+ *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
+ *                                                                           <br/>
+ *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
+ *                                                                          load the SQLTYPE for this condition. <br/>
+ *                                                                          It can be provided in the following ways:<br/>
+ *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
+ *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
+ *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
+ *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
+ *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
+ *                                                                     
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
+ *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
+ *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
+ *                                                                           
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue
+ *                               
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
+ *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
  *
  * @return {SqlBuilder} current SqlBuilder object
- * @ignore
  */
-SqlBuilder.prototype._whereCondition = function(pCondition, pMandatory, pAddPreparedConditionCallback, pBrackets)
+SqlBuilder.prototype.andIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    if (pCondition === undefined)
-        return this;
-    
-    if (pMandatory === undefined)
-        pMandatory = true;
-    
-    var sql = pCondition;
-    var typeofSql = typeof sql;
-
-    // the field is a simple string -> just add the string, no prepared statement
-    if (typeofSql == "string")
-    {
-        pAddPreparedConditionCallback(this, [sql, []]);
-        return this;
-    }
-
-    // the field is an array -> it is a prepared condition
-    if (Array.isArray(sql))
-    {
-        if (sql[0])
-        {
-            this._where.preparedValues = this._where.preparedValues.concat(sql[1]);
-
-            // add only brackets if needed
-            if (pBrackets)
-                sql[0] = " ( " + sql[0] + " ) ";
-
-            pAddPreparedConditionCallback(this, [sql[0], []], pBrackets)
-            return this;
-        }
-        else if (pMandatory)
-            throw SqlBuilder._ERROR_CONDITION_IS_MANDATORY();
-        
-        return this;
-    }
-
-    // the field is a SqlBuilder -> it is a SqlBuilder which contains a condition -> the condition of the SqlBuilder is added.
-    if (sql instanceof SqlBuilder)
-    {
-        // add only brackets if needed
-        var sqlString = sql._where.sqlStorage;
-        
-        
-        var condString = sqlString;
-        if (condString.trim() != "")
-        {
-            if (pBrackets)
-                condString = " ( " + condString + " ) ";
-            
-            pAddPreparedConditionCallback(this, [condString, sql._where.preparedValues], pBrackets);
-            return this;
-        }
-        else if (pMandatory)
-            throw SqlBuilder._ERROR_CONDITION_IS_MANDATORY();
-        
-        return this;
-    }
-
-    throw SqlBuilder._ERROR_INVALID_CONDITION_VALUE_TYPE();
+    return this._and(pFieldOrCond, pValue, false, pCondition, pFieldType);
 }
 
 /**
- * helper function which adds a Subquery-condition
+ * Sets the order by clause of the sql.
  * 
- * @param {SqlBuilder|PreparedSqlArray} pSubquery the subquery to add
- * @param {Boolean} [pMandatory=true] if true: throws error on SqlBuilder without conditon or PreparedSqlArray with empty string. Else: just does nothing
- * @param {Boolean} pCondition the condition to be used: e.g. "exists(?)" the ? is replaced by the subquery
- * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
- *
+ * @param {String|String[]} pOrderBy a string is added as it is, a array is concatenated by ', '
  * @return {SqlBuilder} current SqlBuilder object
- * @ignore
  */
-SqlBuilder.prototype._whereSubquery = function(pSubquery, pMandatory, pCondition, pAddPreparedConditionCallback)
+SqlBuilder.prototype.orderBy = function(pOrderBy)
 {
-    if (pSubquery === undefined)
-        return this;
-    
-    if (pMandatory === undefined)
-        pMandatory = true;
-    
-    var sql = pSubquery;
-
-    // the field is an array -> it is a prepared statement which already SHOULD contain exists or another condition
-    // Both can be handled by _prepare
-    if (Array.isArray(sql))
-    {        
-        if (sql[0])
-            pAddPreparedConditionCallback(this, this._prepare(undefined, sql, pCondition));
-        else if (pMandatory)
-            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
-        
-        return this;
-    }
-
-    // the field is a SqlBuilder -> it is a SqlBuilder which contains a condition -> the condition of the SqlBuilder is added.
-    if (sql instanceof SqlBuilder)
-    {
-         var subQuery = pSubquery;
-
-        // Without condition this function cannot be used with SqlBuilder object as it cannot contain a condition
-        if (!pCondition)
-            throw SqlBuilder._ERROR_NO_CONDITION();
-        
-        if (subQuery.isFullSelect() || subQuery.hasCondition()) //can also be only an condition if SqlBuilder.NOT() is used
-        {
-            var preparedObj = subQuery.build();
-            pAddPreparedConditionCallback(this, this._prepare(undefined, preparedObj, pCondition));
-        } 
-        else if (pMandatory)
-            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
-        
-        return this;
-    }
-    
-    throw SqlBuilder._ERROR_INVALID_SUBQUERY_TYPE();
+    this._orderBy = SqlBuilder._getStatement(pOrderBy, "order by", undefined, true);
+    return this;
 }
 
 /**
- * helper function which adds a condition to the where
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
- * @param {Boolean} [pMandatory=true] if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
- * @param {String} [pCondition="# = ?"] see .where()
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
- * @param {CallbackFunction} pAddPreparedConditionCallback A Callback funtion which receives a PreparedSqlArray as parameter
+ * Sets the group by clause of the sql.
  * 
+ * @param {String|String[]} pFields a string is added as it is, a array is concatenated by ', '
  * @return {SqlBuilder} current SqlBuilder object
- * @ignore
  */
-SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, pAddPreparedConditionCallback)
+SqlBuilder.prototype.groupBy = function(pFields)
 {
-    //In a special case, pCondition can be a function. It will be called with the alias as argument and
-    //must return an array of the condition string and (optionally) the required sql field type.
-    //alternatively the function may return a string only to make the usage more bulletproof and convenient, so both SqlBuilder.EQUAL() 
-    //and SqlBuilder.EQUAL work equally 
-    if (pCondition && typeof pCondition === "function")
-    {
-        var resCond = pCondition(this.alias);
-        if (Array.isArray(resCond))
-        {
-            pCondition = resCond[0];
-            pFieldType = pFieldType || resCond[1];
-        }
-        else if(Utils.isString(pCondition))
-        {
-            pCondition = resCond;
-        }
-    }
-    
-    if (pCondition && !SqlUtils.checkConditionFormat(pCondition)) 
-        throw SqlBuilder._ERROR_CONDITION_WRONG_FORMAT();
-
-    if (pMandatory === undefined)
-        pMandatory = true;
-    
-    if (!this._where._whereWasCalled)
-        throw SqlBuilder._ERROR_WHERE_NOT_FIRST();
-    
-    if (!pMandatory && pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
-        return this;
-
-    if (pFieldOrCond === undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
-        throw SqlBuilder._ERROR_NO_PARAMETER_PROVIDED();
- 
-    // Special case: if only pFieldOrCond is set and we can identify it as a valid field-string (e.g. "Table.Field") we assume that it is not just a condition string.
-    // --> we can check pValue for undefined and also allow simple string-conditions
-    // --> this only works if isFullFieldQualifier() can detect if the supplied string is a valid field-string or if it is some sql. 
-    //     currently it checks for some special characters which should not exist in any field-string but in conditions. 
-    //     If there is a special case missing -> add it to the regexp in isFullFieldQualifier()
-    if (pValue === undefined && pCondition === undefined && pFieldType === undefined && typeof pFieldOrCond == "string" && SqlUtils.isFullFieldQualifier(pFieldOrCond))
-    {
-        if (pMandatory)
-            throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
-        else
-            return this;
-    }
-  
-    // just call the andCondition function if it is only a Condition
-    if (pFieldOrCond !== undefined && pValue === undefined && pCondition === undefined && pFieldType === undefined)
-        return this._whereCondition(pFieldOrCond, pMandatory, pAddPreparedConditionCallback, true);
-    
-    // Subselects containing full select can be used as field, if pValue and pFieldType are provided.
-    if (pFieldOrCond instanceof SqlBuilder)
-    {
-        if (!pFieldOrCond.isFullSelect())
-        {
-            throw SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NOT_COMPLETE();
-        }
-        
-        if (!pFieldType)
-            throw SqlBuilder._ERROR_SUBSELECT_AS_FIELD_NO_FIELD_TYPE();
-            
-        var tmpCond = newWhere(this.alias)
-                            ._addWhere("SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN", pValue, pMandatory, pCondition, pFieldType, pAddPreparedConditionCallback);
-                
-        var subSqlPrepared = pFieldOrCond.build();
-        
-        tmpCond._where.sqlStorage = SqlUtils.replaceConditionTemplate(tmpCond._where.sqlStorage, 'SQL_LIB_DUMMY_TABLE.SQL_LIB_DUMMY_COLUMN', "( " + subSqlPrepared[0] + " )");
-        tmpCond._where.preparedValues = subSqlPrepared[1].concat(tmpCond._where.preparedValues)
-        
-        this._whereCondition(tmpCond, pMandatory, pAddPreparedConditionCallback, true)
-        return this;
-    }
-    
-    // first check the default-mandatory-cases: null or undefined. everything else such as checking $-variables is done later
-    if (pMandatory && (pValue === null || pValue === undefined))
-        throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
-    
-    // a field is string or array -> normal case
-    // OR !pFieldOrCond and pValue and pCondition is given -> preparedSQL/SqlBuilder can be used without field if pCondition is set (e.g. with "exists ?")
-    if(((typeof pFieldOrCond == "string" || Array.isArray(pFieldOrCond) || (!pFieldOrCond && pFieldType)) || (!pFieldOrCond && (pCondition && pValue instanceof SqlBuilder || !(pValue instanceof SqlBuilder)))))
-    {
-        var field = pFieldOrCond;
-        var typeofValue = typeof pValue;
-
-        // ... a string starting with $ -> jdito varable which has to be resolved
-        if (typeofValue == "string" && pValue.length >= 2 && pValue[0] == "$" && pValue[1] != "$") // escape $ if using two $
-        {
-            //important: just overwrite the value because some $local variables may contain an array and then the default handling of arrays (which
-            //is generating an IN-statement) should apply
-            pValue = vars.get(pValue);
-            if (pMandatory && pValue === null)
-                throw SqlBuilder._ERROR_VALUE_IS_MANDATORY_JDITO_VAR();
-            typeofValue = typeof pValue;
-        }
-
-        // remove the first $ if there are two $
-        if (typeofValue == "string" && pValue.length >= 2 && pValue[0] == "$" && pValue[1] == "$")
-            pValue = pValue.slice(1);
-        
-        //support for Set by converting to Array
-        if (pValue instanceof Set)
-            pValue = Array.from(pValue);
-        
-        // pValue can be...
-        // ... a SqlBuilder / Prepared statement array -> it is a SqlBuilder containing a complete subquery or an simple array (in statement)
-        if (pValue instanceof SqlBuilder || Array.isArray(pValue) || (typeofValue == "string" && (pFieldOrCond == undefined || pFieldOrCond == null)))
-        {            
-            // check if the array is really a value-array for an in and not a prepared statement
-            if (Array.isArray(pValue) && (pValue.length <= 1 || !Array.isArray(pValue[1])))
-            {
-                if (pValue.length == 0)
-                {
-                    if (pMandatory)
-                        throw SqlBuilder._ERROR_VALUE_IS_MANDATORY();
-                    
-                    return this;
-                }
-                               
-                // if it is null -> ignore it. -> the pCondition should not contain a # in this case
-                if (field != null)
-                {
-                    var [alias, parsedField] = SqlUtils.parseField(field)
-                    if (pFieldType === undefined || pFieldType === null)
-                        pFieldType = SqlUtils.getSingleColumnType(parsedField, undefined, this.alias);
-                }
-                //overwrite condition to set a default behaviour
-                if (pCondition == undefined)
-                    pCondition = SqlBuilder.IN();
-                // value-array -> convert it to a prepared statement ["(?, ?, ?)", [[val1, type1], [val2, type2], [val3, type3]]]
-                this._whereCondition(this._prepare(field, SqlUtils.getSqlInStatement(undefined, pValue, undefined, true, pFieldType), pCondition, pFieldType, false), undefined, pAddPreparedConditionCallback, true);
-                return this;
-            }
-            
-            if (pFieldOrCond !== null && pFieldOrCond !== undefined)
-            {
-                if (!pCondition)
-                    pCondition = SqlBuilder.EQUAL();
-                
-                pCondition = SqlUtils.replaceConditionTemplate(pCondition, '#', SqlUtils.parseField(pFieldOrCond)[0])
-            }
-            else
-            {
-                if (!pCondition)
-                    pCondition = "?"
-            }
-            
-            // _whereSubquery can handle SqlBuilder and prepared statements as value
-            return this._whereSubquery(pValue, pMandatory, pCondition, pAddPreparedConditionCallback);
-        }
-
-        if (!pCondition)
-            pCondition = SqlBuilder.EQUAL();
-
-        // ... everything else -> just pass it
-        if (pValue === false || pValue === 0 || pValue === "" || pValue)
-        {
-            let prep = this._prepare(field, pValue, pCondition, pFieldType)
-            this._whereCondition(prep, undefined, pAddPreparedConditionCallback);
-        }
-            
-        return this;
-    }
-
-    
-    throw SqlBuilder._ERROR_UNSUPPORTED_PARAMETER_COMBINATION();
+    this._groupBy = SqlBuilder._getStatement(pFields, "group by", undefined, true);
+    return this;
 }
 
 /**
- * helper function to add a condition via "and"
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
- * @param {Boolean} [pMandatory=true] if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
- * @param {String} [pCondition="# = ?"] see .where()
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
+ * Adds another SqlBuilder object or select string with union.
  * 
- * @ignore
+ * @param {SqlBuilder|String} pSelect
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.prototype._and = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType)
+SqlBuilder.prototype.union = function(pSelect)
 {
-    return this._addWhere(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, function(that, pPreparedCondition) 
-    {
-        that._where._previouslyOnlyOr = false;
-        if (pPreparedCondition.length == 2 && typeof pPreparedCondition[0] == "string" && pPreparedCondition[0] != "" && Array.isArray(pPreparedCondition[1]))
-        {
-            if (that.hasCondition())
-                that._where.sqlStorage += " and ";
-            
-            that._where.sqlStorage += pPreparedCondition[0];
-            that._where.preparedValues = that._where.preparedValues.concat(pPreparedCondition[1]);
-        }
-    });
+    this._unions.push(SqlBuilder._getStatement(pSelect, "union"));
+    return this;
 }
 
 /**
- * helper function to add a condition via "or"
- * The callback inside of this function adds brackets where needed.
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} pFieldOrCond see .where()
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} pValue see .where()
- * @param {Boolean} [pMandatory=true] if true: throw error if pValue is null, undefined, SqlBuilder without condition, etc... else just ignore the condition
- * @param {String} [pCondition="# = ?"] see .where()
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] see .where()
+ * Adds another SqlBuilder object or select string with union all.
  * 
- * @ignore
+ * @param {SqlBuilder|String} pSelect
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.prototype._or = function(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType)
-{    
-    return this._addWhere(pFieldOrCond, pValue, pMandatory, pCondition, pFieldType, function(that, pPreparedCondition, pAlreadySurroundedByBrackets) 
-    {
-        if (pPreparedCondition.length == 2 && typeof pPreparedCondition[0] == "string" && pPreparedCondition[0] != "" && Array.isArray(pPreparedCondition[1]))
-        {   
-            if (that._where._previouslyOnlyOr)
-            {
-                that._where.sqlStorage = that._where.sqlStorage + " or " + pPreparedCondition[0];
-                that._where._lastWasOr = true;
-            }
-            else if (that.hasCondition())
-            {
-                let cond = pPreparedCondition[0];
-                
-                if (!pAlreadySurroundedByBrackets)
-                    cond = "(" + cond + ")";
-                
-                if (that._where._lastWasOr)
-                    that._where.sqlStorage = that._where.sqlStorage + " or " + cond;
-                else
-                    that._where.sqlStorage = "(" + that._where.sqlStorage + ") or " + cond;
-                
-                that._where._lastWasOr = true;
-            } 
-            else
-            {
-                if (!that.hasCondition())
-                    that._where._previouslyOnlyOr = true;
-                
-                that._where.sqlStorage = pPreparedCondition[0];
-            }
-            that._where.preparedValues = that._where.preparedValues.concat(pPreparedCondition[1]);
-        }
-    });
+SqlBuilder.prototype.unionAll = function(pSelect)
+{
+    this._unions.push(SqlBuilder._getStatement(pSelect, "union all"));
+    return this;
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "not" statement.
- * 
- * @return {String} 
+ * Adds a having clause to the sql.
  * 
- * @example
- * var cond = newWhere(null, someCondition, SqlBuilder.NOT())
+ * @param {String|SqlBuilder} [pCondition] The having condition. This can be
+ *                                                - a string (without the where keyword)
+ *                                                - a SqlBuilder NOTE: only the condition is used from it
+ *          
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.NOT = function()
+SqlBuilder.prototype.having = function(pCondition)
 {
-    return "not ?";
+    this._having = SqlBuilder._getStatement(pCondition, "having");
+    return this;
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "not in" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", ["Fritz"], SqlBuilder.NOT_IN())
+ * checks if conditions have been added to the object
+ * @return {Boolean} true if conditions have been added, false when not
  */
-SqlBuilder.NOT_IN = function()
-{
-    return "# not in ?";
+SqlBuilder.prototype.hasCondition = function() {
+    if (this._where.sqlStorage)
+        return true;
+    return false;
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "in" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", ["Fritz"], SqlBuilder.IN())
+ * checks if conditions have been added to the object
+ * Note: this does not nessecarily mean that hasCondition() is true
+ * @return {Boolean} true if .where was already called
  */
-SqlBuilder.IN = function()
-{
-    return "# in ?";
+SqlBuilder.prototype.whereWasCalled = function() {
+    return this._where._whereWasCalled;
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "exists" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere(null, mySubSqlBuilder, SqlBuilder.EXISTS())
+ * checks if all mandatory parts to execute the select have been added to the object
+ * ("select" and "from" parts)
+ * @return {Boolean} true if select and from have been added, false if not
  */
-SqlBuilder.EXISTS = function()
+SqlBuilder.prototype.isFullSelect = function() 
 {
-    return "exists ?";
+    return !(!this._select || !this._from);
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "not exists" statement.
- * 
- * @return {String} 
+ * Function that resets the current where-condition as if no conditions would have been added
+ * this is usefull if you want to reuse the same Builder over and over again with a different condition.
+ * This also resets whether where was already called, so you have to use .where to add a condition after this.
  * 
- * @example
- * var cond = newWhere(null, mySubSqlBuilder, SqlBuilder.NOT_EXISTS())
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.NOT_EXISTS = function()
+SqlBuilder.prototype.clearWhere = function() 
 {
-    return "not exists ?";
+    this._initWhere();
+    return this;
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "year(#) = ?" statement.
- * If you use this, the default pFieldType will be SQLTYPES.INTEGER.
- * 
- * @return {Function} 
- * 
- * @example
- * var cond = newWhere("FORECAST.DATE_START", DateUtils.getCurrentYear(), SqlBuilder.YEAR_EQUALS());
+ * function that initializes the properties of the ._where object, this is used in the
+ * constructor and .clearWhere 
  */
-SqlBuilder.YEAR_EQUALS = function ()
+SqlBuilder.prototype._initWhere = function ()
 {
-    //function will be called later so it can use the alias of the SqlBuilder
-    return function (pAlias) {return [(new SqlMaskingUtils(pAlias).yearFromDate("#")) + " = ?", SQLTYPES.INTEGER];};
+    //TODO: maybe put conditions in an object/array for better internal object structure
+    this._where.sqlStorage = "";
+    this._where.preparedValues = [];
+    this._where._lastWasOr = false; // save, if the last condition was an OR. For better bracket-placement
+    this._where._previouslyOnlyOr = false; // also for better bracket-placement
+    this._where._whereWasCalled = false; // where has always to be called first for a better semantic
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "# = ?" statement.
- * This is the default for the pCondition parameter, so it can be omitted.
+ * helper function for composing preparedStatements <br/>
+ *  <br/>
+ * see .where for more information about the parameters
  * 
- * @return {String} 
+ * @param {String | String[]} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} pValue the value that shall be set into the prepared statement
+ * @param {String} pCondition the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname;  <br/>
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\" <br/>
+ *                 Default is "# = ?" 
+ * @param {Numeric | Boolean} [pFieldType] SQL-column-type; if the fieldType is not given it's loaded automatically; <br/>
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop. <br/>
+ *                              e.g. <br/>
+ *                              for (...) { <br/>
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?") <br/>
+ *                              }
+ * @param {Boolean} pSubselectBrackets if true, brackets are added to subselects
+ * @return {PreparedSqlArray} a preparedSqlArray built out of the given parameters
  * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", "Fritz", SqlBuilder.EQUAL())
+ * @ignore
  */
-SqlBuilder.EQUAL = function ()
+SqlBuilder.prototype._prepare = function(pField, pValue, pCondition, pFieldType, pSubselectBrackets) 
 {
-    return "# = ?";
-}
+    if (pSubselectBrackets == undefined)
+        pSubselectBrackets = true;
+    
+    if (pValue == undefined)
+        throw new Error(translate.withArguments("${SQL_LIB_UNDEFINED_VALUE} field: %0", [pField]));
+    
+    if (pCondition == undefined) 
+        pCondition = SqlBuilder.EQUAL();
 
-/**
- * Constant-like function which provides a value for pCondition if you need a "# &lt;&gt; ?" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", "Fritz", SqlBuilder.NOT_EQUALS())
- */
-SqlBuilder.NOT_EQUAL = function ()
-{
-    return "# <> ?";
-}
+    var alias, field;
+    if (pField != null)
+    {
+        [alias, field] = SqlUtils.parseField(pField)
+        if (pFieldType == undefined)
+            pFieldType = SqlUtils.getSingleColumnType(pField, undefined, this.alias);
+        
+        var table = SqlUtils._parseFieldQualifier(pField).table;
+        //Set the table for update/delete if it isn't already set, so you don't need to specify the table if the where-condition contains it
+        if (table && !this._tableName)
+            this._tableName = table;
+    }
+    
+    var values = [];
 
-/**
- * Constant-like function which provides a value for pCondition if you need a "# like ?" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", "F%", SqlBuilder.LIKE())
- */
-SqlBuilder.LIKE = function ()
-{
-    return "# like ?";
-}
+    // If subselect: replace '?' with the subselect
+    if (Array.isArray(pValue))
+    {
+        pCondition = SqlUtils.replaceConditionTemplate(pCondition, "\\?", (pSubselectBrackets ? " ( " : " ") + pValue[0] + (pSubselectBrackets ? " ) " : " "));
+        values = pValue[1];
+    } 
+    else
+    {
+        var type = pFieldType;
+        //booleans are normally stored with TINYINT, this is to support true/false directly
+        if (_isIntegerType(type) && Utils.isBoolean(pValue))
+            pValue = Number(pValue);
+        values = [[pValue.toString(), type]];
+    }
 
-/**
- * Constant-like function which provides a value for pCondition if you need a "# like ?" statement.
- * 
- * @return {String} 
- * 
- * @example
- * var cond = newWhere("PERSON.FIRSTNAME", "F%", SqlBuilder.NOT_LIKE())
- */
-SqlBuilder.NOT_LIKE = function ()
-{
-    return "# not like ?";
+    if (pField != null)
+        pCondition = SqlUtils.replaceConditionTemplate(pCondition, "#", alias);
+    
+    return [pCondition, values];
+    
+    function _isIntegerType (pType)
+    {
+        return pType == SQLTYPES.TINYINT
+            || pType == SQLTYPES.SMALLINT
+            || pType == SQLTYPES.INTEGER
+            || pType == SQLTYPES.BIGINT;
+    }
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "# &gt; ?" statement.
+ * generates a part of the sql
  * 
- * @return {String} 
- */
-SqlBuilder.GREATER = function ()
-{
-    return "# > ?";
-}
-
-/**
- * Constant-like function which provides a value for pCondition if you need a "# &lt; ?" statement.
+ * @param {String|String[]|SqlBuilder} pElement the element to append
+ * @param {String} [pPrefix] string to be added before pElement
+ * @param {String} [pPostfix] string to be added after pElement
+ * @param {Boolean} [pAutoJoin=false] if this is true and pElement is an array, it will be automatically <br/>
+ *                               joined together to a string
+ * @param {Boolean} [pUseSubselectAlias=false] if true the subselectAlias is added if the element is a subquery
  * 
- * @return {String} 
+ * @ignore
  */
-SqlBuilder.LESS = function ()
+SqlBuilder._getStatement = function (pElement, pPrefix, pPostfix, pAutoJoin, pUseSubselectAlias)
 {
-    return "# < ?";
-}
+    var preparedValues = [];
+    if (typeof pElement !== "string")
+    {
+        if (Array.isArray(pElement) && pElement.length !== undefined && pAutoJoin) //array of fields
+        {
+            for (let i = 0; i < pElement.length; i++)
+            {
+                if (typeof pElement[i] !== "string")
+                    pElement[i] = _getElement(pElement[i]);
+            }
+             
+            pElement = ArrayUtils.joinNonEmptyFields(pElement, ", ");
+        }
+        else
+        {
+            pElement = _getElement(pElement);
+        }
+    }
 
-/**
- * Constant-like function which provides a value for pCondition if you need a "# &gt;= ?" statement.
- * 
- * @return {String} 
- */
-SqlBuilder.GREATER_OR_EQUAL = function ()
-{
-    return "# >= ?";
+    if (pPrefix && pElement)
+        pElement = pPrefix + " " + pElement;
+    if (pPostfix && pElement)
+        pElement += " " + pPostfix;
+    
+    return {
+        preparedValues: preparedValues,
+        sqlStorage: pElement.toString()
+    };
+
+    function _getElement (element)
+    {
+        var isSubQuery = false;
+        var subselectAlias = "";
+        if (SqlBuilder.checkCanBuildSql(element))
+        {
+            if (element instanceof SqlBuilder && element.isFullSelect())
+            {
+                isSubQuery = true;
+                
+                if (pUseSubselectAlias && element._subselectAlias)
+                    subselectAlias = " " + element._subselectAlias;
+            }
+            element = element.build();
+        }
+        preparedValues = preparedValues.concat(element[1]);
+        if (isSubQuery || pAutoJoin)
+            return "(" + element[0] + ")" + subselectAlias;
+        return element[0];
+    }
 }
 
 /**
- * Constant-like function which provides a value for pCondition if you need a "# &lt;= ?" statement.
+ * builds a prepared condition out of the object. Only the condition is used. Select, from, ... are ignored.
  * 
- * @return {String}
+ * @return {PreparedSqlArray} prepared condition
  */
-SqlBuilder.LESS_OR_EQUAL = function ()
-{
-    return "# <= ?";
+SqlBuilder.prototype.buildCondition = function()
+{   
+    return [this._where.sqlStorage, this._where.preparedValues];
 }
 
 /**
- * Constant-like function which returns an impossible condition ("1 = 2").
+ * builds a prepared statement out of the object. If a part doesn't exit, it's just ignored.
  * 
- * @return {String}
+ * @param {String} [pDefaultConditionIfNone=""] a default condition string which should be used if the SqlBuilder doesn't have any condition
+ * @return {PreparedSqlArray} prepared statement
  */
-SqlBuilder.NORESULT_CONDITION = function ()
+SqlBuilder.prototype.build = function(pDefaultConditionIfNone)
 {
-    return "1 = 2";
-}
-
-/**
- * Object providing constant-like functions for sql-any-conditions.
- */
-SqlBuilder.ANY = {
-    /**
-     * Constant-like function that returns a "# = any ?" statement.
-     */
-    EQUAL : function () {return "# = any ?";},
-    /**
-     * Constant-like function that returns a "# &lt;&gt; any ?" statement.
-     */
-    NOT_EQUAL : function () {return "# <> any ?";},
-    /**
-     * Constant-like function that returns a "# &gt; any ?" statement.
-     */
-    GREATER : function () {return "# > any ?";},
-    /**
-     * Constant-like function that returns a "# &gt;= any ?" statement.
-     */
-    GREATER_OR_EQUAL : function () {return "# >= any ?";},
-    /**
-     * Constant-like function that returns a "# &lt; any ?" statement.
-     */
-    LESS : function () {return "# < any ?";},
-    /**
-     * Constant-like function that returns a "# &lt;= any ?" statement.
-     */
-    LESS_OR_EQUAL : function () {return "# <= any ?";}
-}
-
-/**
- * Object providing constant-like functions for sql-all-conditions.
- */
-SqlBuilder.ALL = {
-    /**
-     * Constant-like function that returns a "# = all ?" statement.
-     */
-    EQUAL : function () {return "# = all ?";},
-    /**
-     * Constant-like function that returns a "# &lt;&gt; all ?" statement.
-     */
-    NOT_EQUAL : function () {return "# <> all ?";},
-    /**
-     * Constant-like function that returns a "# &gt; all ?" statement.
-     */
-    GREATER : function () {return "# > all ?";},
-    /**
-     * Constant-like function that returns a "# &gt;= all ?" statement.
-     */
-    GREATER_OR_EQUAL : function () {return "# >= all ?";},
-    /**
-     * Constant-like function that returns a "# &lt; all ?" statement.
-     */
-    LESS : function () {return "# < all ?";},
-    /**
-     * Constant-like function that returns a "# &lt;= all ?" statement.
-     */
-    LESS_OR_EQUAL : function () {return "# <= all ?";}
+    var wherePrefix = "";
+    var fromObj = this._from;
+    
+    if (this.isFullSelect())
+    {
+        fromObj = {
+            sqlStorage: "from " + this._from.sqlStorage,
+            preparedValues: this._from.preparedValues
+        };
+        if (this._where.sqlStorage)
+            wherePrefix = "where ";
+    }
+    
+    var whereSql = this._where.sqlStorage;
+    
+    if (!this.hasCondition() && pDefaultConditionIfNone)
+        whereSql = wherePrefix + pDefaultConditionIfNone;
+    
+    var whereObj = {
+        sqlStorage : wherePrefix + whereSql,
+        preparedValues : this._where.preparedValues
+    };
+    
+    var allParts = [
+        this._select,
+        fromObj
+        ].concat(this._joins).concat([
+        whereObj,
+        this._groupBy,
+        this._having,
+        this._orderBy
+        ]).concat(this._unions);
+        
+    var sqlStr = "";
+    var preparedVals = [];
+    for (let i = 0, l = allParts.length; i < l; i++)
+    {
+        let part = allParts[i];
+        if (part)
+        {
+            if (sqlStr && part.sqlStorage)
+                sqlStr += " ";
+            sqlStr += part.sqlStorage;
+            if (part.preparedValues.length)
+                preparedVals = preparedVals.concat(part.preparedValues);
+        }
+    }
+    
+    return [sqlStr, preparedVals];
 }
 
 /**
- * Throws an error if pValue is null, undefined or a SqlBuilder without condition (or if pValue is a $-variable: error if the result of it is null or undefined)<br/>
- * Also throws an error if pFieldOrCond is the only parameter and it is null<br/>
- * <br/>
- * Adds a condition by using "or" to the Sql.<br/>
- * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
+ * Updates data in the database.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is true to prevent updating all rows if the SqlBuilder has no condition.
  * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
- *                                                                          else it is used as Field.<br/>
- *                                                                     <br/>
- *                                                                          If you use it as Subselect (the only param), it can be:<br/>
- *                                                                           - a string: just added as it is<br/>
- *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
- *                                                                                                 the sql is just used as it is.<br/>
- *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
- *                                                                           <br/>
- *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
- *                                                                          load the SQLTYPE for this condition. <br/>
- *                                                                          It can be provided in the following ways:<br/>
- *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
- *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
- *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
- *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
- *                                                                     
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
- *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
- *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
- *                                                                           
- * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue
- *                               
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
- *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
- *                                                                  
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=true] If true, the update is only done if there is a condition.<br/>
+ *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be updated!</strong>
+ * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @param {String[]} pColumns The columns where you want to update.
+ * @param {SQLTYPES[]} [pColumnTypes=null] normally you can set this to null as the types are calculated if not provided
+ * @param {String[]} pValues The values to be updated.
+ * @param {Number} [pTimeout=-1] 
+ * @return {Number} the number of rows affected
+ * @throws {Error} if no table is defined
  */
-SqlBuilder.prototype.or = function(pFieldOrCond, pValue, pCondition, pFieldType)
+SqlBuilder.prototype.updateData = function(pExecuteOnlyIfConditionExists, pTableName, pColumns, pColumnTypes, pValues, pTimeout)
 {
-    return this._or(pFieldOrCond, pValue, true, pCondition, pFieldType);
+    if (this._checkForUpdate(pExecuteOnlyIfConditionExists))
+    {
+        if (!pTableName && !this._tableName)
+            throw SqlBuilder._ERROR_NO_TABLE();
+        
+        if (!pColumns)
+            pColumns = null;
+        
+        return db.updateData(
+            (pTableName ? pTableName : this._tableName),
+            pColumns,
+            pColumnTypes,
+            pValues,
+            this.buildCondition(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            (pTimeout ? pTimeout : -1));
+    }
+    
+    return 0;
 }
 
 /**
- * Difference to or(): or() throws errors on invalid values, orIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
- * <br/>
- * Adds a condition by using "or" to the Sql.<br/>
- * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
+ * Updates data in the database. This function calls SqlBuilder.prototype.updateData, but provides a shorter syntax to
+ * improve the readability.
  * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
- *                                                                          else it is used as Field.<br/>
- *                                                                     <br/>
- *                                                                          If you use it as Subselect (the only param), it can be:<br/>
- *                                                                           - a string: just added as it is<br/>
- *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
- *                                                                                                 the sql is just used as it is.<br/>
- *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
- *                                                                           <br/>
- *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
- *                                                                          load the SQLTYPE for this condition. <br/>
- *                                                                          It can be provided in the following ways:<br/>
- *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
- *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
- *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
- *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
- *                                                                     
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
- *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
- *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
- *                                                                           
- * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue
- *                               
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
- *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
- *
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
+ * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @return {Number} the number of rows affected
+ * @example
+ * newWhere("SALESORDER.SALESORDERID", "$field.SALESORDERID")
+ *  .updateFields({"ORDERSTATUS" : "1"}); //pTableName can be omitted here since it's clearly defined by the given condition
  */
-SqlBuilder.prototype.orIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
+SqlBuilder.prototype.updateFields = function (pFieldValues, pTableName)
 {
-    return this._or(pFieldOrCond, pValue, false, pCondition, pFieldType);
+    if (!pFieldValues || typeof(pFieldValues) !== "object")
+        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
+        
+    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues, true);
+    if (columnValues.columns.length === 0)
+        return 0;
+    return this.updateData(true, pTableName, columnValues.columns, null, columnValues.values);
 }
 
 /**
- * Throws an error if pValue is null, undefined or a SqlBuilder without condition (or if pValue is a $-variable: error if the result of it is null or undefined)<br/>
- * Also throws an error if pFieldOrCond is the only parameter and it is null<br/>
- * <br/>
- * Adds a condition by using "and" to the Sql.<br/>
- * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
+ * Builds an array containing the table and condition for an update.
  * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
- *                                                                          else it is used as Field.<br/>
- *                                                                     <br/>
- *                                                                          If you use it as Subselect (the only param), it can be:<br/>
- *                                                                           - a string: just added as it is<br/>
- *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
- *                                                                                                 the sql is just used as it is.<br/>
- *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
- *                                                                           <br/>
- *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
- *                                                                          load the SQLTYPE for this condition. <br/>
- *                                                                          It can be provided in the following ways:<br/>
- *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
- *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
- *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
- *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
- *                                                                     
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
- *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
- *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
- *                                                                           
- * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue
- *                               
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
- *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
- *                                                                  
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
+ * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @return {Array} array of [tableName, columns, columnTypes, values, preparedCondition], like it is required by db.updates or null if there is no condition
+ * @example 
+ * var updateStatements = [];
+ * updateStatements.push(newWhere("PERSON.PERSONID", pPersonId).buildUpdateStatement({"FIRSTNAME" : firstName}));
+ * updateStatements.push(newWhere("ORGANISATION.ORGANISATIONID", pOrganisationId).buildUpdateStatement({"NAME" : organisationName}));
+ * db.updates(updateStatements);
  */
-SqlBuilder.prototype.and = function(pFieldOrCond, pValue, pCondition, pFieldType)
+SqlBuilder.prototype.buildUpdateStatement = function (pFieldValues, pTableName)
 {
-    return this._and(pFieldOrCond, pValue, true, pCondition, pFieldType);
+    if (!pFieldValues || typeof(pFieldValues) !== "object")
+        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
+        
+    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues, true);
+    if (columnValues.columns.length !== 0 && this._checkForUpdate())
+    {
+        if (!pTableName && !this._tableName)
+            throw SqlBuilder._ERROR_NO_TABLE();
+        
+        return [
+            (pTableName ? pTableName : this._tableName),
+            columnValues.columns,
+            null,
+            columnValues.values,
+            this.buildCondition()
+        ];
+    }
+    return null;
 }
 
 /**
- * Difference to and(): and() throws errors on invalid values, andIfSet just ignores the condition and does nothing (usefull e.g. for the parameter variables ("$param.ddd") in conditionProcesses.)<br/>
- * <br/>
- * Adds a condition by using "and" to the Sql.<br/>
- * Note: You have to call .where before using .and / .or (this is mainly for semantic reasons)
+ * Builds an array containing the data for an insert.
  * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray|null} [pFieldOrCond] If this is the only parameter, it is used as Condition <br/>
- *                                                                          else it is used as Field.<br/>
- *                                                                     <br/>
- *                                                                          If you use it as Subselect (the only param), it can be:<br/>
- *                                                                           - a string: just added as it is<br/>
- *                                                                           - a PreparedSqlArray: an Array in this form: [sqlStr, [[value1, type1], [valueN, typeN]]]<br/>
- *                                                                                                 the sql is just used as it is.<br/>
- *                                                                           - a SqlBuilder: ONLY THE CONDITION is used from it<br/>
- *                                                                           <br/>
- *                                                                          If you use it as a Field (at least pValue has to be filled), this param provides the field information to<br/>
- *                                                                          load the SQLTYPE for this condition. <br/>
- *                                                                          It can be provided in the following ways:<br/>
- *                                                                          - a string: ONLY in this form: "TABLENAME.COLUMNNAME" <br/>
- *                                                                               Note1: you may have problems with names containing a '.' Use the next variant (as array) in this case<br/>
- *                                                                               Note2: if you need a table alias use the next variant (as array)<br/>
- *                                                                          - a array: ["TABLENAME", "COLUMNNAME", "tableAlias"] OR ["TABLENAME", "COLUMNNAME"]<br/>
- *                                                                          Note: this can also be null if you don't need the field and use a pCondition without a #
- *                                                                     
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value which is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       - String, etc: is just used as value for the prepared statement. Of course it has to fit the type of the db-column<br/>
- *                                                                       - String starting with '$' is treated as jdito-variable: is loaded with vars.getString("$..."). <br/>
- *                                                                        Note: Use 2 '$' to escape the $ if you don't want it to be treated as JditoVar
- *                                                                           
- * @param {String} [pCondition="# = ?"] This is the condition which should be used to compare the field with the value.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue
- *                               
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement <br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                This is helpful if you for example have a pCondition "year(#) = ?"<br/>
- *                                                                  then the db-field is DATETIME, but the value is INTEGER. In this case you can overwrite the type.
- *
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Object|Map} pFieldValues Object with the columns to insert into as keys mapped to their values
+ * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @param {String} [pAutoUidField] UID column that should be filled with a random UUID 
+ * @return {Array} array of [tableName, columns, columnTypes, values, preparedCondition], like it is required by db.updates or null if there is no condition
  */
-SqlBuilder.prototype.andIfSet = function(pFieldOrCond, pValue, pCondition, pFieldType)
+SqlBuilder.prototype.buildInsertStatement = function (pFieldValues, pTableName, pAutoUidField)
 {
-    return this._and(pFieldOrCond, pValue, false, pCondition, pFieldType);
+    if (!pFieldValues || !Utils.isObject(pFieldValues))
+        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
+        
+    if (pAutoUidField)
+        pFieldValues[pAutoUidField] = util.getNewUUID();
+    
+    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues);
+    if (columnValues.columns.length !== 0)
+    {
+        if (!pTableName && !this._tableName)
+            throw SqlBuilder._ERROR_NO_TABLE();
+        
+        return [
+            (pTableName ? pTableName : this._tableName),
+            columnValues.columns,
+            null,
+            columnValues.values
+        ];
+    }
+    return null;
 }
 
 /**
- * Sets the order by clause of the sql.
+ * Inserts data in the database. This function doesn't require any where-condition, it is intended to be called right after 'new SqlBuilder()'. <br/>
  * 
- * @param {String|String[]} pOrderBy a string is added as it is, a array is concatenated by ', '
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {String} [pTableName] The table for inserting data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @param {String[]} pColumns The columns where you want to insert into.
+ * @param {SQLTYPES[]} [pColumnTypes=null] normally you can set this to null as the types are calculated if not provided
+ * @param {String[]} pValues The values to be inserted.
+ * @param {Number} [pTimeout=-1] 
+ * @return {Number} the number of rows affected
+ * @throws {Error} if no table is defined
  */
-SqlBuilder.prototype.orderBy = function(pOrderBy)
+SqlBuilder.prototype.insertData = function(pTableName, pColumns, pColumnTypes, pValues, pTimeout)
 {
-    this._orderBy = SqlBuilder._getStatement(pOrderBy, "order by", undefined, true);
-    return this;
+    if (!pTableName && !this._tableName)
+        throw SqlBuilder._ERROR_NO_TABLE();
+
+    if (!pColumns)
+        pColumns = null;
+
+    return db.insertData(
+        (pTableName ? pTableName : this._tableName),
+        pColumns,
+        pColumnTypes,
+        pValues,
+        (this.alias ? this.alias : db.getCurrentAlias()),
+        (pTimeout ? pTimeout : -1));
 }
 
 /**
- * Sets the group by clause of the sql.
+ * Inserts data in the database. This function calls SqlBuilder.prototype.insertData, but provides a shorter syntax to
+ * improve the readability.
  * 
- * @param {String|String[]} pFields a string is added as it is, a array is concatenated by ', '
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
+ * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
+ *      the table of the first where-condition is used.
+ * @param {String} [pAutoUidField] UID column that should be filled with a random UUID 
+ * @return {Number} the number of rows affected
+ * @example
+ * new SqlBuilder().insertFields({
+ *         "ACTIVITY_ID" : pActivityId,
+ *         "OBJECT_ROWID" : pRowId,
+ *         "OBJECT_TYPE" : pObjectType
+ *     }, "ACTIVITYLINK", "ACTIVITYLINKID");
  */
-SqlBuilder.prototype.groupBy = function(pFields)
+SqlBuilder.prototype.insertFields = function (pFieldValues, pTableName, pAutoUidField)
 {
-    this._groupBy = SqlBuilder._getStatement(pFields, "group by", undefined, true);
-    return this;
+    if (!pFieldValues || typeof(pFieldValues) !== "object")
+        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
+        
+    if (pAutoUidField)
+        pFieldValues[pAutoUidField] = util.getNewUUID();
+    
+    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues);
+    if (columnValues.columns.length === 0)
+        return 0;
+    return this.insertData(pTableName, columnValues.columns, null, columnValues.values);
 }
 
-/**
- * Adds another SqlBuilder object or select string with union.
- * 
- * @param {SqlBuilder|String} pSelect
- * @return {SqlBuilder} current SqlBuilder object
- */
-SqlBuilder.prototype.union = function(pSelect)
+SqlBuilder._columnsValuesFromObject = function (pFieldValues, pIncludeNullValues)
 {
-    this._unions.push(SqlBuilder._getStatement(pSelect, "union"));
-    return this;
+    var columns = [];
+    var values = [];
+    if (Utils.isMap(pFieldValues))
+    {
+        pFieldValues.forEach(function (value, key)
+        {
+            if (pIncludeNullValues || (value !== undefined && value !== null))
+            {
+                columns.push(key);
+                values.push(_valueToString(value));
+            }
+        });
+    }
+    else
+    {
+        for (let field in pFieldValues)
+        {
+            if (pIncludeNullValues || (pFieldValues[field] !== undefined && pFieldValues[field] !== null))
+            {
+                columns.push(field);
+                values.push(_valueToString(pFieldValues[field]));
+            }
+        }
+    }
+    return {
+        columns: columns,
+        values: values
+    };
+    
+    function _valueToString (pValue)
+    {
+        if (pValue === undefined || pValue === null)
+            return "";
+        return pValue.toString();
+    }
 }
 
 /**
- * Adds another SqlBuilder object or select string with union all.
- * 
- * @param {SqlBuilder|String} pSelect
- * @return {SqlBuilder} current SqlBuilder object
+ * Deletes data from the database.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is true to prevent updating all rows if the SqlBuilder has no condition.
+
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=true] If true, the deletion is only done if there is a condition.<br/>
+ *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be deleted!</strong>
+ * @param {String} [pTableName] The table for deleting data. If undefined, the from part of the SqlBuilder will be used. If no from is set,
+ *      the table of the first where-condition is used.
+ * @param {Number} [pTimeout=-1]
+ * @return {Number} the number of rows affected
+ * @throws {Error} if no table is defined
+ * @example 
+ * newWhere("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$local.uid")
+ *  .deleteData(); //pTableName can be omitted here since it's clearly defined by the given condition
  */
-SqlBuilder.prototype.unionAll = function(pSelect)
+SqlBuilder.prototype.deleteData = function(pExecuteOnlyIfConditionExists, pTableName, pTimeout)
 {
-    this._unions.push(SqlBuilder._getStatement(pSelect, "union all"));
-    return this;
+    if (this._checkForUpdate(pExecuteOnlyIfConditionExists))
+    {
+        if (!pTableName && !this._tableName)
+            throw SqlBuilder._ERROR_NO_TABLE();
+        
+        return db.deleteData(
+            (pTableName ? pTableName : this._tableName),
+            this.buildCondition(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            (pTimeout ? pTimeout : -1));
+    }
+    else
+    {
+        return 0;
+    }
 }
 
 /**
- * Adds a having clause to the sql.
+ * Builds an array containing the table and condition for a delete.
  * 
- * @param {String|SqlBuilder} [pCondition] The having condition. This can be
- *                                                - a string (without the where keyword)
- *                                                - a SqlBuilder NOTE: only the condition is used from it
- *          
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Boolean} [pOnlyIfConditionExists=true] If true and there is no condition, null is returned.<br/>
+ *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be deleted!</strong>
+ * @param {String} [pTableName] The table for deleting data. If undefined, the from part of the SqlBuilder will be used. If no from is set,
+ *      the table of the first where-condition is used.
+ * @return {Array} array of [tableName, preparedCondition], like it is required by db.deletes
+ * @example 
+ * var deleteStatements = [];
+ * deleteStatements.push(newWhere("PERSON.PERSONID", pPersonId).buildDeleteStatement());
+ * deleteStatements.push(newWhere("CONTACT.CONTACTID", pContactId).buildDeleteStatement());
+ * db.deletes(deleteStatements);
  */
-SqlBuilder.prototype.having = function(pCondition)
+SqlBuilder.prototype.buildDeleteStatement = function(pOnlyIfConditionExists, pTableName)
 {
-    this._having = SqlBuilder._getStatement(pCondition, "having");
-    return this;
+    if (this._checkForUpdate(pOnlyIfConditionExists))
+    {
+        if (!pTableName && !this._tableName)
+            throw SqlBuilder._ERROR_NO_TABLE();
+        
+        return [
+            (pTableName ? pTableName : this._tableName),
+            this.buildCondition()
+        ];
+    }
+    else
+        return null;
 }
 
 /**
- * checks if conditions have been added to the object
- * @return {Boolean} true if conditions have been added, false when not
+ * Executes the SQL using db.cell and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * 
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, "" or the provided FallbackValue is returned
+ * @param {AnyValue} [pFallbackValue=""] here you can provide a fallback value if pExecuteOnlyIfConditionExists is true and the SqlBuilder has no condition.<br/>
+ *                                       This is intended for e.g. select count(*) from ... because there a default value of "0" is more helpful
+ * @return {String} the result of the query
  */
-SqlBuilder.prototype.hasCondition = function() {
-    if (this._where.sqlStorage)
-        return true;
-    return false;
+SqlBuilder.prototype.cell = function(pExecuteOnlyIfConditionExists, pFallbackValue)
+{
+    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
+    {
+        return db.cell(this.build(),
+            (this.alias ? this.alias : db.getCurrentAlias()));
+    }
+    else
+    {
+        return (pFallbackValue ? pFallbackValue : "");
+    }
 }
 
 /**
- * checks if conditions have been added to the object
- * Note: this does not nessecarily mean that hasCondition() is true
- * @return {Boolean} true if .where was already called
+ * Executes the SQL using db.array(db.ROW, ...) and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * 
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pMaxRows=0]
+ * @param {Number} [pTimeout=-1]
+ * @return {String[]} the result of the query
  */
-SqlBuilder.prototype.whereWasCalled = function() {
-    return this._where._whereWasCalled;
+SqlBuilder.prototype.arrayRow = function (pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
+{
+    return this.array(db.ROW, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout);
 }
 
 /**
- * checks if all mandatory parts to execute the select have been added to the object
- * ("select" and "from" parts)
- * @return {Boolean} true if select and from have been added, false if not
+ * Executes the SQL using db.array(db.COLUMN, ...) and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * 
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pMaxRows=0]
+ * @param {Number} [pTimeout=-1]
+ * @return {String[]} the result of the query
  */
-SqlBuilder.prototype.isFullSelect = function() 
+SqlBuilder.prototype.arrayColumn = function (pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
 {
-    return !(!this._select || !this._from);
+    return this.array(db.COLUMN, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout);
 }
 
 /**
- * Function that resets the current where-condition as if no conditions would have been added
- * this is usefull if you want to reuse the same Builder over and over again with a different condition.
- * This also resets whether where was already called, so you have to use .where to add a condition after this.
+ * Executes the SQL using db.array and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
  * 
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {Number} pType db.ROW or db.COLUMN
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pMaxRows=0]
+ * @param {Number} [pTimeout=-1]
+ * @return {String[]} the result of the query
  */
-SqlBuilder.prototype.clearWhere = function() 
+SqlBuilder.prototype.array = function(pType, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
 {
-    this._initWhere();
-    return this;
+    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
+    {
+        return db.array(pType, this.build(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            (pMaxRows ? pMaxRows : 0),
+            (pTimeout ? pTimeout : -1));
+    }
+    else
+    {
+        return [];
+    }
 }
 
 /**
- * function that initializes the properties of the ._where object, this is used in the
- * constructor and .clearWhere 
+ * Executes the SQL using db.arrayPage and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * 
+ * @param {Number} pType db.ROW or db.COLUMN
+ * @param {Number} pStartIndex
+ * @param {Number} pRowCount
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pTimeout=-1]
+ * @return {String[]} the result of the query
  */
-SqlBuilder.prototype._initWhere = function ()
+SqlBuilder.prototype.arrayPage = function(pType, pStartIndex, pRowCount, pExecuteOnlyIfConditionExists, pTimeout)
 {
-    //TODO: maybe put conditions in an object/array for better internal object structure
-    this._where.sqlStorage = "";
-    this._where.preparedValues = [];
-    this._where._lastWasOr = false; // save, if the last condition was an OR. For better bracket-placement
-    this._where._previouslyOnlyOr = false; // also for better bracket-placement
-    this._where._whereWasCalled = false; // where has always to be called first for a better semantic
+    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
+    {
+        return db.arrayPage(pType, this.build(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            pStartIndex === undefined ? this._startRow : pStartIndex,
+            pRowCount === undefined ? this._pageSize : pRowCount,
+            (pTimeout ? pTimeout : -1));
+    }
+    else
+    {
+        return [];
+    }
 }
 
 /**
- * helper function for composing preparedStatements <br/>
- *  <br/>
- * see .where for more information about the parameters
- * 
- * @param {String | String[]} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
- * @param {String} pValue the value that shall be set into the prepared statement
- * @param {String} pCondition the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname;  <br/>
- *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\" <br/>
- *                 Default is "# = ?" 
- * @param {Numeric | Boolean} [pFieldType] SQL-column-type; if the fieldType is not given it's loaded automatically; <br/>
- *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop. <br/>
- *                              e.g. <br/>
- *                              for (...) { <br/>
- *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?") <br/>
- *                              }
- * @param {Boolean} pSubselectBrackets if true, brackets are added to subselects
- * @return {PreparedSqlArray} a preparedSqlArray built out of the given parameters
+ * Executes the SQL using db.table and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
  * 
- * @ignore
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pMaxRows=0]
+ * @param {Number} [pTimeout=-1]
+ * @return {String[][]} the result of the query
  */
-SqlBuilder.prototype._prepare = function(pField, pValue, pCondition, pFieldType, pSubselectBrackets) 
+SqlBuilder.prototype.table = function(pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
 {
-    if (pSubselectBrackets == undefined)
-        pSubselectBrackets = true;
-    
-    if (pValue == undefined)
-        throw new Error(translate.withArguments("${SQL_LIB_UNDEFINED_VALUE} field: %0", [pField]));
-    
-    if (pCondition == undefined) 
-        pCondition = SqlBuilder.EQUAL();
-
-    var alias, field;
-    if (pField != null)
+    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
     {
-        [alias, field] = SqlUtils.parseField(pField)
-        if (pFieldType == undefined)
-            pFieldType = SqlUtils.getSingleColumnType(pField, undefined, this.alias);
-        
-        var table = SqlUtils._parseFieldQualifier(pField).table;
-        //Set the table for update/delete if it isn't already set, so you don't need to specify the table if the where-condition contains it
-        if (table && !this._tableName)
-            this._tableName = table;
+        return db.table(this.build(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            (pMaxRows ? pMaxRows : 0),
+            (pTimeout ? pTimeout : -1));
     }
-    
-    var values = [];
-
-    // If subselect: replace '?' with the subselect
-    if (Array.isArray(pValue))
-    {
-        pCondition = SqlUtils.replaceConditionTemplate(pCondition, "\\?", (pSubselectBrackets ? " ( " : " ") + pValue[0] + (pSubselectBrackets ? " ) " : " "));
-        values = pValue[1];
-    } 
     else
     {
-        var type = pFieldType;
-        //booleans are normally stored with TINYINT, this is to support true/false directly
-        if (_isIntegerType(type) && Utils.isBoolean(pValue))
-            pValue = Number(pValue);
-        values = [[pValue.toString(), type]];
+        return [];
     }
+}
 
-    if (pField != null)
-        pCondition = SqlUtils.replaceConditionTemplate(pCondition, "#", alias);
-    
-    return [pCondition, values];
-    
-    function _isIntegerType (pType)
+/**
+ * Executes the SQL using db.tablePage and returns the result.<br/>
+ * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * 
+ * @param {Number} pStartIndex
+ * @param {Number} pRowCount
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pTimeout=-1]
+ * @return {String[][]} the result of the query
+ */
+SqlBuilder.prototype.tablePage = function(pStartIndex, pRowCount, pExecuteOnlyIfConditionExists, pTimeout)
+{   
+    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
     {
-        return pType == SQLTYPES.TINYINT
-            || pType == SQLTYPES.SMALLINT
-            || pType == SQLTYPES.INTEGER
-            || pType == SQLTYPES.BIGINT;
+        return db.tablePage(this.build(),
+            (this.alias ? this.alias : db.getCurrentAlias()),
+            pStartIndex === undefined ? this._startRow : pStartIndex,
+            pRowCount === undefined ? this._pageSize : pRowCount,
+            (pTimeout ? pTimeout : -1));
+    }
+    else
+    {
+        return [];
     }
 }
 
 /**
- * generates a part of the sql
- * 
- * @param {String|String[]|SqlBuilder} pElement the element to append
- * @param {String} [pPrefix] string to be added before pElement
- * @param {String} [pPostfix] string to be added after pElement
- * @param {Boolean} [pAutoJoin=false] if this is true and pElement is an array, it will be automatically <br/>
- *                               joined together to a string
- * @param {Boolean} [pUseSubselectAlias=false] if true the subselectAlias is added if the element is a subquery
+ * Sets the pagesize for paging
  * 
- * @ignore
+ * @param {Number} pPageSize
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder._getStatement = function (pElement, pPrefix, pPostfix, pAutoJoin, pUseSubselectAlias)
+SqlBuilder.prototype.pageSize = function (pPageSize)
 {
-    var preparedValues = [];
-    if (typeof pElement !== "string")
-    {
-        if (Array.isArray(pElement) && pElement.length !== undefined && pAutoJoin) //array of fields
-        {
-            for (let i = 0; i < pElement.length; i++)
-            {
-                if (typeof pElement[i] !== "string")
-                    pElement[i] = _getElement(pElement[i]);
-            }
-             
-            pElement = ArrayUtils.joinNonEmptyFields(pElement, ", ");
-        }
-        else
-        {
-            pElement = _getElement(pElement);
-        }
-    }
-
-    if (pPrefix && pElement)
-        pElement = pPrefix + " " + pElement;
-    if (pPostfix && pElement)
-        pElement += " " + pPostfix;
-    
-    return {
-        preparedValues: preparedValues,
-        sqlStorage: pElement.toString()
-    };
-
-    function _getElement (element)
-    {
-        var isSubQuery = false;
-        var subselectAlias = "";
-        if (SqlBuilder.checkCanBuildSql(element))
-        {
-            if (element instanceof SqlBuilder && element.isFullSelect())
-            {
-                isSubQuery = true;
-                
-                if (pUseSubselectAlias && element._subselectAlias)
-                    subselectAlias = " " + element._subselectAlias;
-            }
-            element = element.build();
-        }
-        preparedValues = preparedValues.concat(element[1]);
-        if (isSubQuery || pAutoJoin)
-            return "(" + element[0] + ")" + subselectAlias;
-        return element[0];
-    }
+    this._pageSize = pPageSize;
+    return this;
 }
 
 /**
- * builds a prepared condition out of the object. Only the condition is used. Select, from, ... are ignored.
+ * Sets the start row for paging
  * 
- * @return {PreparedSqlArray} prepared condition
+ * @param {Number} pStartRow
+ * @return {SqlBuilder} current SqlBuilder object
  */
-SqlBuilder.prototype.buildCondition = function()
-{   
-    return [this._where.sqlStorage, this._where.preparedValues];
+SqlBuilder.prototype.startRow = function (pStartRow)
+{
+    this._startRow = pStartRow;
+    return this;
 }
 
 /**
- * builds a prepared statement out of the object. If a part doesn't exit, it's just ignored.
+ * Executes the SQL and returns the result. The startRow for paging will be increased by the pageSize, so you can use this method
+ * for iterating over the table pages. You can use SqlBuilder.prototype.hasMoreRows() to check if the end of rows was reached.
  * 
- * @param {String} [pDefaultConditionIfNone=""] a default condition string which should be used if the SqlBuilder doesn't have any condition
- * @return {PreparedSqlArray} prepared statement
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pTimeout=-1]
+ * @return {String[][]} the result of the query
  */
-SqlBuilder.prototype.build = function(pDefaultConditionIfNone)
+SqlBuilder.prototype.nextTablePage = function (pExecuteOnlyIfConditionExists, pTimeout)
 {
-    var wherePrefix = "";
-    var fromObj = this._from;
+    if (this._pageSize == null || isNaN(this._pageSize))
+        throw SqlBuilder._ERROR_PAGESIZE_INVALID();
     
-    if (this.isFullSelect())
+    if (this._startRow == null)
+        this._startRow = 0;
+    
+    if (this._hasMoreRows && this._checkForSelect(pExecuteOnlyIfConditionExists))
     {
-        fromObj = {
-            sqlStorage: "from " + this._from.sqlStorage,
-            preparedValues: this._from.preparedValues
-        };
-        if (this._where.sqlStorage)
-            wherePrefix = "where ";
+        var data = this.tablePage(this._startRow, this._pageSize, pExecuteOnlyIfConditionExists, pTimeout);
+        if (data.length < this._pageSize)
+            this._hasMoreRows = false;
+        this._startRow += this._pageSize;
+        return data;
     }
-    
-    var whereSql = this._where.sqlStorage;
-    
-    if (!this.hasCondition() && pDefaultConditionIfNone)
-        whereSql = wherePrefix + pDefaultConditionIfNone;
-    
-    var whereObj = {
-        sqlStorage : wherePrefix + whereSql,
-        preparedValues : this._where.preparedValues
-    };
-    
-    var allParts = [
-        this._select,
-        fromObj
-        ].concat(this._joins).concat([
-        whereObj,
-        this._groupBy,
-        this._having,
-        this._orderBy
-        ]).concat(this._unions);
-        
-    var sqlStr = "";
-    var preparedVals = [];
-    for (let i = 0, l = allParts.length; i < l; i++)
+    else
     {
-        let part = allParts[i];
-        if (part)
-        {
-            if (sqlStr && part.sqlStorage)
-                sqlStr += " ";
-            sqlStr += part.sqlStorage;
-            if (part.preparedValues.length)
-                preparedVals = preparedVals.concat(part.preparedValues);
-        }
+        this._hasMoreRows = false;
+        return [];
     }
-    
-    return [sqlStr, preparedVals];
 }
 
 /**
- * Updates data in the database.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is true to prevent updating all rows if the SqlBuilder has no condition.
+ * @return {Boolean} whether there are rows left for paging
+ */
+SqlBuilder.prototype.hasMoreRows = function ()
+{
+    return this._hasMoreRows;
+}
+
+/**
+ * Executes the SQL with paging and executes the given callback-function for every resultset until the last row has been reached or the function
+ * returns false.
  * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=true] If true, the update is only done if there is a condition.<br/>
- *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be updated!</strong>
- * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @param {String[]} pColumns The columns where you want to update.
- * @param {SQLTYPES[]} [pColumnTypes=null] normally you can set this to null as the types are calculated if not provided
- * @param {String[]} pValues The values to be updated.
- * @param {Number} [pTimeout=-1] 
- * @return {Number} the number of rows affected
- * @throws {Error} if no table is defined
+ * @param {Function} pCallBackFn CallBack-Function to execute for every page. If the function returns false, the execution will be stopped.
+ * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
+ * @param {Number} [pTimeout=-1]
  */
-SqlBuilder.prototype.updateData = function(pExecuteOnlyIfConditionExists, pTableName, pColumns, pColumnTypes, pValues, pTimeout)
+SqlBuilder.prototype.forEachPage = function (pCallBackFn, pExecuteOnlyIfConditionExists, pTimeout)
 {
-    if (this._checkForUpdate(pExecuteOnlyIfConditionExists))
+    if (typeof pCallBackFn !== "function")
+        throw SqlBuilder._ERROR_NOT_A_FUNCTION();
+    
+    var run = true;
+    var idx = 0;
+    while (run && this.hasMoreRows())
     {
-        if (!pTableName && !this._tableName)
-            throw SqlBuilder._ERROR_NO_TABLE();
-        
-        if (!pColumns)
-            pColumns = null;
-        
-        return db.updateData(
-            (pTableName ? pTableName : this._tableName),
-            pColumns,
-            pColumnTypes,
-            pValues,
-            this.buildCondition(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            (pTimeout ? pTimeout : -1));
+        run = pCallBackFn.call(null, this.nextTablePage(pExecuteOnlyIfConditionExists, pTimeout), idx++) != false;
     }
-    
-    return 0;
 }
 
 /**
- * Updates data in the database. This function calls SqlBuilder.prototype.updateData, but provides a shorter syntax to
- * improve the readability.
+ * Sets an impossible where-condition, so that the query won't return any rows.
  * 
- * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
- * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @return {Number} the number of rows affected
- * @example
- * newWhere("SALESORDER.SALESORDERID", "$field.SALESORDERID")
- *  .updateFields({"ORDERSTATUS" : "1"}); //pTableName can be omitted here since it's clearly defined by the given condition
+ * @return {SqlBuilder} current object
  */
-SqlBuilder.prototype.updateFields = function (pFieldValues, pTableName)
+SqlBuilder.prototype.noResult = function ()
 {
-    if (!pFieldValues || typeof(pFieldValues) !== "object")
-        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
-        
-    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues, true);
-    if (columnValues.columns.length === 0)
-        return 0;
-    return this.updateData(true, pTableName, columnValues.columns, null, columnValues.values);
+    return this.clearWhere().where(SqlBuilder.NORESULT_CONDITION());
 }
 
 /**
- * Builds an array containing the table and condition for an update.
- * 
- * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
- * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @return {Array} array of [tableName, columns, columnTypes, values, preparedCondition], like it is required by db.updates or null if there is no condition
- * @example 
- * var updateStatements = [];
- * updateStatements.push(newWhere("PERSON.PERSONID", pPersonId).buildUpdateStatement({"FIRSTNAME" : firstName}));
- * updateStatements.push(newWhere("ORGANISATION.ORGANISATIONID", pOrganisationId).buildUpdateStatement({"NAME" : organisationName}));
- * db.updates(updateStatements);
+ * checks if an update /delete statement should be called or not
+ * @return {Boolean}
+ * @private
  */
-SqlBuilder.prototype.buildUpdateStatement = function (pFieldValues, pTableName)
+SqlBuilder.prototype._checkForUpdate = function(pExecuteOnlyIfConditionExists) 
 {
-    if (!pFieldValues || typeof(pFieldValues) !== "object")
-        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
-        
-    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues, true);
-    if (columnValues.columns.length !== 0 && this._checkForUpdate())
-    {
-        if (!pTableName && !this._tableName)
-            throw SqlBuilder._ERROR_NO_TABLE();
-        
-        return [
-            (pTableName ? pTableName : this._tableName),
-            columnValues.columns,
-            null,
-            columnValues.values,
-            this.buildCondition()
-        ];
-    }
-    return null;
+    if (pExecuteOnlyIfConditionExists === undefined)
+        pExecuteOnlyIfConditionExists = true;
+    
+    if (typeof pExecuteOnlyIfConditionExists !== "boolean")
+        throw SqlBuilder._ERROR_NOT_BOOLEAN();
+    
+    return !pExecuteOnlyIfConditionExists || this.hasCondition();
 }
 
 /**
- * Builds an array containing the data for an insert.
- * 
- * @param {Object|Map} pFieldValues Object with the columns to insert into as keys mapped to their values
- * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @param {String} [pAutoUidField] UID column that should be filled with a random UUID 
- * @return {Array} array of [tableName, columns, columnTypes, values, preparedCondition], like it is required by db.updates or null if there is no condition
+ * checks if a select statement should be called or not
+ * @return {Boolean}
+ * @private
  */
-SqlBuilder.prototype.buildInsertStatement = function (pFieldValues, pTableName, pAutoUidField)
+SqlBuilder.prototype._checkForSelect = function(pExecuteOnlyIfConditionExists) 
 {
-    if (!pFieldValues || !Utils.isObject(pFieldValues))
-        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
-        
-    if (pAutoUidField)
-        pFieldValues[pAutoUidField] = util.getNewUUID();
+    if (pExecuteOnlyIfConditionExists == undefined)
+        pExecuteOnlyIfConditionExists = false;
     
-    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues);
-    if (columnValues.columns.length !== 0)
+    if (typeof pExecuteOnlyIfConditionExists !== "boolean")
+        throw SqlBuilder._ERROR_NOT_BOOLEAN();
+    
+    if (this.isFullSelect())
     {
-        if (!pTableName && !this._tableName)
-            throw SqlBuilder._ERROR_NO_TABLE();
-        
-        return [
-            (pTableName ? pTableName : this._tableName),
-            columnValues.columns,
-            null,
-            columnValues.values
-        ];
+        return !pExecuteOnlyIfConditionExists || this.hasCondition();
+    }
+    else
+    {
+        throw SqlBuilder._ERROR_INCOMPLETE_SELECT();
     }
-    return null;
 }
 
 /**
- * Inserts data in the database. This function doesn't require any where-condition, it is intended to be called right after 'new SqlBuilder()'. <br/>
+ * translates SqlBuilder to plain SQL. Use this if prepared statements are not supported.
+ * For the db-functions (db.table, db.cell, etc.) use ".build()" as they support prepared statements.
+ * It resolves all prepared values.
+ * @param {String} [pAlias=undefined] the alias to use for db.translateStatement
+ * @return {String} plain SQL statement
  * 
- * @param {String} [pTableName] The table for inserting data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @param {String[]} pColumns The columns where you want to insert into.
- * @param {SQLTYPES[]} [pColumnTypes=null] normally you can set this to null as the types are calculated if not provided
- * @param {String[]} pValues The values to be inserted.
- * @param {Number} [pTimeout=-1] 
- * @return {Number} the number of rows affected
- * @throws {Error} if no table is defined
+ * @deprecated use .toString()
  */
-SqlBuilder.prototype.insertData = function(pTableName, pColumns, pColumnTypes, pValues, pTimeout)
+SqlBuilder.prototype.translate = function(pAlias)
 {
-    if (!pTableName && !this._tableName)
-        throw SqlBuilder._ERROR_NO_TABLE();
-
-    if (!pColumns)
-        pColumns = null;
-
-    return db.insertData(
-        (pTableName ? pTableName : this._tableName),
-        pColumns,
-        pColumnTypes,
-        pValues,
-        (this.alias ? this.alias : db.getCurrentAlias()),
-        (pTimeout ? pTimeout : -1));
+    return SqlUtils.translateStatementWithQuotes(this.build(), pAlias);
 }
 
 /**
- * Inserts data in the database. This function calls SqlBuilder.prototype.insertData, but provides a shorter syntax to
- * improve the readability.
+ * Creates an object for building a case-when statement.
  * 
- * @param {Object|Map} pFieldValues Object with the columns to update as keys mapped to their values
- * @param {String} [pTableName] The table for updating data. If undefined, the from part of the SqlBuilder will be used (works only if it is a tablename). If no from is set,
- *      the table of the first where-condition is used.
- * @param {String} [pAutoUidField] UID column that should be filled with a random UUID 
- * @return {Number} the number of rows affected
- * @example
- * new SqlBuilder().insertFields({
- *         "ACTIVITY_ID" : pActivityId,
- *         "OBJECT_ROWID" : pRowId,
- *         "OBJECT_TYPE" : pObjectType
- *     }, "ACTIVITYLINK", "ACTIVITYLINKID");
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
+ *                                                                     else it is used as Field. <br/>
+ *                                                                     Please see .where() for more information and examples.
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       Please see .where() for more information and examples.
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
+ *                               Please see .where() for more information and examples.
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                Please see .where() for more information and examples.
+ * 
+ * @return {SqlBuilder._CaseWhen}
  */
-SqlBuilder.prototype.insertFields = function (pFieldValues, pTableName, pAutoUidField)
+SqlBuilder.caseWhen = function (pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    if (!pFieldValues || typeof(pFieldValues) !== "object")
-        throw SqlBuilder._ERROR_UPDATE_VALUES_INVALID;
-        
-    if (pAutoUidField)
-        pFieldValues[pAutoUidField] = util.getNewUUID();
-    
-    var columnValues = SqlBuilder._columnsValuesFromObject(pFieldValues);
-    if (columnValues.columns.length === 0)
-        return 0;
-    return this.insertData(pTableName, columnValues.columns, null, columnValues.values);
+    return new SqlBuilder._CaseStatement().when(pFieldOrCond, pValue, pCondition, pFieldType);
 }
 
-SqlBuilder._columnsValuesFromObject = function (pFieldValues, pIncludeNullValues)
+/**
+ * @return {SqlBuilder._CaseStatement}
+ */
+SqlBuilder.caseStatement = function ()
 {
-    var columns = [];
-    var values = [];
-    if (Utils.isMap(pFieldValues))
-    {
-        pFieldValues.forEach(function (value, key)
-        {
-            if (pIncludeNullValues || (value !== undefined && value !== null))
-            {
-                columns.push(key);
-                values.push(_valueToString(value));
-            }
-        });
-    }
-    else
-    {
-        for (let field in pFieldValues)
-        {
-            if (pIncludeNullValues || (pFieldValues[field] !== undefined && pFieldValues[field] !== null))
-            {
-                columns.push(field);
-                values.push(_valueToString(pFieldValues[field]));
-            }
-        }
-    }
-    return {
-        columns: columns,
-        values: values
-    };
-    
-    function _valueToString (pValue)
-    {
-        if (pValue === undefined || pValue === null)
-            return "";
-        return pValue.toString();
-    }
+    return new SqlBuilder._CaseStatement();
 }
 
 /**
- * Deletes data from the database.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is true to prevent updating all rows if the SqlBuilder has no condition.
-
- * @param {Boolean} [pExecuteOnlyIfConditionExists=true] If true, the deletion is only done if there is a condition.<br/>
- *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be deleted!</strong>
- * @param {String} [pTableName] The table for deleting data. If undefined, the from part of the SqlBuilder will be used. If no from is set,
- *      the table of the first where-condition is used.
- * @param {Number} [pTimeout=-1]
- * @return {Number} the number of rows affected
- * @throws {Error} if no table is defined
- * @example 
- * newWhere("AB_ATTRIBUTE.AB_ATTRIBUTEID", "$local.uid")
- *  .deleteData(); //pTableName can be omitted here since it's clearly defined by the given condition
+ * Represents a case-when statement
  */
-SqlBuilder.prototype.deleteData = function(pExecuteOnlyIfConditionExists, pTableName, pTimeout)
+SqlBuilder._CaseStatement = function ()
 {
-    if (this._checkForUpdate(pExecuteOnlyIfConditionExists))
-    {
-        if (!pTableName && !this._tableName)
-            throw SqlBuilder._ERROR_NO_TABLE();
-        
-        return db.deleteData(
-            (pTableName ? pTableName : this._tableName),
-            this.buildCondition(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            (pTimeout ? pTimeout : -1));
-    }
-    else
-    {
-        return 0;
-    }
+    this._whenCondition = null;
+    this._whenThens = [];
+    this._elseValue = null;
+    this._afterWhenMask = new SqlBuilder._CaseWhen(this);
 }
 
+SqlBuilder.defineCanBuildSql(SqlBuilder._CaseStatement.prototype);
+
 /**
- * Builds an array containing the table and condition for a delete.
+ * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
+ *                                                                     else it is used as Field. <br/>
+ *                                                                     Please see .where() for more information and examples.
+ * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
+ *                                                                       Basically it can be nearly everything you need.<br/>
+ *                                                                       Please see .where() for more information and examples.
+ * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
+ *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
+ *                               ? will be replaced by pValue<br/>
+ *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
+ *                               Please see .where() for more information and examples.
+ * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
+ *                                                                In most cases you don't need this.<br/>
+ *                                                                Please see .where() for more information and examples.
  * 
- * @param {Boolean} [pOnlyIfConditionExists=true] If true and there is no condition, null is returned.<br/>
- *      <strong>IMPORTANT: If this is set to false and there is no condition, every row in the table will be deleted!</strong>
- * @param {String} [pTableName] The table for deleting data. If undefined, the from part of the SqlBuilder will be used. If no from is set,
- *      the table of the first where-condition is used.
- * @return {Array} array of [tableName, preparedCondition], like it is required by db.deletes
- * @example 
- * var deleteStatements = [];
- * deleteStatements.push(newWhere("PERSON.PERSONID", pPersonId).buildDeleteStatement());
- * deleteStatements.push(newWhere("CONTACT.CONTACTID", pContactId).buildDeleteStatement());
- * db.deletes(deleteStatements);
+ * @return {SqlBuilder._CaseWhen}
  */
-SqlBuilder.prototype.buildDeleteStatement = function(pOnlyIfConditionExists, pTableName)
+SqlBuilder._CaseStatement.prototype.when = function (pFieldOrCond, pValue, pCondition, pFieldType)
 {
-    if (this._checkForUpdate(pOnlyIfConditionExists))
-    {
-        if (!pTableName && !this._tableName)
-            throw SqlBuilder._ERROR_NO_TABLE();
-        
-        return [
-            (pTableName ? pTableName : this._tableName),
-            this.buildCondition()
-        ];
-    }
-    else
-        return null;
+    this._whenCondition = newWhere(pFieldOrCond, pValue, pCondition, pFieldType);
+    return this._afterWhenMask;
 }
 
 /**
- * Executes the SQL using db.cell and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * Sets the expression used for the else-part
  * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, "" or the provided FallbackValue is returned
- * @param {AnyValue} [pFallbackValue=""] here you can provide a fallback value if pExecuteOnlyIfConditionExists is true and the SqlBuilder has no condition.<br/>
- *                                       This is intended for e.g. select count(*) from ... because there a default value of "0" is more helpful
- * @return {String} the result of the query
+ * @param {String|SqlBuilder} pValue else-value
+ * @return {SqlBuilder._CaseStatement}
  */
-SqlBuilder.prototype.cell = function(pExecuteOnlyIfConditionExists, pFallbackValue)
+SqlBuilder._CaseStatement.prototype.elseValue = function (pValue)
 {
-    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
-    {
-        return db.cell(this.build(),
-            (this.alias ? this.alias : db.getCurrentAlias()));
-    }
-    else
-    {
-        return (pFallbackValue ? pFallbackValue : "");
-    }
+    this._elseValue = pValue;
+    return this;
 }
 
 /**
- * Executes the SQL using db.array(db.ROW, ...) and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
+ * Sets the value used for the else-part, but wraps the value in ''
  * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pMaxRows=0]
- * @param {Number} [pTimeout=-1]
- * @return {String[]} the result of the query
+ * @param {String} pValue else-value
+ * @return {SqlBuilder._CaseStatement}
  */
-SqlBuilder.prototype.arrayRow = function (pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
+SqlBuilder._CaseStatement.prototype.elseString = function (pValue)
 {
-    return this.array(db.ROW, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout);
+    return this.elseValue("'" + pValue + "'");
 }
 
 /**
- * Executes the SQL using db.array(db.COLUMN, ...) and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
- * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pMaxRows=0]
- * @param {Number} [pTimeout=-1]
- * @return {String[]} the result of the query
+ * @return {String} the case-when expression
  */
-SqlBuilder.prototype.arrayColumn = function (pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
+SqlBuilder._CaseStatement.prototype.toString = function (pAlias)
 {
-    return this.array(db.COLUMN, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout);
+    return db.translateStatement(this.build(), pAlias || db.getCurrentAlias());
 }
 
-/**
- * Executes the SQL using db.array and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
- * 
- * @param {Number} pType db.ROW or db.COLUMN
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pMaxRows=0]
- * @param {Number} [pTimeout=-1]
- * @return {String[]} the result of the query
- */
-SqlBuilder.prototype.array = function(pType, pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
+SqlBuilder._CaseStatement.prototype.build = function (pParameters)
 {
-    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
+    var caseStatement = ["case"];
+    var preparedValues = [];
+    this._whenThens.forEach(function (whenThen)
     {
-        return db.array(pType, this.build(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            (pMaxRows ? pMaxRows : 0),
-            (pTimeout ? pTimeout : -1));
-    }
-    else
+        var when = SqlBuilder._getStatement(whenThen.condition, "when");
+        var then = SqlBuilder._getStatement(whenThen.thenValue, "then");
+        caseStatement.push(when.sqlStorage);
+        caseStatement.push(then.sqlStorage);
+        preparedValues = preparedValues.concat(when.preparedValues, then.preparedValues);
+    });
+    if (this._elseValue)
     {
-        return [];
+        let elseStatement = SqlBuilder._getStatement(this._elseValue, "else");
+        caseStatement.push(elseStatement.sqlStorage);
+        preparedValues = preparedValues.concat(elseStatement.preparedValues);
     }
+    caseStatement.push("end");
+    
+    return [
+        caseStatement.join(" "),
+        preparedValues
+    ];
 }
 
 /**
- * Executes the SQL using db.arrayPage and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
- * 
- * @param {Number} pType db.ROW or db.COLUMN
- * @param {Number} pStartIndex
- * @param {Number} pRowCount
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pTimeout=-1]
- * @return {String[]} the result of the query
+ * Custom toJSON method that omits the property '_afterWhenMask', because cyclic references can't be stringified
  */
-SqlBuilder.prototype.arrayPage = function(pType, pStartIndex, pRowCount, pExecuteOnlyIfConditionExists, pTimeout)
+SqlBuilder._CaseStatement.prototype.toJSON = function ()
 {
-    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
-    {
-        return db.arrayPage(pType, this.build(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            pStartIndex === undefined ? this._startRow : pStartIndex,
-            pRowCount === undefined ? this._pageSize : pRowCount,
-            (pTimeout ? pTimeout : -1));
-    }
-    else
-    {
-        return [];
-    }
+    return {
+        _whenCondition: this._whenCondition,
+        _whenThens: this._whenThens,
+        _elseValue: this._elseValue
+    };
 }
 
 /**
- * Executes the SQL using db.table and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
- * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pMaxRows=0]
- * @param {Number} [pTimeout=-1]
- * @return {String[][]} the result of the query
+ * Object providing the then-methods for the case-when expression. It can be only be accessed after calling .when to ensure a 'then' 
+ * can only be added after a 'when'.
  */
-SqlBuilder.prototype.table = function(pExecuteOnlyIfConditionExists, pMaxRows, pTimeout)
+SqlBuilder._CaseWhen = function (pCaseStatement)
 {
-    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
-    {
-        return db.table(this.build(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            (pMaxRows ? pMaxRows : 0),
-            (pTimeout ? pTimeout : -1));
-    }
-    else
-    {
-        return [];
-    }
-}
-
-/**
- * Executes the SQL using db.tablePage and returns the result.<br/>
- * Note: the default for pExecuteOnlyIfConditionExists is false becausse it is more natural to select all rows if no condition exists.
- * 
- * @param {Number} pStartIndex
- * @param {Number} pRowCount
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pTimeout=-1]
- * @return {String[][]} the result of the query
- */
-SqlBuilder.prototype.tablePage = function(pStartIndex, pRowCount, pExecuteOnlyIfConditionExists, pTimeout)
-{   
-    if (this._checkForSelect(pExecuteOnlyIfConditionExists))
-    {
-        return db.tablePage(this.build(),
-            (this.alias ? this.alias : db.getCurrentAlias()),
-            pStartIndex === undefined ? this._startRow : pStartIndex,
-            pRowCount === undefined ? this._pageSize : pRowCount,
-            (pTimeout ? pTimeout : -1));
-    }
-    else
-    {
-        return [];
-    }
+    this._caseStatement = pCaseStatement;
 }
 
 /**
- * Sets the pagesize for paging
+ * Sets the expression for the then
  * 
- * @param {Number} pPageSize
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {String|SqlBuilder} pValue then-value
+ * @return {SqlBuilder._CaseStatement}
  */
-SqlBuilder.prototype.pageSize = function (pPageSize)
+SqlBuilder._CaseWhen.prototype.then = function (pValue)
 {
-    this._pageSize = pPageSize;
-    return this;
+    var condition = this._caseStatement._whenCondition;
+    this._caseStatement._whenCondition = null;
+    this._caseStatement._whenThens.push({condition: condition, thenValue: pValue});
+    return this._caseStatement;
 }
 
 /**
- * Sets the start row for paging
+ * Sets the value for the then, but wraps the value in ''
  * 
- * @param {Number} pStartRow
- * @return {SqlBuilder} current SqlBuilder object
+ * @param {String} pValue then-value
+ * @return {SqlBuilder._CaseStatement}
  */
-SqlBuilder.prototype.startRow = function (pStartRow)
+SqlBuilder._CaseWhen.prototype.thenString = function (pValue)
 {
-    this._startRow = pStartRow;
-    return this;
+    return this.then("'" + pValue + "'");
 }
 
 /**
- * Executes the SQL and returns the result. The startRow for paging will be increased by the pageSize, so you can use this method
- * for iterating over the table pages. You can use SqlBuilder.prototype.hasMoreRows() to check if the end of rows was reached.
+ *provides functions for masking sql functions
+ *
+ * @param {String} [pAlias=currentAlias] database alias, you can specify null if you have no alias available and  you can manually set the dbType property
  * 
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pTimeout=-1]
- * @return {String[][]} the result of the query
+ * @class
  */
-SqlBuilder.prototype.nextTablePage = function (pExecuteOnlyIfConditionExists, pTimeout)
+function SqlMaskingUtils (pAlias) 
 {
-    if (this._pageSize == null || isNaN(this._pageSize))
-        throw SqlBuilder._ERROR_PAGESIZE_INVALID();
-    
-    if (this._startRow == null)
-        this._startRow = 0;
+    this.alias = null;
+    Object.defineProperty(this, "alias", {
+        set: function(v){
+            this._alias = v;
+            if (v != null)
+                this._dbType = db.getDatabaseType(this._alias);
+        },
+        get: function(){
+            return this._alias;
+        }
+    });
+    this.dbType = null;
+    //provide the possibility to just set dbType (e.g. for testing) with no association to an alias
+    Object.defineProperty(this, "dbType", {
+        set: function(v){
+            this._alias = null;
+            this._dbType = v;
+        },
+        get: function(){
+            return this._dbType;
+        }
+    });
     
-    if (this._hasMoreRows && this._checkForSelect(pExecuteOnlyIfConditionExists))
-    {
-        var data = this.tablePage(this._startRow, this._pageSize, pExecuteOnlyIfConditionExists, pTimeout);
-        if (data.length < this._pageSize)
-            this._hasMoreRows = false;
-        this._startRow += this._pageSize;
-        return data;
-    }
+    if (pAlias === undefined)
+        this.alias = vars.getString("$sys.dbalias");
     else
-    {
-        this._hasMoreRows = false;
-        return [];
-    }
-}
-
-/**
- * @return {Boolean} whether there are rows left for paging
- */
-SqlBuilder.prototype.hasMoreRows = function ()
-{
-    return this._hasMoreRows;
+        this.alias = pAlias;
 }
 
 /**
- * Executes the SQL with paging and executes the given callback-function for every resultset until the last row has been reached or the function
- * returns false.
+ * Returns the reminder of: arg1 / arg2
  * 
- * @param {Function} pCallBackFn CallBack-Function to execute for every page. If the function returns false, the execution will be stopped.
- * @param {Boolean} [pExecuteOnlyIfConditionExists=false] if true and there is no condition, [] is returned
- * @param {Number} [pTimeout=-1]
+ * @param {string|SqlBuilder} pField1 arg1
+ * @param {string|SqlBuilder} pField2 arg2
+ * 
+ * @returns the reminder of the division
  */
-SqlBuilder.prototype.forEachPage = function (pCallBackFn, pExecuteOnlyIfConditionExists, pTimeout)
+SqlMaskingUtils.prototype.modulo = function(pField1, pField2)
 {
-    if (typeof pCallBackFn !== "function")
-        throw SqlBuilder._ERROR_NOT_A_FUNCTION();
-    
-    var run = true;
-    var idx = 0;
-    while (run && this.hasMoreRows())
+    switch(this.dbType)
     {
-        run = pCallBackFn.call(null, this.nextTablePage(pExecuteOnlyIfConditionExists, pTimeout), idx++) != false;
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_DERBY10:
+        case db.DBTYPE_POSTGRESQL8:
+            return "mod((" + pField1.toString() + "), (" + pField2.toString() + "))";
+        case db.DBTYPE_SQLSERVER2000:
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+            return "((" + pField1.toString() + ") % (" + pField2.toString() + "))";
     }
 }
 
 /**
- * Sets an impossible where-condition, so that the query won't return any rows.
+ * Returns an sql expression resolving into the diffrence between 2 dates in days
+ * The expression is a s follows: first_date - second_date
+ * So if the first date is smaller than the second date you will get a negative value returned
  * 
- * @return {SqlBuilder} current object
+ * @param {string|SqlBuilder} pDateField1 the first date
+ * @param {string|SqlBuilder} pDateField2 the second date
+ * 
+ * @returns the diffrence between the two dates in days
  */
-SqlBuilder.prototype.noResult = function ()
+SqlMaskingUtils.prototype.dayDateDifference = function(pDateField1, pDateField2)
 {
-    return this.clearWhere().where(SqlBuilder.NORESULT_CONDITION());
-}
-
-/**
- * checks if an update /delete statement should be called or not
- * @return {Boolean}
- * @private
- */
-SqlBuilder.prototype._checkForUpdate = function(pExecuteOnlyIfConditionExists) 
-{
-    if (pExecuteOnlyIfConditionExists === undefined)
-        pExecuteOnlyIfConditionExists = true;
+    var maskingUtils = new SqlMaskingUtils();
     
-    if (typeof pExecuteOnlyIfConditionExists !== "boolean")
-        throw SqlBuilder._ERROR_NOT_BOOLEAN();
+    var date1 = maskingUtils.cast("(" + pDateField1.toString() + ")", SQLTYPES.DATE);
+    var date2 = maskingUtils.cast("(" + pDateField2.toString() + ")", SQLTYPES.DATE);
     
-    return !pExecuteOnlyIfConditionExists || this.hasCondition();
+    switch(this.dbType)
+    {
+        case db.DBTYPE_DERBY10:
+            // JDBC escape systax is required for timestampdiff
+            return "{fn timestampdiff(SQL_TSI_DAY, " + date2 + ", " + date1 + ")}";
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_SQLSERVER2000:
+            return "datediff(day, " + date1 + ", " + date2 + ")";
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+            return "datediff(" + date1 + ", " + date2 + ")";
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_ORACLE10_THIN:
+            return "(" + date1 + " - " + date2 + ")";
+    }
 }
 
 /**
- * checks if a select statement should be called or not
- * @return {Boolean}
- * @private
+ * returns the concat symbol depending on database type
+ * @return {String} Concat Symbol
  */
-SqlBuilder.prototype._checkForSelect = function(pExecuteOnlyIfConditionExists) 
+SqlMaskingUtils.prototype.getConcatSymbol = function() 
 {
-    if (pExecuteOnlyIfConditionExists == undefined)
-        pExecuteOnlyIfConditionExists = false;
-    
-    if (typeof pExecuteOnlyIfConditionExists !== "boolean")
-        throw SqlBuilder._ERROR_NOT_BOOLEAN();
-    
-    if (this.isFullSelect())
-    {
-        return !pExecuteOnlyIfConditionExists || this.hasCondition();
-    }
-    else
+    switch(this.dbType) 
     {
-        throw SqlBuilder._ERROR_INCOMPLETE_SELECT();
+        case db.DBTYPE_SQLSERVER2000:
+            return " + ";
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_DERBY10:
+        default:
+            return " || ";
     }
 }
 
+
 /**
- * generates a part of the sql
- * 
- * @param {String|String[]|SqlBuilder|SqlCondition} pElement the element to append
- * @param {String} [pPrefix] string to be added before pElement
- * @param {Boolean} [pAutoJoin] if this is true and pElement is an array, it will be automatically
- *                               joined together to a string
- * 
- * @private
- * @deprecated this method is only needed by deprecated methods
- */
-SqlBuilder.prototype._getClause = function (pElement, pPrefix, pAutoJoin)
+*  Returns the group_concat function, which groups <br>
+*  multiple Row Values into one Cell. Note: This function <br>
+*  does not work on Derby<br>
+*
+* @param {String} pField                 <p>
+*                                       Expression that shall be grouped.<br>
+* @param {String} pSeperator             <p>
+*                                       Character that shall be used as Seperator<br>
+* @return {String}                      <p>
+*                                       Returns the field with groupConcat wrapped around<br>
+*/
+SqlMaskingUtils.prototype.getGroupConcat = function(pField, pSeperator) 
 {
-    var preparedValues = [];
-    if (typeof pElement !== "string")
-    {
-        if (pElement.length !== undefined && pAutoJoin) //array of fields
-        {
-            for (let i = 0, l = pElement.length; i < l; i++)
-            {
-                if (typeof pElement[i] !== "string")
-                    pElement[i] = _getElement(pElement[i]);
-            }
-            pElement = pElement.join(", ");
-        }
-        else
-        {
-            pElement = _getElement(pElement);
-        }
-    }
-
-    if (pPrefix && pElement)
-        pElement = pPrefix + " " + pElement;
-    
-    return [pElement.toString(), preparedValues];
+    var group;
+    if(pField == null || pSeperator == null || pField == null && pSeperator == null)
+        throw new Error(translate.withArguments("Field or Seperator were empty function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]));
     
-    function _getElement (element)
+    switch(this.dbType) 
     {
-        if (element instanceof SqlBuilder || element instanceof SqlCondition)
-            element = element.build();
-        preparedValues = preparedValues.concat(element[1]);
-        if (element instanceof SqlBuilder || pAutoJoin)
-            return "(" + element[0] + ")";
-        return element[0];
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+            group = " GROUP_CONCAT("+pField+" SEPARATOR "+pSeperator+")";
+            break;
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_SQLSERVER2000:
+            group = " STRING_AGG("+pField+", "+pSeperator+")";
+            break;
+        case db.DBTYPE_DERBY10:
+            logging.log(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]), logging.ERROR);
+            break;
     }
+    return group;
 }
 
 /**
- * translates SqlBuilder to plain SQL. Use this if prepared statements are not supported.
- * For the db-functions (db.table, db.cell, etc.) use ".build()" as they support prepared statements.
- * It resolves all prepared values.
- * @param {String} [pAlias=undefined] the alias to use for db.translateStatement
- * @return {String} plain SQL statement
- * 
- * @deprecated use .toString()
- */
-SqlBuilder.prototype.translate = function(pAlias)
-{
-    return SqlUtils.translateStatementWithQuotes(this.build(), pAlias);
-}
-
-/**
- * Creates an object for building a case-when statement.
- * 
- * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
- *                                                                     else it is used as Field. <br/>
- *                                                                     Please see .where() for more information and examples.
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       Please see .where() for more information and examples.
- * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
- *                               Please see .where() for more information and examples.
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                Please see .where() for more information and examples.
- * 
- * @return {SqlBuilder._CaseWhen}
- */
-SqlBuilder.caseWhen = function (pFieldOrCond, pValue, pCondition, pFieldType)
+*  Returns the trim function, which removes the<br>
+*  leading and trailing spaces in a string, depending<br>
+*  on the database behind the given alias note that<br>
+*  this function does not verifiy where the types of<br>
+*  your expression are trimable or not.<br>
+*
+* @param {String} pField                 <p>
+*                                       Expression that shall be trimmed.<br>
+* @return {String}                      <p>
+*                                       Returns the trimmed string.<br>
+*/
+SqlMaskingUtils.prototype.trim = function (pField) 
 {
-    return new SqlBuilder._CaseStatement().when(pFieldOrCond, pValue, pCondition, pFieldType);
+    if (this.dbType == db.DBTYPE_SQLSERVER2000)
+        return "ltrim(rtrim(" + pField + "))";
+    return "trim(" + pField + ")";
 }
 
 /**
- * @return {SqlBuilder._CaseStatement}
- */
-SqlBuilder.caseStatement = function ()
+*  returns the max-value sql expressions depending on the database behind the given alias
+*  note that this function does not verifiy if the field (and type) usage is valid at all
+*
+* @param {String} pField expression
+*
+* @return {String} sql-part that can be used in a select
+*/
+SqlMaskingUtils.prototype.max = function (pField) 
 {
-    return new SqlBuilder._CaseStatement();
+    return "max(" + pField + ")";
 }
 
 /**
- * Represents a case-when statement
- */
-SqlBuilder._CaseStatement = function ()
+* returns the min-value sql expressions depending on the database behind the given alias
+* note that this function does not verifiy if the field (and type) usage is valid at all
+*
+* @param {String} pField expression
+*
+* @return {String} sql-part that can be used in a select
+*/
+SqlMaskingUtils.prototype.min = function (pField) 
 {
-    this._whenCondition = null;
-    this._whenThens = [];
-    this._elseValue = null;
-    this._afterWhenMask = new SqlBuilder._CaseWhen(this);
+    return "min(" + pField + ")";
 }
 
-SqlBuilder.defineCanBuildSql(SqlBuilder._CaseStatement.prototype);
-
 /**
- * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
- *                                                                     else it is used as Field. <br/>
- *                                                                     Please see .where() for more information and examples.
- * @param {String|SqlBuilder|PreparedSqlArray|Array|OtherTypes} [pValue] This is the value whitch is used for the condition.<br/>
- *                                                                       Basically it can be nearly everything you need.<br/>
- *                                                                       Please see .where() for more information and examples.
- * @param {String} [pCondition="# = ?"] This is the condition which should be used.<br/>
- *                               # will be replaced by the field (pFieldOrCond) If pFieldOrCond is null, you can ommit #<br/>
- *                               ? will be replaced by pValue<br/>
- *                               <strong>IMPORTANT: the # has to be before the ?</strong><br/>
- *                               Please see .where() for more information and examples.
- * @param {SQLTYPES|Numeric} [pFieldType=AutomaticallyLoadedType] You can specify which datatype should be used for the prepared statement<br/>
- *                                                                In most cases you don't need this.<br/>
- *                                                                Please see .where() for more information and examples.
- * 
- * @return {SqlBuilder._CaseWhen}
+ * masks the function cast of standard sql
+ * please note that this function does not do any validation if it's possible to cast the expression's datatype you pass to the function in every supported DBMS
+ *
+ * Problems:
+ * Derby has problems with casting to CHAR({> 254}) https://db.apache.org/derby/docs/10.14/ref/rrefsqlj13733.html
+ *
+ * @param {String} pField name of the database field that shall be castet
+ * @param {String} [pTargetDatatype] a SQLTYPES-value of the following: SQLTYPES.CHAR, SQLTYPES.VARCHAR, SQLTYPES.INTEGER, 
+ *                                   SQLTYPES.DECIMAL, SQLTYPES.DATE
+ * @param {int|int[]} pTargetLength specifies the length of the target data type;
+ *                                   <br/>- char/varchar: length
+ *                                   <br/>- decimal: [length, decimals]
+ *
+ * @return {String} sql part to be included in sql-statements
  */
-SqlBuilder._CaseStatement.prototype.when = function (pFieldOrCond, pValue, pCondition, pFieldType)
+SqlMaskingUtils.prototype.cast = function (pField, pTargetDatatype, pTargetLength) 
 {
-    this._whenCondition = newWhere(pFieldOrCond, pValue, pCondition, pFieldType);
-    return this._afterWhenMask;
-}
+    /* Some information if you want to add supported databaseTypes or dataTypes:
+     * You should consider using the _mapDefaults function-expression (details in the functions doc)
+     * However you shouldn't use the function in a "default"-Block of a switch-case because of the following behaviour:
+     * If a datatype is not supported you just have to NOT specify "sqlDataType" (leave it "undefined") -> an error is then raised
+     * Therefore you should explicitly define which Data-type is supported and which is not
+     */
+    var sqlDataType;
+    var functionName = "cast";//overwrite this in the "switch (dbType)" if needed with your DBMS
 
-/**
- * Sets the expression used for the else-part
- * 
- * @param {String|SqlBuilder} pValue else-value
- * @return {SqlBuilder._CaseStatement}
- */
-SqlBuilder._CaseStatement.prototype.elseValue = function (pValue)
-{
-    this._elseValue = pValue;
-    return this;
-}
-
-/**
- * Sets the value used for the else-part, but wraps the value in ''
- * 
- * @param {String} pValue else-value
- * @return {SqlBuilder._CaseStatement}
- */
-SqlBuilder._CaseStatement.prototype.elseString = function (pValue)
-{
-    return this.elseValue("'" + pValue + "'");
-}
+    /**
+     * handles default-scenarios for mapping input-targetDatatype to a string for a sql-statement
+     * e.g. SQLTYPES.INTEGER --> int
+     * @param {Number} dataType input as a value of "SQLTYPES." that will be mapped to a string
+     * @return {String} the mapped dataType for using in a sql-statement
+     */
+    var _mapDefaults = function (dataType) 
+    {
+        switch (dataType) 
+        {
+            case SQLTYPES.CHAR:
+                return "char";
+            case SQLTYPES.VARCHAR:
+                return "char";
+            case SQLTYPES.NVARCHAR:
+                return "nvarchar";
+            case SQLTYPES.INTEGER:
+                return "int";
+            case SQLTYPES.DECIMAL:
+                return "decimal";
+            case SQLTYPES.DATE:
+                return "date";
+            case SQLTYPES.TIMESTAMP:
+                return "timestamp";
+        }
+        return null;
+    }
+        
+    switch (this.dbType) 
+    {
+        case db.DBTYPE_DERBY10:
+            switch(pTargetDatatype) 
+            {
+                case SQLTYPES.NVARCHAR:
+                case SQLTYPES.VARCHAR:
+                    // Because of a Derby bug, you can't cast INTEGER into VARCHAR
+                    // Therefor first cast to char then to varchar
+                    // https://issues.apache.org/jira/browse/DERBY-2072
+                    // This cast to char is only done if the length is not bigger than 254,
+                    // otherwise the additional cast would result in a different error
+                    if (pTargetLength <= 254)
+                        pField = "rtrim(" + this.cast(pField, SQLTYPES.CHAR, pTargetLength) + ")";
+                    sqlDataType = "varchar";
+                    break;
+                case SQLTYPES.CHAR:
+                    sqlDataType = "char";
+                    break;
+                case SQLTYPES.DECIMAL:
+                case SQLTYPES.INTEGER:
+                case SQLTYPES.DATE:
+                case SQLTYPES.TIMESTAMP:
+                    sqlDataType = _mapDefaults(pTargetDatatype);
+                    break;
+            }
+            break;
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+            switch(pTargetDatatype) 
+            {
+                case SQLTYPES.TIMESTAMP:
+                    sqlDataType = "datetime";
+                    break;
+                case SQLTYPES.NVARCHAR:
+                case SQLTYPES.VARCHAR:
+                case SQLTYPES.CHAR:
+                case SQLTYPES.INTEGER:
+                case SQLTYPES.DECIMAL:
+                case SQLTYPES.DATE:
+                    sqlDataType = _mapDefaults(pTargetDatatype);
+                    break;
+            }
+            break;
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+            switch(pTargetDatatype)
+            {
+                case SQLTYPES.VARCHAR:
+                    sqlDataType = "varchar2";
+                    break;
+                case SQLTYPES.NVARCHAR:
+                    sqlDataType = "nvarchar2";
+                    break;
+                case SQLTYPES.INTEGER:
+                    sqlDataType = "number";
+                    pTargetLength = "10"
+                    break;
+                case SQLTYPES.CHAR:
+                case SQLTYPES.DECIMAL:
+                case SQLTYPES.DATE:
+                case SQLTYPES.TIMESTAMP:
+                    sqlDataType = _mapDefaults(pTargetDatatype);
+                    break;
+            }
+            break;
+        case db.DBTYPE_POSTGRESQL8:
+            switch(pTargetDatatype)
+            {
+                case SQLTYPES.DATE:
+                case SQLTYPES.TIMESTAMP:
+                case SQLTYPES.DECIMAL:
+                case SQLTYPES.INTEGER:
+                case SQLTYPES.CHAR:
+                case SQLTYPES.VARCHAR:
+                case SQLTYPES.NVARCHAR:
+                    sqlDataType = _mapDefaults(pTargetDatatype);
+                    break;
+            }
+            break;
+        case db.DBTYPE_SQLSERVER2000:
+            switch(pTargetDatatype)
+            {
+                case SQLTYPES.TIMESTAMP:
+                    sqlDataType = "datetime";
+                    break;
+                case SQLTYPES.DATE:
+                case SQLTYPES.DECIMAL:
+                case SQLTYPES.INTEGER:
+                case SQLTYPES.CHAR:
+                case SQLTYPES.VARCHAR:
+                case SQLTYPES.NVARCHAR:
+                    sqlDataType = _mapDefaults(pTargetDatatype);
+                    break;
+            }
+            //TODO: firebird support?
+    }
 
-/**
- * @return {String} the case-when expression
- */
-SqlBuilder._CaseStatement.prototype.toString = function (pAlias)
-{
-    return db.translateStatement(this.build(), pAlias || db.getCurrentAlias());
-}
+    if (sqlDataType == undefined) 
+        throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.cast._mapDefaults"]));
 
-SqlBuilder._CaseStatement.prototype.build = function (pParameters)
-{
-    var caseStatement = ["case"];
-    var preparedValues = [];
-    this._whenThens.forEach(function (whenThen)
-    {
-        var when = SqlBuilder._getStatement(whenThen.condition, "when");
-        var then = SqlBuilder._getStatement(whenThen.thenValue, "then");
-        caseStatement.push(when.sqlStorage);
-        caseStatement.push(then.sqlStorage);
-        preparedValues = preparedValues.concat(when.preparedValues, then.preparedValues);
-    });
-    if (this._elseValue)
+    if (pTargetLength == undefined)
+        pTargetLength = "";
+    else if (pTargetLength != "")
     {
-        let elseStatement = SqlBuilder._getStatement(this._elseValue, "else");
-        caseStatement.push(elseStatement.sqlStorage);
-        preparedValues = preparedValues.concat(elseStatement.preparedValues);
+        if (Array.isArray(pTargetLength))
+            pTargetLength = "(" + pTargetLength.join(", ") + ")";
+        else
+            pTargetLength = "(" + pTargetLength + ")";
     }
-    caseStatement.push("end");
-    
-    return [
-        caseStatement.join(" "),
-        preparedValues
-    ];
-}
 
-/**
- * Custom toJSON method that omits the property '_afterWhenMask', because cyclic references can't be stringified
- */
-SqlBuilder._CaseStatement.prototype.toJSON = function ()
-{
-    return {
-        _whenCondition: this._whenCondition,
-        _whenThens: this._whenThens,
-        _elseValue: this._elseValue
-    };
+    return functionName + "(" + pField + " as " + sqlDataType + pTargetLength + ")";
 }
 
 /**
- * Object providing the then-methods for the case-when expression. It can be only be accessed after calling .when to ensure a 'then' 
- * can only be added after a 'when'.
+ * masks the cast function for lob datatypes(clob, blob) into varchar or similar
+ *
+ * @param {String} pField expression that shall be casted
+ * @param {Number|Number[]} pTargetLength desired length of the datatype
+ *                                         decimal: [length, decimals]
+ *
+ * @return {String} part of sql-expression that can be used
  */
-SqlBuilder._CaseWhen = function (pCaseStatement)
+SqlMaskingUtils.prototype.castLob = function (pField, pTargetLength) 
 {
-    this._caseStatement = pCaseStatement;
+    switch (this.dbType) 
+    {
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+            return "DBMS_LOB.SUBSTR(" + pField + ", " + pTargetLength + ", 1)";
+        default:
+            return this.cast(pField, SQLTYPES.VARCHAR, pTargetLength);
+    }
 }
 
 /**
- * Sets the expression for the then
- * 
- * @param {String|SqlBuilder} pValue then-value
- * @return {SqlBuilder._CaseStatement}
+ *  returns the function which determines the length of binary data
+ *
+ * @param {String} pField name of the checked field
+ *
+ * @return {String}
  */
-SqlBuilder._CaseWhen.prototype.then = function (pValue)
+SqlMaskingUtils.prototype.binDataLength = function (pField) 
 {
-    var condition = this._caseStatement._whenCondition;
-    this._caseStatement._whenCondition = null;
-    this._caseStatement._whenThens.push({condition: condition, thenValue: pValue});
-    return this._caseStatement;
+    switch (this.dbType) 
+    {
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_DERBY10:
+            return "length(" + pField + ")";
+        case db.DBTYPE_SQLSERVER2000:
+            return "datalength(" + pField + ")";
+        default:
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.binDataLength"]));
+    }
 }
 
 /**
- * Sets the value for the then, but wraps the value in ''
- * 
- * @param {String} pValue then-value
- * @return {SqlBuilder._CaseStatement}
- */
-SqlBuilder._CaseWhen.prototype.thenString = function (pValue)
+    * masks the sql function substring
+    *
+    * @param {String } pField the expression that shall be substringed
+    * @param {Number} pStartPos posistion where the substring starts
+    * @param {Number} pLength amount of characters of the expression will be returned by the sql function
+    *
+    * @return {String} part of sql-expression that can be used for substringing
+    */
+SqlMaskingUtils.prototype.substring = function (pField, pStartPos, pLength) 
 {
-    return this.then("'" + pValue + "'");
-}
-
-/**
- *provides functions for masking sql functions
- *
- * @param {String} [pAlias=currentAlias] database alias, you can specify null if you have no alias available and  you can manually set the dbType property
- * 
- * @class
- */
-function SqlMaskingUtils (pAlias) 
-{
-    this.alias = null;
-    Object.defineProperty(this, "alias", {
-        set: function(v){
-            this._alias = v;
-            if (v != null)
-                this._dbType = db.getDatabaseType(this._alias);
-        },
-        get: function(){
-            return this._alias;
-        }
-    });
-    this.dbType = null;
-    //provide the possibility to just set dbType (e.g. for testing) with no association to an alias
-    Object.defineProperty(this, "dbType", {
-        set: function(v){
-            this._alias = null;
-            this._dbType = v;
-        },
-        get: function(){
-            return this._dbType;
-        }
-    });
-    
-    if (pAlias === undefined)
-        this.alias = vars.getString("$sys.dbalias");
-    else
-        this.alias = pAlias;
-}
-
-/**
- * Returns the reminder of: arg1 / arg2
- * 
- * @param {string|SqlBuilder} pField1 arg1
- * @param {string|SqlBuilder} pField2 arg2
- * 
- * @returns the reminder of the division
- */
-SqlMaskingUtils.prototype.modulo = function(pField1, pField2)
-{
-    switch(this.dbType)
+    var sqlFnName;
+    switch (this.dbType)
     {
         case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_OCI:
         case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+            sqlFnName = "substr";
+            break;
         case db.DBTYPE_DERBY10:
+            sqlFnName = "substr";
+            break;
         case db.DBTYPE_POSTGRESQL8:
-            return "mod((" + pField1.toString() + "), (" + pField2.toString() + "))";
-        case db.DBTYPE_SQLSERVER2000:
-        case db.DBTYPE_MARIADB10:
-        case db.DBTYPE_MYSQL4:
-            return "((" + pField1.toString() + ") % (" + pField2.toString() + "))";
-    }
-}
-
-/**
- * Returns an sql expression resolving into the diffrence between 2 dates in days
- * The expression is a s follows: first_date - second_date
- * So if the first date is smaller than the second date you will get a negative value returned
- * 
- * @param {string|SqlBuilder} pDateField1 the first date
- * @param {string|SqlBuilder} pDateField2 the second date
- * 
- * @returns the diffrence between the two dates in days
- */
-SqlMaskingUtils.prototype.dayDateDifference = function(pDateField1, pDateField2)
-{
-    var maskingUtils = new SqlMaskingUtils();
-    
-    var date1 = maskingUtils.cast("(" + pDateField1.toString() + ")", SQLTYPES.DATE);
-    var date2 = maskingUtils.cast("(" + pDateField2.toString() + ")", SQLTYPES.DATE);
-    
-    switch(this.dbType)
-    {
-        case db.DBTYPE_DERBY10:
-            // JDBC escape systax is required for timestampdiff
-            return "{fn timestampdiff(SQL_TSI_DAY, " + date2 + ", " + date1 + ")}";
-        case db.DBTYPE_POSTGRESQL8:
+            sqlFnName = "substr";
+            break;
         case db.DBTYPE_SQLSERVER2000:
-            return "datediff(day, " + date1 + ", " + date2 + ")";
+            sqlFnName = "substring";
+            break;
         case db.DBTYPE_MYSQL4:
         case db.DBTYPE_MARIADB10:
-            return "datediff(" + date1 + ", " + date2 + ")";
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_ORACLE10_THIN:
-            return "(" + date1 + " - " + date2 + ")";
+            sqlFnName = "substring";
+            break;
+        default:
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.substring"]));
     }
+    return sqlFnName + "(" + pField + ", " + pStartPos + ", " + pLength + ")";
 }
 
 /**
- * returns the concat symbol depending on database type
- * @return {String} Concat Symbol
- */
-SqlMaskingUtils.prototype.getConcatSymbol = function() 
+* masks the function concat (without separator)
+*
+* @param {Array} pFields fields (or expressions) that should be concatenated
+*
+* @return {String} part of SQL-querey
+*/
+SqlMaskingUtils.prototype.concatenate = function (pFields)
 {
-    switch(this.dbType) 
+    if (pFields.length === 0)
+        return "''";
+
+    switch (this.dbType)
     {
-        case db.DBTYPE_SQLSERVER2000:
-            return " + ";
-        case db.DBTYPE_MARIADB10:
         case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_POSTGRESQL8:
+            return " concat(" + pFields.join(", ") + ")";
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_POSTGRESQL8:
+            break;
+        case db.DBTYPE_SQLSERVER2000:
+            //MS SQL Server supports "concat_ws" (and ignoring null values) from version SQL Server 2017 and newer:
+            //https://docs.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-2017
+            break;
         case db.DBTYPE_DERBY10:
+            break;
         default:
-            return " || ";
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.concatenate"]));
+    }
+    
+    var fields = [];
+    
+    for (let i = 0; i < pFields.length; i++)
+    {
+        let field = pFields[i];
+        let isLast = i + 1 === pFields.length;
+        
+        if (field && field != "''")
+        {
+            if (_isFixedValue(field))
+                fields.push(field);
+            else
+                fields.push(this.isNull(field));
+        }
+    }
+    
+    return fields.join(this.getConcatSymbol()) || "''";
+    
+    function _isFixedValue (pSqlField)
+    {
+        return pSqlField.startsWith("'") && pSqlField.endsWith("'") && !pSqlField.slice(1, -1).includes("'");
     }
 }
 
-
 /**
-*  Returns the group_concat function, which groups <br>
-*  multiple Row Values into one Cell. Note: This function <br>
-*  does not work on Derby<br>
+* masks the function concat_ws
+* if a sql field is empty no separator will be added
+* note that this function will often create a lot of sql-code
 *
-* @param {String} pField                 <p>
-*                                       Expression that shall be grouped.<br>
-* @param {String} pSeperator             <p>
-*                                       Character that shall be used as Seperator<br>
-* @return {String}                      <p>
-*                                       Returns the field with groupConcat wrapped around<br>
+* @param {Array} pFields fields (or expressions) that should be concatenated
+* @param {String} [pSeparator=space-character] character for separating the fields
+* @param {String} [pAutoTrimFields=true] autoTrimFields if true the expressions are always trimmed, false no change will be applied
+*
+* @return {String} part of SQL-querey
 */
-SqlMaskingUtils.prototype.getGroupConcat = function(pField, pSeperator) 
+SqlMaskingUtils.prototype.concatWithSeparator = function (pFields, pSeparator, pAutoTrimFields) 
 {
-    var group;
-    if(pField == null || pSeperator == null || pField == null && pSeperator == null)
-        throw new Error(translate.withArguments("Field or Seperator were empty function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]));
+    if (pFields.length === 0)
+        return "''";
+    if (pFields.length === 1)
+        return pFields[0];
     
-    switch(this.dbType) 
+    if (pSeparator === "" && pAutoTrimFields == false)
+        return this.concatenate(pFields);
+    
+    if (pAutoTrimFields == undefined)
+        pAutoTrimFields = true;
+    
+    if (pSeparator === null || pSeparator === undefined)
+        pSeparator = "' '";
+    else if (pSeparator || pSeparator === "")
+        pSeparator = "'" + db.quote(pSeparator, this.alias) + "'";
+    
+    var isEmptyStringNull = false;
+
+    switch (this.dbType)
     {
-        case db.DBTYPE_MARIADB10:
         case db.DBTYPE_MYSQL4:
-            group = " GROUP_CONCAT("+pField+" SEPARATOR "+pSeperator+")";
-            break;
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_POSTGRESQL8:
+            if (pAutoTrimFields)
+                pFields = pFields.map(this.trim, this);
+            return " concat_ws(" + pSeparator + ", " + pFields.join(", ") + ")";
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_POSTGRESQL8:
+            isEmptyStringNull = true; //empty strings are changed to DB-null-values internally in oracle
+            break;
         case db.DBTYPE_SQLSERVER2000:
-            group = " STRING_AGG("+pField+", "+pSeperator+")";
+            //MS SQL Server supports "concat_ws" (and ignoring null values) from version SQL Server 2017 and newer:
+            //https://docs.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-2017
             break;
         case db.DBTYPE_DERBY10:
-            logging.log(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]), logging.ERROR);
             break;
+        default:
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.concatWithSeparator"]));
     }
-    return group;
-}
+    
+    var concatCharacter = this.getConcatSymbol();
+    var concatSql = "";
+    
+    for (let i = 0; i < pFields.length; i++)
+    {
+        let field = pFields[i];
+        let isLast = i + 1 === pFields.length;
+        
+        if (_isFixedValue(field))
+            concatSql += (pAutoTrimFields ? "'" + field.slice(1, -1).trim() + "'" : field);
+        else
+        {
+            let stringField = isEmptyStringNull ? field : this.isNull(field);
+            concatSql += (pAutoTrimFields ? this.trim(stringField) : this.isNull(stringField));
+        }
+        
+        if (!isLast)
+        {
+            concatSql += concatCharacter;
+            let nextField = pFields[i+1];
+            if (pSeparator && _isFixedValue(nextField))
+            {
+                if (nextField.slice(1, -1).trim())
+                    concatSql += pSeparator + concatCharacter;
+            }
+            else if (pSeparator)
+            {
+                let nextNotNullCondition;
+                let nextFieldTrimmed = pAutoTrimFields ? this.trim(nextField) : nextField;
+                if (isEmptyStringNull)
+                    nextNotNullCondition = nextFieldTrimmed + " is not null";
+                else
+                    nextNotNullCondition = nextField + " is not null and " + nextFieldTrimmed + " != ''";
 
-/**
-*  Returns the trim function, which removes the<br>
-*  leading and trailing spaces in a string, depending<br>
-*  on the database behind the given alias note that<br>
-*  this function does not verifiy where the types of<br>
-*  your expression are trimable or not.<br>
-*
-* @param {String} pField                 <p>
-*                                       Expression that shall be trimmed.<br>
-* @return {String}                      <p>
-*                                       Returns the trimmed string.<br>
-*/
-SqlMaskingUtils.prototype.trim = function (pField) 
-{
-    if (this.dbType == db.DBTYPE_SQLSERVER2000)
-        return "ltrim(rtrim(" + pField + "))";
-    return "trim(" + pField + ")";
-}
-
-/**
-*  returns the max-value sql expressions depending on the database behind the given alias
-*  note that this function does not verifiy if the field (and type) usage is valid at all
-*
-* @param {String} pField expression
-*
-* @return {String} sql-part that can be used in a select
-*/
-SqlMaskingUtils.prototype.max = function (pField) 
-{
-    return "max(" + pField + ")";
-}
-
-/**
-* returns the min-value sql expressions depending on the database behind the given alias
-* note that this function does not verifiy if the field (and type) usage is valid at all
-*
-* @param {String} pField expression
-*
-* @return {String} sql-part that can be used in a select
-*/
-SqlMaskingUtils.prototype.min = function (pField) 
-{
-    return "min(" + pField + ")";
+                concatSql += "case when " + nextNotNullCondition + " then " + pSeparator + " else '' end " + concatCharacter;
+            }
+        }
+    }
+    
+    return concatSql;
+    
+    function _isFixedValue (pSqlField)
+    {
+        return pSqlField.startsWith("'") && pSqlField.endsWith("'") && !pSqlField.slice(1, -1).includes("'");
+    }
 }
 
 /**
- * masks the function cast of standard sql
- * please note that this function does not do any validation if it's possible to cast the expression's datatype you pass to the function in every supported DBMS
- *
- * Problems:
- * Derby has problems with casting to CHAR({> 254}) https://db.apache.org/derby/docs/10.14/ref/rrefsqlj13733.html
- *
- * @param {String} pField name of the database field that shall be castet
- * @param {String} [pTargetDatatype] a SQLTYPES-value of the following: SQLTYPES.CHAR, SQLTYPES.VARCHAR, SQLTYPES.INTEGER, 
- *                                   SQLTYPES.DECIMAL, SQLTYPES.DATE
- * @param {int|int[]} pTargetLength specifies the length of the target data type;
- *                                   <br/>- char/varchar: length
- *                                   <br/>- decimal: [length, decimals]
+ * get the current timestamp
  *
- * @return {String} sql part to be included in sql-statements
+ * @return {String} sql expression for the current timestamp
  */
-SqlMaskingUtils.prototype.cast = function (pField, pTargetDatatype, pTargetLength) 
+SqlMaskingUtils.prototype.currentTimestamp = function() 
 {
-    /* Some information if you want to add supported databaseTypes or dataTypes:
-     * You should consider using the _mapDefaults function-expression (details in the functions doc)
-     * However you shouldn't use the function in a "default"-Block of a switch-case because of the following behaviour:
-     * If a datatype is not supported you just have to NOT specify "sqlDataType" (leave it "undefined") -> an error is then raised
-     * Therefore you should explicitly define which Data-type is supported and which is not
-     */
-    var sqlDataType;
-    var functionName = "cast";//overwrite this in the "switch (dbType)" if needed with your DBMS
-
-    /**
-     * handles default-scenarios for mapping input-targetDatatype to a string for a sql-statement
-     * e.g. SQLTYPES.INTEGER --> int
-     * @param {Number} dataType input as a value of "SQLTYPES." that will be mapped to a string
-     * @return {String} the mapped dataType for using in a sql-statement
-     */
-    var _mapDefaults = function (dataType) 
-    {
-        switch (dataType) 
-        {
-            case SQLTYPES.CHAR:
-                return "char";
-            case SQLTYPES.VARCHAR:
-                return "char";
-            case SQLTYPES.NVARCHAR:
-                return "nvarchar";
-            case SQLTYPES.INTEGER:
-                return "int";
-            case SQLTYPES.DECIMAL:
-                return "decimal";
-            case SQLTYPES.DATE:
-                return "date";
-            case SQLTYPES.TIMESTAMP:
-                return "timestamp";
-        }
-        return null;
-    }
-        
-    switch (this.dbType) 
+    let now;
+    switch(this.dbType) 
     {
-        case db.DBTYPE_DERBY10:
-            switch(pTargetDatatype) 
-            {
-                case SQLTYPES.NVARCHAR:
-                case SQLTYPES.VARCHAR:
-                    // Because of a Derby bug, you can't cast INTEGER into VARCHAR
-                    // Therefor first cast to char then to varchar
-                    // https://issues.apache.org/jira/browse/DERBY-2072
-                    // This cast to char is only done if the length is not bigger than 254,
-                    // otherwise the additional cast would result in a different error
-                    if (pTargetLength <= 254)
-                        pField = "rtrim(" + this.cast(pField, SQLTYPES.CHAR, pTargetLength) + ")";
-                    sqlDataType = "varchar";
-                    break;
-                case SQLTYPES.CHAR:
-                    sqlDataType = "char";
-                    break;
-                case SQLTYPES.DECIMAL:
-                case SQLTYPES.INTEGER:
-                case SQLTYPES.DATE:
-                case SQLTYPES.TIMESTAMP:
-                    sqlDataType = _mapDefaults(pTargetDatatype);
-                    break;
-            }
-            break;
         case db.DBTYPE_MARIADB10:
+            now = "NOW()"
+            break;
         case db.DBTYPE_MYSQL4:
-            switch(pTargetDatatype) 
-            {
-                case SQLTYPES.TIMESTAMP:
-                    sqlDataType = "datetime";
-                    break;
-                case SQLTYPES.NVARCHAR:
-                case SQLTYPES.VARCHAR:
-                case SQLTYPES.CHAR:
-                case SQLTYPES.INTEGER:
-                case SQLTYPES.DECIMAL:
-                case SQLTYPES.DATE:
-                    sqlDataType = _mapDefaults(pTargetDatatype);
-                    break;
-            }
+            now = "CURRENT_TIMESTAMP()";
             break;
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            switch(pTargetDatatype)
-            {
-                case SQLTYPES.VARCHAR:
-                    sqlDataType = "varchar2";
-                    break;
-                case SQLTYPES.NVARCHAR:
-                    sqlDataType = "nvarchar2";
-                    break;
-                case SQLTYPES.INTEGER:
-                    sqlDataType = "number";
-                    pTargetLength = "10"
-                    break;
-                case SQLTYPES.CHAR:
-                case SQLTYPES.DECIMAL:
-                case SQLTYPES.DATE:
-                case SQLTYPES.TIMESTAMP:
-                    sqlDataType = _mapDefaults(pTargetDatatype);
-                    break;
-            }
+            now = "SYSTIMESTAMP";
             break;
         case db.DBTYPE_POSTGRESQL8:
-            switch(pTargetDatatype)
-            {
-                case SQLTYPES.DATE:
-                case SQLTYPES.TIMESTAMP:
-                case SQLTYPES.DECIMAL:
-                case SQLTYPES.INTEGER:
-                case SQLTYPES.CHAR:
-                case SQLTYPES.VARCHAR:
-                case SQLTYPES.NVARCHAR:
-                    sqlDataType = _mapDefaults(pTargetDatatype);
-                    break;
-            }
+            now = "LOCALTIMESTAMP";
             break;
         case db.DBTYPE_SQLSERVER2000:
-            switch(pTargetDatatype)
-            {
-                case SQLTYPES.TIMESTAMP:
-                    sqlDataType = "datetime";
-                    break;
-                case SQLTYPES.DATE:
-                case SQLTYPES.DECIMAL:
-                case SQLTYPES.INTEGER:
-                case SQLTYPES.CHAR:
-                case SQLTYPES.VARCHAR:
-                case SQLTYPES.NVARCHAR:
-                    sqlDataType = _mapDefaults(pTargetDatatype);
-                    break;
-            }
-            //TODO: firebird support?
-    }
-
-    if (sqlDataType == undefined) 
-        throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.cast._mapDefaults"]));
-
-    if (pTargetLength == undefined)
-        pTargetLength = "";
-    else if (pTargetLength != "")
-    {
-        if (Array.isArray(pTargetLength))
-            pTargetLength = "(" + pTargetLength.join(", ") + ")";
-        else
-            pTargetLength = "(" + pTargetLength + ")";
+            now = "GETDATE()";
+            break;
+        case db.DBTYPE_DERBY10:
+            now = "CURRENT_TIMESTAMP";
+            break;
+        default:
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.currentTimestamp"]));
+    
     }
-
-    return functionName + "(" + pField + " as " + sqlDataType + pTargetLength + ")";
+    return now;
 }
 
+ 
 /**
- * masks the cast function for lob datatypes(clob, blob) into varchar or similar
- *
- * @param {String} pField expression that shall be casted
- * @param {Number|Number[]} pTargetLength desired length of the datatype
- *                                         decimal: [length, decimals]
- *
- * @return {String} part of sql-expression that can be used
+ * masks the function which will generate a new UUID
+ * <br> 
+ * <p>
+ * Note: 
+ * The function is not supported for the DB-Types Derby and PostgreSQL  <br>
+ * When using a postreSQL DB ensure that the module 'uuid-ossp' is implemented otherwise it's not supported
+ * </p>
+ * 
+ * @return {String} sql expression that creates a new UUID
  */
-SqlMaskingUtils.prototype.castLob = function (pField, pTargetLength) 
+SqlMaskingUtils.prototype.newUUID = function() 
 {
-    switch (this.dbType) 
+    let uuID;
+    switch(this.dbType) 
     {
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+            uuID = "UUID()";
+            break;
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            return "DBMS_LOB.SUBSTR(" + pField + ", " + pTargetLength + ", 1)";
-        default:
-            return this.cast(pField, SQLTYPES.VARCHAR, pTargetLength);
-    }
-}
-
-/**
- *  returns the function which determines the length of binary data
- *
- * @param {String} pField name of the checked field
- *
- * @return {String}
- */
-SqlMaskingUtils.prototype.binDataLength = function (pField) 
+            uuID = "(select regexp_replace(rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5') as UUID from dual)";
+            break;
+        case db.DBTYPE_POSTGRESQL8:
+            uuID = "uuid_generate_v4()";//be carefull: to run this fuction the PostgreSQL module "uuid-ossp" has to be implemented
+        case db.DBTYPE_SQLSERVER2000:
+            uuID = "NEWID()";
+            break;
+        case db.DBTYPE_DERBY10:
+            logging.log(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.uUID"]), logging.ERROR);
+            break;
+        default:
+            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.uUID"]));
+    
+    }
+    return uuID;
+}
+
+/**
+* masks the function concat
+* if a sql field is empty no separator will be added
+* note that this function will often create a lot of sql-code
+*
+* @param {Array} pFields fields (or expressions) that should be concatenated
+* @param {String} [pSeparator=space-character] character for separating the fields
+* @param {String} [pAutoTrimFields=true] autoTrimFields if true the expressions are always trimmed, false no change will be applied
+*
+* @return {String} part of SQL-querey
+* 
+* @deprecated The function has been renamed to SqlMaskingUtils.prototype.concatWithSeparator to differentiate it from 
+*             SqlMaskingUtils.prototype.concatenate.
+*/
+SqlMaskingUtils.prototype.concat = function (pFields, pSeparator, pAutoTrimFields) 
+{
+    return this.concatWithSeparator(pFields, pSeparator, pAutoTrimFields);
+}
+
+/**
+     * returns the function for replacing a null value
+     *
+     * @param {String} pField expression that shall be checked for a null value
+     * @param {String} [pReplacement=empty string] expression that shall be used if the field contains null
+     *
+     * @return {string}
+     */
+SqlMaskingUtils.prototype.isNull = function (pField, pReplacement) 
 {
+    if (pReplacement == undefined)
+        pReplacement = "''";
+    if (pField instanceof SqlBuilder)
+        pField = "(" + pField.toString() + ")";
     switch (this.dbType) 
     {
-        case db.DBTYPE_MARIADB10:
-        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_SQLSERVER2000:
+            return "isnull(" + pField + ", " + pReplacement + ")";
         case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_ORACLE10_THIN :
+            return "nvl(" + pField + ", " + pReplacement + ")";
         case db.DBTYPE_POSTGRESQL8:
         case db.DBTYPE_DERBY10:
-            return "length(" + pField + ")";
-        case db.DBTYPE_SQLSERVER2000:
-            return "datalength(" + pField + ")";
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
         default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.binDataLength"]));
+            return "coalesce(" + pField + ", " + pReplacement + ")";
     }
 }
 
 /**
-    * masks the sql function substring
-    *
-    * @param {String } pField the expression that shall be substringed
-    * @param {Number} pStartPos posistion where the substring starts
-    * @param {Number} pLength amount of characters of the expression will be returned by the sql function
-    *
-    * @return {String} part of sql-expression that can be used for substringing
-    */
-SqlMaskingUtils.prototype.substring = function (pField, pStartPos, pLength) 
+ * gets the day from a timestamp
+ *
+ * @param {String} pField timestamp to get the day from
+ *
+ * @return {String} sql expression that extracts the day from a timestamp
+ */
+SqlMaskingUtils.prototype.dayFromDate = function (pField)
 {
-    var sqlFnName;
     switch (this.dbType)
     {
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            sqlFnName = "substr";
-            break;
+            return "to_char(" + pField + ",'dd')";
         case db.DBTYPE_DERBY10:
-            sqlFnName = "substr";
-            break;
-        case db.DBTYPE_POSTGRESQL8:
-            sqlFnName = "substr";
-            break;
         case db.DBTYPE_SQLSERVER2000:
-            sqlFnName = "substring";
-            break;
         case db.DBTYPE_MYSQL4:
         case db.DBTYPE_MARIADB10:
-            sqlFnName = "substring";
-            break;
-        default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.substring"]));
+            return "day(" + pField + ")";
+        case db.DBTYPE_POSTGRESQL8:
+            return "extract (day from " + pField + ")";
     }
-    return sqlFnName + "(" + pField + ", " + pStartPos + ", " + pLength + ")";
 }
 
 /**
-* masks the function concat (without separator)
-*
-* @param {Array} pFields fields (or expressions) that should be concatenated
-*
-* @return {String} part of SQL-querey
-*/
-SqlMaskingUtils.prototype.concatenate = function (pFields)
+ * gets the month from a timestamp
+ *
+ * @param {String} pField timestamp to get the month from
+ *
+ * @return {String} sql expression that extracts the month from a timestamp
+ */
+SqlMaskingUtils.prototype.monthFromDate = function (pField)
 {
-    if (pFields.length === 0)
-        return "''";
-
     switch (this.dbType)
     {
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+            return "to_char(" + pField + ",'MM')";
+        case db.DBTYPE_DERBY10:
+        case db.DBTYPE_SQLSERVER2000:
         case db.DBTYPE_MYSQL4:
         case db.DBTYPE_MARIADB10:
+            return "month(" + pField + ")";
         case db.DBTYPE_POSTGRESQL8:
-            return " concat(" + pFields.join(", ") + ")";
+            return "extract (month from " + pField + ")";
+    }
+}
+
+/**
+ * gets the year from a timestamp
+ *
+ * @param {String} pField timestamp to get the year from
+ *
+ * @return {String} sql expression that extracts the year from a timestamp
+ */
+SqlMaskingUtils.prototype.yearFromDate = function(pField)
+{
+    switch (this.dbType)
+    {
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            break;
-        case db.DBTYPE_SQLSERVER2000:
-            //MS SQL Server supports "concat_ws" (and ignoring null values) from version SQL Server 2017 and newer:
-            //https://docs.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-2017
-            break;
+            return "to_char(" + pField + ",'yyyy')";
         case db.DBTYPE_DERBY10:
-            break;
-        default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.concatenate"]));
-    }
-    
-    var fields = [];
-    
-    for (let i = 0; i < pFields.length; i++)
-    {
-        let field = pFields[i];
-        let isLast = i + 1 === pFields.length;
-        
-        if (field && field != "''")
-        {
-            if (_isFixedValue(field))
-                fields.push(field);
-            else
-                fields.push(this.isNull(field));
-        }
-    }
-    
-    return fields.join(this.getConcatSymbol()) || "''";
-    
-    function _isFixedValue (pSqlField)
-    {
-        return pSqlField.startsWith("'") && pSqlField.endsWith("'") && !pSqlField.slice(1, -1).includes("'");
+        case db.DBTYPE_SQLSERVER2000:
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+            return "YEAR(" + pField + ")";
+        case db.DBTYPE_POSTGRESQL8:
+            return "EXTRACT (YEAR FROM " + pField + ")";
     }
 }
 
+
 /**
-* masks the function concat_ws
-* if a sql field is empty no separator will be added
-* note that this function will often create a lot of sql-code
-*
-* @param {Array} pFields fields (or expressions) that should be concatenated
-* @param {String} [pSeparator=space-character] character for separating the fields
-* @param {String} [pAutoTrimFields=true] autoTrimFields if true the expressions are always trimmed, false no change will be applied
-*
-* @return {String} part of SQL-querey
-*/
-SqlMaskingUtils.prototype.concatWithSeparator = function (pFields, pSeparator, pAutoTrimFields) 
+ * gets the hour from a timestamp
+ *
+ * @param {String} pField timestamp to get the year from
+ *
+ * @return {String} sql expression that extracts the hour from a timestamp
+ */
+SqlMaskingUtils.prototype.hourFromDate = function(pField)
 {
-    if (pFields.length === 0)
-        return "''";
-    if (pFields.length === 1)
-        return pFields[0];
-    
-    if (pSeparator === "" && pAutoTrimFields == false)
-        return this.concatenate(pFields);
-    
-    if (pAutoTrimFields == undefined)
-        pAutoTrimFields = true;
-    
-    if (pSeparator === null || pSeparator === undefined)
-        pSeparator = "' '";
-    else if (pSeparator || pSeparator === "")
-        pSeparator = "'" + db.quote(pSeparator, this.alias) + "'";
-    
-    var isEmptyStringNull = false;
-
     switch (this.dbType)
     {
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+            return "to_char(" + pField + ",'HH24')";
+        case db.DBTYPE_DERBY10:
         case db.DBTYPE_MYSQL4:
         case db.DBTYPE_MARIADB10:
+            return "HOUR(" + pField + ")";
+        case db.DBTYPE_SQLSERVER2000:
+            return "DATEPART(hour, "+ pField + ")";
         case db.DBTYPE_POSTGRESQL8:
-            if (pAutoTrimFields)
-                pFields = pFields.map(this.trim, this);
-            return " concat_ws(" + pSeparator + ", " + pFields.join(", ") + ")";
+            return "EXTRACT (HOUR FROM " + pField + ")";
+    }
+}
+
+
+/**
+ * gets the last day of the month from a timestamp
+ *
+ * @param {String} pField timestamp to get the last day of the month from
+ *
+ * @return {String} sql expression that extracts the last day of the month from a timestamp
+ */
+SqlMaskingUtils.prototype.lastDayOfMonth = function(pField)
+{
+    switch (this.dbType)
+    {
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            isEmptyStringNull = true; //empty strings are changed to DB-null-values internally in oracle
-            break;
-        case db.DBTYPE_SQLSERVER2000:
-            //MS SQL Server supports "concat_ws" (and ignoring null values) from version SQL Server 2017 and newer:
-            //https://docs.microsoft.com/de-de/sql/t-sql/functions/concat-ws-transact-sql?view=sql-server-2017
-            break;
         case db.DBTYPE_DERBY10:
-            break;
-        default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.concatWithSeparator"]));
-    }
-    
-    var concatCharacter = this.getConcatSymbol();
-    var concatSql = "";
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+            return "LAST_DAY(" + pField + ")";
+        case db.DBTYPE_SQLSERVER2000:
+            return "DATEADD(MONTH, DATEDIFF(MONTH, 0 ," + pField + ") + 1, 0) -1";
+        case db.DBTYPE_POSTGRESQL8:
+            return "DATE_TRUNC('month'," + pField + "::date) + interval '1 month' - interval '1 day')::date"; 
+    }
+}
+
+
+/**
+ * returns the first field, that is not null or empty
+ * masks the behaviour of coalesce with case when
+ * 
+ * @param pFields {Array} Array of fieldnames. Has to be in the right order. It will be checked from 0 upwards
+ */
+SqlMaskingUtils.prototype.coalesce = function(pFields)
+{
+    var retSql = "";
     
-    for (let i = 0; i < pFields.length; i++)
+    if(pFields && typeof pFields == "object" && pFields.length)
     {
-        let field = pFields[i];
-        let isLast = i + 1 === pFields.length;
-        
-        if (_isFixedValue(field))
-            concatSql += (pAutoTrimFields ? "'" + field.slice(1, -1).trim() + "'" : field);
-        else
+        retSql = "case ";
+
+        for(let i = 0; i < pFields.length; i++)
         {
-            let stringField = isEmptyStringNull ? field : this.isNull(field);
-            concatSql += (pAutoTrimFields ? this.trim(stringField) : this.isNull(stringField));
+            retSql += " when (" + pFields[i] + " is not null or " + pFields[i] + " <> '') then " + pFields[i] + " "
         }
-        
-        if (!isLast)
-        {
-            concatSql += concatCharacter;
-            let nextField = pFields[i+1];
-            if (pSeparator && _isFixedValue(nextField))
-            {
-                if (nextField.slice(1, -1).trim())
-                    concatSql += pSeparator + concatCharacter;
-            }
-            else if (pSeparator)
-            {
-                let nextNotNullCondition;
-                let nextFieldTrimmed = pAutoTrimFields ? this.trim(nextField) : nextField;
-                if (isEmptyStringNull)
-                    nextNotNullCondition = nextFieldTrimmed + " is not null";
-                else
-                    nextNotNullCondition = nextField + " is not null and " + nextFieldTrimmed + " != ''";
 
-                concatSql += "case when " + nextNotNullCondition + " then " + pSeparator + " else '' end " + concatCharacter;
-            }
-        }
+        retSql += " else null end";
     }
+    else
+        throw {message:"The input to coalesce has to be an Array containing the column names"};
     
-    return concatSql;
-    
-    function _isFixedValue (pSqlField)
-    {
-        return pSqlField.startsWith("'") && pSqlField.endsWith("'") && !pSqlField.slice(1, -1).includes("'");
-    }
+    return retSql;
 }
 
 /**
- * get the current timestamp
- *
- * @return {String} sql expression for the current timestamp
+ * Converts day, month and year fields into a date
+ * The parameters can't be null!!!
+ * 
+ * @param pYear {String} year field
+ * @param pMonth {String} month field
+ * @param pDay {String} day field
  */
-SqlMaskingUtils.prototype.currentTimestamp = function() 
+SqlMaskingUtils.prototype.makeDate = function(pYear, pMonth, pDay)
 {
-    let now;
-    switch(this.dbType) 
+    switch(this.dbType)
     {
-        case db.DBTYPE_MARIADB10:
-            now = "NOW()"
-            break;
-        case db.DBTYPE_MYSQL4:
-            now = "CURRENT_TIMESTAMP()";
-            break;
+        case db.DBTYPE_POSTGRESQL8:
+            return "make_date" + "(" + pYear + ", " + pMonth + ", " + pDay + ")";
+        case db.DBTYPE_SQLSERVER2000:
+            return "datefromparts" + "(" + pYear + ", " + pMonth + ", " + pDay + ")";
+
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            now = "SYSTIMESTAMP";
-            break;
-        case db.DBTYPE_POSTGRESQL8:
-            now = "LOCALTIMESTAMP";
-            break;
-        case db.DBTYPE_SQLSERVER2000:
-            now = "GETDATE()";
-            break;
+            return "to_date(lpad(" + pYear + ", 4, '0') || lpad(" + pMonth + ", 2, '0') || lpad(" + pYear + ", 2, '0'), 'YYYY-MM-DD')";
+
+        // from datestr without filled zeros e.g.: 2000-5-2
+        case db.DBTYPE_MYSQL4:
+        case db.DBTYPE_MARIADB10:
+            var dateFields = [
+                this.cast(pYear, SQLTYPES.CHAR, 4),
+                this.cast(pMonth, SQLTYPES.CHAR, 2),
+                this.cast(pDay, SQLTYPES.CHAR, 2)
+            ];
+            var dateStr = this.concatWithSeparator(dateFields, "-", false);
+            return this.cast(dateStr, SQLTYPES.DATE);
+
+        // from datestr with filled zeros e.g.: 2000-05-02
         case db.DBTYPE_DERBY10:
-            now = "CURRENT_TIMESTAMP";
-            break;
-        default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.currentTimestamp"]));
-    
+            // maskingUtils: workaround
+            function _castWithZeros(maskingUtils, field, size) {
+                var casted = maskingUtils.cast(field, SQLTYPES.VARCHAR, size);
+                var concatenated = maskingUtils.concatenate(["'000'", casted]);
+                // + 1: derbys substr starts counting at 1, for whatever reason
+                return "substr(" + concatenated + ", length(" + concatenated + ") - " + size + " + 1)";
+            }
+            var dateFieldsZeroed = [
+                _castWithZeros(this, pYear, 4),
+                _castWithZeros(this, pMonth, 2),
+                _castWithZeros(this, pDay, 2)
+            ];
+            var dateStrZeroed = this.concatWithSeparator(dateFieldsZeroed, "-", false);
+            return this.cast(dateStrZeroed, SQLTYPES.DATE);
     }
-    return now;
 }
 
- 
 /**
- * masks the function which will generate a new UUID
- * <br> 
- * <p>
- * Note: 
- * The function is not supported for the DB-Types Derby and PostgreSQL  <br>
- * When using a postreSQL DB ensure that the module 'uuid-ossp' is implemented otherwise it's not supported
- * </p>
+ * Returns the database equivalent of the current date function as String depending on the database type
  * 
- * @return {String} sql expression that creates a new UUID
  */
-SqlMaskingUtils.prototype.newUUID = function() 
+SqlMaskingUtils.prototype.currentDate = function()
 {
-    let uuID;
-    switch(this.dbType) 
+    switch(this.dbType)
     {
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            uuID = "UUID()";
-            break;
+        case db.DBTYPE_POSTGRESQL8:
         case db.DBTYPE_ORACLE10_CLUSTER:
         case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-            uuID = "(select regexp_replace(rawtohex(sys_guid()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5') as UUID from dual)";
-            break;
-        case db.DBTYPE_POSTGRESQL8:
-            uuID = "uuid_generate_v4()";//be carefull: to run this fuction the PostgreSQL module "uuid-ossp" has to be implemented
-        case db.DBTYPE_SQLSERVER2000:
-            uuID = "NEWID()";
-            break;
+        case db.DBTYPE_MARIADB10:
         case db.DBTYPE_DERBY10:
-            logging.log(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.uUID"]), logging.ERROR);
-            break;
-        default:
-            throw new Error(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.uUID"]));
-    
+            return "current_date";
+        case db.DBTYPE_MYSQL4:
+            return "current_date()";
+        case db.DBTYPE_SQLSERVER2000:
+            return "getdate()";
     }
-    return uuID;
-}
-
-/**
-* masks the function concat
-* if a sql field is empty no separator will be added
-* note that this function will often create a lot of sql-code
-*
-* @param {Array} pFields fields (or expressions) that should be concatenated
-* @param {String} [pSeparator=space-character] character for separating the fields
-* @param {String} [pAutoTrimFields=true] autoTrimFields if true the expressions are always trimmed, false no change will be applied
-*
-* @return {String} part of SQL-querey
-* 
-* @deprecated The function has been renamed to SqlMaskingUtils.prototype.concatWithSeparator to differentiate it from 
-*             SqlMaskingUtils.prototype.concatenate.
-*/
-SqlMaskingUtils.prototype.concat = function (pFields, pSeparator, pAutoTrimFields) 
-{
-    return this.concatWithSeparator(pFields, pSeparator, pAutoTrimFields);
 }
 
 /**
-     * returns the function for replacing a null value
-     *
-     * @param {String} pField expression that shall be checked for a null value
-     * @param {String} [pReplacement=empty string] expression that shall be used if the field contains null
-     *
-     * @return {string}
-     */
-SqlMaskingUtils.prototype.isNull = function (pField, pReplacement) 
+ * Returns the database equivalent of the fetch first row only function as String depending on the database type
+ * 
+ */
+SqlMaskingUtils.prototype.limit = function(pRowAmount)
 {
-    if (pReplacement == undefined)
-        pReplacement = "''";
-    if (pField instanceof SqlBuilder)
-        pField = "(" + pField.toString() + ")";
-    switch (this.dbType) 
+    switch(this.dbType)
     {
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+            return "LIMIT " + pRowAmount;
         case db.DBTYPE_SQLSERVER2000:
-            return "isnull(" + pField + ", " + pReplacement + ")";
+        return "offset 0 rows fetch first " + pRowAmount + " rows only";
+        case db.DBTYPE_DERBY10:
         case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
         case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_ORACLE10_THIN :
-            return "nvl(" + pField + ", " + pReplacement + ")";
-        case db.DBTYPE_POSTGRESQL8:
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-        default:
-            return "coalesce(" + pField + ", " + pReplacement + ")";
+            return "fetch first " + pRowAmount + " rows only";
     }
 }
 
 /**
- * gets the day from a timestamp
- *
- * @param {String} pField timestamp to get the day from
+ * functions for various Sql-actions
+ * Do not create an instance of this!
  *
- * @return {String} sql expression that extracts the day from a timestamp
+ * @class
+ * @static
  */
-SqlMaskingUtils.prototype.dayFromDate = function (pField)
-{
-    switch (this.dbType)
-    {
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "to_char(" + pField + ",'dd')";
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_SQLSERVER2000:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            return "day(" + pField + ")";
-        case db.DBTYPE_POSTGRESQL8:
-            return "extract (day from " + pField + ")";
-    }
-}
-
-/**
- * gets the month from a timestamp
- *
- * @param {String} pField timestamp to get the month from
- *
- * @return {String} sql expression that extracts the month from a timestamp
- */
-SqlMaskingUtils.prototype.monthFromDate = function (pField)
-{
-    switch (this.dbType)
-    {
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "to_char(" + pField + ",'MM')";
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_SQLSERVER2000:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            return "month(" + pField + ")";
-        case db.DBTYPE_POSTGRESQL8:
-            return "extract (month from " + pField + ")";
-    }
-}
-
-/**
- * gets the year from a timestamp
- *
- * @param {String} pField timestamp to get the year from
- *
- * @return {String} sql expression that extracts the year from a timestamp
- */
-SqlMaskingUtils.prototype.yearFromDate = function(pField)
-{
-    switch (this.dbType)
-    {
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "to_char(" + pField + ",'yyyy')";
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_SQLSERVER2000:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            return "YEAR(" + pField + ")";
-        case db.DBTYPE_POSTGRESQL8:
-            return "EXTRACT (YEAR FROM " + pField + ")";
-    }
-}
-
-
-/**
- * gets the hour from a timestamp
- *
- * @param {String} pField timestamp to get the year from
- *
- * @return {String} sql expression that extracts the hour from a timestamp
- */
-SqlMaskingUtils.prototype.hourFromDate = function(pField)
-{
-    switch (this.dbType)
-    {
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "to_char(" + pField + ",'HH24')";
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            return "HOUR(" + pField + ")";
-        case db.DBTYPE_SQLSERVER2000:
-            return "DATEPART(hour, "+ pField + ")";
-        case db.DBTYPE_POSTGRESQL8:
-            return "EXTRACT (HOUR FROM " + pField + ")";
-    }
-}
-
-
-/**
- * gets the last day of the month from a timestamp
- *
- * @param {String} pField timestamp to get the last day of the month from
- *
- * @return {String} sql expression that extracts the last day of the month from a timestamp
- */
-SqlMaskingUtils.prototype.lastDayOfMonth = function(pField)
-{
-    switch (this.dbType)
-    {
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            return "LAST_DAY(" + pField + ")";
-        case db.DBTYPE_SQLSERVER2000:
-            return "DATEADD(MONTH, DATEDIFF(MONTH, 0 ," + pField + ") + 1, 0) -1";
-        case db.DBTYPE_POSTGRESQL8:
-            return "DATE_TRUNC('month'," + pField + "::date) + interval '1 month' - interval '1 day')::date"; 
-    }
-}
-
-
-/**
- * returns the first field, that is not null or empty
- * masks the behaviour of coalesce with case when
- * 
- * @param pFields {Array} Array of fieldnames. Has to be in the right order. It will be checked from 0 upwards
- */
-SqlMaskingUtils.prototype.coalesce = function(pFields)
-{
-    var retSql = "";
-    
-    if(pFields && typeof pFields == "object" && pFields.length)
-    {
-        retSql = "case ";
-
-        for(let i = 0; i < pFields.length; i++)
-        {
-            retSql += " when (" + pFields[i] + " is not null or " + pFields[i] + " <> '') then " + pFields[i] + " "
-        }
-
-        retSql += " else null end";
-    }
-    else
-        throw {message:"The input to coalesce has to be an Array containing the column names"};
-    
-    return retSql;
-}
-
-/**
- * Converts day, month and year fields into a date
- * The parameters can't be null!!!
- * 
- * @param pYear {String} year field
- * @param pMonth {String} month field
- * @param pDay {String} day field
- */
-SqlMaskingUtils.prototype.makeDate = function(pYear, pMonth, pDay)
-{
-    switch(this.dbType)
-    {
-        case db.DBTYPE_POSTGRESQL8:
-            return "make_date" + "(" + pYear + ", " + pMonth + ", " + pDay + ")";
-        case db.DBTYPE_SQLSERVER2000:
-            return "datefromparts" + "(" + pYear + ", " + pMonth + ", " + pDay + ")";
-
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "to_date(lpad(" + pYear + ", 4, '0') || lpad(" + pMonth + ", 2, '0') || lpad(" + pYear + ", 2, '0'), 'YYYY-MM-DD')";
-
-        // from datestr without filled zeros e.g.: 2000-5-2
-        case db.DBTYPE_MYSQL4:
-        case db.DBTYPE_MARIADB10:
-            var dateFields = [
-                this.cast(pYear, SQLTYPES.CHAR, 4),
-                this.cast(pMonth, SQLTYPES.CHAR, 2),
-                this.cast(pDay, SQLTYPES.CHAR, 2)
-            ];
-            var dateStr = this.concatWithSeparator(dateFields, "-", false);
-            return this.cast(dateStr, SQLTYPES.DATE);
-
-        // from datestr with filled zeros e.g.: 2000-05-02
-        case db.DBTYPE_DERBY10:
-            // maskingUtils: workaround
-            function _castWithZeros(maskingUtils, field, size) {
-                var casted = maskingUtils.cast(field, SQLTYPES.VARCHAR, size);
-                var concatenated = maskingUtils.concatenate(["'000'", casted]);
-                // + 1: derbys substr starts counting at 1, for whatever reason
-                return "substr(" + concatenated + ", length(" + concatenated + ") - " + size + " + 1)";
-            }
-            var dateFieldsZeroed = [
-                _castWithZeros(this, pYear, 4),
-                _castWithZeros(this, pMonth, 2),
-                _castWithZeros(this, pDay, 2)
-            ];
-            var dateStrZeroed = this.concatWithSeparator(dateFieldsZeroed, "-", false);
-            return this.cast(dateStrZeroed, SQLTYPES.DATE);
-    }
-}
-
-/**
- * Returns the database equivalent of the current date function as String depending on the database type
- * 
- */
-SqlMaskingUtils.prototype.currentDate = function()
-{
-    switch(this.dbType)
-    {
-        case db.DBTYPE_POSTGRESQL8:
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-        case db.DBTYPE_MARIADB10:
-        case db.DBTYPE_DERBY10:
-            return "current_date";
-        case db.DBTYPE_MYSQL4:
-            return "current_date()";
-        case db.DBTYPE_SQLSERVER2000:
-            return "getdate()";
-    }
-}
-
-/**
- * Returns the database equivalent of the fetch first row only function as String depending on the database type
- * 
- */
-SqlMaskingUtils.prototype.limit = function(pRowAmount)
-{
-    switch(this.dbType)
-    {
-        case db.DBTYPE_POSTGRESQL8:
-        case db.DBTYPE_MARIADB10:
-        case db.DBTYPE_MYSQL4:
-            return "LIMIT " + pRowAmount;
-        case db.DBTYPE_SQLSERVER2000:
-        return "offset 0 rows fetch first " + pRowAmount + " rows only";
-        case db.DBTYPE_DERBY10:
-        case db.DBTYPE_ORACLE10_CLUSTER:
-        case db.DBTYPE_ORACLE10_THIN:
-        case db.DBTYPE_ORACLE10_OCI:
-            return "fetch first " + pRowAmount + " rows only";
-    }
-}
-
-/**
- * functions for various Sql-actions
- * Do not create an instance of this!
- *
- * @class
- * @static
- */
-function SqlUtils() {}
+function SqlUtils() {}
 
 /**
 * parses given name of table and name of column to clearly find out the tablename and columnanme
@@ -4385,11 +3751,31 @@ SqlUtils._parseFieldQualifier = function(pFieldOrTableName, pColumnName)
 SqlUtils.isFullFieldQualifier = function(pFieldOrTableName, pColumnName)
 {
     var parsed = SqlUtils._parseFieldQualifier(pFieldOrTableName, pColumnName);
-    if (parsed instanceof TypeError)
-        return false;
-    return true;
+    return !(parsed instanceof TypeError);
 };
 
+/**
+ * Checks if the given value has the correct format for a prepared sql array
+ * 
+ * @param {Object|Array} pPreparedArray     value to check
+ * @return {Boolean} if the format is correct
+ */
+SqlUtils.isPreparedSqlArray = function (pPreparedArray)
+{
+    return pPreparedArray.length === 2 && Utils.isString(pPreparedArray[0]) && Array.isArray(pPreparedArray[1]);
+}
+
+/**
+ * Checks if the given value has the correct format for a prepared sql array and contains something
+ * 
+ * @param {Object|Array} pPreparedArray     value to check
+ * @return {Boolean} if the format is correct
+ */
+SqlUtils.isNonEmptyPreparedSqlArray = function (pPreparedArray)
+{
+    return SqlUtils.isPreparedSqlArray(pPreparedArray) && pPreparedArray[0] != "";
+}
+
 /**
 * determines the type of a single database column in a table; if you want to  get several columntypes at once use db.getColumnTypes instead 
 *
@@ -4539,313 +3925,980 @@ SqlUtils._pageData = function(sqlType ,sqlStatement, blockSize, callbackFn, dbAl
         else if (data.length < blockSize || blockSize == 0)//blocksize 0 is everything
             startOffset = -1;//call callback the last time
 
-        if (callbackFn.call(this, data, ++count) === false)
-            return false;//callback can return false to manually stop the paging-process
+        if (callbackFn.call(this, data, ++count) === false)
+            return false;//callback can return false to manually stop the paging-process
+    }
+    return true;
+}
+
+/**
+ *  @return the alias for table asys_binaries
+ */
+SqlUtils.getBinariesAlias = function()
+{
+    return SqlUtils.getSystemAlias();
+}
+
+/**
+ *  @return the sytemalias
+ */
+SqlUtils.getSystemAlias = function()
+{
+    return "_____SYSTEMALIAS";
+}
+
+/**
+ *  @return the dataalias
+ */
+SqlUtils.getDataAlias = function()
+{
+    return "Data_alias";
+}
+
+/**
+     * Builds a SQL IN condition, while accounting for the 1000 elements maximum
+     * Single conditions are concatenated with OR, which can be devastating for performance!
+     *
+     * @param {String} pFieldname req name of the field with table alias
+     *                                z.B ORGREL.CONTACTID
+     * @param {String[]|String[][]} pData req Data as ID Array
+     * @param {String} [pQuoteSymbol=""] symbol for quoting values,
+     *                                  Strings i.e.: ' default is no symbol
+     * @param {Boolean} [pAsPrepared=undefined] true if result should be returned as prepared condition     
+     * @param {Boolean} [pPreparedDbType=undefined] if pAsPrepared is true, this param has to be filld with the correct db type
+     *
+     * @return {String|Array} SQL condition: where VALS in (1,2,3) OR as prepared Statement if pAsPrepared is true ["VALS in (1,2,3)", [...]
+     */
+SqlUtils.getSqlInStatement = function(pFieldname, pData, pQuoteSymbol, pAsPrepared, pPreparedDbType) 
+{
+    var MAX_COUNT = 1000;
+    if (pData.length > 1000)
+        logging.log(translate.text("SqlUtils.getSqlInStatement: WARNING: You should not create in-statements with more than 1000 values. As this has a very bad performance."))
+    
+    if (pData.length == 0)
+        return " 1 = 2 ";
+
+    var res = "";
+    var qs = pQuoteSymbol || "";
+    
+    var preparedValues;
+    if (pAsPrepared)
+    {
+        preparedValues = [];
+        if (!pPreparedDbType)
+            throw new Error(translate.text("SqlUtils.getSqlInStatement: if pAsPrepared is true, pPreparedDbType has to be filld with the correct db type"));
+    }
+
+    //pData.length -1 um für den Fall, dass MAX_COUNT == pData.length ist trotzdem nur einen Aufruf
+    //zu machen
+    var count = ((pData.length -1) / MAX_COUNT) >> 0;//aus kommazahl eine ganzzahl machen
+    //<= verwenden, da bei einer Länge von "126" der Vorgang einmal ausgeführt werden soll
+    for (var i = 0; i <= count; i++)
+    {
+        if (i > 0)
+            res += " or ";
+        
+        if (pAsPrepared) 
+        {
+            res += (pFieldname ? pFieldname + " in " : "") + "(";
+            var subData = pData.slice(i * MAX_COUNT, i * MAX_COUNT + MAX_COUNT);
+            
+            subData.forEach(function(pVal, pIndex) {
+                res += "?";
+                preparedValues.push([pVal, pPreparedDbType])
+                if (pIndex != subData.length-1)
+                    res += ", ";
+            });
+            res += ")"
+        }
+        else
+        {
+            res += (pFieldname ? pFieldname + " in " : "") + "(" + qs + pData.slice(i * MAX_COUNT, i * MAX_COUNT + MAX_COUNT)
+                    .join(qs + ", " + qs) + qs + ") ";
+        }
+    }
+
+    //wenn mehrere Zeilen mit "or" verknüpft wurden nochmal klammern
+    if (count > 0)
+        res = "(" + res + ")";
+
+    if (pAsPrepared)
+        return [res, preparedValues];
+    else
+        return res;
+}
+
+/**
+* resolves key-value pairs (of strings) into a case when expression; 
+* This function tries to get the columntype for better type comparison
+* 
+* @param {String[][]} pKeyValueArray you've to pass a 2D-Array where each element has at pos0 the key and pos1 the value
+* @param {String} pDbFieldName name fo the database field where the KEY-value is stored; prefers TABLENAME.COLUMNNAME
+* @param {String} [pLocale=current client language] specifies the locale for translating the title; can be false if nothing shalle be translated
+* 
+* @return {String} a SQL-expression (case-when-statement) that resolves the KEYID into the title -> as preparedSatement-elements
+*/
+SqlUtils.getResolvingCaseWhen = function(pKeyValueArray, pDbFieldName, pLocale) 
+{
+    var keyData = pKeyValueArray;
+    if (keyData.length == 0)
+        return  ["''", []];
+    
+    //a helper function for easy translation
+    var translateValue = function(value){
+        if (pLocale === false)
+            return value;
+        else if (pLocale)
+            return translate.text(value, pLocale);
+        else
+            return translate.text(value);
+    };
+    //!SqlBuilder
+    var resSql = "case ", preparedValues = [];
+    
+    var colTypeKeyId = SQLTYPES.CHAR; //the standard type is char
+    var fields = SqlUtils._parseFieldQualifier(pDbFieldName); //validate the DB-field for proper form (CONTACT.CONTACTID)
+    if (!(fields instanceof TypeError))
+        colTypeKeyId = SqlUtils.getSingleColumnType(pDbFieldName, undefined, this.alias); 
+     //some databases dont auto cast on their own so we need the proper type
+    
+    var colTypeTitle = SQLTYPES.NVARCHAR;
+    for (var i = 0, l = keyData.length; i < l; i++) 
+    {
+        var translatedTitle = translateValue(keyData[i][1]);
+        resSql += " when " + pDbFieldName + " = ? then ? "
+        preparedValues.push([keyData[i][0], colTypeKeyId]);
+        preparedValues.push([translatedTitle, colTypeTitle]);
+    }
+    resSql += " else '' end ";
+    resSql = [resSql, preparedValues];
+    return resSql;
+};
+
+/**
+* resolves an array of key-value pairs (of strings) into a sql case when expression<br/>
+* This is useful for results of entities.getRows for example.
+* 
+* @param {Array} pKeyValueObject    <p/>you've to pass a 2D-Array where each element has to be an object with at least one key: value-pair, e.g.: 
+*                                   <br/>[{uid: "uid1", value: "value1"}, {uid: "uidN", value: "valueN"}]
+* @param {String} pUid              <p/>name of the key where the rawvalue (the uid) is located in the object
+* @param {String} pTranslatedValue  <p/>name of the key where the already translated value is located in the object
+* @param {String} pDbFieldName      <p/>name fo the database field where the KEY-value is stored
+* 
+* @return {String}                  <p/>a SQL-expression (case-when-statement) that resolves the KEYID into the title -> as 
+*                                       preparedSatement-elements
+*                                   <br/>The else-value is "unassigned".
+* 
+* @example
+* var exampleDataStack = [
+*       {keyVal: "PHONE", titleOriginal: "Phone", titleTranslated: "Telefon", origin: "MetaImporter"},
+*       {keyVal: "EMAIL", titleOriginal: "email", titleTranslated: "E-Mail", origin: "MetaImporter"}
+* ];
+* 
+* var sqlExpr = SqlUtils.getResolvingCaseWhenFromObject(exampleDataStack, "keyVal", "titleTranslated", "FORM.COMMUNICATION");
+* //results in a sql case when as prepared statement that is resolvedas following: 
+* //case  when FORM.COMMUNICATION = 'PHONE'  then 'Telefon'   when FORM.COMMUNICATION = 'EMAIL'  then 'E-Mail'   else 'nicht zugeordnet' end
+*/
+SqlUtils.getResolvingCaseWhenFromObject = function(pKeyValueObject, pUid, pTranslatedValue, pDbFieldName) 
+{
+    var keyData = pKeyValueObject;
+    if (keyData.length == 0)
+        return  ["''", []];
+    
+    var translateValue = pTranslatedValue;
+    var uid = pUid;
+    var unassigned = translate.text("unassigned")
+    
+    var resSql = "case ", preparedValues = [];
+    var colTypeKeyId = SQLTYPES.CHAR;
+    var colTypeTitle = SQLTYPES.NVARCHAR;
+    for (var i = 0, l = keyData.length; i < l; i++) 
+    {
+        var translatedTitle = keyData[i][translateValue];
+        resSql += " when " + pDbFieldName + " = ? then ? "
+        preparedValues.push([keyData[i][pUid], colTypeKeyId]);
+        preparedValues.push([translatedTitle, colTypeTitle]);
+
+    }
+    resSql += " else '"+ unassigned +"' end ";
+    resSql = [resSql, preparedValues];
+    return resSql;
+};
+
+/**
+ * Will quote all prepared statement values from the given statement.
+ * 
+ * @param {PreparedSqlArray} pStatement Same as first paraemter of db.translateStatement.
+ * @param {String} pAlias The database alias
+ * @param {Callback} pExecutionCallback (PreparedSqlArray) => String A function which must return the final SQL.
+ * @return The SQL, same as the result of db.translateStatement.
+ */
+SqlUtils.translateWithQuotes = function(pStatement, pAlias, pExecutionCallback) 
+{
+    // Validate type of incoming paramter.
+    if (!Array.isArray(pStatement))
+        return null;
+
+    // The second element of the array has to be an array.
+    if (!Array.isArray(pStatement[1]))
+        return null;
+
+    // As the second element represents the prepared statements we need to map it...
+    var preparedStatements = pStatement[1].map(function(pValue) 
+    {
+        // Just in case as a fallback value..
+        if (!(Array.isArray(pValue)))
+            return pValue;
+
+        // As the first element represents the value it will be quoted here.
+        if(pAlias)
+        {
+            return [db.quote(pValue[0], pAlias), pValue[1]];
+        }
+        else
+        {
+            return [db.quote(pValue[0]), pValue[1]];
+        }
+    });
+
+    return pExecutionCallback([pStatement[0], preparedStatements]);
+}
+
+/**
+ * Will quote all prepared statement values from the given statement.
+ * 
+ * @param {PreparedSqlArray} pStatement Same as the first parameter of db.translateStatement.
+ * @param {String} [pAlias] the alias which should be used for db.translateStatement()
+ * @returns {String} The SQL, same as the result of db.translateStatement.
+ */
+SqlUtils.translateStatementWithQuotes = function(pStatement, pAlias) 
+{
+    return SqlUtils.translateWithQuotes(pStatement, pAlias, function(pValue) 
+    {
+        if (pAlias)
+            return db.translateStatement(pValue, pAlias)
+        else
+            return db.translateStatement(pValue)
+    });
+}
+
+/**
+ * Will quote all prepared statement values from the given statement.
+ * 
+ * @param {PreparedSqlArray} pStatement Same as the first parameter of db.translateCondition.
+ * @param {String} [pAlias] the alias which should be used for db.translateStatement()
+ * @returns {String} The SQL, same as the result of db.translateCondition.
+ */
+SqlUtils.translateConditionWithQuotes = function(pStatement, pAlias) 
+{
+    return SqlUtils.translateWithQuotes(pStatement, pAlias, function(pValue) 
+    {
+        if (pAlias)
+            return db.translateCondition(pValue, pAlias)
+        else
+            return db.translateCondition(pValue)
+    });
+}
+
+SqlUtils.parseField = function(pField)
+{
+    var alias = "";
+    if (typeof pField === 'string')
+    {
+        var pointPos = pField.indexOf(".");
+
+        if (pointPos > 0 && pointPos < pField.length-1)
+            alias = pField;
+        else
+            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + pField + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [pField]));
+    }
+    else
+    {
+        if (pField.length == 2)
+            pField.push(pField[0]);
+        
+        if (pField.length == 3)
+        {
+            alias = pField[2] + "." + pField[1];
+            pField = pField[0] + "." + pField[1];
+        }
+        else
+            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + field.toSource() + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [field.toSource()]));
+    }
+    return [alias, pField]
+}
+
+SqlUtils.replaceConditionTemplate = function(pCondition, pPlaceholder, pReplacement) 
+{
+    //SqlUtils.replaceConditionTemplate(pCondition, '#', SqlUtils.parseField(pFieldOrCond).join("."))
+    
+    //this function looks more complex (and slower) than it actually is
+    /* the following regex looks like this after javascript-escaping of the backslash: (?<!\\)((?:\\\\)*)#
+    the regexp searches for the unescaped character and these characters are replaced by the field name
+
+    examples:
+    ---------------------
+    | # --match         |
+    | \# --no-match     |
+    | \\# --match       |
+    | \\\# --no-match   |
+    | \\\\# --match     |
+    ---------------------
+    */
+    //use replaceAll because it's faster and supports negative lookbehinds
+    var replacements = {};
+    //manually readd the replaced backslashes by using a group reference, because they a part of the match and therefore replaced by "replaceAll"
+    //since the field COULD contain already a group reference (I think this is extremely uncommon; 
+    //probably that never happens but better stay save): escape that references within the fieldname
+    replacements["(?<!\\\\)((?:\\\\\\\\)*)" + pPlaceholder] = "$1" + text.replaceAll(pReplacement, {
+        "$1": "\\$1"
+    })
+     //now that we've replaced the correct field placeholder let's replace the escaped placeholder sign "\#" to a normal placeholder sign "#"
+    replacements["\\\\" + pPlaceholder] = pPlaceholder
+    
+    return text.replaceAll(pCondition, replacements);
+}
+
+/**
+ * Checks if the '#' is 0 or 1 time in pCondition, '?' has to be 1 time in pCondition.
+ * Also checks if '#' is before '?'
+ * @param {String} pCondition
+ * 
+ * @return {Boolean} true if the format is ok
+ */
+SqlUtils.checkConditionFormat = function(pCondition) 
+{
+    // replace by {@NUMBERSIGN@} / {@QUESTIONSIGN@} as the js-regexp cannot do lookbehind (needed by the regexp used in replaceConditionTemplate to check escapes)
+    // so we just use replaceConditionTemplate to replace by something which never should occur anywhere (it uses text.replaceAll which supports lookbehind because it uses java)
+    pCondition = SqlUtils.replaceConditionTemplate(pCondition, "#", "{@NUMBERSIGN@}")
+    pCondition = SqlUtils.replaceConditionTemplate(pCondition, "\\?", "{@QUESTIONSIGN@}")
+
+    var indexOfNumberSign = pCondition.indexOf("{@NUMBERSIGN@}");
+    var indexOfQuestionSign = pCondition.indexOf("{@QUESTIONSIGN@}");
+    
+    return !(indexOfQuestionSign == -1 || indexOfNumberSign > indexOfQuestionSign || indexOfNumberSign != pCondition.lastIndexOf("{@NUMBERSIGN@}") || indexOfQuestionSign != pCondition.lastIndexOf("{@QUESTIONSIGN@}"))
+}
+
+/**
+ * Escapes a jdito variable for the value of a SqlBuilder condition. SqlBuilder.prototype.where/and/or/... automatically resolve the value as a jdito
+ * variable if it starts with a single '$', so you can use this function to make sure the value is used as it is.
+ * <br>
+ * Note: The main purpose of this is to prevent errors resulting from unexpected user inputs. But if you are loading the input from a jdito variable
+ * anyways, you can just wite the variable name as the condition value and it will be safe.
+ * 
+ * @param {String} pValue the value
+ * @return {String} the escaped string
+ * @example
+ * 
+ * var sqlCond = newWhere("TABLE.COLUMN", SqlUtils.escapeVars(userInput)); //userInput could start with '$'
+ */
+SqlUtils.escapeVars = function (pValue)
+{
+    if (typeof(pValue) == "string" && pValue.charAt(0) == "$")
+        return "$" + pValue;
+    return pValue;
+}
+
+SqlUtils.getSqlConditionalOperator = function(pOperator)
+{
+    switch(parseInt(pOperator))
+    {
+        case 1:
+            return SqlBuilder.EQUAL();
+        case 2:
+            return SqlBuilder.NOT_EQUAL();
+        
+        case 4:
+            return SqlBuilder.LESS();
+        case 5:
+            return SqlBuilder.LESS_OR_EQUAL();
+            
+        case 3:
+            return SqlBuilder.GREATER();
+        case 6:
+            return SqlBuilder.GREATER_OR_EQUAL();
+            
+        case 11:
+            return "# is not null";
+        case 12:
+            return "# is null";
+            
+        default:
+            throw new Error("Unsupported operator " + pOperator);
+    }
+}
+
+/**
+ * Returns the pNullableExpr if pNullableExpr is not null
+ * otherwise it returns pDefaultExpr
+ * 
+ * @param {string|SqlBuilder} pNullableExpr a nullable expression
+ * @param {string|SqlBuilder} pDefaultExpr the default expression if pNullableExpr is null
+ * 
+ * @returns {SqlBuilder._CaseStatement} pNullableExpr with pDefaultExpr as fallback
+ */
+SqlUtils.nullableWithDefault = function(pNullableExpr, pDefaultExpr)
+{
+    return SqlBuilder.caseStatement()
+        .when("(" + pNullableExpr.toString() + ") is null").then(pDefaultExpr)
+        .elseValue(pNullableExpr);
+}
+
+
+
+
+
+
+
+
+
+/**
+ * object for easier handling of conditions;
+ * With this object you do not have to check if the string is empty or not;
+ * you don't need to append a "1=1" condition or similar;
+ * this objects gains most benefit if you have a lot of conditions that are added (or not) depending on tons of JDito-conditions
+ * 
+ * You can also use SqlCondition.begin(alias) for simpler object creation without new and without the need for an extra variable to save the object.
+ * 
+ * @class 
+ * @param {String} [alias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
+ * @example 
+ * see others/guide/HowToSqlConditionLib.adoc
+ * 
+ * @deprecated The SqlCondition will be removed in version >= 2020.x
+ *             Use the SqlBuilder instead.
+ *             For SqlBuilder usage see the documentation-property of the Sql_lib.
+ */
+function SqlCondition(alias) {
+    // setting null is only needed to provide autocomplete for the ADITO-designer
+    this.preparedValues = null;
+    this._init(); // the properties are initalized in an extra function because init is nearly the same as resetting (clearing) the SqlConditions
+    this.alias = alias;
+    
+    // save, if the last condition was an OR. For better bracket-placement
+    this._lastWasOr = false;
+}
+
+/**
+ * Alternative possibility to crate a new condition.
+ * With this you don't need new SqlCondition and you can use the object directly after it's creation
+ * --> cleaner code
+ * 
+ * It is very usefull for the orSqlCondition() and andSqlCondition() because now you can create a new condition inline.
+ * You can also use it for simple selects without the need to save the conditionObject in an extra variable.
+ * See Examples!
+ * 
+ * @param {String} [alias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
+ * @return {SqlCondition} the new SqlCondition-object
+ * 
+ * @example 
+ * vars mySelect = SqlCondition.begin(alias)
+ *                             .and("MYID = '123'")
+ *                             .and(SqlCondition.begin()
+ *                                              .or("NAME = 'Max'")
+ *                                              .or("NAME = 'Bob'")
+ *                              )
+ *                             .buildSql("select * from MYTABLE");
+ *                             
+ * // Or use it for simple selects:
+ * var sum = db.cell(SqlCondition.begin()
+ *                               .andPrepared("STOCK.PRODUCT_ID", pid)
+ *                               .buildSql("select sum(QUANTITY * IN_OUT) from STOCK"));
+ *                               
+ * @deprecated The SqlCondition will be removed in version >= 2020.x
+ *             Use the SqlBuilder instead.
+ *             For SqlBuilder usage see the documentation-property of the Sql_lib."
+ */
+SqlCondition.begin = function(alias) {
+    return new SqlCondition(alias);
+}
+
+/**
+ * checks if conditions have been added to the object
+ * @return {Boolean} true if conditions have been added, false when not
+ */
+SqlCondition.prototype.isSet = function() {
+    if (this._sqlStorage)
+        return true;
+    return false;
+}
+
+
+/**
+ * append with SQL-and; no paranthesize of existing conditions is done
+ * @param {String} cond the condition string which shall be appended
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.and = function(cond) {
+    if (!cond)
+        return this;
+    if (this.isSet())
+        this._sqlStorage += " and ";
+    this._sqlStorage += cond;
+    return this;
+}
+
+/**
+ * append with SQL-or; Also paranthesize the existing conditions
+ * @param {String} cond the condition string which shall be appended
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.or = function(cond) {
+    if (!cond)
+        return this;
+    
+    if (this.isSet() && !this._lastWasOr) {
+        this._sqlStorage = "(" + this._sqlStorage + ") or (" + cond + ")";
+        this._lastWasOr = true;
+        
+    } else if (this.isSet() && this._lastWasOr) {
+        this._sqlStorage = this._sqlStorage + " or (" + cond + ")";
+        this._lastWasOr = true;
+        
+    } else {
+        this._sqlStorage = cond;
+    }
+    return this;
+}
+
+/**
+ * append a prepared-array to this sql condition with SQL-and
+ * @param {Array} preparedObj a prepared condition-array
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.andAttachPrepared = function(preparedObj) {
+    if (preparedObj)
+    {
+        this.preparedValues = this.preparedValues.concat(preparedObj[1]);
+        return this.and(preparedObj[0]);
     }
-    return true;
+    
+    return this;
 }
 
 /**
- *  @return the alias for table asys_binaries
+ * append a prepared-array to this sql condition with SQL-or
+ * @param {Array} preparedObj a prepared condition-array
+ * @return {SqlCondition} current SqlCondition-object
  */
-SqlUtils.getBinariesAlias = function()
-{
-    return SqlUtils.getSystemAlias();
+SqlCondition.prototype.orAttachPrepared = function(preparedObj) {
+    if (preparedObj)
+    {
+        this.preparedValues = this.preparedValues.concat(preparedObj[1]);
+        return this.or(preparedObj[0]);
+    }
+    
+    return this;
 }
 
 /**
- *  @return the sytemalias
+ * append another condition with SQL-and
+ * 
+ * @param {SqlCondition} cond the condition which shall be appended
+ * @param {String} [alternativeCond=""] condition if the given SqlCondition has none
+ * @return {SqlCondition} current SqlCondition-object
  */
-SqlUtils.getSystemAlias = function()
-{
-    return "_____SYSTEMALIAS";
+SqlCondition.prototype.andSqlCondition = function(cond, alternativeCond) {
+    if (!cond)
+        return this
+        
+    var otherCondition = cond.toString(alternativeCond);
+    if (otherCondition.trim() != "")
+    {
+        this.and(" ( " + cond.toString(alternativeCond) + " ) ");
+        if (cond.preparedValues) {
+            this.preparedValues = this.preparedValues.concat(cond.preparedValues);
+        }
+    }
+    return this;
 }
 
 /**
- *  @return the dataalias
+ * append another condition with SQL-or; Also paranthesize the existing conditions
+ * 
+ * @param {SqlCondition} cond the condition which shall be appended
+ * @param {String} [alternativeCond=""] condition if the given SqlCondition has none
+ * @return {SqlCondition} current SqlCondition-object
  */
-SqlUtils.getDataAlias = function()
-{
-    return "Data_alias";
+SqlCondition.prototype.orSqlCondition = function(cond, alternativeCond) {
+    var otherCondition = cond.toString(alternativeCond);
+    if (otherCondition.trim() != "")
+    {
+        this.or(" ( " + cond.toString(alternativeCond) + " ) ");
+        if (cond.preparedValues) {
+            this.preparedValues = this.preparedValues.concat(cond.preparedValues);
+        }
+    }
+    return this;
 }
 
 /**
-     * Builds a SQL IN condition, while accounting for the 1000 elements maximum
-     * Single conditions are concatenated with OR, which can be devastating for performance!
-     *
-     * @param {String} pFieldname req name of the field with table alias
-     *                                z.B ORGREL.CONTACTID
-     * @param {String[]|String[][]} pData req Data as ID Array
-     * @param {String} [pQuoteSymbol=""] symbol for quoting values,
-     *                                  Strings i.e.: ' default is no symbol
-     * @param {Boolean} [pAsPrepared=undefined] true if result should be returned as prepared condition     
-     * @param {Boolean} [pPreparedDbType=undefined] if pAsPrepared is true, this param has to be filld with the correct db type
-     *
-     * @return {String|Array} SQL condition: where VALS in (1,2,3) OR as prepared Statement if pAsPrepared is true ["VALS in (1,2,3)", [...]
-     */
-SqlUtils.getSqlInStatement = function(pFieldname, pData, pQuoteSymbol, pAsPrepared, pPreparedDbType) 
-{
-    var MAX_COUNT = 1000;
-    if (pData.length > 1000)
-        logging.log(translate.text("SqlUtils.getSqlInStatement: WARNING: You should not create in-statements with more than 1000 values. As this has a very bad performance."))
+ * append an condition that uses a subQuery with SQL-and
+ * 
+ * @param {SqlBuilder} subQuery the SqlBuilder object that will be used as a subquery
+ * @param {String} [cond="exists"] condition that is used (e. g. exists, not exists, COLUMN = any, COLUMN in, ...)
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.andSqlBuilder = function(subQuery, cond) {
+    if (!cond)
+        cond = "exists";
     
-    if (pData.length == 0)
-        return " 1 = 2 ";
+    var preparedObj = subQuery.build();
+    preparedObj[0] = cond + " ( " + preparedObj[0] + " ) ";
+    this.andAttachPrepared(preparedObj);
+    
+    return this;
+}
 
-    var res = "";
-    var qs = pQuoteSymbol || "";
+/**
+ * append an condition that uses a subQuery with SQL-or
+ * 
+ * @param {SqlBuilder} subQuery the SqlBuilder object that will be used as a subquery
+ * @param {String} [cond="exists"] condition that is used (e. g. exists, not exists, COLUMN = any, COLUMN in, ...)
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.orSqlBuilder = function(subQuery, cond) {
+    if (!cond)
+        cond = "exists";
     
-    var preparedValues;
-    if (pAsPrepared)
-    {
-        preparedValues = [];
-        if (!pPreparedDbType)
-            throw new Error(translate.text("SqlUtils.getSqlInStatement: if pAsPrepared is true, pPreparedDbType has to be filld with the correct db type"));
-    }
+    var preparedObj = subQuery.build();
+    preparedObj[0] = cond + " ( " + preparedObj[0] + " ) ";
+    this.orAttachPrepared(preparedObj);
+    
+    return this;
+}
 
-    //pData.length -1 um für den Fall, dass MAX_COUNT == pData.length ist trotzdem nur einen Aufruf
-    //zu machen
-    var count = ((pData.length -1) / MAX_COUNT) >> 0;//aus kommazahl eine ganzzahl machen
-    //<= verwenden, da bei einer Länge von "126" der Vorgang einmal ausgeführt werden soll
-    for (var i = 0; i <= count; i++)
-    {
-        if (i > 0)
-            res += " or ";
-        
-        if (pAsPrepared) 
-        {
-            res += (pFieldname ? pFieldname + " in " : "") + "(";
-            var subData = pData.slice(i * MAX_COUNT, i * MAX_COUNT + MAX_COUNT);
-            
-            subData.forEach(function(pVal, pIndex) {
-                res += "?";
-                preparedValues.push([pVal, pPreparedDbType])
-                if (pIndex != subData.length-1)
-                    res += ", ";
-            });
-            res += ")"
-        }
-        else
-        {
-            res += (pFieldname ? pFieldname + " in " : "") + "(" + qs + pData.slice(i * MAX_COUNT, i * MAX_COUNT + MAX_COUNT)
-                    .join(qs + ", " + qs) + qs + ") ";
-        }
-    }
+/**
+ * same as the "and"-function but with preparedStatement functionality
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} value the value that shall be set into the prepared statement
+ * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.andPrepare = function(field, value, cond, fieldType) {
+    cond = this._prepare(field, value, cond, fieldType);
+    return this.and(cond);
+}
 
-    //wenn mehrere Zeilen mit "or" verknüpft wurden nochmal klammern
-    if (count > 0)
-        res = "(" + res + ")";
+/**
+ * same as the "or"-function but with preparedStatement functionality
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} value the value that shall be set into the prepared statement
+ * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.orPrepare = function(field, value, cond, fieldType) {
+    cond = this._prepare(field, value, cond, fieldType);
+    return this.or(cond);
+}
 
-    if (pAsPrepared)
-        return [res, preparedValues];
-    else
-        return res;
+/**
+ * same as the "andPrepare"-function but only applied if the passed "value" is truely
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} value the value that shall be set into the prepared statement
+ * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.andPrepareIfSet = function(field, value, cond, fieldType) {
+    if (value)
+        return this.andPrepare(field, value, cond, fieldType);
+    return this;
+}
+
+/**
+ * same as the "orPrepare"-function but only applied if the passed "value" is truely
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} value the value that shall be set into the prepared statement
+ * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.orPrepareIfSet = function(field, value, cond, fieldType) {
+    if (value)
+        return this.orPrepare(field, value, cond, fieldType);
+    return this;
 }
 
 /**
-* resolves key-value pairs (of strings) into a case when expression; 
-* This function tries to get the columntype for better type comparison
-* 
-* @param {String[][]} pKeyValueArray you've to pass a 2D-Array where each element has at pos0 the key and pos1 the value
-* @param {String} pDbFieldName name fo the database field where the KEY-value is stored; prefers TABLENAME.COLUMNNAME
-* @param {String} [pLocale=current client language] specifies the locale for translating the title; can be false if nothing shalle be translated
-* 
-* @return {String} a SQL-expression (case-when-statement) that resolves the KEYID into the title -> as preparedSatement-elements
-*/
-SqlUtils.getResolvingCaseWhen = function(pKeyValueArray, pDbFieldName, pLocale) 
-{
-    var keyData = pKeyValueArray;
-    if (keyData.length == 0)
-        return  ["''", []];
-    
-    //a helper function for easy translation
-    var translateValue = function(value){
-        if (pLocale === false)
-            return value;
-        else if (pLocale)
-            return translate.text(value, pLocale);
-        else
-            return translate.text(value);
-    };
-    //!SqlBuilder
-    var resSql = "case ", preparedValues = [];
-    
-    var colTypeKeyId = SQLTYPES.CHAR; //the standard type is char
-    var fields = SqlUtils._parseFieldQualifier(pDbFieldName); //validate the DB-field for proper form (CONTACT.CONTACTID)
-    if (!(fields instanceof TypeError))
-        colTypeKeyId = SqlUtils.getSingleColumnType(pDbFieldName, undefined, this.alias); 
-     //some databases dont auto cast on their own so we need the proper type
-    
-    var colTypeTitle = SQLTYPES.NVARCHAR;
-    for (var i = 0, l = keyData.length; i < l; i++) 
-    {
-        var translatedTitle = translateValue(keyData[i][1]);
-        resSql += " when " + pDbFieldName + " = ? then ? "
-        preparedValues.push([keyData[i][0], colTypeKeyId]);
-        preparedValues.push([translatedTitle, colTypeTitle]);
+ * same as the "andPrepare"-function but with validation of adito-variables functionality
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} variable the adito-variable that shall be set into the prepared statement
+ * @param {String} [cond = "# = ?" ] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.andPrepareVars = function(field, variable, cond, fieldType) {
+    variable = this._checkVars(variable)
+    if (variable) {
+        return this.andPrepare(field, variable, cond, fieldType);
     }
-    resSql += " else '' end ";
-    resSql = [resSql, preparedValues];
-    return resSql;
-};
+    return this;
+}
 
 /**
-* resolves an array of key-value pairs (of strings) into a sql case when expression<br/>
-* This is useful for results of entities.getRows for example.
-* 
-* @param {Array} pKeyValueObject    <p/>you've to pass a 2D-Array where each element has to be an object with at least one key: value-pair, e.g.: 
-*                                   <br/>[{uid: "uid1", value: "value1"}, {uid: "uidN", value: "valueN"}]
-* @param {String} pUid              <p/>name of the key where the rawvalue (the uid) is located in the object
-* @param {String} pTranslatedValue  <p/>name of the key where the already translated value is located in the object
-* @param {String} pDbFieldName      <p/>name fo the database field where the KEY-value is stored
-* 
-* @return {String}                  <p/>a SQL-expression (case-when-statement) that resolves the KEYID into the title -> as 
-*                                       preparedSatement-elements
-*                                   <br/>The else-value is "unassigned".
-* 
-* @example
-* var exampleDataStack = [
-*       {keyVal: "PHONE", titleOriginal: "Phone", titleTranslated: "Telefon", origin: "MetaImporter"},
-*       {keyVal: "EMAIL", titleOriginal: "email", titleTranslated: "E-Mail", origin: "MetaImporter"}
-* ];
-* 
-* var sqlExpr = SqlUtils.getResolvingCaseWhenFromObject(exampleDataStack, "keyVal", "titleTranslated", "FORM.COMMUNICATION");
-* //results in a sql case when as prepared statement that is resolvedas following: 
-* //case  when FORM.COMMUNICATION = 'PHONE'  then 'Telefon'   when FORM.COMMUNICATION = 'EMAIL'  then 'E-Mail'   else 'nicht zugeordnet' end
-*/
-SqlUtils.getResolvingCaseWhenFromObject = function(pKeyValueObject, pUid, pTranslatedValue, pDbFieldName) 
-{
-    var keyData = pKeyValueObject;
-    if (keyData.length == 0)
-        return  ["''", []];
-    
-    var translateValue = pTranslatedValue;
-    var uid = pUid;
-    var unassigned = translate.text("unassigned")
-    
-    var resSql = "case ", preparedValues = [];
-    var colTypeKeyId = SQLTYPES.CHAR;
-    var colTypeTitle = SQLTYPES.NVARCHAR;
-    for (var i = 0, l = keyData.length; i < l; i++) 
-    {
-        var translatedTitle = keyData[i][translateValue];
-        resSql += " when " + pDbFieldName + " = ? then ? "
-        preparedValues.push([keyData[i][pUid], colTypeKeyId]);
-        preparedValues.push([translatedTitle, colTypeTitle]);
-
+ * same as the "orPrepare"-function but with validation of adito-variables functionality
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} variable the adito-variable that shall be set into the prepared statement
+ * @param {String} [cond="# = ?"] the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.orPrepareVars = function(field, variable, cond, fieldType) {
+    variable = this._checkVars(variable)
+    if (variable) {
+        return this.orPrepare(field, variable, cond, fieldType);
     }
-    resSql += " else '"+ unassigned +"' end ";
-    resSql = [resSql, preparedValues];
-    return resSql;
-};
+    return this;
+}
 
 /**
- * Will quote all prepared statement values from the given statement.
+ * creates a IN-statement out of a field and an array of values.
+ * Be carefull with a big number of values. This may have a bad performance.
  * 
- * @param {PreparedSqlArray} pStatement Same as first paraemter of db.translateStatement.
- * @param {String} pAlias The database alias
- * @param {Callback} pExecutionCallback (PreparedSqlArray) => String A function which must return the final SQL.
- * @return The SQL, same as the result of db.translateStatement.
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String[]} values the value that shall be set into the prepared statement
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @param {Boolean} [not = undefined] if true, add not before in
+ * @return {SqlCondition} current SqlCondition-object
  */
-SqlUtils.translateWithQuotes = function(pStatement, pAlias, pExecutionCallback) 
-{
-    // Validate type of incoming paramter.
-    if (!Array.isArray(pStatement))
-        return null;
+SqlCondition.prototype.andIn = function(field, values, fieldType, not) {
+    return this.andAttachPrepared(this._in(field, values, fieldType, not));
+}
 
-    // The second element of the array has to be an array.
-    if (!Array.isArray(pStatement[1]))
-        return null;
+/**
+ * creates a IN-statement out of a field and an array of values.
+ * Be carefull with a big number of values. This may have a bad performance.
+ * 
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String[]} values the value that shall be set into the prepared statement
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @param {Boolean} [not = undefined] if true, add not before in
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype.orIn = function(field, values, fieldType, not) {
+    return this.orAttachPrepared(this._in(field, values, fieldType, not));
+}
 
-    // As the second element represents the prepared statements we need to map it...
-    var preparedStatements = pStatement[1].map(function(pValue) 
+/**
+ * creates a IN-statement out of a field and an array of values.
+ * Be carefull with a big number of values. This may have a bad performance.
+ * 
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String[]} values the value that shall be set into the prepared statement
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @param {Boolean} [not = undefined] if true, add not before in
+ * @return {SqlCondition} current SqlCondition-object
+ */
+SqlCondition.prototype._in = function(field, values, fieldType, not) {
+    if (values && values.length > 0)
     {
-        // Just in case as a fallback value..
-        if (!(Array.isArray(pValue)))
-            return pValue;
+        if (fieldType == undefined)
+            fieldType = SqlUtils.getSingleColumnType(field, undefined, this.alias);
+        
+        preparedStatement = SqlUtils.getSqlInStatement(field, values, undefined, true, fieldType);
+        if (not)
+            preparedStatement[0] = " not " + preparedStatement[0];
+        return preparedStatement;
+    }
+    
+    return null;
+}
 
-        // As the first element represents the value it will be quoted here.
-        if(pAlias)
-        {
-            return [db.quote(pValue[0], pAlias), pValue[1]];
-        }
-        else
-        {
-            return [db.quote(pValue[0]), pValue[1]];
-        }
-    });
+/**
+ * ready to use string; does not contain a where keyword at the beginning
+ * @param {String} [alternativeCond=""] condition that is returned when nothing has been appended.
+ * @return {String} concatenated SQL-condition; empty string if nothing has been appended or - if passed - the alternativeCond
+ */
+SqlCondition.prototype.toString = function(alternativeCond) {
+    if (!this.isSet() && alternativeCond)
+        return alternativeCond
+    else
+        return this._sqlStorage;
+}
 
-    return pExecutionCallback([pStatement[0], preparedStatements]);
+/**
+ * ready to use string; does contain a where keyword at the beginning
+ * @param {String} [alternativeCond=""] condition that is returned when nothing has been appended.
+ * @return {SqlCondition} concatenated SQL-condition; empty string if nothing has been appended or - if passed - the alternativeCond
+ */
+SqlCondition.prototype.toWhereString = function(alternativeCond) {
+    var cond = this.toString(alternativeCond);
+    if (cond)
+        return " where " + cond;
+    else 
+        return cond;
+}
+
+/**
+ * ready to use prepared condition; does not contain a where keyword at the beginning
+ * @param {String} [alternativeCond=""] Condition that is returned when nothing has been appended.
+ * @return {Array[][][]} Prepared condition with [condition, [[field1, type1], [field2, type2]]]
+ */
+SqlCondition.prototype.build = function(alternativeCond) {
+    return [this.toString(alternativeCond), this.preparedValues];
+}
+
+/**
+ * ready to use prepared select
+ * @param {String} pBeforeCondition Part of the sql before the condition without where (e.g. "select FIRSTNAME from PERSON")
+ * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
+ * @param {String} [pAfterCondition=""] Part of the sql after the condition (e.g. "order by FIRSTNAME").
+ * @param {Boolean} [pWithWere=true] true if where should be added to the bginning
+ * @return {Array[][][]} Prepared condition with [condition, [[field1, type1], [field2, type2]]]
+ */
+SqlCondition.prototype.buildSql = function(pBeforeCondition, pAlternativeCond, pAfterCondition, pWithWere) {
+    if (pAfterCondition == undefined)
+        pAfterCondition = "";
+    
+    if (pWithWere == undefined) 
+        pWithWere = true;
+    
+    return [pBeforeCondition  + " " + 
+            (pWithWere ? this.toWhereString(pAlternativeCond) : this.toString(pAlternativeCond)) +
+            " " + pAfterCondition, this.preparedValues];
 }
 
 /**
- * Will quote all prepared statement values from the given statement.
- * 
- * @param {PreparedSqlArray} pStatement Same as the first parameter of db.translateStatement.
- * @param {String} [pAlias] the alias which should be used for db.translateStatement()
- * @returns {String} The SQL, same as the result of db.translateStatement.
+ * translates SqlCondition to plain SQL. Use this if prepared statements are not supported.
+ * It resolves all prepared values.
+ * @param {String} pAlternativeCond used if the SqlCondition does not contain any condition.
+ * @return {String} plain SQL condition
  */
-SqlUtils.translateStatementWithQuotes = function(pStatement, pAlias) 
+SqlCondition.prototype.translate = function(pAlternativeCond) 
 {
-    return SqlUtils.translateWithQuotes(pStatement, pAlias, function(pValue) 
-    {
-        if (pAlias)
-            return db.translateStatement(pValue, pAlias)
-        else
-            return db.translateStatement(pValue)
-    });
+    return SqlUtils.translateConditionWithQuotes(this.build(pAlternativeCond, this.alias));
 }
 
 /**
- * Will quote all prepared statement values from the given statement.
+ * Check if (adito-)variable exists and vars.getString is not empty
+ * @param {String} variable the variable name (e.g. "$field.CONTACT_ID")
+ * @return {String | Boolean} The value of the field as string OR false if it doesn't exist.
  * 
- * @param {PreparedSqlArray} pStatement Same as the first parameter of db.translateCondition.
- * @param {String} [pAlias] the alias which should be used for db.translateStatement()
- * @returns {String} The SQL, same as the result of db.translateCondition.
+ * @ignore
  */
-SqlUtils.translateConditionWithQuotes = function(pStatement, pAlias) 
-{
-    return SqlUtils.translateWithQuotes(pStatement, pAlias, function(pValue) 
-    {
-        if (pAlias)
-            return db.translateCondition(pValue, pAlias)
-        else
-            return db.translateCondition(pValue)
-    });
+SqlCondition.prototype._checkVars = function(variable) {
+    if (vars.exists(variable)) {
+        var value = vars.getString(variable);
+        if (value) {
+            return value;
+        }
+    }
+    return false;
 }
 
-SqlUtils.parseField = function(pField)
-{
-    var alias = "";
-    if (typeof pField === 'string')
+/**
+ * hidden function for composing preparedStatements
+ * @param {String | String[]} field the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME" or as array with column-alias: ["ORGANISATION", "NAME", "myorgAlias"]
+ * @param {String} value the value that shall be set into the prepared statement
+ * @param {String} cond the strucutre of the SQL condition as preparedString, you can use a number sign "#" as placeholder for you fieldname; 
+ *                 e.g. "# > ?"; escaping the number sign is possible with a backslash "\"
+ *                 Default is "# = ?" 
+ * @param {Numeric | Boolean} [fieldType] SQL-column-type; if the fieldType is not given it's loaded automatically;
+ *                              The loaded type is cached if no type is given. So it is also safe to use this in a loop.
+ *                              e.g.
+ *                              for (...) {
+ *                                  cond.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry, "# <> ?")
+ *                              }
+ * @return {String} the replaced SQL-condition string (replace # by the fieldname)
+ * @ignore
+ */
+SqlCondition.prototype._prepare = function(field, value, cond, fieldType) {
+    if (value == undefined)
     {
-        var pointPos = pField.indexOf(".");
+        throw new Error(translate.withArguments("${SQL_LIB_UNDEFINED_VALUE} field: %0", [field]));
+    }
+    
+    if (cond == undefined) {
+        cond = "# = ?"
+    }
 
-        if (pointPos > 0 && pointPos < pField.length-1)
-            alias = pField;
+    var alias;
+    
+    if (typeof field === 'string')
+    {
+        var pointPos = field.indexOf(".");
+        
+        if (pointPos > 0 && pointPos < field.length-1)
+        {
+            alias = field;
+        }
         else
-            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + pField + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [pField]));
+        {
+            throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + field + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [field]));
+        }
     }
     else
     {
-        if (pField.length == 2)
-            pField.push(pField[0]);
-        
-        if (pField.length == 3)
+        if (field.length == 3)
         {
-            alias = pField[2] + "." + pField[1];
-            pField = pField[0] + "." + pField[1];
+            alias = field[2] + "." + field[1];
+            field = field[0] + "." + field[1];
         }
         else
+        {
             throw new Error(translate.text("${SQL_LIB_FIELD_WRONG_FORMAT}") + field.toSource() + translate.withArguments("${SQL_LIB_FIELD_WRONG_FORMAT} field: %0", [field.toSource()]));
+        }
     }
-    return [alias, pField]
-}
-
-SqlUtils.replaceConditionTemplate = function(pCondition, pPlaceholder, pReplacement) 
-{
-    //SqlUtils.replaceConditionTemplate(pCondition, '#', SqlUtils.parseField(pFieldOrCond).join("."))
     
+    var type;
+    
+    if (fieldType == undefined)
+        fieldType = SqlUtils.getSingleColumnType(field, undefined, this.alias);
+
     //this function looks more complex (and slower) than it actually is
     /* the following regex looks like this after javascript-escaping of the backslash: (?<!\\)((?:\\\\)*)#
     the regexp searches for the unescaped character and these characters are replaced by the field name
@@ -4860,101 +4913,75 @@ SqlUtils.replaceConditionTemplate = function(pCondition, pPlaceholder, pReplacem
     ---------------------
     */
     //use replaceAll because it's faster and supports negative lookbehinds
-    var replacements = {};
-    //manually readd the replaced backslashes by using a group reference, because they a part of the match and therefore replaced by "replaceAll"
-    //since the field COULD contain already a group reference (I think this is extremely uncommon; 
-    //probably that never happens but better stay save): escape that references within the fieldname
-    replacements["(?<!\\\\)((?:\\\\\\\\)*)" + pPlaceholder] = "$1" + text.replaceAll(pReplacement, {
-        "$1": "\\$1"
-    })
-     //now that we've replaced the correct field placeholder let's replace the escaped placeholder sign "\#" to a normal placeholder sign "#"
-    replacements["\\\\" + pPlaceholder] = pPlaceholder
+    cond = text.replaceAll(cond, {
+        //manually readd the replaced backslashes by using a group reference, because they a part of the match and therefore replaced by "replaceAll"
+        //since the field COULD contain already a group reference (I think this is extremely uncommon; 
+        //probably that never happens but better stay save): escape that references within the fieldname
+        "(?<!\\\\)((?:\\\\\\\\)*)#": "$1" + text.replaceAll(alias, {
+            "$1": "\\$1"
+        }),
+        //now that we've replaced the correct field placeholder let's replace the escaped number sign "\#" to a normal number sign "#"
+        "\\\\#": "#"
+    });
     
-    return text.replaceAll(pCondition, replacements);
+    
+    
+    type = fieldType
+    this.preparedValues.push([value.toString(), type]);
+    return cond;
 }
 
+
 /**
- * Checks if the '#' is 0 or 1 time in pCondition, '?' has to be 1 time in pCondition.
- * Also checks if '#' is before '?'
- * @param {String} pCondition
- * 
- * @return {Boolean} true if the format is ok
+ * function that resets the current SqlCondition as if no conditions would have been added
+ * this is usefull if you want to reuse the same object over and over
+ * @return {null} 
  */
-SqlUtils.checkConditionFormat = function(pCondition) 
-{
-    // replace by {@NUMBERSIGN@} / {@QUESTIONSIGN@} as the js-regexp cannot do lookbehind (needed by the regexp used in replaceConditionTemplate to check escapes)
-    // so we just use replaceConditionTemplate to replace by something which never should occur anywhere (it uses text.replaceAll which supports lookbehind because it uses java)
-    pCondition = SqlUtils.replaceConditionTemplate(pCondition, "#", "{@NUMBERSIGN@}")
-    pCondition = SqlUtils.replaceConditionTemplate(pCondition, "\\?", "{@QUESTIONSIGN@}")
-
-    var indexOfNumberSign = pCondition.indexOf("{@NUMBERSIGN@}");
-    var indexOfQuestionSign = pCondition.indexOf("{@QUESTIONSIGN@}");
-    
-    return !(indexOfQuestionSign == -1 || indexOfNumberSign > indexOfQuestionSign || indexOfNumberSign != pCondition.lastIndexOf("{@NUMBERSIGN@}") || indexOfQuestionSign != pCondition.lastIndexOf("{@QUESTIONSIGN@}"))
+SqlCondition.prototype.clear = function() {
+    this._sqlStorage = "";
+    this.preparedValues = [];
+    return this;
 }
 
 /**
- * Escapes a jdito variable for the value of a SqlBuilder condition. SqlBuilder.prototype.where/and/or/... automatically resolve the value as a jdito
- * variable if it starts with a single '$', so you can use this function to make sure the value is used as it is.
- * <br>
- * Note: The main purpose of this is to prevent errors resulting from unexpected user inputs. But if you are loading the input from a jdito variable
- * anyways, you can just wite the variable name as the condition value and it will be safe.
- * 
- * @param {String} pValue the value
- * @return {String} the escaped string
- * @example
+ * hidden function for initializing all properties for the sql conditions
+ * @return {null} 
  * 
- * var sqlCond = newWhere("TABLE.COLUMN", SqlUtils.escapeVars(userInput)); //userInput could start with '$'
+ * @ignore
  */
-SqlUtils.escapeVars = function (pValue)
-{
-    if (typeof(pValue) == "string" && pValue.charAt(0) == "$")
-        return "$" + pValue;
-    return pValue;
+SqlCondition.prototype._init = function() {
+    //init only wraps the clear function to avoid confusion in the constructor (and provide better extensibility)
+    return this.clear();
 }
 
-SqlUtils.getSqlConditionalOperator = function(pOperator)
-{
-    switch(parseInt(pOperator))
-    {
-        case 1:
-            return SqlBuilder.EQUAL();
-        case 2:
-            return SqlBuilder.NOT_EQUAL();
-        
-        case 4:
-            return SqlBuilder.LESS();
-        case 5:
-            return SqlBuilder.LESS_OR_EQUAL();
-            
-        case 3:
-            return SqlBuilder.GREATER();
-        case 6:
-            return SqlBuilder.GREATER_OR_EQUAL();
-            
-        case 11:
-            return "# is not null";
-        case 12:
-            return "# is null";
-            
-        default:
-            throw new Error("Unsupported operator " + pOperator);
-    }
-}
+// some static functions for often used tasks. They are only provided for very simple tasks.
 
 /**
- * Returns the pNullableExpr if pNullableExpr is not null
- * otherwise it returns pDefaultExpr
+ * pField = pValue
+ * @param {String} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME"
+ * @param {String} pValue the value that shall be set into the prepared statement
+ * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
+ * @param {String} [pAlias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
  * 
- * @param {string|SqlBuilder} pNullableExpr a nullable expression
- * @param {string|SqlBuilder} pDefaultExpr the default expression if pNullableExpr is null
+ * @return {Array[][][]} Prepared condition with [condition, [[field, type]]]
  * 
- * @returns {SqlBuilder._CaseStatement} pNullableExpr with pDefaultExpr as fallback
+ * @deprecated
  */
-SqlUtils.nullableWithDefault = function(pNullableExpr, pDefaultExpr)
-{
-    return SqlBuilder.caseStatement()
-        .when("(" + pNullableExpr.toString() + ") is null").then(pDefaultExpr)
-        .elseValue(pNullableExpr);
+SqlCondition.equals = function(pField, pValue, pAlternativeCond, pAlias) {
+    return SqlCondition["begin"](pAlias).andPrepare(pField, pValue).build(pAlternativeCond);
 }
 
+/**
+ * pField <> pValue
+ * @param {String} pField the database field as "tablename.columnname"; e.g. "ORGANISATION.NAME"
+ * @param {String} pValue the value that shall be set into the prepared statement
+ * @param {String} [pAlternativeCond=""] Condition that is returned when nothing has been appended.
+ * @param {String} [pAlias=the current alias] the database alias where the condition shall be executed later (important for column types of preparedStatements)
+ * 
+ * @return {Array[][][]} Prepared condition with [condition, [[field, type]]]
+ * 
+ * @deprecated
+ */
+SqlCondition.equalsNot = function(pField, pValue, pAlternativeCond, pAlias) {
+    return SqlCondition["begin"](pAlias).andPrepare(pField, pValue, "# <> ?").build(pAlternativeCond);
+}
\ No newline at end of file
diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js
index 3ebc86d452..42c2763d2e 100644
--- a/process/Util_lib/process.js
+++ b/process/Util_lib/process.js
@@ -1138,6 +1138,33 @@ ArrayUtils.concatNonEmptyElements = function(pElement1, pElementN)
     return res;
 };
 
+/**
+ * Splits the elements of an array into chunks with a defined length
+ * 
+ * @param {Array} pArray        array to split
+ * @param {Number} pChunkSize   desired length of the sub-arrays
+ * @return {Array} array of chunked arrays
+ * @example
+ * 
+ * var arr = [1,2,3,4,5,6,7,8,9,10,11];
+ * var chunks = ArrayUtils.chunk(arr, 4);
+ * logging.log(JSON.stringify(chunks)); //result: [[1,2,3,4],[5,6,7,8],[9,10,11]]
+ */
+ArrayUtils.chunk = function (pArray, pChunkSize)
+{
+    var chunkSize = Number(pChunkSize);
+    if (chunkSize <= 0)
+    {
+        return [];
+    }
+    var chunked = [];
+    for (let i = 0; i < pArray.length;)
+    {
+        chunked.push(pArray.slice(i, i += chunkSize));
+    }
+    return chunked;
+}
+
 /**
  * Class containing utility functions for use with JSON
  * @class
-- 
GitLab