Please read the documentation in the Sql_lib itelsf if something is unclear.
== Why
=== Prepared Statements
Prepared Statements should always be used because of
* Security (Sql injection): If you are used to something like
`"select * from PERSON where PERSONID = '" + persId + "'"`
you *WILL* forget to use prepared statements if it is really nessecary:
`"select * from PERSON where FIRSTNAME = '" + userInput + "'"`
Here the user could input also any Sql.
If you are used to Prepared Statements even if you just select for ID's, the security and quality of your code will be much higher.
* Speed: If the server executes the prepared statement, it may improve the performance compared to executing the sql-string.
For me the most important aspect is the security.
=== Problem
Prepared Statements in JDito are not very easy to use, because you have to load the datatypes for the columns manually and create an array like this:
`["select * from PERSON where FIRSTNAME = ?", \[[userinput, SQLTYPES.VARCHAR]]]`
=== Solution
The SqlBuilder (former SqlCondition) provides methods to add conditions without manually loading the type.
So this:
[source,js]
----
var type = db.getColumnTypes("PERSON", ["FIRSTNAME"])[0]
var persons = db.table(["select FIRSTNAME, LASTNAME from PERSON where FIRSTNAME = ?", [[usereInput, type]]]);
----
becomes this:
[source,js]
----
var persons = newSelect("FIRSTNAME, LASTNAME")
.from("PERSON")
.where("PERSON.FIRSTNAME", userInput)
.table()
----
which is much cleaner code.
== Basic usage
=== new SqlBuilder creation
Basically you can create a new builder by
[source,js]
----
var personSelect = new SqlBuilder()
.select("FIRSTNAME, LASTNAME")
.from("PERSON")
.where("PERSON.PERSONID", persId)
----
or with the shorthand function newSelect()
[source,js]
----
var personSelect = newSelect("FIRSTNAME, LASTNAME")
.from("PERSON")
.where("PERSON.PERSONID", persId)
----
It is also possible to only use the builder for *conditions*:
[source,js]
----
var personCond = new SqlBuilder()
.where("PERSON.PERSONID", persId)
----
or with the shorthand function newWhere()
[source,js]
----
var personCond = newWhere("PERSON.PERSONID", persId)
----
=== Conditions
For the conditions we have the methods:
* where
* and
* or
and for each of them also a version with *IfSet: (see 2.2.3 for the difference)
* whereIfSet
* andIfSet
* orIfSet
==== where
Has to be always used for the first condition.
This is mainly for a better semantic:
[source,js]
----
new SqlCondition.select("COL1")
.from("TAB1")
.where("TAB1.COL2", "myVal")
.and("TAB1.COL3", "myVal")
----
looks better and more intuitive than:
[source,js]
----
new SqlCondition.select("COL1")
.from("TAB1")
.and("TAB1.COL2", "myVal")
.and("TAB1.COL3", "myVal")
----
-> if you add a condition, the where keyword is mandatory.
newWhere() already includes the where. So this is perfectly valid:
[source,js]
----
newWhere("TAB1.COL2", "myVal")
.and("TAB1.COL3", "myVal")
----
It is also valid to use .where without parameters. So something like this is possible:
[source,js]
----
var cond = newWhere();
if (someCondition...)
{
cond.and("TAB1.COL3", "myVal");
}
if (someOtherCondition...)
{
cond.and("TAB1.COL2", "myVal2");
}
----
===== Parameters
where() can have the following parameters:
* pFieldOrCond
This is eithoer for specifying the field for the condition
(e.g. if you need the sql-condition: "FIRSTNAME = 'Fritz'" then this parameter can be "PERSON.FIRSTNAME").
Or you can provide a already complete condition if all other parameters are not used.
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.
* pValue
This is the value for the condition:
(e.g. if you need the sql-condition: "FIRSTNAME = 'Fritz'" then this parameter is "Fritz")
* pCondition
This parameter defines which condition is used. You can use any SQL. # will be replaced by the pFieldOrCond and ? is used for the pValue (default is "# = ?")
(e.g. if you need the sql-condition: "FIRSTNAME <> 'Fritz'" then this parameter is "# <> ?")
* pFieldType
This parameter is for specifying the SQLTYPE explicitely.
Most of the time you won't need it, as the type is loaded from the db by the Tablename and Collumname you specified in pFieldOrCond
==== and / or
After you called .where somehow, you can add more conditions by using .and() / .or() they work exactly like .where() and also have the same parameters
==== *IfSet
The addition ot "IfSet" to the methods .where, .and, .or makes the condition _optional_.
This means:
While calling .where with a pValue which is null or undefined will result in an error:
[source,js]
----
var value = null;
var cond = newWhere("TAB1.COL2", null)
.and("TAB1.COL3", value)
// execution stops with error
----
whereIfSet will just ignore the whole condition
[source,js]
----
var value = null;
var cond = newWhereIfSet("TAB1.COL2", null)
.and("TAB1.COL3", value)
// code is executet and cond.build will result in ["TAB1.COL3 = ?", [['myVal', SQLTYPES.VARCHAR]]]
// As you can see. TAB1.COL2 is just ignored completely
----
=== join
=== group by
=== having
=== order by
=== Examples and Use Cases
==== already complete condition
If the first parameter is the only one you set, it is treated as a complete condition:
[source,js]
----
var name = 'Admin'
var cond = newWhere("PERSON.FIRSTNAME is not null") // a simple String
.and(newWhere("PERSON.LASTNAME", name)) // another SqlBuilder (note: only the condition is used from it)
.or(["FIRSTNAME is null and LASTNAME = ?", [["Meier", SQLTYPES.VARCHAR]]]) // a prepared statement array
//cond.build() will result in ["( (PERSON.FIRSTNAME = 'Tim' and PERSON.LASTNAME = 'Admin' ) or (PERSON.FIRSTNAME = 'Peter' ) and PERSON.LASTNAME = 'Meier' ) ", [['Tim', SQLTYPES.VARCHAR], ['Admin', SQLTYPES.VARCHAR], ['Peter', SQLTYPES.VARCHAR], ['Meier', SQLTYPES.VARCHAR]]]
----
This is also usefull for creating Brackets e.g. for smth. like "(FIRSTNAME = 'Tim' or FIRSTNAME = 'Fritz) and LASTNAME = 'Admin'"
[source,js]
----
var cond = newWhere(newWhere("PERSON.FIRSTNAME", "Tim")
.or("PERSON.FIRSTNAME", "Fritz"))
.and("PERSON.FIRSTNAME", "Peter")
// ( ( PERSON.FIRSTNAME = 'Tim' or PERSON.FIRSTNAME = 'Fritz' ) and PERSON.FIRSTNAME = 'Peter' )
----
By using another newWhere the Tim and Fritz are grouped together.
As comparison without the grouping:
[source,js]
----
var cond = newWhere("PERSON.FIRSTNAME", "Tim")
.or("PERSON.FIRSTNAME", "Fritz")
.and("PERSON.FIRSTNAME", "Peter")
// ( PERSON.FIRSTNAME = 'Tim' or PERSON.FIRSTNAME = 'Fritz' and PERSON.FIRSTNAME = 'Peter' )
----
==== simple condition with and & or
You can just combine and & or as you like:
[source,js]
----
var cond = newWhere("PERSON.FIRSTNAME", "Tim")
.and("PERSON.LASTNAME", "Admin")
.or("PERSON.FIRSTNAME", "Peter")
.and("PERSON.LASTNAME", "Meier")
//cond.build() will result in ["( (PERSON.FIRSTNAME = 'Tim' and PERSON.LASTNAME = 'Admin' ) or (PERSON.FIRSTNAME = 'Peter' ) and PERSON.LASTNAME = 'Meier' ) ", [['Tim', SQLTYPES.VARCHAR], ['Admin', SQLTYPES.VARCHAR], ['Peter', SQLTYPES.VARCHAR], ['Meier', SQLTYPES.VARCHAR]]]
----
(Note: the AND before OR: (Tim && Admin) || (Peter && Meier))
==== JDito Variables
It is also possible to just pass JDito variables directly:
[source,js]
----
newWhere("PERSON.FIRSTNAME", "$field.FIRSTNAME")
----
Note: it is checked for vars.getString(...) != null and vars.getString(...) != undefined
If you want to check also for != "" and vars.exists(...) you have to do it by yourself explicitely.
==== other condition than "# = ?"
If you need something different as the default "# = ?" condition just pass a string to pCondition.
Note that the # is replaced by the Table.Column from the first param and ? is the place where the value schould be inserted.
==== codition like "year(#) = ?" with specific FieldType
It is also possible to use more complex sql such as "year(#) = ?" But keep in mind that this may change the SQLTYPE needed and you may have to provede it manually by the 4th param if it cannot be loaded by the table and column name.
==== Subquery
Subqueries are also possible by providing either another Sql condition or a prepared-statement array.