public class FqlToSqlConverter
extends java.lang.Object
Dialect
. The implementation targets the correctness and performance, so a minimum
number of walks on the parsed tree must be executed. TRPL is obviously, excluded.Modifier and Type | Class and Description |
---|---|
private static class |
FqlToSqlConverter.CompositeDmoInfo
The immutable structure for a composite table.
|
private static class |
FqlToSqlConverter.ContainsRewriteData
Auxiliary date used for CONTAINS UDF call re-writing
|
private static class |
FqlToSqlConverter.FieldInfo
Field info holder
|
private static class |
FqlToSqlConverter.Weight
Weight column data
|
Modifier and Type | Field and Description |
---|---|
private java.util.ArrayDeque<java.util.HashMap<java.lang.String,DmoMeta>> |
aliases
The aliases (table names) currently used in this statement.
|
private java.util.function.Consumer<java.lang.String> |
appendColAlias
column alias appender
|
private static char |
ASTERISK
The ASTERISK literal used in aggregate SQL functions.
|
private int |
columnUid
Counter for making column aliases unique.
|
private int |
complexSubscript
The number of subscript with substitution parameter, like
[?] . |
private java.util.List<FqlToSqlConverter.ContainsRewriteData> |
containsRewriteData
Re-write data for CONTAINS operators
|
private FqlToSqlConverter.ContainsRewriteData |
cte4order
CTE used for implicit ordering imposed by CONTAINS
|
private int |
cteNo
generator for the CTE suffix
|
private int |
currentContains
index of currently processed CONTAINS
|
private Database |
db
The database
|
private Dialect |
dialect
The dialect used to construct the SQL statement.
|
private boolean |
forceNoAlias
Flag identifying to alias will be emitted for properties.
|
private boolean |
generateUniqueSqlColumnNames
If this flag is
true , the selected columns are named. |
private boolean |
lockTableRead
Flag indicating that the query reads from _Lock VST
|
private static java.util.logging.Logger |
LOG
Logger
|
private int |
maxResults
The maximum numbers of results in a query.
|
private int |
nargs
positional parameters found so far
|
private int |
nCteArgs
number of CTE parameters
|
private int |
nestedInContains
level of nested function calls for the CONTAINS first argument
|
private static java.lang.String |
NO_ALIAS
Constant for a virtual alias when none is specified (used in case of single table statements).
|
private int |
paramCount
The number of SQL parameters encountered during parsing.
|
private static java.lang.String |
PK
Name of the primary key column.
|
private java.util.Set<FQLAst> |
processedAsts
The set of all processed ASTs.
|
private Query.QueryParams |
queryParams
Query params
|
private boolean |
queryWasRewritten
Flags the a query was re-written during conversion to SQL
|
private java.util.List<java.lang.String> |
rowAliases
List of the row fields/ aliases
|
private java.util.List<RowStructure> |
rowStructure
The structure of the result to be returned.
|
private java.lang.StringBuilder |
sb
The
StringBuilder used to construct the SQL statement. |
private java.lang.String |
schema
The schema of the database.
|
private java.util.List<HQLPreprocessor.SessionAttr> |
sessionAttrs
Mutable session attributes' values used in the query
|
private java.util.Map<java.lang.String,java.lang.String> |
sqlTableAliasMap
The aliases used in SQL queries.
|
private int |
startOffset
The offset of the record to start a result with.
|
private boolean |
userTableStatRead
Flag indicating that the query reads from _UserTableStat VST
|
private boolean |
useSqlTableAlias
If this flag is
true the SQL names will be used for table aliases, otherwise the
FQL named received in FQL string will be used. |
private boolean |
useWordTables
'use word tables' flag
|
private java.lang.StringBuilder |
weigths
Holder for 'weights' used for implicit ordering imposed by CONTAINS
|
Modifier | Constructor and Description |
---|---|
private |
FqlToSqlConverter(Dialect dialect,
Database db,
Query.QueryParams queryParams,
boolean useSqlTableAlias)
The constructor is private.
|
Modifier and Type | Method and Description |
---|---|
private void |
collectNormalizedExtentsAliases(FQLAst node,
java.util.Map<java.lang.String,DmoMeta> aliasesMap,
java.util.Map<java.lang.String,java.lang.String> innerJoins,
java.util.function.BiConsumer<FQLAst,java.util.List<FQLAst>> staticSubscriptConstraints,
java.util.function.BiConsumer<FQLAst,java.util.List<FQLAst>> dynamicSubscriptConstraints)
Collects into
aliasesMap all occurrences of aliases with normalized accesses to extents fields
(that is, using bracket [ notation). |
private void |
collectTableAliases(FQLAst node,
java.util.Map<java.lang.String,DmoMeta> aliases)
Collects the list of aliases declared in a FQL
FROM or JOIN . |
void |
containsWithUdf(FqlToSqlConverter.ContainsRewriteData crwd)
Re-write CONTAINS using UDF.
|
void |
containsWithUdfAndCTE(FqlToSqlConverter.ContainsRewriteData crwd)
Re-write CONTAINS using UDF if corresponding word index is used for sorting
|
void |
containsWithWordTable(FqlToSqlConverter.ContainsRewriteData crwd)
Re-write CONTAINS using word tables
|
void |
containsWithWordTableAndCTE(FqlToSqlConverter.ContainsRewriteData crwd)
Re-write CONTAINS using word tables and CTE
|
private void |
expandAlias(DmoMeta dmoMeta,
java.lang.String alias,
boolean first)
Expands the alias into all its properties in a SELECT clause.
|
private java.lang.String |
generateCteName(java.lang.String base,
java.util.Set<java.lang.String> tbls)
Generate the alias for the common table expression for CONTAINS re-writing
|
private void |
generateExpression(FQLAst node)
Recursively walks a FQL predicate and generates the SQL one.
|
private void |
generateFrom(FQLAst node,
java.util.Map<java.lang.String,java.lang.String> innerJoins)
Generates the list of source tables where the records are to be fetched (ie
FROM and
any type of JOIN part. |
private void |
generateJoin(FQLAst joinNode,
java.lang.String rightAlias,
DmoMeta rightDmo)
Generates the JOIN part of the query statement.
|
private void |
generateOrderBy(FQLAst order)
Generates the
ORDER BY part of the query. |
private boolean |
generateOrderByComponent(FQLAst node)
Generates am order-by component.
|
private void |
generatePaging()
Analyzes the
maxResults and startOffset values and constructs the
paging clauses. |
private boolean |
generateProperty(FQLAst node,
boolean forceCsPrefix,
boolean qualified)
Converts a FQL property to a SQL field name.
|
private boolean |
generateProperty(FQLAst node,
boolean forceCsPrefix,
boolean qualified,
FqlToSqlConverter.FieldInfo fieldInfo)
Converts a FQL property to a SQL field name.
|
private void |
generateSet(FQLAst set)
Generate the
SET phrase for the UPDATE statement. |
private void |
generateSingle(FQLAst single)
Convert the SINGLE FQL token to a dialect-based
LIMIT 1 expression. |
private void |
generateWhere(FQLAst where,
java.util.Map<java.lang.String,java.lang.String> extIdxConstraints)
Generated the
WHERE predicate of the SQL query. |
static FqlToSqlConverter |
getInstance(Dialect dialect,
Database db)
Builder method for create an instance of this class.
|
static FqlToSqlConverter |
getInstance(Dialect dialect,
Database db,
Query.QueryParams queryParams)
Builder method for create an instance of this class.
|
int |
getLastConversionParamCount()
Returns the number of parameters in the last converted statement.
|
private DmoMeta |
getScopedDmoInfo(java.lang.String aliasStr)
Search for a specific alias in the stack of scoped aliases declared by current statement.
|
private java.lang.String |
getSqlColumnAlias(Property property)
Computes an unique SQL name for a field/column.
|
private java.lang.String |
getSqlTableAliasName(java.lang.String alias)
Obtain the SQL table alias for a FQL alias.
|
private boolean |
isBinaryOperator(Aast ast)
Convenience method to detect whether an AST represents a binary operator, based on its
token type.
|
private boolean |
isCaseInsensitiveField(FQLAst alias)
Checks whether this is case-insensitive character field.
|
boolean |
isLockTableRead()
Check if the query reads _Lock VST.
|
boolean |
isQueryWasRewritten()
Check if the query was re-written
|
boolean |
isUserTableStatRead()
Check if the query reads _UserTableStat VST.
|
static void |
main(java.lang.String[] args)
Test program
|
private static java.lang.String |
op(LogicalExpressionConverter.Term token,
java.util.List<java.lang.String> args)
Generate a SQL condition ('=' or 'LIKE') for an operation on the CNF.
|
private void |
preprocess(FQLAst root)
TODO: replace
upper(rtrim(alias.ch-property)) with alias.__ich-property and
rtrim(alias.ch-property) with alias.__sch-property
if the dialect requires so (H2) and only if the property is part of an indexed.
|
private void |
processDelete(FQLAst stmt)
Converts a
DELETE FQL statement into the SQL language. |
private void |
processSelect(FQLAst stmt)
Converts a
SELECT FQL statement into the SQL language. |
private void |
processStatement(FQLAst root)
Converts any FQL statement into the SQL language.
|
private void |
processUpdate(FQLAst stmt)
Converts a
UPDATE FQL statement into the SQL language. |
private void |
rewriteNoAliasPropertyNodes(java.lang.String alias,
FQLAst where)
Rewrite
FQLParserTokenTypes.PROPERTY ASTs so that they are parented by an ALIAS
AST. |
private void |
rewriteSelectDynamicSubscripts(FQLAst node,
java.util.List<FQLAst> dynamicSubscripts)
Rewrite the dynamic subscripts in a SELECT's WHERE clause, so that the field's index is matched before
testing the field's value, to preserve substitution argument order.
|
private void |
rewriteSubscriptsAsSubselect(FQLAst node,
java.util.List<FQLAst> subscripts)
For DELETE and UPDATE statements which reference extent fields in the WHERE clause, these need to be
rewritten via subselects.
|
java.util.List<HQLPreprocessor.SessionAttr> |
sessionAttrs()
Get list of mutable session attributes' values used in the query.
|
void |
setFieldProperty(FqlToSqlConverter.ContainsRewriteData crwd)
Set field property for CONTAINS.
|
java.lang.String |
toSQL(java.lang.String fql,
int maxResults,
int startOffset,
java.util.List<RowStructure> rowStructure)
Converts a
FQL statement into its SQL representation. |
private static void |
toUpper(boolean wrap,
java.lang.StringBuilder sb,
java.lang.Runnable op)
Conditionally wrap operation on a StringBuilder with upper() call
|
private static void |
warn(java.lang.String msg,
FQLAst node)
Logging utility method.
|
private static final java.util.logging.Logger LOG
private static final java.lang.String PK
private static final java.lang.String NO_ALIAS
private static final char ASTERISK
private final Dialect dialect
private final Database db
private final java.lang.String schema
private java.lang.StringBuilder sb
StringBuilder
used to construct the SQL statement.private int columnUid
private java.util.ArrayDeque<java.util.HashMap<java.lang.String,DmoMeta>> aliases
private final java.util.Map<java.lang.String,java.lang.String> sqlTableAliasMap
private final boolean useSqlTableAlias
true
the SQL names will be used for table aliases, otherwise the
FQL named received in FQL string will be used.private final boolean generateUniqueSqlColumnNames
true
, the selected columns are named. An unique name is computed for
each column.private int maxResults
private int startOffset
private int paramCount
private java.util.List<RowStructure> rowStructure
Record
s.
This is a by-product of the query statement conversion.private final java.util.Set<FQLAst> processedAsts
private int complexSubscript
[?]
.private boolean forceNoAlias
private final Query.QueryParams queryParams
private boolean queryWasRewritten
private boolean userTableStatRead
private boolean lockTableRead
private final java.util.List<FqlToSqlConverter.ContainsRewriteData> containsRewriteData
private int nestedInContains
private int currentContains
private int nargs
private int nCteArgs
private int cteNo
private FqlToSqlConverter.ContainsRewriteData cte4order
private final java.lang.StringBuilder weigths
private final boolean useWordTables
private final java.util.List<java.lang.String> rowAliases
private java.util.function.Consumer<java.lang.String> appendColAlias
private final java.util.List<HQLPreprocessor.SessionAttr> sessionAttrs
private FqlToSqlConverter(Dialect dialect, Database db, Query.QueryParams queryParams, boolean useSqlTableAlias)
getInstance
builder method.dialect
- The SQL dialect to be used when generating SQL statements.db
- The database.queryParams
- queryParameters;useSqlTableAlias
- When true
, the SQL names will be used for table aliases, otherwise the
FQL named received in FQL string will be kept.public static FqlToSqlConverter getInstance(Dialect dialect, Database db)
dialect
- The SQL dialect to be used when generating SQL statements.db
- The database.public static FqlToSqlConverter getInstance(Dialect dialect, Database db, Query.QueryParams queryParams)
dialect
- The SQL dialect to be used when generating SQL statements.db
- The database.queryParams
- queryParameters;public boolean isQueryWasRewritten()
true
if the query was re-writtenpublic java.util.List<HQLPreprocessor.SessionAttr> sessionAttrs()
public boolean isUserTableStatRead()
true
if the query reads _UserTableStat VST.public boolean isLockTableRead()
true
if the query reads _Lock VST.public void containsWithWordTableAndCTE(FqlToSqlConverter.ContainsRewriteData crwd)
crwd
- re-write data holderpublic void containsWithWordTable(FqlToSqlConverter.ContainsRewriteData crwd)
crwd
- re-write data holderpublic void setFieldProperty(FqlToSqlConverter.ContainsRewriteData crwd)
crwd
- re-write data holderpublic void containsWithUdf(FqlToSqlConverter.ContainsRewriteData crwd)
crwd
- re-write data holderpublic void containsWithUdfAndCTE(FqlToSqlConverter.ContainsRewriteData crwd)
crwd
- re-write data holderpublic java.lang.String toSQL(java.lang.String fql, int maxResults, int startOffset, java.util.List<RowStructure> rowStructure)
FQL
statement into its SQL representation.fql
- A valid FQL
statement.maxResults
- The maximum numbers of results tobe returned.startOffset
- The start offset. Together with maxResults
this parameter helps
implementing paging of a bigger the result set.rowStructure
- (Acts as an output parameter). If not null
, at return, the map will contain
the list of entities returned and the number of fields coded in the generated
SELECT
statement for each of them.fql
.public int getLastConversionParamCount()
private void preprocess(FQLAst root)
paramCount
field;DmoMetadataManager
root
- The root of the AST tree of a statement.private void processStatement(FQLAst root)
sb
.
For the moment, only SELECT
statements are supported.root
- The root of the FQLAst
statement.private void processUpdate(FQLAst stmt)
UPDATE
FQL statement into the SQL language. The output is directly
appended to sb
.stmt
- The UPDATE
FQL statement to be converted.private void generateSet(FQLAst set)
SET
phrase for the UPDATE
statement.set
- The AST for the SET phrase.private void processDelete(FQLAst stmt)
DELETE
FQL statement into the SQL language. The output is directly
appended to sb
.stmt
- The UPDATE
FQL statement to be converted.private void rewriteNoAliasPropertyNodes(java.lang.String alias, FQLAst where)
FQLParserTokenTypes.PROPERTY
ASTs so that they are parented by an ALIAS
AST.alias
- The alias to add to all unqualified properties.where
- The WHERE
AST.private void rewriteSubscriptsAsSubselect(FQLAst node, java.util.List<FQLAst> subscripts)
generateExpression(com.goldencode.p2j.persist.orm.FQLAst)
.node
- The root WHERE node.subscripts
- The list of subscript properties which need to be rewritten.private void processSelect(FQLAst stmt)
SELECT
FQL statement into the SQL language. The output is directly
appended to sb
.stmt
- The SELECT
FQL statement to be converted.private java.lang.String generateCteName(java.lang.String base, java.util.Set<java.lang.String> tbls)
base
- base of the nametbls
- Set of already used table names/aliasesprivate void expandAlias(DmoMeta dmoMeta, java.lang.String alias, boolean first)
dmoMeta
- The node meta-data for the node to be expanded.alias
- The node alias (text of the node) to be expanded.first
- true
if this is the first expanded node.private void generatePaging()
maxResults
and startOffset
values and constructs the
paging clauses.
Note: MSSQL dialect (SELECT TOP n
) not supported at this time.
private void generateOrderBy(FQLAst order)
ORDER BY
part of the query. The output is directly appended to
sb
.order
- The FQL ORDER BY
node.private boolean generateOrderByComponent(FQLAst node)
Note: only a one-argument functions are supported (like upper
and rtrim
used
for character fields). If more complex expressions are needed the more generic
generateExpression()
should be used instead.
node
- The root node for this expression.true
if this is the '_multiplex' or PK column.private void generateSingle(FQLAst single)
LIMIT 1
expression.single
- The SINGLE AST. May be null
.private boolean isCaseInsensitiveField(FQLAst alias)
alias
- The alias
node. This represents the table this property belongs to.
Normally it should have a single child, with the name of the PROPERTY
.true
if this property is detected as a case-insensitive character field.private boolean generateProperty(FQLAst node, boolean forceCsPrefix, boolean qualified)
sb
.node
- The alias
or property
node. If the former, this represents the table this
property belongs to. Normally it should have a single child, with the name of the PROPERTY
. If the latter, this represents an unqualified property name (and a single-table
statement is assumed).forceCsPrefix
- If true
, the dialect specific case-sensitive prefix will be injected.qualified
- true
if property is qualified by an alias; false
if node
represents an unqualified property name.true
if this is the '_multiplex' or PK column.private boolean generateProperty(FQLAst node, boolean forceCsPrefix, boolean qualified, FqlToSqlConverter.FieldInfo fieldInfo)
sb
.node
- The alias
or property
node. If the former, this represents the table this
property belongs to. Normally it should have a single child, with the name of the PROPERTY
. If the latter, this represents an unqualified property name (and a single-table
statement is assumed).forceCsPrefix
- If true
, the dialect specific case-sensitive prefix will be injected.qualified
- true
if property is qualified by an alias; false
if node
represents an unqualified property name.fieldInfo
- Auxiliary structure holding table names and aliastrue
if this is the '_multiplex' or PK column.private DmoMeta getScopedDmoInfo(java.lang.String aliasStr)
aliasStr
- The name of the alias to be find.DmoMeta
structure for the specified alias.private void generateWhere(FQLAst where, java.util.Map<java.lang.String,java.lang.String> extIdxConstraints)
WHERE
predicate of the SQL query. Walks each FQL node and converts
it into a SQL representation which is appended to the sb
.where
- The WHERE
node of the FQL query.extIdxConstraints
- The set of extra constraints required by extent indexes. For the specified aliases,
a new constraint for the list__index
is injected.private void generateExpression(FQLAst node)
sb
.node
- The root node for a FQL predicate to be processed.private boolean isBinaryOperator(Aast ast)
ast
- AST node to test.true
if the AST's token type indicates a binary operator, otherwise
false
is returned.private void generateFrom(FQLAst node, java.util.Map<java.lang.String,java.lang.String> innerJoins)
FROM
and
any type of JOIN
part. For the first occurrence FROM
keyword is used, for
the subsequent, the appropriate JOIN
is generated.node
- A FQLAst
node which contains the node/s.innerJoins
- A map with inner aliases to be joined.private void generateJoin(FQLAst joinNode, java.lang.String rightAlias, DmoMeta rightDmo)
joinNode
- The FQLAst
node which has the FQL JOIN data.rightAlias
- The right alias.rightDmo
- The DMO info for the right table.private void collectNormalizedExtentsAliases(FQLAst node, java.util.Map<java.lang.String,DmoMeta> aliasesMap, java.util.Map<java.lang.String,java.lang.String> innerJoins, java.util.function.BiConsumer<FQLAst,java.util.List<FQLAst>> staticSubscriptConstraints, java.util.function.BiConsumer<FQLAst,java.util.List<FQLAst>> dynamicSubscriptConstraints)
aliasesMap
all occurrences of aliases with normalized accesses to extents fields
(that is, using bracket [
notation).
Important: only the normalized tables that are already in the map are processed. If other normalized extent fields are encountered, they are skipped as they will be processed at the moment their main alias is collected (as part of a subselect, most likely).
node
- The root node to start from (the WHERE
node).aliasesMap
- The Map
where the new aliases will be stored.innerJoins
- The map where to save the inner join information.staticSubscriptConstraints
- The consumer to be applied for static subscript fields.dynamicSubscriptConstraints
- The consumer to be applied for dynamic subscript fields.private void rewriteSelectDynamicSubscripts(FQLAst node, java.util.List<FQLAst> dynamicSubscripts)
node
- The parent WHERE node.dynamicSubscripts
- The list of ALIAS ASTs which use dynamic subscripts.private void collectTableAliases(FQLAst node, java.util.Map<java.lang.String,DmoMeta> aliases)
FROM
or JOIN
. For each encountered alias,
a DmoMeta
structure is added. It will be used later to convert the table name and properties to
SQL values.node
- The node which contains the declarations of aliases for this query.private java.lang.String getSqlTableAliasName(java.lang.String alias)
alias
- The FQL alias for a table.private java.lang.String getSqlColumnAlias(Property property)
property
- The property that represent the field to be returned by a SELECT statement.private static void warn(java.lang.String msg, FQLAst node)
msg
- The message to be printed.node
- The node that caused the issue (may be null).private static java.lang.String op(LogicalExpressionConverter.Term token, java.util.List<java.lang.String> args)
token
- CNF tokenargs
- holder for the generated query parametersprivate static void toUpper(boolean wrap, java.lang.StringBuilder sb, java.lang.Runnable op)
wrap
- Flag indicating that wrapping is requiredsb
- StringBuilder instanceop
- Operation to be wrappedpublic static void main(java.lang.String[] args)
args
- command line args