use of org.datanucleus.store.query.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method processCaseExpression.
protected Object processCaseExpression(CaseExpression expr, SQLExpression typeExpr) {
processingCase = true;
try {
List<ExpressionPair> conditions = expr.getConditions();
Iterator<ExpressionPair> whenExprIter = conditions.iterator();
SQLExpression[] whenSqlExprs = new SQLExpression[conditions.size()];
SQLExpression[] actionSqlExprs = new SQLExpression[conditions.size()];
boolean numericCase = false;
boolean booleanCase = false;
boolean stringCase = false;
boolean typeSet = false;
if (typeExpr != null) {
if (typeExpr instanceof NumericExpression) {
numericCase = true;
typeSet = true;
} else if (typeExpr instanceof BooleanExpression) {
booleanCase = true;
typeSet = true;
} else if (typeExpr instanceof StringExpression) {
stringCase = true;
typeSet = true;
}
}
int i = 0;
while (whenExprIter.hasNext()) {
ExpressionPair pair = whenExprIter.next();
Expression whenExpr = pair.getWhenExpression();
whenExpr.evaluate(this);
whenSqlExprs[i] = stack.pop();
if (!(whenSqlExprs[i] instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("IF/ELSE conditional expression should return boolean but doesn't : " + expr);
}
Expression actionExpr = pair.getActionExpression();
actionExpr.evaluate(this);
actionSqlExprs[i] = stack.pop();
if (!typeSet) {
if (actionSqlExprs[i] instanceof NumericExpression) {
numericCase = true;
typeSet = true;
} else if (actionSqlExprs[i] instanceof BooleanExpression) {
booleanCase = true;
typeSet = true;
} else if (actionSqlExprs[i] instanceof StringExpression) {
stringCase = true;
typeSet = true;
}
}
i++;
}
Expression elseExpr = expr.getElseExpression();
elseExpr.evaluate(this);
SQLExpression elseActionSqlExpr = stack.pop();
// Check that all action sql expressions are consistent
for (int j = 1; j < actionSqlExprs.length; j++) {
if (!checkCaseExpressionsConsistent(actionSqlExprs[0], actionSqlExprs[j])) {
throw new QueryCompilerSyntaxException("IF/ELSE action expression " + actionSqlExprs[j] + " is of different type to first action " + actionSqlExprs[0] + " - must be consistent");
}
}
if (!checkCaseExpressionsConsistent(actionSqlExprs[0], elseActionSqlExpr)) {
throw new QueryCompilerSyntaxException("IF/ELSE action expression " + elseActionSqlExpr + " is of different type to first action " + actionSqlExprs[0] + " - must be consistent");
}
SQLExpression caseSqlExpr = null;
if (numericCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseNumericExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else if (booleanCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseBooleanExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else if (stringCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseStringExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
}
stack.push(caseSqlExpr);
return caseSqlExpr;
} finally {
processingCase = false;
}
}
use of org.datanucleus.store.query.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method processParameterExpression.
/**
* Method to process a parameter expression.
* The optional argument controls whether we should create this as a parameter or as a literal (i.e the param value is known etc).
* If the parameter doesn't have its value defined then returns ParameterLiteral otherwise we get an XXXLiteral of the (declared) type of the parameter
* @param expr The ParameterExpression
* @param asLiteral Whether to create a SQLLiteral rather than a parameter literal
* @return The processed expression
*/
protected Object processParameterExpression(ParameterExpression expr, boolean asLiteral) {
if (compileComponent == CompilationComponent.ORDERING || compileComponent == CompilationComponent.RESULT) {
// All JDBC drivers I know don't allow parameters in the order-by, or update clause
// Note that we also don't allow parameters in result clause since SQLStatement squashes all SELECT expression to a String so losing info about params
asLiteral = true;
} else if (compileComponent == CompilationComponent.UPDATE && processingCase && !storeMgr.getDatastoreAdapter().supportsOption(DatastoreAdapter.PARAMETER_IN_CASE_IN_UPDATE_CLAUSE)) {
// This database doesn't support parameters within a CASE expression in the UPDATE clause, so process as a literal
asLiteral = true;
}
if (expr.getPosition() >= 0) {
if (paramNameByPosition == null) {
paramNameByPosition = new HashMap<>();
}
paramNameByPosition.put(Integer.valueOf(expr.getPosition()), expr.getId());
}
// Find the parameter value if supplied
Object paramValue = null;
boolean paramValueSet = false;
if (parameters != null && parameters.size() > 0) {
// Check if the parameter has a value
if (parameters.containsKey(expr.getId())) {
// Named parameter
paramValue = parameters.get(expr.getId());
paramValueSet = true;
} else if (parameterValueByName != null && parameterValueByName.containsKey(expr.getId())) {
// Positional parameter, but already encountered
paramValue = parameterValueByName.get(expr.getId());
paramValueSet = true;
} else {
// Positional parameter, not yet encountered
int position = positionalParamNumber;
if (positionalParamNumber < 0) {
position = 0;
}
if (parameters.containsKey(Integer.valueOf(position))) {
paramValue = parameters.get(Integer.valueOf(position));
paramValueSet = true;
positionalParamNumber = position + 1;
if (parameterValueByName == null) {
parameterValueByName = new HashMap<>();
}
parameterValueByName.put(expr.getId(), paramValue);
}
}
}
// Find the type to use for the parameter
JavaTypeMapping m = paramMappingForName.get(expr.getId());
if (m == null) {
// Try to determine from provided parameter value or from symbol table (declared type)
if (paramValue != null) {
if (!storeMgr.getMetaDataManager().isClassPersistable(paramValue.getClass().getName()) && !paramValue.getClass().isArray() && !paramValue.getClass().isInterface() && !Collection.class.isAssignableFrom(paramValue.getClass()) && !Map.class.isAssignableFrom(paramValue.getClass()) && !storeMgr.getNucleusContext().getTypeManager().isSupportedSecondClassType(paramValue.getClass().getName())) {
// Test for this being the "id" of a persistable object
// Persistable/array/interface/collection/map/simple cannot be an object "id"
String className = storeMgr.getClassNameForObjectID(paramValue, clr, ec);
if (className != null) {
// Identity for persistable class
AbstractClassMetaData cmd = storeMgr.getMetaDataManager().getMetaDataForClass(className, clr);
if (cmd.getIdentityType() == IdentityType.APPLICATION) {
Class cls = clr.classForName(className);
m = exprFactory.getMappingForType(cls, false);
m = new PersistableIdMapping((PersistableMapping) m);
}
}
}
if (m == null) {
// Use the type of the input parameter value
try {
m = exprFactory.getMappingForType(paramValue.getClass(), false);
if (m instanceof TypeConverterMapping && expr.getSymbol().getValueType() != null && expr.getSymbol().getValueType() != m.getJavaType()) {
// This is because if we have a parameter of type "ZoneInfo" it needs to use TimeZone since we have the TypeConverter for that
try {
m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
} catch (NucleusUserException nue) {
}
}
} catch (NucleusUserException nue) {
// Maybe it needs a TypeConverter so try with the (declared) symbol type of this parameter
m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
}
}
if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
if (!QueryUtils.queryParameterTypesAreCompatible(expr.getSymbol().getValueType(), paramValue.getClass())) {
throw new QueryCompilerSyntaxException(Localiser.msg("021118", expr.getId(), expr.getSymbol().getValueType().getName(), paramValue.getClass().getName()));
}
if (expr.getSymbol().getValueType() != paramValue.getClass()) {
// Mark as not precompilable since the supplied type implies a subclass of the declared type
setNotPrecompilable();
}
}
} else if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
Class valueType = expr.getSymbol().getValueType();
if (!paramValueSet) {
if (valueType.isInterface()) {
// Special case where we have an interface parameter (not set), and don't know the type, so we pick the first implementation just to get something that works
// This is recompiled when the parameter is provided so is just for use in "compile()"
String[] implNames = storeMgr.getMetaDataManager().getClassesImplementingInterface(valueType.getName(), clr);
if (implNames != null && implNames.length > 0) {
valueType = clr.classForName(implNames[0]);
setNotPrecompilable();
}
}
}
// Use the declared type of the parameter (explicit params)
m = exprFactory.getMappingForType(valueType, false);
}
}
if (asLiteral && m != null && !m.representableAsStringLiteralInStatement()) {
// Must keep this as a parameter since its String form is no good in statements
asLiteral = false;
}
if (asLiteral) {
// Parameter being represented as a literal (for whatever reason), so no longer precompilable
if (isPrecompilable()) {
NucleusLogger.QUERY.debug("Parameter " + expr + " is being resolved as a literal, so the query is no longer precompilable");
}
setNotPrecompilable();
} else if (paramValue == null && expr.getSymbol() != null) {
if (isPrecompilable()) {
NucleusLogger.QUERY.debug("Parameter " + expr + " is set to null so this has to be resolved as a NullLiteral, and the query is no longer precompilable");
}
setNotPrecompilable();
}
// Create the SQLExpression for this parameter, either as value-literal or as parameter-literal
SQLExpression sqlExpr = null;
if (paramValueSet && paramValue == null && options.contains(OPTION_NULL_PARAM_USE_IS_NULL)) {
// Value is set to null, but we enforce a NullLiteral for the case of null comparisons e.g we don't want "field = ?", but instead "field IS NULL"
sqlExpr = exprFactory.newLiteral(stmt, null, null);
} else if (asLiteral) {
// Create a value-literal as requested
sqlExpr = exprFactory.newLiteral(stmt, m, paramValue);
} else {
// Create a parameter-literal with it tied to the parameter name for later replacement in the statement
sqlExpr = exprFactory.newLiteralParameter(stmt, m, paramValue, expr.getId());
if (sqlExpr instanceof ParameterLiteral) {
((ParameterLiteral) sqlExpr).setName(expr.getId());
}
if (expressionForParameter == null) {
expressionForParameter = new HashMap<>();
}
expressionForParameter.put(expr.getId(), sqlExpr);
paramMappingForName.put(expr.getId(), m);
}
stack.push(sqlExpr);
return sqlExpr;
}
use of org.datanucleus.store.query.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileFilter.
/**
* Method to compile the WHERE clause of the query into the SQLStatement.
*/
protected void compileFilter() {
if (compilation.getExprFilter() != null) {
// Apply the filter to the SQLStatement
compileComponent = CompilationComponent.FILTER;
if (QueryUtils.expressionHasOrOperator(compilation.getExprFilter())) {
compileProperties.put("Filter.OR", true);
}
if (QueryUtils.expressionHasNotOperator(compilation.getExprFilter())) {
// Really we want to know if there is a NOT contains, but just check NOT for now
compileProperties.put("Filter.NOT", true);
}
if (stmt instanceof SelectStatement && ((SelectStatement) stmt).getNumberOfUnions() > 0) {
// Process each UNIONed statement separately TODO This is only necessary when the filter contains things like "instanceof"/"TYPE" so maybe detect that
List<SelectStatement> unionStmts = ((SelectStatement) stmt).getUnions();
// a). Main SelectStatement, disable unions while we process it
SQLStatement originalStmt = stmt;
((SelectStatement) stmt).setAllowUnions(false);
SQLExpression filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
BooleanExpression filterExpr = getBooleanExpressionForUseInFilter((BooleanExpression) filterSqlExpr);
stmt.whereAnd(filterExpr, true);
((SelectStatement) stmt).setAllowUnions(true);
// b). UNIONed SelectStatements
for (SelectStatement unionStmt : unionStmts) {
stmt = unionStmt;
stmt.setQueryGenerator(this);
filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
filterExpr = getBooleanExpressionForUseInFilter((BooleanExpression) filterSqlExpr);
stmt.whereAnd(filterExpr, true);
stmt.setQueryGenerator(null);
}
stmt = originalStmt;
} else {
SQLExpression filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
BooleanExpression filterExpr = (BooleanExpression) filterSqlExpr;
filterExpr = getBooleanExpressionForUseInFilter(filterExpr);
stmt.whereAnd(filterExpr, true);
}
compileComponent = null;
}
}
use of org.datanucleus.store.query.QueryCompilerSyntaxException in project datanucleus-core by datanucleus.
the class JPQLParser method processLiteral.
/**
* A literal is one value of any type.
* Supported literals are of types String, Floating Point, Integer, Character, Boolean and null e.g. 'J', "String", 1, 1.8, true, false, null.
* Also supports JDBC "escape syntax" for literals.
* @return The compiled literal
*/
protected boolean processLiteral() {
if (lexer.parseChar('{')) {
// JDBC escape syntax {d '...'} or {ts '...'} or {t '...'}
StringBuilder jdbcLiteralStr = new StringBuilder("{");
if (lexer.parseChar('d')) {
jdbcLiteralStr.append("d ");
} else if (lexer.parseString("ts")) {
jdbcLiteralStr.append("ts ");
} else if (lexer.parseChar('t')) {
jdbcLiteralStr.append("t ");
} else {
throw new QueryCompilerSyntaxException("d, ts or t expected after { (JDBC escape syntax)", lexer.getIndex(), lexer.getInput());
}
if (lexer.nextIsSingleQuote()) {
String datetimeLit = lexer.parseStringLiteral();
jdbcLiteralStr.append("'").append(datetimeLit).append("'");
if (lexer.parseChar('}')) {
jdbcLiteralStr.append('}');
stack.push(new Node(NodeType.LITERAL, jdbcLiteralStr.toString()));
return true;
}
throw new QueryCompilerSyntaxException("} expected in JDBC escape syntax", lexer.getIndex(), lexer.getInput());
}
throw new QueryCompilerSyntaxException("'...' expected in JDBC escape syntax", lexer.getIndex(), lexer.getInput());
}
Object litValue = null;
String sLiteral;
BigDecimal fLiteral;
BigInteger iLiteral;
Boolean bLiteral;
boolean single_quote_next = lexer.nextIsSingleQuote();
if ((sLiteral = lexer.parseStringLiteral()) != null) {
// Both String and Character are allowed to use single-quotes so we need to check if it was single-quoted and use CharacterLiteral if length is 1.
if (sLiteral.length() == 1 && single_quote_next) {
litValue = Character.valueOf(sLiteral.charAt(0));
} else {
litValue = sLiteral;
}
} else if ((fLiteral = lexer.parseFloatingPointLiteral()) != null) {
litValue = fLiteral;
} else if ((iLiteral = lexer.parseIntegerLiteral()) != null) {
// Represent as BigInteger or Long depending on length
String longStr = "" + iLiteral.longValue();
if (longStr.length() < iLiteral.toString().length()) {
litValue = iLiteral;
} else {
litValue = iLiteral.longValue();
}
} else if ((bLiteral = lexer.parseBooleanLiteralIgnoreCase()) != null) {
litValue = bLiteral;
} else if (lexer.parseNullLiteralIgnoreCase()) {
} else {
return false;
}
stack.push(new Node(NodeType.LITERAL, litValue));
return true;
}
use of org.datanucleus.store.query.QueryCompilerSyntaxException in project datanucleus-core by datanucleus.
the class JPQLParser method processMethod.
/**
* Method to parse "methodName(param1[,param2], ...)" and create a Node of type INVOKE.
* The Node at the top of the stack after this call will have any arguments defined in its "properties".
* @return whether method syntax was found.
*/
private boolean processMethod() {
String method = lexer.parseMethod();
if (method != null) {
lexer.skipWS();
lexer.parseChar('(');
// Use uppercase forms of aggregate methods in generic compilation
if (method.equalsIgnoreCase("COUNT")) {
method = "COUNT";
} else if (method.equalsIgnoreCase("AVG")) {
method = "AVG";
} else if (method.equalsIgnoreCase("MIN")) {
method = "MIN";
} else if (method.equalsIgnoreCase("MAX")) {
method = "MAX";
} else if (method.equalsIgnoreCase("SUM")) {
method = "SUM";
} else if (method.equalsIgnoreCase("ABS")) {
method = "ABS";
} else if (method.equalsIgnoreCase("INDEX")) {
method = "INDEX";
} else if (method.equalsIgnoreCase("FUNCTION")) {
// JPA 2.1 FUNCTION for invoking SQL functions
method = "FUNCTION";
}
if (method.equalsIgnoreCase("Object")) {
// "Object(p)", so interpret as "p"
// identifier at top of stack
processExpression();
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
return true;
} else if (method.equalsIgnoreCase("MOD")) {
// Convert to be {first} % {second}
Node modNode = new Node(NodeType.OPERATOR, "%");
// argument 1
processExpression();
Node firstNode = stack.pop();
if (!lexer.parseChar(',')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
// argument 2
processExpression();
Node secondNode = stack.pop();
modNode.appendChildNode(firstNode);
modNode.appendChildNode(secondNode);
stack.push(modNode);
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
return true;
} else if (method.equalsIgnoreCase("TYPE")) {
// Convert to a TYPE node with the primary as a child node
Node typeNode = new Node(NodeType.TYPE);
// argument
processExpression();
Node typePrimaryNode = stack.pop();
typeNode.appendChildNode(typePrimaryNode);
stack.push(typeNode);
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
return true;
} else if (method.equalsIgnoreCase("SUBSTRING")) {
// SUBSTRING(string_primary, simple_arithmetic_expression[, simple_arithmetic_expression])
// Convert to be {primary}.INVOKE(substring, {arg1[, arg2]})
Node invokeNode = new Node(NodeType.INVOKE, "substring");
processExpression();
Node primaryNode = stack.pop();
if (!lexer.parseChar(',')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
// First arg to substring(...) has origin 0, but JPQL has origin 1!
processExpression();
Node arg1 = stack.pop();
Node oneNode = new Node(NodeType.LITERAL, 1);
Node arg1Node = new Node(NodeType.OPERATOR, "-");
arg1Node.insertChildNode(arg1);
arg1Node.appendChildNode(oneNode);
if (lexer.parseChar(',')) {
// String.substring(arg1, arg2)
// Second arg to substring(...) has origin 0, but in JPQL is length of result!
processExpression();
Node arg2 = stack.pop();
Node arg2Node = new Node(NodeType.OPERATOR, "+");
arg2Node.appendChildNode(arg2);
arg2Node.appendChildNode(arg1Node);
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
primaryNode.appendChildNode(invokeNode);
invokeNode.addProperty(arg1Node);
invokeNode.addProperty(arg2Node);
stack.push(primaryNode);
return true;
} else if (lexer.parseChar(')')) {
// String.substring(arg1)
primaryNode.appendChildNode(invokeNode);
invokeNode.addProperty(arg1Node);
stack.push(primaryNode);
return true;
} else {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
} else if (method.equalsIgnoreCase("UPPER")) {
// UPPER(string_primary)
// Convert to be {primary}.INVOKE(toUpper)
Node invokeNode = new Node(NodeType.INVOKE, "toUpperCase");
processExpression();
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
Node primaryNode = stack.pop();
Node primaryRootNode = primaryNode;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(primaryRootNode);
return true;
} else if (method.equalsIgnoreCase("LOWER")) {
// UPPER(string_primary)
// Convert to be {primary}.INVOKE(toLower)
Node invokeNode = new Node(NodeType.INVOKE, "toLowerCase");
processExpression();
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
Node primaryNode = stack.pop();
Node primaryRootNode = primaryNode;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(primaryRootNode);
return true;
} else if (method.equalsIgnoreCase("LENGTH")) {
// LENGTH(string_primary)
// Convert to be {primary}.INVOKE(length)
Node invokeNode = new Node(NodeType.INVOKE, "length");
processExpression();
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
Node primaryNode = stack.pop();
Node primaryRootNode = primaryNode;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(primaryRootNode);
return true;
} else if (method.equalsIgnoreCase("CONCAT")) {
// CONCAT(string_primary, string_primary[, string_primary])
// Convert to be {primary1}+{primary2}[+primary3]
processExpression();
Node prevNode = stack.pop();
while (true) {
if (!lexer.parseChar(',')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
processExpression();
Node thisNode = stack.pop();
Node currentNode = new Node(NodeType.OPERATOR, "+");
currentNode.appendChildNode(prevNode);
currentNode.appendChildNode(thisNode);
if (lexer.parseChar(')')) {
stack.push(currentNode);
return true;
}
prevNode = currentNode;
currentNode = null;
}
} else if (method.equalsIgnoreCase("LOCATE")) {
// LOCATE(string_primary, string_primary[, simple_arithmetic_expression])
// Convert to ({stringExpr}.indexOf(strExpr[, posExpr]) + 1)
processExpression();
Node searchNode = stack.pop();
Node invokeNode = new Node(NodeType.INVOKE, "indexOf");
invokeNode.addProperty(searchNode);
if (!lexer.parseChar(',')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
processExpression();
Node primaryNode = stack.pop();
Node primaryRootNode = primaryNode;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
Node oneNode = new Node(NodeType.LITERAL, 1);
if (lexer.parseChar(',')) {
processExpression();
Node fromPosNode = stack.pop();
Node positionNode = new Node(NodeType.OPERATOR, "-");
positionNode.appendChildNode(fromPosNode);
positionNode.appendChildNode(oneNode);
invokeNode.addProperty(positionNode);
}
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
Node locateNode = new Node(NodeType.OPERATOR, "+");
locateNode.appendChildNode(primaryRootNode);
locateNode.appendChildNode(oneNode);
stack.push(locateNode);
return true;
} else if (method.equalsIgnoreCase("TRIM")) {
// TRIM([[LEADING | TRAILING | BOTH] [trim_character] FROM] string_primary)
// Convert to be {primary}.INVOKE(trim|trimLeft|trimRight, [{trimChar}])
String methodName = "trim";
if (lexer.parseStringIgnoreCase("LEADING")) {
methodName = "trimLeft";
} else if (lexer.parseStringIgnoreCase("TRAILING")) {
methodName = "trimRight";
} else if (lexer.parseStringIgnoreCase("BOTH")) {
// Default
}
Node invokeNode = new Node(NodeType.INVOKE, methodName);
processExpression();
Node next = stack.pop();
if (lexer.parseChar(')')) {
// TRIM(string_primary)
// Find the last part of the identifier node and append the invoke
Node primaryNode = next;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(next);
return true;
}
if (next.getNodeType() == NodeType.LITERAL) {
// TRIM(dir trimChar FROM string_primary)
Node trimCharNode = next;
if (lexer.parseStringIgnoreCase("FROM ")) {
// Ignore the FROM
}
processExpression();
next = stack.pop();
if (trimCharNode != null) {
// Append the trim character to the invoke node
invokeNode.addProperty(trimCharNode);
}
} else if (next.getNodeType() == NodeType.IDENTIFIER) {
// TRIM(dir FROM string_primary)
Object litValue = next.getNodeValue();
if (litValue instanceof String && ((String) litValue).equals("FROM")) {
// FROM so ignore
// field expression that we are trimming
processExpression();
next = stack.pop();
} else {
throw new QueryCompilerSyntaxException("Unexpected expression", lexer.getIndex(), lexer.getInput());
}
} else {
// No "trimChar" or FROM, so "next" is the string expression node
}
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
// Find the last part of the identifier node and append the invoke
Node primaryNode = next;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(next);
return true;
} else if (method.equalsIgnoreCase("SIZE")) {
// SIZE(collection_valued_path_expression)
// Convert to be {primary}.INVOKE(size)
Node invokeNode = new Node(NodeType.INVOKE, "size");
processExpression();
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("',' expected", lexer.getIndex(), lexer.getInput());
}
// Could check type ? (Collection/Map/array)
Node primaryNode = stack.pop();
Node primaryRootNode = primaryNode;
while (primaryNode.getFirstChild() != null) {
primaryNode = primaryNode.getFirstChild();
}
primaryNode.appendChildNode(invokeNode);
stack.push(primaryRootNode);
return true;
} else if (method.equalsIgnoreCase("FUNCTION")) {
// FUNCTION - Convert to be {primary}.INVOKE("SQL_function", ...)
// Extract sql function name
processExpression();
Node sqlFunctionNode = stack.pop();
Node invokeNode = new Node(NodeType.INVOKE, "SQL_function");
invokeNode.addProperty(sqlFunctionNode);
if (lexer.parseChar(',')) {
// Process arguments for function "aaa[,bbb[,ccc]] etc )"
do {
// Argument for the method call, add as a node property
processExpression();
invokeNode.addProperty(stack.pop());
} while (lexer.parseChar(','));
}
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
stack.push(invokeNode);
return true;
} else {
// Found syntax for a method, so invoke the method
// TODO What if the method is not supported for JPQL?
Node node = new Node(NodeType.INVOKE, method);
if (!lexer.parseChar(')')) {
do {
// Argument for the method call, add as a node property
processExpression();
node.addProperty(stack.pop());
} while (lexer.parseChar(','));
if (!lexer.parseChar(')')) {
throw new QueryCompilerSyntaxException("')' expected", lexer.getIndex(), lexer.getInput());
}
}
stack.push(node);
return true;
}
}
return false;
}
Aggregations