From d9250f827785ea0958e99255ffe6f3764fafa1d8 Mon Sep 17 00:00:00 2001
From: Johannes Hoermann <j.hoermann@adito.de>
Date: Wed, 23 Oct 2019 14:20:36 +0200
Subject: [PATCH] SqlLib Dokumentation & remove old SqlCondition docu

---
 others/guide/HowToSqlBuilder.adoc      |  48 ------
 others/guide/HowToSqlConditionLib.adoc | 229 -------------------------
 process/Sql_lib/documentation.adoc     |   8 +-
 process/Sql_lib/process.js             |  61 ++++++-
 4 files changed, 58 insertions(+), 288 deletions(-)
 delete mode 100644 others/guide/HowToSqlBuilder.adoc
 delete mode 100644 others/guide/HowToSqlConditionLib.adoc

diff --git a/others/guide/HowToSqlBuilder.adoc b/others/guide/HowToSqlBuilder.adoc
deleted file mode 100644
index b0abf23041..0000000000
--- a/others/guide/HowToSqlBuilder.adoc
+++ /dev/null
@@ -1,48 +0,0 @@
-How to use the SqlBuilder (state: 10.07.2019)
-==============================================
-:toc2: left
-:numbered:
-
-
-== What is SqlBuilder? ==
-A SqlBuilder object helps to build sql queries.
-
-== Why would I need that? ==
-SqlBuilder makes it easier to build complex queries while maintaining clean code,
-especially if you want to append sql parts dynamically. With SqlBuilder you can also use SqlCondition
-objects in many parts of the query at the same time, for example in join, where or having clauses.
-
-== How to use it ==
-* import the lib:
-[source,javascript]
-----
-import("Sql_lib");
-----
-* create an object ()
-[source,javascript]
-----
-var myDescriptiveNameOfTheQuery = new SqlBuilder();
-// or
-var myDescriptiveNameOfTheQuery = SqlBuilder.begin();
-----
-* use the object, set clauses
-[source,javascript]
-----
-myDescriptiveNameOfTheQuery.select(["PERSON.FIRSTNAME", "PERSON.LASTNAME"]) //you can use an array or a string here
-    .from("PERSON")
-    .join("CONTACT", SqlCondition.begin()
-        .and("CONTACT.PERSON_ID = PERSON.PERSONID"))
-    .where(SqlCondition.begin()
-        .andPrepare("CONTACT.CONTACTID", contactId)
-        .build("1=1")); 
-----
-* Using .build on the SqlCondition is optional, but you could use it to set the alternative condition.
-* Before using the sql, call .build():
-[source,javascript]
-----
-var names = db.table(myDescriptiveNameOfTheQuery.build());
-----
-
-== available methods ==
-see the comments and documentation in the lib
-
diff --git a/others/guide/HowToSqlConditionLib.adoc b/others/guide/HowToSqlConditionLib.adoc
deleted file mode 100644
index df7adebc5d..0000000000
--- a/others/guide/HowToSqlConditionLib.adoc
+++ /dev/null
@@ -1,229 +0,0 @@
-How to use the SqlCondition (state: 06.12.2018)
-===============================================
-:toc2: left
-:numbered:
-
-(This lib is work in progress and may change in the future.)
-
-== What is the SqlCondition ==
-It is a lib which helps to create SQL statements and especially simplifies prepared Statements.
-
-== When should prepared statements be used ==
-If possible ALWAYS. 
-Prepared statements improve the application security significantly.
-See also AID068-DE - SQL Injections
-
-== basics ==
-* import the lib:
-[source,javascript]
-----
-import("Sql_lib");
-----
-* create an object (alias is optional)
-[source,javascript]
-----
-var myDescriptiveNameOfTheCondition = new SqlCondition(alias);
-// or
-var myDescriptiveNameOfTheCondition = SqlCondition.begin(alias);
-----
-* use the object, add conditions
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.orPrepare("PERSON.FIRSTNAME", "Bob") 
-                               .orPrepare("PERSON.LASTNAME", "Meier"); // orPrepare explained in "available methods" below
-----
-* You can also use table aliases, if you provide the table, column and alias as array.
-  The table name has to be provided for loading the data types but the alias is built into the sql.
-  You need this, if you need to call a table by the alias name instead of the full table name.
-  This is especially useful if you build a condition for joins. (See example at the end).
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.orPrepare(["PERSON", "FIRSTNAME", "bob"], "Bob")
-                               .orPrepare(["PERSON", "LASTNAME", "bob"], "Meier");
-----
-* build sql
-[source,javascript]
-----
-var myPreparedSelect = myDescriptiveNameOfTheCondition.buildSql("select PERSONID from PERSON", "1 = 2", "order by FIRSTNAME");
-----
-* use the select
-[source,javascript]
-----
-var bobsId = db.cell(myPreparedSelect);
-----
-
-You can do everything in one command also:
-[source,javascript]
-----
-var bobsId = db.cell(SqlCondition.begin(alias)
-                .orPrepare("PERSON.FIRSTNAME", "Bob")
-                .orPrepare("PERSON.LASTNAME", "Meier");
-                .buildSql("select PERSONID from PERSON"));
-----
-
-== available methods ==
-This is only a simple overview.
-for more information see the comments and documentation in the lib!
-
-=== and / or ===
-Adds a condition. Doesn't use prepared Statements!!
-!!Please prefer the prepared version!!
-
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.or("FIRSTNAME = 'Bob'");
-----
-
-=== andPrepared / orPrepared ===
-Same as and / or but uses prepared statements.
-The field name needs to be given with 
-[TABLENAME].[COLUMNNAME]
-
-Or if you use only COLUMNNAME, you have to provide the fieldType.
-
-But keep in mind: Other than the original adito db. functions this lib caches the field types automatically (for the livetime of an SqlCondition Object).
-So most times it is save to use the first method. Even in loops or if you use the same column several times.
-
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.orPrepared("PERSON.FIRSTNAME", 'Bob', "#<?"); // #(field) < ?(value)
-----
-
-=== andPreparedVars / orPreparedVars ===
-Same as andPrepared / orPrepared but you can provide a jdito-variable instead of a value.
-If this variable doesn't exist or is empty, no condition is added.
-
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.andPrepareVars("COMM.RELATION_ID", "$param.RelId_param");
-----
-
-=== andSqlCondition / orSqlCondition ===
-adds another SqlCondition object
-
-[source,javascript]
-----
-myDescriptiveNameOfTheCondition.andSqlCondition(SqlCondition.begin()
-                        .orPrepare("PRODUCTPRICE.VALID_TO", today, "# >= ?")
-                        .or("PRODUCTPRICE.VALID_TO is null"), "1 = 2");
-----
-
-=== toString ===
-Returns the condition as SQL string with ? for each  prepared value.
-You can provide a default condition if it is empty.
-
-[source,javascript]
-----
-var myConditionSql = myDescriptiveNameOfTheCondition.toString("1=0");
-----
-
-=== toWhereString ===
-same as toString, but appends a where in front of it
-
-[source,javascript]
-----
-var myConditionSql = myDescriptiveNameOfTheCondition.toWhereString("1=0");
-----
-
-=== preparedValues ===
-This is no method but a member variable.
-It contains the array with the prepared values.
-
-[source,javascript]
-----
-var myValues = myDescriptiveNameOfTheCondition.preparedValues;
-----
-
-=== build ===
-Combines toString with the prepared values just like Adito needs it.
-
-[source,javascript]
-----
-var myPreparedStatementArray = myDescriptiveNameOfTheCondition.build("1=0");
-----
-
-=== buildSql ===
-Same as build and adds a string before and after the condition.
-
-[source,javascript]
-----
-var myPreparedStatementArray = myDescriptiveNameOfTheCondition.buildSql("select * from PERSON", "1=0", "order by FIRSTNAME");
-----
-
-== real world example ==
-Some examples, how this lib can be used.
-
-[source,javascript]
-----
-var productPriceData = (db.ROW, db.array(SqlCondition.begin()
-                    .andPrepare("PRODUCTPRICE.BUYSELL", buySell)
-                    .andPrepare("PRODUCTPRICE.PRODUCT_ID", pid)
-                    .andPrepare("PRODUCTPRICE.VALID_FROM", today, "# <= ?")
-                    .andSqlCondition(SqlCondition.begin()
-                        .orPrepare("PRODUCTPRICE.VALID_TO", today, "# >= ?")
-                        .or("PRODUCTPRICE.VALID_TO is null"), "1 = 2");
-                    .buildSql("select PRICE, CURRENCY from PRODUCTPRICE", "1 = 2", "order by VALID_FROM desc")));
-----
-
-With a loop:
-[source,javascript]
-----
-function _getClassificationCondition(pInclude) 
-{
-    var resultingCondition = new SqlCondition();
-    resultingCondition.andPrepare("SALESPROJECT_CLASSIFICATION.SALESPROJECT_ID", salesprojectId)
-        .andPrepare("SALESPROJECT_CLASSIFICATION.CLASS", classId);
-
-    var typeCondition = new SqlCondition();
-
-    entryKeywords.forEach(function(entry) 
-    {
-        if (pInclude)
-        {
-            typeCondition.orPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry[0], "# = ?");
-        }
-        else
-        {
-            typeCondition.andPrepare("SALESPROJECT_CLASSIFICATION.TYPE", entry[0], "# <> ?");
-        }
-    });
-    
-    resultingCondition.andSqlCondition(typeCondition, "1 = 0");
-    return resultingCondition;
-}
-----
-
-Condition as join is also possible. It is also an example on how to use aliases:
-[source,javascript]
-----
-joins.push("left join PRODUCTPRICE validPP on " 
-                    + db.translateCondition(SqlCondition.begin()
-                               .and("validPP.PRODUCT_ID = PRODUCTID")
-                               .andPrepare(["PRODUCTPRICE", "CURRENCY", "validPP"], priceListFilter.currency)
-                               .andPrepare(["PRODUCTPRICE", "VALID_FROM", "validPP"], datetime.date().toString(), "# <= ?")
-                               .andPrepare(["PRODUCTPRICE", "FROMQUANTITY", "validPP"], priceListFilter.quantity, "# <= ?")
-                               .andSqlCondition(SqlCondition.begin()
-                                    .orPrepare(["PRODUCTPRICE", "RELATION_ID", "validPP"], priceListFilter.relationId, "# <= ?")
-                                    .orSqlCondition(SqlCondition.begin()
-                                        .and("validPP.RELATION_ID is null")
-                                        .andPrepare(["PRODUCTPRICE", "BUYSELL", "validPP"], 'SP'), 
-                                    "1 = 2"), 
-                                "1 = 2")
-                        .build("1 = 2")))
-----
-
-== simple wrapper ==
-There are a few wrapper for often used conditions.
-They return an already built prepared condition.
-
-equals
-[source,javascript]
-----
-db.updateData("OFFER", cols, null, offerItems.getNetAndVat(), SqlCondition.equals("OFFER.OFFERID", oid, "1 = 2"));
-----
-
-equalsNot
-[source,javascript]
-----
-db.updateData("OFFER", cols, null, offerItems.getNetAndVat(), SqlCondition.equalsNot("OFFER.OFFERID", oid, "1 = 2"));
-----
\ No newline at end of file
diff --git a/process/Sql_lib/documentation.adoc b/process/Sql_lib/documentation.adoc
index c02447e967..4071937329 100644
--- a/process/Sql_lib/documentation.adoc
+++ b/process/Sql_lib/documentation.adoc
@@ -146,6 +146,7 @@ If used as pField, you can provide the table and collumn in different ways.
 * as string: "TABLENAME.COLUMNNAME" (this only works if you have no '.' in your names and if you don't need a table alias
 * as array: ["TABLENAME", "COLUMNNAME"]
 * as array with tableAlias: ["TABLENAME", "COLUMNNAME", "tableAlias"] Here the TABLENAME is used to load the SQLTYPE and tableAlias is used for generating the sql.
+* as SqlBuilder containing a full subselect. -> pFieldType and pValue have to be set also if you are using this variant.
 
 * pValue
 This is the value for the condition: 
@@ -196,8 +197,8 @@ The following examples are all perfectly valid:
 * just an array of strings: ["CAMPAIGNCOSTID", "CAMPAIGNSTEP_ID", "CAMPAIGNSTEP.NAME", "CATEGORY", "NET"]
 * additional keywords like distinct: "distinct CAMPAIGNCOSTID, CAMPAIGNSTEP_ID, CAMPAIGNSTEP.NAME, CATEGORY, NET"
 * array and additional keyword: ["distinct CAMPAIGNCOSTID", "CAMPAIGNSTEP_ID", "CAMPAIGNSTEP.NAME", "CATEGORY", "NET"]
-* subselect: newSelect("ORGANISATION.\"NAME\").from("ORGANISATION").where("ORGANISATION.CONTACT_ID = CONTACT.CONTACTID")
-* array with strings and subselects: ["CAMPAIGNCOSTID", "CAMPAIGNSTEP_ID", "CAMPAIGNSTEP.NAME", "CATEGORY", "NET", newSelect("ORGANISATION.\"NAME\").from("ORGANISATION").where("ORGANISATION.CONTACT_ID = CONTACT.CONTACTID")]
+* subselect: newSelect("ORGANISATION.\"NAME\").from("ORGANISATION").where("ORGANISATION.CONTACT_ID = CONTACT.CONTACTID").subselectAlias("orgname")
+* array with strings and subselects: ["CAMPAIGNCOSTID", "CAMPAIGNSTEP_ID", "CAMPAIGNSTEP.NAME", "CATEGORY", "NET", newSelect("ORGANISATION.\"NAME\").from("ORGANISATION").where("ORGANISATION.CONTACT_ID = CONTACT.CONTACTID").subselectAlias("orgname")]
 
 === from
 from(pTable, pTableAlias) adds a from-part to the sql.
@@ -597,7 +598,8 @@ Only use this if you really need a string. Use .build() if you can use prepared
 
 === Recommended steps for conversion
 
-Search for "SqlConditon" for each find:
+Search for "SqlConditon".
+For each find:
 
 * See if it is used as condition or if there is somewhere a .buildSql with a full select.
 * If it's a condition you may use newWhere(...) / newWhereIfSet(...).
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index 0daa4583b7..d46cfdfb0e 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -21,6 +21,14 @@ import("Util_lib");
  *                                          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)
 {
@@ -48,6 +56,26 @@ function newSelect(pFields, pAlias)
  *                                                                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());
+ * 
+ * //////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 newWhere(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
 {
@@ -57,7 +85,7 @@ function newWhere(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
 /** 
  * 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 .whereIfSet again. You can directly write .and / .or after newWhere().
+ * 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/>
@@ -75,6 +103,26 @@ function newWhere(pFieldOrCond, pValue, pCondition, pFieldType, pAlias)
  *                                                                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)
 {
@@ -303,7 +351,6 @@ SqlBuilder.prototype.from = function(pTable, pTableAlias)
 
 /**
  * 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/>
@@ -699,6 +746,7 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon
     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())
@@ -726,7 +774,7 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon
         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
+    // 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;
@@ -746,10 +794,7 @@ SqlBuilder.prototype._addWhere = function(pFieldOrCond, pValue, pMandatory, pCon
                     
                     return this;
                 }
-                
-                if (!pCondition)
-                    pCondition = "# in ?";
-                
+                               
                 // if it is null -> ignore it. -> the pCondition should not contain a # in this case
                 if (field != null)
                 {
@@ -2775,7 +2820,7 @@ SqlUtils.replaceConditionTemplate = function(pCondition, pPlaceholder, pReplacem
 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
+    // 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@}")
 
-- 
GitLab