Basic META SQL
META SQL
Dynamic input values
Static input values
Embedded mapping rules
META SQL statements
Conditional SQL fragment IF
Advanced conditional SQL fragment IF
Conditional SQL fragment AND
Advanced conditional SQL fragment AND
Conditional SQL fragment OR
Advanced conditional SQL fragment OR
Conditional SQL fragment OPT
Advanced conditional fragment OPT
Special fragment WHERE
Special fragment VALUES
Special fragment COLUMNS
Special fragment SET
Ordering SQL fragment
META SQL is an ANSI SQL extension based on ANTLR defined grammar. Based on data provided by a user (or an application) the final SQL statement is generated. From the point of view of SQL Processor the SQL statement is a stream of text. In this text special patterns are identified and processed.
A META SQL statement definition itself begins with a left curly bracket {
and ends with a right curly bracket }
. The input values are identified in a text stream with a colon character :
or a dollar sign $
. The output mapping rules are identified in a text stream with an at sign @
. In the SQL Processor 1.6 there are two more control characters - %
and %%
. These are use by the SQL Processor Eclipse plugin. In the runtime they are ignored. In the SQL Processor 2.2 there are two more control characters - ::
and $$
. These are used for SQL operators.
The SQL Processor query definition as a stream of a text is shown here:
Special characters and tokens are the next ones:
-
COLON is a colon character
:
- identifies a dynamic input value (=identifier) -
STRING is a dollar sign
$
- identifies a static input value (=constant) - COLON COLON - identifies a dynamic SQL operator (=identifierOperator)
- STRING STRING - identifies a static SQL operator (=constantOperator)
-
AT is an at sign
@
- identifies an output mapping rule (=column) -
PERCENT is a percent character
%
- identifies a database column (=dbcolumn) - PERCENT PERCENT - identifies a database table (=dbtable)
-
LBRACE is a left curly bracket
{
- identifies the beginning of a META SQL statement (=metaSql) -
RBRACE is a right curly bracket
}
- identifies the end of a META SQL statement (=metaSql) - AND...WS are all ASCII characters except the characters defined above. In fact they form a fragment of the final SQL statement.
So the SQL Processor statement is an extended ANSI SQL statement. This enhancement consists of input values (dynamic or static ones), dynamic operators (dynamic or static ones), output mapping rules and META SQL statements. For example the query
QUERY(QRY)=
select p.ID @id, $-name.first @firstName, :+name.last @lastName { , :ssn @ssnum} from PERSON p
;
-
select p.ID id,
is a query fragment, the at sign is deleted from this fragment -
$-name.first
is a static input value -
@firstName,
is a query fragment (and a mapping rule), the at sign is deleted from this fragment -
:+name.last
is a dynamic input value -
@lastName
is a query fragment (and a mapping rule), the at sign is deleted from this fragment -
{ , :ssn @ssnum}
is a META SQL statement (of type OPT) -
from PERSON p
is a query fragment
A dynamic input value is simply a value of an attribute from a search form. This value is bounded to a JDBC prepared statement, which is created from the final SQL statement. The input value can have an influence, how the final SQL statement is generated. In the case its occurrence is outside of a META SQL statement (so outside of a text between curly brackets), it must be non-empty and
- it can be located in the SELECT part of the SQL query. In this case the value is counted into the result of an SQL query SELECT as a virtual column. For example in the previously quoted
QUERY
there's a virtual column:+name.last
created from an input value. - it can be located in the WHERE, SET or UPDATE part of the SQL statement. In this case the value is bounded to a JDBC prepared statement in a standard way.
The non-emptiness of a dynamic input value depends on the Java type of the related attribute
- for a String it must be not null and the length must be >0
- for a Collection it must be not null and the size must be >0
- for the rest Java types it must be not null
- an enumeration based attribute is treated in a special way, please see the advanced tutorial
The isNotEmpty()
method in the SQLP plugins is responsible for the emptiness detection.
The schema of a dynamic input value (=identifier) is the next one:
Special characters and tokens are:
-
PLUS is a plus character
+
- means the input value is converted to upper-case -
MINUS is a minus character
-
- means the input value is converted to lower-case - IDENT is a Java identifier. It's the name of the attribute in a search form.
- IDENT_DOT is a complex Java identifier. It's the name of the embedded attribute in a search form.
A static input value is simply a value of an attribute from a search form. This value is embedded directly into the final SQL statement. All the SQL Processor Engine main methods can have one search form for dynamic input values and one search form for static input values as parameters. Obviously a search form for static input values can't be filled by an end user (to protect a database against SQL query injection). So this form is used mainly by an application layer in a web application for example. In the case its occurrence is outside of a META SQL statement (so outside of a text between curly brackets), it must be non-empty and
- it can be located in the SELECT part of the SQL query. In this case the value is counted into the result of an SQL query SELECT as a virtual column. For example in the previously quoted
QUERY
there's a virtual column$-name.first
created from an input value. - it can be located in the WHERE, SET or UPDATE part of the SQL statement. In this case the value is embedded into the final SQL statement.
The non-emptiness of a static input value depends on the Java type of the related attribute
- for a String it must be not null and the length must be >0
- for a Collection it must be not null and the size must be >0
- for the rest Java types it must be not null
- an enumeration based attribute is treated in a special way, please see the advanced tutorial
The isNotEmpty()
method in the SQLP plugins is responsible for the emptiness detection.
The schema of a static input value (=constant) is the next one:
Special characters and tokens are:
-
PLUS is a plus character
+
- means the input value is converted to upper-case -
MINUS is a minus character
-
- means the input value is converted to lower-case - IDENT is a Java identifier. It's the name of the attribute in a search form.
- IDENT_DOT is a complex Java identifier. It's the name of the embedded attribute in a search form.
The mapping rules are a prescription how to fill the instances of the result class with the output values from an SQL query execution. In fact, it's a list of database columns or aliases, values of which are seeded into the instances of the result class. These rules can be defined explicitly or implicitly. In the second case they are identified in the META SQL query with the prefix @
followed by the alias/attribute name. More on mapping rules is shown in the separate tutorial.
A META SQL statement is a text between curly brackets, which is treated in a special way. There are several types of META SQL statements. The schema is the next one:
Special characters and tokens are the next ones:
-
QUESTI is a question mark
?
- identifies a conditional SQL fragment of type IF (logical expression) -
BAND is an ampersand
&
- identifies a conditional SQL fragment of type AND (conjunction) -
BOR is a vertical bar
|
- identifies a conditional SQL fragment of type OR (disjunction) -
EQUALS is an equals sign
=
- identifies a special SQL fragment. The following IDENT token begins the identification of this special SQL fragment and it can have one of the next values- WHERE
- VALUES
- SET
-
HASH DIGIT is a hash sign
#
followed by a number - identifies an ordering SQL fragment with the ordering id, which is equal to this number - all other ASCII characters except the characters defined above – in this case it's one of several possible conditional SQL fragments of type OPT (optional query). These alternative fragments can be separated with a BOR (a vertical bar).
A logical expression consists of a boolean expression and one or two alternative SQL fragments separated with a BOR (a vertical bar).
A boolean expression is an expression of input values with the following operands between them:
-
!
an exclamation mark - for a logical negation -
(
and)
left and right round brackets – for a logical grouping -
&&
two ampersands – for a logical conjunction -
||
two vertical bars – for a logical disjunction
A logical value of an input value (dynamic or static) depends on the Java type of the related attribute. If the value is null, it is evaluated as false. Otherwise it is evaluated as true in the following cases
- Boolean or boolean – a logical value of the related attribute
- any type of Number – if the related attribute value is a positive one
- String - if the related attribute length is greater than zero. A special case is a token
false
, which is evaluated as false. - an enumeration based attribute is treated in a special way, please see the advanced tutorial
The isTrue()
method in the SQLP plugins is responsible for this detection.
The first alternative SQL fragment will become a part of the final SQL statement in the case that the boolean expression is evaluated as true. The optional second alternative SQL fragment will become part of the final SQL statement in the case that the boolean expression is evaluated as false.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE
{? :fname | , p.NAME_FIRST first | , '' first}
{? :lname | , p.NAME_LAST last | , '' last}
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
{? :lname || :fname | left join PERSON p on e.PERSON = p.ID}
{= where
{? :lname | AND UPPER(p.NAME_LAST) = :+lname | AND UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:fname
is true, the columnNAME_FIRST
will become a part of the final SELECT (otherwise the value of the aliasfirst
is an empty text ``) - in the case the input value
:lname
is true, the columnNAME_LAST
will become a part of the final SELECT (otherwise the value of the aliaslast
is an empty text ``) - in the case the input value
:fname
or the input value:lname
are true, the tableENGAGEMENT
will be joined to the tablePERSON
- in the case the input value
:lname
is true, the conditionAND UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - otherwise in the case the input value
:lname
is false, the conditionAND UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
Every alternative SQL fragment is itself a META SQL conditional fragment (of type OPT). So it's treated based on input values in this META SQL fragment. In the previous example there was the next alternative SQL fragment AND UPPER(p.NAME_FIRST) = :+fname
. It contains the input value identified by +fname
. If the value is empty, this SQL fragment won't become a part of the final SQL query regardless the value of the related boolean expression. So the last WHERE fragment in this example has these conditions
- in the case the input value
:lname
is non empty text, the conditionAND UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - otherwise in the case the input value
:fname
is non empty text, the conditionAND UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
An SQL fragment conjunction is an ANSI SQL fragment with embedded input values (static or dynamic ones). It's treated based on the emptiness of these input values. This fragment is divided into segments. Each segment is finished with one input value. If this input value is non-empty, the related segment will become a part of the final SQL statement. The conditional fragment itself is embedded into the final SQL statement with the prefix AND. So this fragment can only be embedded into the WHERE part of the final SQL statement. Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where
{& UPPER(p.NAME_LAST) = :+lname}
{& UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:lname
is not empty, the conditionAND UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - in the case the input value
:fname
is not empty, the conditionAND UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
An SQL fragment conjunction can consists from several alternative fragments separated with a BOR (a vertical bar). In this case, the first fragment, which has at least one input value non-empty, will be embedded in the final SQL statement. Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where
{& UPPER(p.NAME_LAST) = :+lname | UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:lname
is not empty, the conditionAND UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - otherwise in the case the input value
:fname
is not empty, the conditionAND UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
An SQL fragment disjunction is an ANSI SQL query fragment with embedded input values (static or dynamic ones). It's treated based on the emptiness of these input values. This fragment is divided into segments. Each segment is finished with one input value. If this input value is non-empty, the related segment will become a part of the final SQL statement. The conditional fragment itself is embedded into the final SQL statement with the prefix OR. So this fragment can only be embedded into the WHERE part of the final SQL statement.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where
{| UPPER(p.NAME_LAST) = :+lname}
{| UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:lname
is not empty, the conditionOR UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - in the case the input value
:fname
is not empty, the conditionOR UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
A SQL fragment disjunction can consists from several alternative fragments separated with a BOR (a vertical bar). In this case, the first fragment, which has at least one input value non-empty, will be embedded in the final SQL statement.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where
{| UPPER(p.NAME_LAST) = :+lname | UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:lname
is not empty, the conditionOR UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE - otherwise in the case the input value
:fname
is not empty, the conditionOR UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE
An optional SQL fragment is an ANSI SQL fragment with embedded input values (static or dynamic ones). It's treated based on the emptiness of these input values. This fragment is divided into segments. Each segment is finished with one input value. If this input value is non-empty, the related segment will become a part of the final SQL statement. This fragment can be embedded into the SELECT, WHERE or any other part of the final SQL statement.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{=where { UPPER(p.NAME_LAST) = :+lname}}
;
- in the case the input value
:lname
is not empty, the WHERE part of the final query will becomeUPPER(p.NAME_LAST) = :+lname
An optional SQL fragment can consists from several alternative fragments separated with a BOR (a vertical bar). In this case, the first fragment, which has at least one input value non-empty, will be embedded in the final SQL statement.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where { UPPER(p.NAME_LAST) = :+lname | UPPER(p.NAME_FIRST) = :+fname}}
;
- in the case the input value
:lname
is not empty, the WHERE part of the final query will becomeUPPER(p.NAME_LAST) = :+lname
- otherwise in the case the input value
:fname
is not empty, the WHERE part of the final query will becomeUPPER(p.NAME_FIRST) = :+fname
- otherwise there's no WHERE fragment in the final query
Special fragment WHERE is used to delimit an SQL command WHERE and switch a special handling of input values in it. At the same time the keyword WHERE will become a prefix of this fragment, and potential leading AND/OR keywords will be stripped. This can help for example with the embedded conditional fragments of type AND and OR.
Let's have a query
QUERY(QRY)=
select m.id, m.TITLE, e.ROLE, p.NAME_FIRST first, p.NAME_LAST last
from MEDIA m left join ENGAGEMENT e on e.MEDIA = m.ID
left join PERSON p on e.PERSON = p.ID
{= where
{& UPPER(p.NAME_LAST) = :+lname}
{& UPPER(p.NAME_FIRST) = :+fname}
}
;
- in the case the input value
:lname
is not empty, the conditionAND UPPER(p.NAME_LAST) = :+lname
will become a part of the final WHERE. Without embedding in{= where … }
, the AND follows immediately after the command WHERE. - in the case the input value
:fname
is not empty, the conditionAND UPPER(p.NAME_FIRST) = :+fname
will become a part of the final WHERE. In the case:lname
is empty and without being embedding in{= where … }
, the AND follows immediately after the command WHERE.
Special fragment VALUES is used to delimit an SQL command VALUES for the INSERT SQL command and switches a special handling of the input values in it. At the same time the keyword VALUES will become a prefix of this fragment, and potential leading and trailing commas will be stripped. The special handling of the input values means the emptiness of the input values is suppressed. In this way also null input values can be bounded to the final SQL prepared statement.
Let's have a statement
INSERT_PERSON(CRUD)=
insert into PERSON (id, name)
{= values (:id(seq=SEQ1), :name) }
;
- the generated input value
:id
is inserted into columnid
. The sequenceSEQ1
is used to generate this value. This sequence has to be defined as an optional featureSEQ1(OPT)=…
. - the input value
:name
is inserted into columnname
regardless if it's empty or not.
Special fragment COLUMNS is used for the INSERT SQL command and switches a special handling of the input values in it. A typical usage is the next one
INSERT_PERSON(CRUD)=
insert into %%PERSON {= columns (
, %ID
{? :name | ,%NAME}
) }
{= values (
,:id(seq=SIMPLE_SEQUENCE)
{? :name | ,:name}
) }
;
- the generated input value
:id
is inserted into columnid
. The sequenceSEQ1
is used to generate this value. This sequence has to be defined as an optional featureSEQ1(OPT)=…
. - the input value
:name
is inserted into columnname
only in the case it's not null.
Special fragment SET is used to delimit an SQL command SET for the UPDATE SQL command and switches a special handling of the input values in it. At the same time the keyword SET will become a prefix of this fragment, and potential leading and trailing commas will be stripped. The special handling of the input values means the emptiness of the input values is suppressed. In this way also null input values can be bounded to the final SQL prepared statement.
Let's have a statement
UPDATE_PERSON(CRUD)=
update PERSON
{= set name = :name}
{= where {& id = :id(notnull)}}
;
- the input value
:name
is updated the columnname
' regardless if it's empty or not.
An ordering query fragment is identified with the number, which is at the begin of it. If this number is used in ordering directives (as a parameter to the SQL Processor Engine), the related ordering fragment will become a part of the final SQL query. The ordering directives can be
-
SqlOrder.getAscOrder(number)
– the related ordering query fragment will be embedded into the final SQL query with a suffix ASC -
SqlOrder.getDescOrder(number)
– the related ordering query fragment will be embedded into the final SQL query with a suffix DESC
Let's have a query
QUERY(QRY)=
select p.ID, p.NAME_FIRST first, p.NAME_LAST last, p.SSN_NUMBER ssn
from PERSON p
{#1 order by p.ID}
{#2 order by p.NAME_LAST}
;
- in the case the ordering directive is (for example)
SqlOrder.getAscOrder(1)
, the ordering commandorder by p.ID ASC
will become a part of the final SQL query - in the case the ordering directive is (for example)
SqlOrder.getDescOrder(2)
, the ordering commandorder by p. NAME_LAST DESC
will become a part of the final SQL query - in the case the ordering directives are combined (for example)
SqlOrder.getDescOrder(2).addAscOrder(1)
, the ordering commandorder by p. NAME_LAST DESC, p.ID ASC
will become a part of the final SQL query
- SQL Processor Home
- SQL Processor News
- SQL Processor Eclipse Plugin Home
- SQL Processor Eclipse Plugin News
- SQL Processor Architecture
- SQLP and SQLM*P Tutorials
- SQLM*P Control directives
- Basic Tutorials
- 10 minutes Tutorial
- Simple Tutorial
- CRUD Tutorial
- Associations Tutorial
- Inheritance Tutorial
- More inheritance Tutorial
- Custom Types Tutorial
- Stored procedures Tutorial
- IDE Setup and Coding Standards
- Catalog (JPA) Sample
- Catalog (Hibernate) Sample
- Catalog (Spring) Sample
- Advanced catalog (Spring) Sample
- The Reference Guide
- The Eclipse Plugin Tutorials
- The Improvements
- Tutorials archive