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.
|
static class |
HQLPreprocessor.PropertyPair
A public container for storing a pair of properties.
|
static class |
HQLPreprocessor.SessionAttr
Mutable SESSION attributes added to the UDF calls
|
private static class |
HQLPreprocessor.UdfNamesHolder
Holds the set of UDFs synthetic names.
|
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.util.Deque<RecordBuffer> |
defaultBuffers
The stack of default buffers when sub-SELECTs are processed.
|
private boolean |
dependsOnSessionAttribute
Flag indicating that the query depends on mutable SESSION attribute(s).
|
private java.lang.String |
dropAlias
DMO alias to drop during HQL where clause generation or
null if none |
private java.util.LinkedList<java.lang.String> |
dropAliases
The stack of original aliases names when sub-SELECTs are processed.
|
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 static java.lang.String |
GUARDED
Prefix for guarded versions of UDFs
|
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.BitSet |
queryProperties
The set of properties used in the query predicate.
|
private java.lang.String |
replacementAlias
If not
null the dropAlias is forced instead of dropping it. |
private java.util.LinkedList<java.lang.String> |
replacementAliases
The stack of unique aliases for the outer sub-SELECTs are processed.
|
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 java.util.List<HQLPreprocessor.PropertyPair> |
substPairs
The substitution pairs.
|
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.
|
private static java.lang.String |
UDF_SCHEMA
UDF schema
|
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, JOIN, 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, SQL_TYPE, 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,
FqlType[] paramTypes,
java.lang.String[] referenceSubs,
Dialect dialect,
boolean overload,
boolean inline,
java.lang.String dropAlias,
java.lang.String replacementAlias,
boolean informational,
java.util.Set<java.lang.String> indexedFields)
Constructor which accepts an unprocessed where clause.
|
private |
HQLPreprocessor(java.util.List<RecordBuffer> bound,
java.util.List<RecordBuffer> definition,
java.lang.String where)
Translate this
where clause to use the bound DMO's alias and property names,
instead the definition (conversion-time) alias and property names. |
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,
FqlType[] 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)
Emit the
restructured where clause AST into an
infix notation expression, which is compliant with the HQL syntax
supported by Hibernate. |
private HQLAst |
fixEmptyContains(HQLAst root,
java.lang.Object[] parameters)
Replaces
contains function node with false is the second argument is an empty string. |
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,
Dialect dialect,
RecordBuffer[] buffers,
java.lang.Object[] parameters,
java.lang.String[] referenceSubs,
boolean inline,
java.lang.String dropAlias,
java.lang.String replacementAlias,
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.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.
|
java.util.BitSet |
getQueryProperties()
Obtains the list of properties the preprocessed FQL is based on.
|
(package private) static java.lang.String |
getRegisteredFunction(Database database,
java.lang.String fqlFunction,
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.
|
java.util.List<HQLPreprocessor.PropertyPair> |
getSubstPairs()
Obtain the substitution pair list, if any.
|
(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 java.util.List<HQLPreprocessor.PropertyPair> |
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,
Dialect 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 |
isDependsOnSessionAttribute()
Checks if the query depends on mutable SESSION attribute(s).
|
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 static boolean |
isUDF(HQLAst n)
Check if node represents UDF.
|
private static boolean |
isUDFArgument(HQLAst n)
Check if node represents UDF argument.
|
private RecordBuffer |
lookupBuffer(Aast alias)
Look up the record buffer associated with the given alias.
|
private HQLAst |
mainWalk(HQLAst root,
java.lang.Object[] parameters,
FqlType[] paramTypes,
Dialect dialect,
boolean overload)
Walk the AST depth-first from root to leaves and rewrite certain nodes.
|
private void |
makeExplicitCast(HQLAst parent,
int index,
Dialect dialect)
Replaces the specified child node of a given parent 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,
FqlType[] paramTypes,
java.lang.String[] referenceSubs,
Dialect 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,
HQLAst relRoot,
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.
|
(package private) static java.lang.String |
translate(java.util.List<RecordBuffer> bound,
java.util.List<RecordBuffer> definition,
java.lang.String where)
Translate this
where clause to use the bound DMO's alias and property names,
instead the definition (conversion-time) alias and property names. |
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.lang.String UDF_SCHEMA
private static final java.lang.String GUARDED
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 java.lang.String dropAlias
null
if noneprivate java.lang.String replacementAlias
null
the dropAlias
is forced instead of dropping it. This is needed
in order to allow Hibernate to properly resolve the fields in a query in a very specific
case:
- nested queries;
- the dropAlias
table is part of both outer and inner selects;
- the dropAlias
table has a field with same name.
For unknown cause, Hibernate will generate a SQL query that instead of this particular
field, the id
field of the table from outer select will be used.
Valid only if dropAlias
is not null
.
private java.util.Deque<RecordBuffer> defaultBuffers
private java.util.LinkedList<java.lang.String> dropAliases
private java.util.LinkedList<java.lang.String> replacementAliases
private 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 java.util.List<HQLPreprocessor.PropertyPair> substPairs
preprocess
if replacementAliases
are present.private java.util.BitSet queryProperties
private boolean dependsOnSessionAttribute
private HQLPreprocessor(java.util.List<RecordBuffer> bound, java.util.List<RecordBuffer> definition, java.lang.String where)
where
clause to use the bound DMO's alias and property names,
instead the definition (conversion-time) alias and property names.bound
- The runtime-bound buffers.definition
- The buffers as it was used to generate the where
clause at conversion
time.where
- The HQL to be translated.private HQLPreprocessor(Database database, java.lang.String where, java.util.Map<java.lang.String,RecordBuffer> bufferMap, java.lang.Object[] parameters, FqlType[] paramTypes, java.lang.String[] referenceSubs, Dialect dialect, boolean overload, boolean inline, java.lang.String dropAlias, java.lang.String replacementAlias, 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 equivalent 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, Dialect dialect, RecordBuffer[] buffers, java.lang.Object[] parameters, java.lang.String[] referenceSubs, boolean inline, java.lang.String dropAlias, java.lang.String replacementAlias, 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.referenceSubs
- TODO: add descriptioninline
- 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.replacementAlias
- In the case of a nested SELECT, use this unique alias as a replacement for default
alias that may cause Hibernate to generate bad SQL.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 fqlFunction, java.lang.Class<?>[] paramTypes)
registerFunction(Database, String, String, Method)
.database
- Database for which user-defined function is registered.fqlFunction
- FQL function name (as generated in java code).paramTypes
- The function signature.static java.lang.String translate(java.util.List<RecordBuffer> bound, java.util.List<RecordBuffer> definition, java.lang.String where)
where
clause to use the bound DMO's alias and property names,
instead the definition (conversion-time) alias and property names.bound
- The runtime-bound buffers.definition
- The buffers as it was used to generate the where
clause at conversion
time.where
- The HQL to be translated.HQLExpression getHQL()
where
keyword.public java.util.List<HQLPreprocessor.PropertyPair> getSubstPairs()
null
if none was detected.public java.util.BitSet getQueryProperties()
public boolean isDependsOnSessionAttribute()
true
if the query depends on mutable SESSION attribute(s).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, FqlType[] paramTypes, java.lang.String[] referenceSubs, Dialect 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 fixEmptyContains(HQLAst root, java.lang.Object[] parameters)
contains
function node with false
is the second argument is an empty string.root
- The parsed HQL root.parameters
- The query parameters.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 java.util.List<HQLPreprocessor.PropertyPair> inlineReferenceSubs(HQLAst root, java.lang.String[] referenceSubs)
root
- HQL AST root node.referenceSubs
- Array of field references and nulls.private void annotateParameters(HQLAst root, FqlType[] paramTypes)
datatype
.root
- Root AST node at which we begin the walk.paramTypes
- The type of each substitution parameter.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, FqlType[] paramTypes, Dialect 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 parent, int index, Dialect dialect)
cast(? as <datatype>)
if it represents
a substitution parameter. E.g. the element will be replaced with cast(? as big_decimal)
is we
have a decimal
variable.parent
- Parent of the node for which the cast must be done.index
- Index of the child element.dialect
- The dialect the query will be using.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, Dialect 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 expression 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)
).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, HQLAst relRoot, 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.relRoot
- The injection point for the index condition for the alias.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)
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).private static boolean isUDF(HQLAst n)
n
- node to be checkedtrue
if node represents UDF.private static boolean isUDFArgument(HQLAst n)
n
- node to be checkedtrue
if node represents UDF argument.