public final class HQLPreprocessor extends java.lang.Object implements HQLParserTokenTypes
true
in order to work around a defect in the Hibernate
HQL parser which disallows unary logical conditions.
checkError()
function. The first
parameter to this function is initError(false)
, and the
second parameter is the expression requiring error handling. The
criteria are:
The call to initError(false)
initializes the error
handler for the current checkError()
scope. The
execution of the expression may or may not report an error to the
error handler. The checkError()
function checks the
error handler's current scope for an error and handles it as
necessary.
Furthermore, each instance of this class provides the necessary contextual information required by the enclosing query to properly embed the rewritten where clause into an overall HQL statement.
For example, the P2J environment allows the following syntax:
from SomeDMOImpl as alias where alias.propertyABC[5] = ?as shorthand for something like:
from SomeDMOImpl as alias join alias.composite32 as alias_composite32 where alias_composite32.propertyABC = ? and index(alias_composite32) = 5
The former syntax is not directly supported by Hibernate and so the where
clause alias.propertyABC[5] = ?
is expanded to the latter
form internally, within the P2J persistence framework.
This rewriting is only necessary in the particular situation where a DMO class contains a list of composite elements, and a where clause references a property of such a composite element. The above example corresponds to a Hibernate mapping of:
... [class name="SomeDMOImpl" ...] ... [list name="composite32" ] ... [composite-element class="SomeDMOImpl$Composite32"] [property name="propertyABC" ... /] ... [/composite-element] [/list] ... [/class] ...
Allowing the shorthand syntax hides the complexity of the DMO implementation underlying the Hibernate mapping, and prevents the public API of the persistence runtime from becoming more complicated. The latter point is made because, in addition to the expansion of the where clause, one or more ANSI-style joins will be added to the overall HQL statement, and the order of query substitution parameters may be changed. The following service methods are provided to enable enclosing queries to embed the preprocessed where clause in the HQL statement, and to organize substitution parameters in the proper order:
A where clause is preprocessed during construction of an instance of this class. Parsing errors are logged, but are otherwise ignored. Ultimately, Hibernate will report an HQL problem if the input where clause is invalid.
Modifier and Type | Class and Description |
---|---|
private static class |
HQLPreprocessor.CacheKey
Instances of this class are used as keys to cache instances of
HQLPreprocessor . |
(package private) static class |
HQLPreprocessor.PropertyMatch
Object which contains information about a single comparison of a database property to a
substitution parameter or literal value.
|
Modifier and Type | Field and Description |
---|---|
private java.util.LinkedHashSet<java.lang.String> |
ansiJoins
HQL subexpressions to perform ANSI joins of associated lists
|
private java.util.Map<java.lang.String,RecordBuffer> |
bufferMap
Temporary map of dmo alias names to record buffers
|
private static java.util.Map<HQLPreprocessor.CacheKey,HQLPreprocessor> |
cache
Cache of prepared instances, indexed by Persistence and where clause
|
private Database |
database
Database associated with this object
|
private java.lang.String |
dropAlias
DMO alias to drop during HQL where clause generation or
null if none |
private java.util.List<java.lang.String> |
earlyPublishEntities
List of entities which publish uncommitted changes prematurely
|
private boolean |
findByRowid
Flag the query that it should performed on recid/rowid lookup table.
|
private java.lang.Long |
findByRowidValue
The actual
rowid value that is to be loaded if it is a constant detected in
hql preprocessing. |
private HQLExpression |
hql
HQL-compliant where clause (possibly rewritten)
|
private boolean |
inline
Whether substitution parameter inlining is permitted/was performed
|
private boolean |
inlinedTernary
Flag indicating if a ternary then/else was removed based on substitution parameter value,
thus caching will not be possible.
|
private static java.util.logging.Logger |
LOG
Logger
|
private static HQLPreprocessor |
NOP_INSTANCE
A do-nothing instance for empty where clauses
|
private static java.util.Map<FunctionKey,java.lang.String> |
overloadedFunctions
Map of HQL function keys to overloaded SQL (unique) function names
|
private ParameterIndices |
paramIndices
Mapping of index positions for substitution parameters after rewrite
|
private java.util.List<HQLPreprocessor.PropertyMatch> |
propertyMatches
List of property matches detected in a where clause
|
private java.util.Map<java.lang.String,java.util.Set<java.lang.String>> |
restrictionProperties
Map of properties used by this where clause, keyed by DMO entity
|
private static HQLAst |
TERNARY_COMPARE_EXP_TEMPLATE
The template for the normalized ternary which was used in a comparison expression.
|
private static HQLAst |
TERNARY_LOGICAL_EXP_TEMPLATE
The template for the normalized ternary which was used in a logical expression.
|
ALIAS, AND, AS, BOOL_FALSE, BOOL_TRUE, CASE, CAST, COMMA, CONCAT, DEC_LITERAL, DIGIT, DIVIDE, DMO, DOT, ELSE, END, EOF, EQUALS, ESCAPE, FROM, FUNCTION, GT, GTE, IN, INDEX, IS, IS_NULL, LBRACKET, LETTER, LIKE, LONG_LITERAL, LPARENS, LT, LTE, MINUS, MULTIPLY, NOT, NOT_EQ, NOT_NULL, NULL, NULL_TREE_LOOKAHEAD, NUM_LITERAL, OR, PLUS, PROPERTY, RBRACKET, RPARENS, SELECT, STRING, SUBSCRIPT, SUBSELECT, SUBST, SYM_CHAR, SYMBOL, TERNARY, THEN, UN_MINUS, VALID_1ST_IDENT, VALID_SYM_CHAR, WHEN, WHERE, WS
Modifier | Constructor and Description |
---|---|
private |
HQLPreprocessor(Database database,
java.lang.String where,
java.util.Map<java.lang.String,RecordBuffer> bufferMap,
java.lang.Object[] parameters,
org.hibernate.type.Type[] paramTypes,
java.lang.String[] referenceSubs,
P2JDialect dialect,
boolean overload,
boolean inline,
java.lang.String dropAlias,
boolean informational,
java.util.Set<java.lang.String> indexedFields)
Constructor which accepts an unprocessed where clause.
|
Modifier and Type | Method and Description |
---|---|
private void |
addRestrictionProperty(java.lang.String entity,
java.lang.String property)
Add the given property name to the set of properties used by this where
clause as restriction criteria.
|
private java.util.Map<HQLAst,java.lang.String> |
analyzeComposites(HQLAst root)
Analyze the AST generated for the HQL expression, identifying those
subtrees which are targets for conversion.
|
private void |
annotateParameters(HQLAst root,
org.hibernate.type.Type[] paramTypes)
Walk the HQL AST, annotating query substitution parameter nodes with
their data types as they are visited.
|
(package private) java.util.Iterator<java.lang.String> |
ansiJoins()
Get an iterator on the ANSI-style join subexpressions which must be
merged by the enclosing query into the overall HQL query statement, in
order to properly join associated composite element lists (if any).
|
private HQLAst |
augmentForUnknownValue(HQLAst root)
Walk the tree, which has been simplified/rolled-up
simplifyUnknowns(Set) by now,
and augment certain binary sub-expressions to match Progress' unknown value semantics. |
private boolean |
checkFindByRowid(HQLAst root)
Analyze the query preprocessed tree and checks if it represent a simple ROWID/RECID lookup.
|
private void |
collectPropertyMatches(HQLAst root)
Walk the where clause AST and collect all simple property matches.
|
private static HQLAst |
createAstNode(int type,
java.lang.String text,
HQLAst parent)
Utility method for creating a designed HQLAst node.
|
private ParameterIndices |
createParameterIndices(HQLAst root,
java.lang.Object[] parameters)
Create the mapping of substitution parameters to their corresponding
substitution placeholders in the rewritten HQL where clause.
|
private void |
duplicateAugmentedOperand(HQLAst source,
HQLAst parent)
Duplicate the given source operand node and graft the duplicate under
parent . |
private HQLExpression |
emit(HQLAst root,
java.lang.String dropAlias)
Emit the
restructured where clause AST into an
infix notation expression, which is compliant with the HQL syntax
supported by Hibernate. |
private java.lang.String |
generateJoin(java.lang.String compName,
java.lang.String alias,
java.lang.String verboseAlias)
Generate a single HQL subexpression for an ANSI-style join, which joins
a "master" DMO record with an associated list of composite elements.
|
private java.util.LinkedHashSet<java.lang.String> |
generateJoins(java.util.Map<HQLAst,java.lang.String> targets)
Define the set of ANSI-style join statements required to join the
"master" DMO with one or more associated lists of composite elements.
|
static HQLPreprocessor |
get(java.lang.String where,
Database database,
P2JDialect dialect,
RecordBuffer[] buffers,
java.lang.Object[] parameters,
java.lang.String[] referenceSubs,
boolean inline,
java.lang.String dropAlias,
boolean informational,
java.util.Set<java.lang.String> indexedFields)
Factory method which accepts an unprocessed where clause, the database
instance and the database dialect associated with the enclosing query,
the DMO implementation class, and the substitution parameters, if any,
for the query.
|
private java.lang.String |
getDatatypeSubstitutionForCastFunction(java.lang.String datatype)
Returns for the given HQL type its replacement which can be used into CAST function.
|
private java.lang.Iterable<HQLAst> |
getDenormalizedFields(HQLAst root)
Get list of denormalized fields with extent for where clause AST.
|
(package private) java.util.List<java.lang.String> |
getEarlyPublishEntities()
Retrieve the list of DMO entity names which will trigger the premature
publication of uncommitted changes across sessions, once the query
containing this preprocessor's underlying where clause is executed.
|
long |
getFindByRowid(java.lang.Object[] queryParams)
If this query is a ROWID/RECID lookup, get the record ID we are looking for.
|
(package private) HQLExpression |
getHQL()
Get the (possibly rewritten) where clause, which is compliant with HQL
syntax.
|
(package private) ParameterIndices |
getParameterIndices()
Get the
ParameterIndices object associated with this query. |
(package private) java.util.List<HQLPreprocessor.PropertyMatch> |
getPropertyMatches()
Get an unmodifiable list of the property matches collected for this where clause.
|
(package private) static java.lang.String |
getRegisteredFunction(Database database,
java.lang.String hqlFunction,
java.lang.Class<?>[] paramTypes)
Returns the SQL name of the user-defined function previously registered by
registerFunction(Database, String, String, Method) . |
(package private) java.util.Map<java.lang.String,java.util.Set<java.lang.String>> |
getRestrictionProperties()
Get an unmodifiable map of DMO entity names to sets of the names of the
DMO properties used as restriction criteria in the preprocessed where
clause.
|
(package private) boolean |
hasAnsiJoins()
Report whether any ANSI-style join subexpressions are present.
|
private boolean |
injectComputedColumn(HQLAst astNode)
Given an AST which represents a qualified or an unqualified text property, replace the
property's text (the property name) with a name which represents a computed column.
|
private void |
inlineDenormalizedField(HQLAst ast,
java.lang.Object[] parameters)
Replace legacy indexed property with extent with custom property.
|
private void |
inlineReferenceSubs(HQLAst root,
java.lang.String[] referenceSubs)
Given an array containing one or more strings and zero or more nulls, replace those query
substitution parameters in the where clause AST which positionally coincide with the
non-null strings in the array.
|
private int |
inlineSubstitutionParameter(HQLAst ast,
java.lang.Object[] parameters,
P2JDialect dialect)
Attempt to inline a single query substitution parameter, if it is a
numeric, text, or date type.
|
private boolean |
isBinaryOperator(Aast ast)
Convenience method to detect whether an AST represents a binary
operator, based on its token type.
|
boolean |
isFindByRowid()
Check if the preprocessed query is a simple RECID/ROWID lookup query.
|
private boolean |
isMandatoryProperty(HQLAst alias,
HQLAst property)
Determine whether the given property in the context of the DMO class represented by the
given buffer alias represents a mandatory (i.e., non-nullable) database column.
|
private boolean |
isOppositeOperandNullable(HQLAst ast,
int index)
Given an AST and its index, and assuming it is one of two operands in a
binary comparison, indicate whether the opposite operand is nullable.
|
private RecordBuffer |
lookupBuffer(Aast alias)
Look up the record buffer associated with the given alias.
|
private HQLAst |
mainWalk(HQLAst root,
java.lang.Object[] parameters,
org.hibernate.type.Type[] paramTypes,
P2JDialect dialect,
boolean overload)
Walk the AST depth-first from root to leaves and rewrite certain nodes.
|
private void |
makeExplicitCast(HQLAst ternaryElement,
int index)
Replaces the element of a ternary ("then element1 else element2") with "cast(? as
<datatype>)" if it represents a substitution parameter.
|
private void |
manuallyOverload(HQLAst function)
Manually disambiguate a user defined function by taking its overloaded
function name and data type signature and replacing the name in its AST
with a unique name generated when the function was registered.
|
private HQLAst |
normalizeTernary(HQLAst root,
java.lang.Object[] parameters)
Normalize a ternary clause into an equivalent logical expression.
|
private HQLAst |
parse()
Scan and parse the input where clause and return it in the form of an
abstract syntax tree.
|
private void |
preprocess(java.lang.String where,
java.lang.Object[] parameters,
org.hibernate.type.Type[] paramTypes,
java.lang.String[] referenceSubs,
P2JDialect dialect,
boolean overload,
boolean informational,
java.util.Set<java.lang.String> indexedFields)
The main worker method which drives preprocessing.
|
private HQLAst |
processCompareToUnknowns(HQLAst root,
java.lang.Object[] parameters,
java.util.Set<java.lang.String> indexedFields)
Simplifies and applies 4GL semantics to particular cases of predicate expressions when a
field is compared to unknown value.
|
static void |
registerFunction(Database database,
java.lang.String hqlFunction,
java.lang.String sqlFunction,
java.lang.reflect.Method method)
Register a user-defined function to the specified backing method for the given database.
|
private HQLAst |
restructure(HQLAst root,
java.util.Map<HQLAst,java.lang.String> targets)
Restructure the AST subtrees (if any) defined as rewrite targets by the
composite analysis step . |
private void |
rewriteAlias(HQLAst alias,
java.lang.String compName)
Given a particular, target subtree and a composite list entity name,
rewrite the AST as described in the
restructure(com.goldencode.p2j.persist.hql.HQLAst, java.util.Map<com.goldencode.p2j.persist.hql.HQLAst, java.lang.String>) method. |
private java.util.Set<HQLAst> |
simplifyUnknowns(java.util.Set<HQLAst> unknowns)
Get rid of unknowns in an HQL AST by evaluating relations in which these unknowns
participate.
|
private HQLAst |
trySimplifyBooleans(HQLAst booleanOperand,
HQLAst root)
Tries to roll up subexpressions according to the following rules:
{condition} or false --> {condition}
{condition} or true --> true
{condition} and true --> {condition}
{condition} and false --> false
|
(package private) boolean |
wasInlined()
Indicate whether query substitution parameters participating in range
checks were inlined directly into the query string.
|
private static final java.util.logging.Logger LOG
private static final HQLPreprocessor NOP_INSTANCE
private static final java.util.Map<HQLPreprocessor.CacheKey,HQLPreprocessor> cache
private static final java.util.Map<FunctionKey,java.lang.String> overloadedFunctions
private static final HQLAst TERNARY_LOGICAL_EXP_TEMPLATE
private static final HQLAst TERNARY_COMPARE_EXP_TEMPLATE
private final Database database
private final java.lang.String dropAlias
null
if noneprivate java.util.LinkedHashSet<java.lang.String> ansiJoins
private HQLExpression hql
private boolean inline
private boolean inlinedTernary
private ParameterIndices paramIndices
private java.util.Map<java.lang.String,java.util.Set<java.lang.String>> restrictionProperties
private java.util.List<java.lang.String> earlyPublishEntities
private java.util.List<HQLPreprocessor.PropertyMatch> propertyMatches
private boolean findByRowid
private java.lang.Long findByRowidValue
rowid
value that is to be loaded if it is a constant detected in
hql
preprocessing. If it is null
but findByRowid = true
then
the row index was not yet evaluated because it is a SUBST node. The actual value cannot be
cached in this object because it may be different at each call. To detect whether the query
looks for the template record of a table, the parameter[0]
of the query iteration
must be analyzed.private java.util.Map<java.lang.String,RecordBuffer> bufferMap
private HQLPreprocessor(Database database, java.lang.String where, java.util.Map<java.lang.String,RecordBuffer> bufferMap, java.lang.Object[] parameters, org.hibernate.type.Type[] paramTypes, java.lang.String[] referenceSubs, P2JDialect dialect, boolean overload, boolean inline, java.lang.String dropAlias, boolean informational, java.util.Set<java.lang.String> indexedFields) throws PersistenceException
database
- Database within which the associated query will be run.where
- An unprocessed where clause which will be scanned, parsed, and possibly rewritten.
Should not include the leading where
keyword.bufferMap
- A map of DMO aliases to their corresponding record buffers. If dropAlias
is not null
, a null
key will be mapped to the buffer corresponding
with this alias.parameters
- Query substitution parameters for the where clause.paramTypes
- Hibernate query substitution parameter types.dialect
- Database dialect in use.overload
- true
to manually overload user-defined functions.inline
- true
to allow substitution parameters involved in range checks to be
inlined; false
to disallow such inlining. If inlining occurs, the
preprocessor will not be cached.dropAlias
- If non-null
, suppress this alias qualifier when emitting the final
HQL where clause string; if null
, emit aliases normally.informational
- Flag indicating preprocessing is analytical only and query will not be executed.indexedFields
- The list of indexed fields. In all cases, except for indexed FIND queries:
t.f <= ?
and ? >= t.f
expressions are always evaluated to
true
;t.f < ?
and ? > t.f
expressions are equvalent to
t.f <> ?
?
) is sorted high in P4GL index.
In the case of FIND queries where the compared field is not part of the current index, P4GL has a flaw that evaluates the mentioned expressions:
t.f <= ?
and ? >= t.f
to true
only if t.f
is unknownt.f < ?
and ? > t.f
are always false
.PersistenceException
- if there is any error rewriting the where clause.public static HQLPreprocessor get(java.lang.String where, Database database, P2JDialect dialect, RecordBuffer[] buffers, java.lang.Object[] parameters, java.lang.String[] referenceSubs, boolean inline, java.lang.String dropAlias, boolean informational, java.util.Set<java.lang.String> indexedFields) throws PersistenceException
where
- An unprocessed where clause which will be scanned, parsed, and possibly rewritten.
Should not include the leading where
keyword.database
- Database object associated with the client query.dialect
- Database dialect in use.buffers
- All buffers referenced by the where
clause.parameters
- Query substitution parameters for the where clause.inline
- true
to permit inlining of substitution parameters involved in range
matches (>, >=, <, <=, like); false
to disallow such inlining.
Inlining will embed such parameters into the HQL string directly.dropAlias
- If non-null
, suppress this alias qualifier when emitting the final
HQL where clause string; if null
, emit aliases normally.informational
- Flag indicating preprocessing is analytical only and query will not be executed.indexedFields
- The list of indexed fields. In all cases, except for indexed FIND queries:
t.f <= ?
and ? >= t.f
expressions are always evaluated to
true
;t.f < ?
and ? > t.f
expressions are equvalent to
t.f <> ?
?
) is sorted high in P4GL index.
In the case of FIND queries where the compared field is not part of the current index, P4GL has a flaw that evaluates the mentioned expressions:
t.f <= ?
and ? >= t.f
to true
only if t.f
is unknownt.f < ?
and ? > t.f
are always false
.PersistenceException
- if there is any error rewriting the where clause.public static void registerFunction(Database database, java.lang.String hqlFunction, java.lang.String sqlFunction, java.lang.reflect.Method method)
database
- Database for which user-defined function is registered.hqlFunction
- HQL function name (as generated in java code).sqlFunction
- SQL function name that will be used in queries.method
- Method which will back the given function and provide function signature.static java.lang.String getRegisteredFunction(Database database, java.lang.String hqlFunction, java.lang.Class<?>[] paramTypes)
registerFunction(Database, String, String, Method)
.database
- Database for which user-defined function is registered.hqlFunction
- HQL function name (as generated in java code).paramTypes
- The function signature.HQLExpression getHQL()
where
keyword.public boolean isFindByRowid()
Generally, we can easily find such records using persistent database or dirty database for uncommitted transient records. Using normal queries, the transient record that has not validated its index, won't break the transaction isolation idiom and normally should not be shared to any context. However, using RECID/ROWID lookup query the record should be find across transactions and contexts, by direct accessing the record lookup table.
The predicate of such query will only test for equality of the RECID/ROWID of the record against a SUBST value. Using LTE or GTE will fail to find the requested record.
true
if the preprocessed query is a simple RECID/ROWID lookup query.public long getFindByRowid(java.lang.Object[] queryParams)
By convention, if the result is negative, the template record should be loaded. Values strictly positive are looked up into the database.
queryParams
- The current parameter list for the query.ParameterIndices getParameterIndices()
ParameterIndices
object associated with this query.
This object maintains an array of zero-based indices into the array of
query substitution arguments for the where clause, as it exists
after preprocessing is complete. This additional level of
indirection is necessary because the query substitution placeholders may
have been reordered within the HQL where clause expression during the
HQL preprocessing rewrite step, and some parameters may have been
inlined into the where clause.java.util.Iterator<java.lang.String> ansiJoins()
null
.boolean hasAnsiJoins()
true
if there are ANSI joins; else false
.java.util.List<java.lang.String> getEarlyPublishEntities()
This method is intended to support the emulation of a quirk/bug in Progress whereby the execution of a query (in the generic sense of the term) in a session after uncommitted changes, will trigger those changes to be prematurely published to other sessions.
TODO: this behavior may represent a version-specific bug in Progress which should be disabled in P2J for other versions than 9.1C.
null
.java.util.List<HQLPreprocessor.PropertyMatch> getPropertyMatches()
java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRestrictionProperties()
null
if the where clause contained no restriction
properties.boolean wasInlined()
true
if inlining was performed (with the caveats
noted above); else false
.private RecordBuffer lookupBuffer(Aast alias)
alias
- AST representing the alias (variable name) with which the buffer was defined in
business logic.java.lang.IllegalArgumentException
- if alias
does not match any buffer whose scope is currently open.private void preprocess(java.lang.String where, java.lang.Object[] parameters, org.hibernate.type.Type[] paramTypes, java.lang.String[] referenceSubs, P2JDialect dialect, boolean overload, boolean informational, java.util.Set<java.lang.String> indexedFields) throws PersistenceException
true
.
If the analysis performed in step 3 determines that rewriting is not necessary, the following three steps are skipped, and the original where clause simply is stored.
where
- An unprocessed where clause which will be scanned, parsed, and possibly rewritten.
Should not include the leading where
keyword.parameters
- Query substitution parameters for the where clause.paramTypes
- Hibernate query substitution parameter types.dialect
- Database dialect in use.overload
- true
to manually overload user-defined functions.informational
- Flag indicating preprocessing is analytical only and query will not be executed.indexedFields
- The list of indexed fields. In all cases, except for indexed FIND queries:
t.f <= ?
and ? >= t.f
expressions are always evaluated to
true
;t.f < ?
and ? > t.f
expressions are equvalent to
t.f <> ?
?
) is sorted high in P4GL index.
In the case of FIND queries where the compared field is not part of the current index, P4GL has a flaw that evaluates the mentioned expressions:
t.f <= ?
and ? >= t.f
to true
only if t.f
is unknownt.f < ?
and ? > t.f
are always false
.PersistenceException
- if there is any error rewriting the where clause.private HQLAst normalizeTernary(HQLAst root, java.lang.Object[] parameters)
root
- The parsed HQL root.parameters
- The query parameters.private boolean checkFindByRowid(HQLAst root)
A simple ROWID/RECID lookup query only test for equality of the id
field.
root
- The root of the tree obtained from preprocessing the HQL query.true
if the root
represent the tree of a simple ROWID/RECID lookup
query and false
for any other kind of query.private HQLAst parse() throws PersistenceException
PersistenceException
- if there is any error parsing the HQL.private void inlineReferenceSubs(HQLAst root, java.lang.String[] referenceSubs)
root
- HQL AST root node.referenceSubs
- Array of field references and nulls.private void annotateParameters(HQLAst root, org.hibernate.type.Type[] paramTypes)
datatype
.root
- Root AST node at which we begin the walk.paramTypes
- Hibernate query substitution parameter types.private void collectPropertyMatches(HQLAst root)
The purpose of this collection is to conduct a rudimentary analysis of the complexity of the where clause and its suitability to participate as a sub-expression in a server-side join.
root
- HQL AST root node.private HQLAst mainWalk(HQLAst root, java.lang.Object[] parameters, org.hibernate.type.Type[] paramTypes, P2JDialect dialect, boolean overload) throws PersistenceException
root
- Root node of the AST for the HQL expression.parameters
- Query substitution parameters for the where clause.paramTypes
- Hibernate query substitution parameter types.dialect
- Database dialect in use.overload
- true
to manually overload user-defined functions.PersistenceException
- if any error occurred during rewriting the AST.java.lang.IllegalArgumentException
- if an alias encountered in the where clause is not mapped in
the buffer manager.private HQLAst augmentForUnknownValue(HQLAst root) throws PersistenceException
simplifyUnknowns(Set)
by now,
and augment certain binary sub-expressions to match Progress' unknown value semantics.root
- Root node of the AST for the HQL expression.PersistenceException
- if any error occurred during rewriting the AST.private void duplicateAugmentedOperand(HQLAst source, HQLAst parent)
parent
.
If source
represents an extent field, annotate the duplicate node to indicate
that an index check node should not be created (it would be redundant) when rewriting the
alias node.source
- Original alias node (or enclosing function).parent
- Parent node onto which to graft the duplicate of source
as a child.private boolean isMandatoryProperty(HQLAst alias, HQLAst property)
alias
- Buffer alias ASTproperty
- Property AST.true
if the property is mandatory, else false
.private HQLAst processCompareToUnknowns(HQLAst root, java.lang.Object[] parameters, java.util.Set<java.lang.String> indexedFields)
t.f <= ?
and ? >= t.f
expressions are always evaluated to
true
;t.f < ?
and ? > t.f
expressions are equivalent to
t.f
is not unknown (t.f <> ?
). Processed in other
method.?
) is sorted high in P4GL index.
t.f <= ?
and ? >= t.f
to true
only if t.f
is unknown (t.f = ?
). Processed in other method.t.f < ?
and ? > t.f
are always false
.root
- Root node of the AST for the HQL expression.parameters
- Query substitution parameters for the where clause.indexedFields
- The list of fields from the sorting index in the case of FIND * query is processed.
Otherwise null
.private java.util.Set<HQLAst> simplifyUnknowns(java.util.Set<HQLAst> unknowns)
unknowns
- Set of HQL nodes which represent all unknowns in the HQL AST. Nodes can be
substitution parameters or whole statements which evaluate to unknown values.BOOL_TRUE
and BOOL_FALSE
nodes which were
created during the simplifications of unknowns. May be null
.private void makeExplicitCast(HQLAst ternaryElement, int index)
Originally designed as a workaround of the H2 bug.
ternaryElement
- Node which represents the ternary element (i.e. the child of "when" or "then") in
which replacement should be done.index
- Index of the ternary element in ternary ("1" or "2").private java.lang.String getDatatypeSubstitutionForCastFunction(java.lang.String datatype)
datatype
- Original HQL type.private HQLAst trySimplifyBooleans(HQLAst booleanOperand, HQLAst root) throws AstException
If an expression has been rolled up to a boolean value, it tries to roll this value up with other conditions and so on.
booleanOperand
- A node which represents a boolean operand in the expressions above.root
- The root node of the whole HQL tree.AstException
- If there is any error grafting subtrees in the process of
restructuring the subtree.private void inlineDenormalizedField(HQLAst ast, java.lang.Object[] parameters)
ast
- A node which represents a denormalized field with extent.parameters
- Query substitution parameters for the where clause.private int inlineSubstitutionParameter(HQLAst ast, java.lang.Object[] parameters, P2JDialect dialect)
?
) is replaced with the text of
the parameter's value. The node is annotated as "inlined" to enable
downstream processing to accurately track the remaining placeholders.ast
- AST node which represents the query substitution parameter to
be inlined.parameters
- The array of substitution parameters from which the value of
the inlined parameter will be determined.dialect
- Database dialect in use.SUBST
if
the parameter wasn't inlined.private void manuallyOverload(HQLAst function) throws PersistenceException
function
- AST node for the function.PersistenceException
- if there is an error determining the data type of any parameter
of the function.private boolean injectComputedColumn(HQLAst astNode) throws AstException
If the property reference is already embedded within an upper()
function call, that
call is discarded if it is redundant with the computed column definition.
astNode
- Qualified or unqualified property AST node.true
if the computed column reference was injected;
false
if the current database dialect does not use computed columns, or if
the given property is not a text property.AstException
- if there is an error restructuring the alias node or its parent node.private java.util.Map<HQLAst,java.lang.String> analyzeComposites(HQLAst root)
The AST subtree structure which represents our target is as follows:
ALIAS | +--PROPERTY | +--LBRACKET | +--{...} (SUBST or NUM_LITERAL)
In its original form, this would look like:
alias.property[subscript]
If any such constructs are identified, they are added to a map of targets and returned.
root
- Root node of the AST for the HQL expression.null
if no targets were found. The composite
names are retrieved by the DatabaseManager
from the
appropriate Hibernate ORM configurations.private java.lang.Iterable<HQLAst> getDenormalizedFields(HQLAst root)
root
- Root node of the AST for the HQL expression.private void addRestrictionProperty(java.lang.String entity, java.lang.String property)
In the current implementation, the nature of the restriction is not preserved (e.g., equality match, function parameter, etc.).
entity
- DMO entity containing the given property.property
- Name of a property used by this where clause as a restrictive criterion.private HQLAst restructure(HQLAst root, java.util.Map<HQLAst,java.lang.String> targets) throws PersistenceException
composite analysis step
. This involves the
expansion of an expression, for instance:...
OR [ or ] | +--EQUALS [ = ] | | | +--ALIAS [ alias ] | | | | | +--PROPERTY [ propertyABC ] | | | | | +--LBRACKET [ [ ] | | | | | +--NUM_LITERAL [ 0 ] | | | +--SUBST [ ? ] | +--EQUALS [ = ] | +--ALIAS [ alias ] | | | +--PROPERTY [ propertyABC ] | | | +--LBRACKET [ [ ] | | | +--NUM_LITERAL [ 1 ] | +--SUBST [ ? ]...to an expresion which consists of the modified original expression and index specifications added to the lop level of the tree using logical AND operators:
AND [ and ] | +--AND [ and ] | | | +--LPARENS [ ( ] | | | | | +--OR [ or ] | | | | | +--EQUALS [ = ] | | | | | | | +--ALIAS [ alias_composite2_0 ] | | | | | | | | | +--PROPERTY [ propertyABC ] | | | | | | | +--SUBST [ ? ] | | | | | +--EQUALS [ = ] | | | | | +--ALIAS [ alias_composite2_1 ] | | | | | | | +--PROPERTY [ propertyABC ] | | | | | +--SUBST [ ? ] | | | +--EQUALS [ = ] | | | +--FUNCTION [ index ] | | | | | +--ALIAS [ alias_composite2_0 ] | | | +--NUM_LITERAL [ 0 ] | +--EQUALS [ = ] | +--FUNCTION [ index ] | | | +--ALIAS [ alias_composite2_1 ] | +--NUM_LITERAL [ 1 ]
This corresponds to the following expression expansion (for this expression, two composite targets will be passed to the function):
alias.propertyABC[0] = ? or alias.propertyABC[1] = ?...to this one:
(alias_composite2_0.propertyABC = ? or alias_composite2_1.propertyABC = ?) and index(alias_composite2_0) = 0 and index(alias_composite2_1) = 1However, the infix example above is informational only; the generation of the final, infix form of the expression is performed in a later step (see
emit(com.goldencode.p2j.persist.hql.HQLAst, java.lang.String)
).targets
- Map of AST subtrees targeted for rewriting by the analysis
step. The map's keys are the subtrees themselves; the map's
values are the names of the composite list entities retrieved
by the DatabaseManager
.root
- The root node of the AST of the whole HQL expression.PersistenceException
- if there is any error restructuring the ASTs.private void rewriteAlias(HQLAst alias, java.lang.String compName) throws AstException
restructure(com.goldencode.p2j.persist.hql.HQLAst, java.util.Map<com.goldencode.p2j.persist.hql.HQLAst, java.lang.String>)
method.alias
- Alias subtree to be expanded.compName
- Name of the composite list associated with the combination of
the alias name and property name encountered in the
alias
subtree.AstException
- if there is any error grafting subtrees in the process of
restructuring the subtree.private HQLExpression emit(HQLAst root, java.lang.String dropAlias)
restructured
where clause AST into an
infix notation expression, which is compliant with the HQL syntax
supported by Hibernate. This is the post-rewrite, "finished product"
expression which will be returned by the getHQL()
method.root
- Root of the restructured where clause AST.private java.util.LinkedHashSet<java.lang.String> generateJoins(java.util.Map<HQLAst,java.lang.String> targets)
Duplicates are eliminated. The order in which joins are detected is preserved in the resulting data structure, such that later iterations over the set will return these in the same order in which they were added.
targets
- Map of AST subtrees targeted for rewriting by the analysis
step. The map's keys are the subtrees themselves; the map's
values are the names of the composite list entities retrieved
by the DatabaseManager
.null
.private java.lang.String generateJoin(java.lang.String compName, java.lang.String alias, java.lang.String verboseAlias)
compName
- Name of the associated list of composite elements.alias
- Alias for the "master" DMO record to which the composite list
is to be joined.verboseAlias
- Verbose alias name, including the composite portion and
subscript qualifier.private ParameterIndices createParameterIndices(HQLAst root, java.lang.Object[] parameters)
?
) may have been reordered or removed (due to
inlining) during the rewrite process.
During parsing of the HQL clause, an index annotation was added to each
AST of type SUBST
(token type for the query substitution
parameter placeholder), denoting that placeholder's position in the
original where clause. We now walk the rewritten AST, extracting this
annotation and storing it in a new list. The resulting array defines
the new order of the substitution parameters. For parameters which
were dropped, due to inlining, a null placeholder will appear in the new
list.
root
- Root node of the rewritten where clause AST.parameters
- Query substitution parameters for the where clause.private boolean isBinaryOperator(Aast ast)
ast
- AST node to test.true
if the AST's token type indicates a binary
operator, else false
.private boolean isOppositeOperandNullable(HQLAst ast, int index)
TODO: currently, we only test if the property is the reserved, primary key property. We should augment this test to check for other non-nullable properties.
ast
- The operand opposite the operand we wish to test.index
- The index of ast
among its siblings. This must be
0 or 1, since the expression is binary.true
if the operand opposite ast
is
nullable, else false
.private static HQLAst createAstNode(int type, java.lang.String text, HQLAst parent)
type
- The type of the new HQLAst node.text
- The text of the new HQLAst node.parent
- The parent of the new HQLAst node (if not null).