Search in sources :

Example 1 with QueryCompilerSyntaxException

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;
    }
}
Also used : SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) NumericExpression(org.datanucleus.store.rdbms.sql.expression.NumericExpression) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) CaseExpression(org.datanucleus.query.expression.CaseExpression) BooleanSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.BooleanSubqueryExpression) StringExpression(org.datanucleus.store.rdbms.sql.expression.StringExpression) JoinExpression(org.datanucleus.query.expression.JoinExpression) NumericSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.NumericSubqueryExpression) StringSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.StringSubqueryExpression) ClassExpression(org.datanucleus.query.expression.ClassExpression) InvokeExpression(org.datanucleus.query.expression.InvokeExpression) MapExpression(org.datanucleus.store.rdbms.sql.expression.MapExpression) SubqueryExpression(org.datanucleus.query.expression.SubqueryExpression) NewObjectExpression(org.datanucleus.store.rdbms.sql.expression.NewObjectExpression) TemporalSubqueryExpression(org.datanucleus.store.rdbms.sql.expression.TemporalSubqueryExpression) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) OrderExpression(org.datanucleus.query.expression.OrderExpression) PrimaryExpression(org.datanucleus.query.expression.PrimaryExpression) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) UnboundExpression(org.datanucleus.store.rdbms.sql.expression.UnboundExpression) TemporalExpression(org.datanucleus.store.rdbms.sql.expression.TemporalExpression) ArrayExpression(org.datanucleus.query.expression.ArrayExpression) ResultAliasExpression(org.datanucleus.store.rdbms.sql.expression.ResultAliasExpression) CreatorExpression(org.datanucleus.query.expression.CreatorExpression) Expression(org.datanucleus.query.expression.Expression) TypeExpression(org.datanucleus.query.expression.TypeExpression) NumericExpression(org.datanucleus.store.rdbms.sql.expression.NumericExpression) CollectionExpression(org.datanucleus.store.rdbms.sql.expression.CollectionExpression) DyadicExpression(org.datanucleus.query.expression.DyadicExpression) ParameterExpression(org.datanucleus.query.expression.ParameterExpression) ColumnExpression(org.datanucleus.store.rdbms.sql.expression.ColumnExpression) VariableExpression(org.datanucleus.query.expression.VariableExpression) StringExpression(org.datanucleus.store.rdbms.sql.expression.StringExpression) QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) ExpressionPair(org.datanucleus.query.expression.CaseExpression.ExpressionPair)

Example 2 with QueryCompilerSyntaxException

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;
}
Also used : ParameterLiteral(org.datanucleus.store.rdbms.sql.expression.ParameterLiteral) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) HashMap(java.util.HashMap) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) PersistableIdMapping(org.datanucleus.store.rdbms.mapping.java.PersistableIdMapping) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) TypeConverterMapping(org.datanucleus.store.rdbms.mapping.java.TypeConverterMapping) QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) Map(java.util.Map) HashMap(java.util.HashMap)

Example 3 with QueryCompilerSyntaxException

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;
    }
}
Also used : SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) BooleanExpression(org.datanucleus.store.rdbms.sql.expression.BooleanExpression) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) SQLStatement(org.datanucleus.store.rdbms.sql.SQLStatement)

Example 4 with QueryCompilerSyntaxException

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;
}
Also used : QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) BigInteger(java.math.BigInteger) BigDecimal(java.math.BigDecimal)

Example 5 with QueryCompilerSyntaxException

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;
}
Also used : QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException)

Aggregations

QueryCompilerSyntaxException (org.datanucleus.store.query.QueryCompilerSyntaxException)20 ArrayList (java.util.ArrayList)7 List (java.util.List)5 HashMap (java.util.HashMap)3 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)3 SQLExpression (org.datanucleus.store.rdbms.sql.expression.SQLExpression)3 Map (java.util.Map)2 AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)2 Symbol (org.datanucleus.query.compiler.Symbol)2 Expression (org.datanucleus.query.expression.Expression)2 ParameterExpression (org.datanucleus.query.expression.ParameterExpression)2 PrimaryExpression (org.datanucleus.query.expression.PrimaryExpression)2 VariableExpression (org.datanucleus.query.expression.VariableExpression)2 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)2 BooleanExpression (org.datanucleus.store.rdbms.sql.expression.BooleanExpression)2 BigDecimal (java.math.BigDecimal)1 BigInteger (java.math.BigInteger)1 StringTokenizer (java.util.StringTokenizer)1 FetchPlanForClass (org.datanucleus.FetchPlanForClass)1 NucleusException (org.datanucleus.exceptions.NucleusException)1