Package org.datanucleus.query.compiler

Source Code of org.datanucleus.query.compiler.SQLParser

/**********************************************************************
Copyright (c) 2008 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.query.compiler;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.query.node.Node;
import org.datanucleus.store.query.QueryCompilerSyntaxException;

/**
* Implementation of a parser for SQL query language.
*/
public class SQLParser implements Parser
{
    private Lexer p;
    private Stack stack = new Stack();

    /** Characters that parameters can be prefixed by. */
    private static String paramPrefixes = ":?"; // TODO Fix this - params are "?"

    /**
     * Constructor for an SQL Parser.
     * @param options parser options
     */
    public SQLParser(Map options)
    {
       
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parse(java.lang.String)
     */
    public Node parse(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        stack = new Stack();
        return compileExpression();
    }
   
    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseVariable(java.lang.String)
     */
    public Node parseVariable(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        stack = new Stack();
        if (!compileIdentifier())
        {
            throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
        }
        if (!compileIdentifier())
        {
            throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
        }
        Node nodeVariable = (Node) stack.pop();
        Node nodeType = (Node) stack.pop();
        nodeType.appendChildNode(nodeVariable);
        return nodeType;
    }

    /**
     * Method to compile the "from" clause, but JDOQL has no "from" so do nothing.
     * @param expression From string
     * @return Node trees for this from clause
     */
    public Node[] parseFrom(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        stack = new Stack();
        return compileFromExpression();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseUpdate(java.lang.String)
     */
    public Node[] parseUpdate(String expression)
    {
        // Bulk update not supported (yet) by SQL
        return null;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseOrder(java.lang.String)
     */
    public Node[] parseOrder(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        stack = new Stack();
        return compileOrderExpression();
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseTupple(java.lang.String)
     */
    public Node[] parseTupple(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        stack = new Stack();
        List nodes = new ArrayList();
        do
        {
            compileExpression();
            Node expr = (Node) stack.pop();
            nodes.add(expr);
        }
        while (p.parseString(","));
        return (Node[])nodes.toArray(new Node[nodes.size()]);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseVariables(java.lang.String)
     */
    public Node[][] parseVariables(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        List nodes = new ArrayList();
        do
        {
            compilePrimary();
            if (stack.isEmpty())
            {
                throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
            }
            if (!compileIdentifier())
            {
                throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
            }
            Node nodeVariable = (Node) stack.pop();
            Node nodeType = (Node) stack.pop();
            nodes.add(new Node[]{nodeType, nodeVariable});
        }
        while (p.parseString(";"));
        return (Node[][]) nodes.toArray(new Node[nodes.size()][2]);
    }

    /* (non-Javadoc)
     * @see org.datanucleus.query.compiler.Parser#parseParameters(java.lang.String)
     */
    public Node[][] parseParameters(String expression)
    {
        p = new Lexer(expression, paramPrefixes);
        List nodes = new ArrayList();
        do
        {
            compilePrimary();
            if (stack.isEmpty())
            {
                throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
            }
            if (!compileIdentifier())
            {
                throw new QueryCompilerSyntaxException("expected identifier", p.getIndex(), p.getInput());
            }
            Node nodeVariable = (Node) stack.pop();
            Node nodeType = (Node) stack.pop();
            nodes.add(new Node[]{nodeType, nodeVariable});
        }
        while (p.parseString(","));
        return (Node[][]) nodes.toArray(new Node[nodes.size()][2]);
    }

    /**
     * The FROM expression in SQL is a comma-separated list of expressions. Each expression can be
     * <ul>
     * <li>"IN {expression} [AS] alias [JOIN ... AS ...]"</li>
     * <li>mydomain.MyClass [AS] alias [JOIN ... AS ...]"</li>
     * </ul>
     * TODO Change this to be ANSI JOIN conditions
     * @return Node tree(s) for the FROM expression
     */
    private Node[] compileFromExpression()
    {
        String candidateClassName = null;
        List nodes = new ArrayList();
        do
        {
            if (p.parseStringIgnoreCase("IN"))
            {
                // IN expression
                // This will create a node of type "Node.CLASS" (candidate) and child of type "Node.NAME" (alias)
                // Any IN/JOINs will be child nodes of type "Node.OPERATOR"
                // Node(CLASS, "org.datanucleus.CandidateClass)
                // +--- Node(OPERATOR, "JOIN_INNER")
                //    +--- Node(IDENTIFIER, "p.myField")
                //       +--- Node(NAME, "f")
                if (!p.parseChar('('))
                {
                    throw new QueryCompilerSyntaxException("Expected: '(' but got " + p.remaining(),
                        p.getIndex(), p.getInput());
                }

                // Find what we are joining to
                String name = p.parseIdentifier();
                if (p.nextIsDot())
                {
                    p.parseChar('.');
                    name += ".";
                    name += p.parseName();
                }

                if (!p.parseChar(')'))
                {
                    throw new QueryCompilerSyntaxException("Expected: ')' but got " + p.remaining(),
                        p.getIndex(), p.getInput());
                }

                p.parseStringIgnoreCase("AS"); // Optional
                String alias = p.parseIdentifier();

                // Create candidate class node with alias
                Node classNode = new Node(Node.CLASS, candidateClassName);
                stack.push(classNode);
                Node joinNode = new Node(Node.OPERATOR, "JOIN_INNER");
                classNode.insertChildNode(joinNode);
                Node joinedToNode = new Node(Node.IDENTIFIER, name);
                joinNode.insertChildNode(joinedToNode);
                Node aliasNode = new Node(Node.NAME, alias);
                joinNode.insertChildNode(aliasNode);

                // Include any joins for this expression
                compileFromJoinExpression();
            }
            else
            {
                // "candidate [AS] alias"
                // This will create a node of type "Node.CLASS" and child of type "Node.NAME" (alias)
                // Any JOINs will be child nodes of type "Node.OPERATOR"
                compileExpression();
                Node id = (Node)stack.pop();
                String className = id.getNodeValue().toString();
                while (id.getChildNodes().size() > 0)
                {
                    id = id.getFirstChild();
                    className = className + "." + id.getNodeValue().toString();
                }

                String alias = p.parseIdentifier();
                if (alias != null && alias.equalsIgnoreCase("AS"))
                {
                    alias = p.parseIdentifier();
                }
                if (candidateClassName == null)
                {
                    candidateClassName = className;
                }
                Node classNode = new Node(Node.CLASS, className);
                stack.push(classNode);
                Node aliasNode = new Node(Node.NAME, alias);
                classNode.insertChildNode(aliasNode);

                // Include any joins for this expression
                compileFromJoinExpression();

                nodes.add(classNode);
            }
        }
        while (p.parseString(","));

        return (Node[]) nodes.toArray(new Node[nodes.size()]);
    }

    /**
     * Convenience method to process what remains of a component of the FROM clause, processing
     * the JOIN conditions. The leading part (candidate, or "IN") was processed just before.
     * This will append a child to the candidate node for each join condition encountered.
     * For example, "org.datanucleus.MyClass p INNER JOIN p.myField AS f" will translate to
     * <pre>
     * Node(CLASS, "org.datanucleus.MyClass)
     * +--- Node(NAME, "p")
     * +--- Node(OPERATOR, "JOIN_INNER")
     *    +--- Node(IDENTIFIER, "p.myField")
     *       +--- Node(NAME, "f")
     * </pre>
     * When we enter this method we expect the candidate node to be at the top of the stack, and when
     * we leave this method we leave the candidate node at the top of the stack.
     */
    private void compileFromJoinExpression()
    {
        Node candidateNode = (Node)stack.pop();
        boolean moreJoins = true;
        while (moreJoins)
        {
            // Check for JOIN syntax "[LEFT [OUTER] | INNER] JOIN ..."  (EJB3 syntax)
            boolean leftJoin = false;
            boolean innerJoin = false;
            if (p.parseStringIgnoreCase("INNER"))
            {
                innerJoin = true;
            }
            else if (p.parseStringIgnoreCase("LEFT"))
            {
                //optional and useless (for parser) outer keyword
                p.parseStringIgnoreCase("OUTER");
                leftJoin = true;
            }

            if (p.parseStringIgnoreCase("JOIN"))
            {
                // Find what we are joining to
                String id = p.parseIdentifier();
                String name = id;
                if (p.nextIsDot())
                {
                    p.parseChar('.');
                    name += ".";
                    name += p.parseName();
                }

                // And the alias we know this joined field by
                p.parseStringIgnoreCase("AS"); // Optional
                String alias = p.parseName();

                // TODO Is the joinedNode correct in that it contains a name like "a.field"
                // whereas IDENTIFIER nodes are usually "a" with a subnode of "field"
                Node joinedNode = new Node(Node.IDENTIFIER, name);
                Node joinedAliasNode = new Node(Node.NAME, alias);
                joinedNode.insertChildNode(joinedAliasNode);

                String joinType = "JOIN_INNER";
                if (leftJoin)
                {
                    joinType = "JOIN_OUTER";
                }
                Node joinNode = new Node(Node.OPERATOR, joinType);
                joinNode.insertChildNode(joinedNode);
                candidateNode.appendChildNode(joinNode);
            }
            else
            {
                if (innerJoin || leftJoin)
                {
                    throw new NucleusUserException("Expected JOIN after INNER/LEFT keyword at"+p.remaining());
                }
                moreJoins = false;
            }
        }
        stack.push(candidateNode);
    }

    private Node[] compileOrderExpression()
    {
        List nodes = new ArrayList();
        do
        {
            compileExpression();
            if (p.parseStringIgnoreCase("asc"))
            {
                Node expr = new Node(Node.OPERATOR, "ascending");
                stack.push(expr);
            }
            else if (p.parseStringIgnoreCase("desc"))
            {
                Node expr = new Node(Node.OPERATOR, "descending");
                stack.push(expr);
            }

            Node expr = new Node(Node.OPERATOR, "order");
            expr.insertChildNode((Node) stack.pop());
            if (!stack.empty())
            {
                expr.insertChildNode((Node) stack.pop());
            }
            nodes.add(expr);
        }
        while (p.parseString(","));
        return (Node[]) nodes.toArray(new Node[nodes.size()]);
    }

    private Node compileExpression()
    {
        compileOrExpression();
        return (Node) stack.peek();
    }

    /**
     * This method deals with the OR condition.
     * A condition specifies a combination of one or more expressions and logical (Boolean) operators and
     * returns a value of TRUE, FALSE, or unknown
     */
    private void compileOrExpression()
    {
        compileAndExpression();

        while (p.parseStringIgnoreCase("OR"))
        {
            compileAndExpression();
            Node expr = new Node(Node.OPERATOR, "||");
            expr.insertChildNode((Node) stack.pop());
            expr.insertChildNode((Node) stack.pop());
            stack.push(expr);
        }
    }

    /**
     * This method deals with the AND condition.
     * A condition specifies a combination of one or more expressions and
     * logical (Boolean) operators and returns a value of TRUE, FALSE, or
     * unknown
     */
    private void compileAndExpression()
    {
        compileEqualityExpression();

        while (p.parseStringIgnoreCase("AND"))
        {
            compileEqualityExpression();
            Node expr = new Node(Node.OPERATOR, "&&");
            expr.insertChildNode((Node) stack.pop());
            expr.insertChildNode((Node) stack.pop());
            stack.push(expr);
        }
    }

    private void compileEqualityExpression()
    {
        compileRelationalExpression();

        for (;;)
        {
            if (p.parseString("="))
            {
                compileRelationalExpression();
                Node expr = new Node(Node.OPERATOR, "==");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseString("!=")) // Valid in JPQL ?
            {
                compileRelationalExpression();
                Node expr = new Node(Node.OPERATOR, "!=");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseStringIgnoreCase("NOT"))
            {
                if (p.parseStringIgnoreCase("LIKE"))
                {
                    compileLikeExpression();
                    Node notNode = new Node(Node.OPERATOR, "!");
                    notNode.insertChildNode((Node) stack.pop());
                    stack.push(notNode);
                }
                else
                {
                    throw new NucleusException("Unsupported query syntax NOT followed by unsupported keyword");
                }
            }
            else if (p.parseStringIgnoreCase("LIKE"))
            {
                // {expression} LIKE {pattern_value} [ESCAPE {escape_char}]
                compileLikeExpression();
            }
            else if (p.parseStringIgnoreCase("IS"))
            {
                // {expression} IS [NOT] [NULL | EMPTY]
                Node inputNode = (Node)stack.pop();
                boolean not = false;
                if (p.parseStringIgnoreCase("NOT"))
                {
                    not = true;
                }
                if (p.parseStringIgnoreCase("NULL"))
                {
                    Node isNode = new Node(Node.OPERATOR, (not ? "!=" : "=="));
                    Node compareNode = new Node(Node.LITERAL, null);
                    isNode.insertChildNode(compareNode);
                    isNode.insertChildNode(inputNode);
                    stack.push(isNode);
                }
                else if (p.parseStringIgnoreCase("EMPTY"))
                {
                    // Convert IS EMPTY to a method call of isEmpty() on collection/map
                    Node isNode = new Node(Node.INVOKE, "isEmpty");
                    inputNode.insertChildNode(isNode);
                    if (not)
                    {
                        Node notExpr = new Node(Node.OPERATOR, "!");
                        notExpr.insertChildNode(inputNode);
                        stack.push(notExpr);
                    }
                    else
                    {
                        stack.push(inputNode);
                    }
                }
                else
                {
                    throw new NucleusException("Encountered IS " + (not ? "NOT " : " ") +
                        " that should be followed by NULL | EMPTY but isnt");
                }
            }
            else
            {
                break;
            }
        }
    }

    /**
     * Convenience handler to compile a LIKE expression.
     * Expression is of the form "{expression} LIKE {pattern_value} [ESCAPE {escape_char}]".
     * Requires that the node at the top of the stack is the field expression node.
     * At return the like expression node will be at the top of the stack.
     */
    private void compileLikeExpression()
    {
        Node inputNode = (Node)stack.pop();
        compileAdditiveExpression();
        Node likeExprNode = (Node)stack.pop();
        if (p.parseStringIgnoreCase("ESCAPE"))
        {
            compileAdditiveExpression();
            /*Node escapeNode = (Node)*/stack.pop();
            // TODO Use the escapeNode
            Node likeNode = new Node(Node.OPERATOR, "LIKE");
            likeNode.insertChildNode(likeExprNode);
            likeNode.insertChildNode(inputNode);
            stack.push(likeNode);
        }
        else
        {
            Node likeNode = new Node(Node.OPERATOR, "LIKE");
            likeNode.insertChildNode(likeExprNode);
            likeNode.insertChildNode(inputNode);
            stack.push(likeNode);
        }
    }

    private void compileRelationalExpression()
    {
        compileAdditiveExpression();

        for (;;)
        {
            if (p.parseString("<="))
            {
                compileAdditiveExpression();
                Node expr = new Node(Node.OPERATOR, "<=");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseString(">="))
            {
                compileAdditiveExpression();
                Node expr = new Node(Node.OPERATOR, ">=");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseChar('<'))
            {
                compileAdditiveExpression();
                Node expr = new Node(Node.OPERATOR, "<");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseChar('>'))
            {
                compileAdditiveExpression();
                Node expr = new Node(Node.OPERATOR, ">");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseStringIgnoreCase("instanceof"))
            {
                compileAdditiveExpression();
                Node expr = new Node(Node.OPERATOR, "instanceof");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else
            {
                break;
            }
        }
    }

    protected void compileAdditiveExpression()
    {
        compileMultiplicativeExpression();

        for (;;)
        {
            if (p.parseChar('+'))
            {
                compileMultiplicativeExpression();
                Node expr = new Node(Node.OPERATOR, "+");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseChar('-'))
            {
                compileMultiplicativeExpression();
                Node expr = new Node(Node.OPERATOR, "-");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else
            {
                break;
            }
        }
    }

    protected void compileMultiplicativeExpression()
    {
        compileUnaryExpression();

        for (;;)
        {
            if (p.parseChar('*'))
            {
                compileMultiplicativeExpression();
                Node expr = new Node(Node.OPERATOR, "*");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseChar('/'))
            {
                compileMultiplicativeExpression();
                Node expr = new Node(Node.OPERATOR, "/");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else if (p.parseChar('%'))
            {
                compileMultiplicativeExpression();
                Node expr = new Node(Node.OPERATOR, "%");
                expr.insertChildNode((Node) stack.pop());
                expr.insertChildNode((Node) stack.pop());
                stack.push(expr);
            }
            else
            {
                break;
            }
        }
    }

    protected void compileUnaryExpression()
    {
        if (p.parseString("++"))
        {
            throw new NucleusUserException("Unsupported operator '++'");
        }
        else if (p.parseString("--"))
        {
            throw new NucleusUserException("Unsupported operator '--'");
        }

        if (p.parseChar('+'))
        {
            compileUnaryExpression();
            Node expr = new Node(Node.OPERATOR, "+");
            expr.insertChildNode((Node) stack.pop());
            stack.push(expr);
        }
        else if (p.parseChar('-'))
        {
            compileUnaryExpression();
            Node expr = new Node(Node.OPERATOR, "-");
            expr.insertChildNode((Node) stack.pop());
            stack.push(expr);
        }
        else
        {
            compileUnaryExpressionNotPlusMinus();
        }
    }

    protected void compileUnaryExpressionNotPlusMinus()
    {
        if (p.parseStringIgnoreCase("NOT"))
        {
            compileUnaryExpression();
            Node expr = new Node(Node.OPERATOR, "!");
            expr.insertChildNode((Node) stack.pop());
            stack.push(expr);
        }
        else
        {
            compilePrimary();
        }
    }

    /**
     * Compiles the primary. First look for a literal (e.g. "text"), then
     * an identifier(e.g. variable) In the next step, call a function, if
     * executing a function, on the literal or the identifier found.
     */
    protected void compilePrimary()
    {
        if (p.parseStringIgnoreCase("DISTINCT"))
        {
            // Aggregates can have "count(DISTINCT field1)"
            Node distinctNode = new Node(Node.OPERATOR, "DISTINCT");
            compileIdentifier();
            Node identifierNode = (Node)stack.pop();
            distinctNode.appendChildNode(identifierNode);
            stack.push(distinctNode);
            return;
        }
        if (compileLiteral())
        {
            return;
        }
        if (compileMethod())
        {
            return;
        }
        if (p.parseChar('('))
        {
            compileExpression();
            if (!p.parseChar(')'))
            {
                throw new QueryCompilerSyntaxException("expected ')'", p.getIndex(), p.getInput());
            }
            return;
        }
        // if primary == null, literal not found...
        // We will have an identifier (variable, parameter, or field of candidate class)
        if (!compileIdentifier())
        {
            throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
        }
        int size = stack.size();
        /*
         * run function on literals or identifiers
         * e.g. "primary.runMethod(arg)"
         */
        while (p.parseChar('.'))
        {
            if (compileMethod())
            {
                //((ExpressionNode) stack.pop()).addChildNode((ExpressionNode) stack.pop());
            }
            else
            {
                if (!compileIdentifier())
                {
                    throw new QueryCompilerSyntaxException("Identifier expected", p.getIndex(), p.getInput());
                }
            }
        }
        while (stack.size() > size)
        {
            Node top = (Node) stack.pop();
            Node peek = ((Node) stack.peek());
            peek.insertChildNode(top);
        }
    }

    private boolean compileMethod()
    {
        String method = p.parseMethod();
        if (method != null)
        {
            p.skipWS();
            p.parseChar('(');

            if (method.equalsIgnoreCase("EXISTS"))
            {
                // TODO Implement EXISTS (subquery)
                throw new NucleusException("EXISTS is not yet supported for generic JPQL compilation");
            }
            else if (method.equalsIgnoreCase("ANY"))
            {
                // TODO Implement ANY (subquery)
                throw new NucleusException("ANY is not yet supported for generic JPQL compilation");
            }
            else if (method.equalsIgnoreCase("ALL"))
            {
                // TODO Implement ALL (subquery)
                throw new NucleusException("ALL is not yet supported for generic JPQL compilation");
            }
            else if (method.equalsIgnoreCase("SOME"))
            {
                // TODO Implement SOME (subquery)
                throw new NucleusException("SOME is not yet supported for generic JPQL compilation");
            }
            else if (method.equalsIgnoreCase("TRIM"))
            {
                // TRIM([[LEADING | TRAILING | BOTH] [trim_character] FROM] string_primary)
                String methodName = "TRIM";
                if (p.parseStringIgnoreCase("LEADING"))
                {
                    methodName = "TRIM_LEADING";
                }
                else if (p.parseStringIgnoreCase("TRAILING"))
                {
                    methodName = "TRIM_TRAILING";
                }
                else if (p.parseStringIgnoreCase("BOTH"))
                {
                    // Default
                }

                Node trimCharNode = null;
                Node expr = new Node(Node.INVOKE, methodName);
                compileExpression();
                Node next = (Node)stack.pop();
                if (next.getNodeType() == Node.LITERAL)
                {
                    Object litValue = next.getNodeValue();
                    if (litValue instanceof String && ((String)litValue).equals("FROM"))
                    {
                        // FROM so ignore
                        compileExpression(); // field expression that we are trimming
                        next = (Node)stack.pop();
                    }
                    else
                    {
                        // trim character first
                        trimCharNode = next;
                        if (p.parseStringIgnoreCase("FROM"))
                        {
                            // Ignore the FROM
                        }
                        compileExpression();
                        next = (Node)stack.pop();
                    }
                }
                else
                {
                    // No "trimChar" or FROM
                }

                if (!p.parseChar(')'))
                {
                    throw new QueryCompilerSyntaxException("')' expected", p.getIndex(), p.getInput());
                }
                expr.appendChildNode(next);
                if (trimCharNode != null)
                {
                    expr.appendChildNode(trimCharNode);
                }
                stack.push(expr);
                return true;
            }
            else
            {
                // Found syntax for a method, so invoke the method
                Node expr = new Node(Node.INVOKE, method);
                if (!p.parseChar(')'))
                {
                    do
                    {
                        compileExpression();
                        expr.appendChildNode((Node) stack.pop());
                    }
                    while (p.parseChar(','));

                    if (!p.parseChar(')'))
                    {
                        throw new QueryCompilerSyntaxException("')' expected", p.getIndex(), p.getInput());
                    }
                }
                stack.push(expr);
                return true;
            }
        }
        return false;
    }

    /**
     * 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.
     * @return The compiled literal
     */
    protected boolean compileLiteral()
    {
        Object litValue = null;

        String sLiteral;
        BigDecimal fLiteral;
        BigInteger iLiteral;
        Boolean bLiteral;

        boolean single_quote_next = p.nextIsSingleQuote();
        if ((sLiteral = p.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 = new Character(sLiteral.charAt(0));
            }
            else
            {
                litValue = sLiteral;
            }
        }
        else if ((fLiteral = p.parseFloatingPointLiteral()) != null)
        {
            litValue = fLiteral;
        }
        else if ((iLiteral = p.parseIntegerLiteral()) != null)
        {
            litValue = new Long(iLiteral.longValue());
        }
        else if ((bLiteral = p.parseBooleanLiteralIgnoreCase()) != null)
        {
            litValue = bLiteral;
        }
        else if (p.parseNullLiteralIgnoreCase())
        {
            litValue = null;
        }
        else
        {
            return false;
        }

        stack.push(new Node(Node.LITERAL, litValue));
        return true;
    }

    /**
     * An identifier always designates a reference to a single value.
     * A single value can be one collection, one field.
     * @return The compiled identifier
     */
    private boolean compileIdentifier()
    {
        String id = p.parseIdentifier();
        if (id == null)
        {
            return false;
        }
        Node expr = new Node(Node.IDENTIFIER, id);
        stack.push(expr);
        return true;
    }
}
TOP

Related Classes of org.datanucleus.query.compiler.SQLParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.