persistence@glassfish.java.net

EJBQL NOT queries

From: Michael Bouschen <Michael.Bouschen_at_Sun.COM>
Date: Thu, 27 Oct 2005 15:46:25 +0200

Hi Tom,

I looked into EJBQL queries using the NOT operator in the WHERE clause
and I found two cases where the generated SQL is not correct:
(1) ... WHERE NOT (NOT (c.name = 'Michael Bouschen'))
(2) ... WHERE NOT c.name <> 'Michael Bouschen'
The SQL WHERE clause for both queries misses a NOT operator:
   ... WHERE NOT ((NAME = 'Michael Bouschen'))

I think (I hope) I fixed the problem, you find a fix attached below.
Here are some details about the fix. Problem (1) is caused by method
handleRightRoundBracket calling finishedConstructingNode instead of
finishedExpression. Only the latter takes care of any pending not
operations to be added before the helper instance is closed. The reason
for (2) is that the ParseTreeNodeConstructor does not support having two
NOT operations in the same context, method handleNot overwrites any
notNode registered before. An expression "x<>y" is mapped to NOT x=y, so
query (2) is mapped to a query with two NOT operators. I fixed the code
in ParseTreeNodeConstructor.handleNot.

Please have a look. Thanks!

Regards Michael


/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the "License"). You may not use this file except
 * in compliance with the License.
 *
 * You can obtain a copy of the license at
 * glassfish/bootstrap/legal/CDDLv1.0.txt or
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * HEADER in each file and include the License file at
 * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
 * add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your
 * own identifying information: Portions Copyright [yyyy]
 * [name of copyright owner]
 */
// Copyright (c) 1998, 2005, Oracle. All rights reserved.
package oracle.toplink.essentials.internal.parsing;


// TopLink imports
import oracle.toplink.essentials.queryframework.*;

/**
 * INTERNAL
 * <p><b>Purpose</b>: Construct Nodes for the parse tree
 * <p><b>Responsibilities</b>:<ul>
 * <li> Add Parameters to the parse tree
 * <li> Handle the construction of nodes for the parse tree
 * <li> Spawn a new constrcutor as needed
 * <li> Finish the construction of nodes when signaled
 * </ul>
 * @author Jon Driscoll and Joel Lucuik
 * @since TopLink 4.0
 */
public class ParseTreeNodeConstructor {
    public ParseTreeConstructor treeConstructor;
    protected Node currentNode;
    public ParseTreeNodeConstructor parentConstructor;
    private NotNode notNode = null;
    public Node schemaIdentifierNode;
    public Node lastPlacedNode;

    //boolean indicating that brackets have been encountered, immediately following
    //a +, -. This triggers a precedence override
    public boolean bracketsOverrideMultiplyOrDivide = false;

    /**
     * Return a new, initialized ParseTreeNodeConstructor
     */
    public ParseTreeNodeConstructor() {
        super();
    }

    /**
     * Return a new, initialized ParseTreeNodeConstructor
     */
    public ParseTreeNodeConstructor(ParseTreeConstructor newTreeConstructor) {
        super();
        setTreeConstructor(newTreeConstructor);
    }

    /**
     * INTERNAL
     * Add this parameter to the parse tree
     */
    public void addParameter(String theParameter) {
        getParseTree().addParameter(theParameter);
    }

    /**
     * INTERNAL
     * Answer the node for the local variable (left most).
     */
    public Node buildLeftMostLocalVariableNode(String variableName) {
        return new LocalVariableNode(variableName);

    }

    /**
     * INTERNAL
     * Answer a new node for the local variable.

     */
    public Node buildLocalVariableNode(String variableName) {
        return new LocalVariableNode(variableName);
    }

    /**
     * INTERNAL
     * Create a new helper to construct the incoming nodes. Answer the new helper
     */
    public ParseTreeNodeConstructor createHelper() {
        ParseTreeNodeConstructor helper = new ParseTreeNodeConstructor(this.getTreeConstructor());
        getTreeConstructor().setNodeConstructor(helper);
        helper.setParentConstructor(this);
        return helper;
    }

    /**
     * INTERNAL
     * Finish the ABS clause by passing control to my parent constructor
     */
    public void finishedAbs() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the ABS variable clause by passing control to my parent constructor
     */
    public void finishedAbsVariable() {
        finishedExpression();
    }

    /**
     * Finish the AND clause by passing control to my parent constructor
     */
    public void finishedAnd() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finished the Aggregate node
     */
    public void finishedAggregate() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finished the first arithmetic expression in the MOD. Finish the expression
     * , then start another helper.
     */
    public void finishedFirstArithmeticExpressionInMOD() {
        finishedConstructingNode();
    }

    /**
     * INTERNAL
     * Finished the second arithmetic expression in the MOD. Finish the expression.
     */
    public void finishedSecondArithmeticExpressionInMOD() {
        finishedConstructingNode();
    }

    /**
     * Finish the Expression, by finishing the node, and tying up loose ends
     */
    public void finishedExpression() {
        finishedConstructingNode();
        if (hasParentConstructor()) {
            getParentConstructor().finishPendingNodes();
        } else {
            finishPendingNodes();
        }
    }

    /**
     * Finish the BETWEEN..AND clause by passing control to my parent constructor
     */
    public void finishedBetweenAnd() {
        finishedExpression();
    }

    /**
     * Finish the >, >=, <, <=, =, <> clause(s)
     */
    public void finishedComparisonExpression() {
        finishedExpression();
    }

    /**
     * Finish the Concat expression by passing control to my parent constructor
     */
    public void finishedConcat() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * The current node has been finished, notify the parent constructor
     */
    public void finishedConstructingNode() {
        if (!hasParentConstructor()) {
            return;
        }
        getParentConstructor().helperDone(this);
    }

    /**
     * INTERNAL
     * Finish the escape clause
     */
    public void finishedEscape() {
        finishedExpression();
    }

    /**
     * Finish the *, /
     */
    public void finishedMultiplyOrDivide() {
        if (getCurrentNode().isMultiplyNode() || getCurrentNode().isDivideNode()) {
            finishedExpression();
        }

        //we don't care about brackets any more, because we're done with *, /
        bracketsOverrideMultiplyOrDivide = false;
    }

    /**
     * INTERNAL
     * The Locate is finished
     */
    public void finishedLocate() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * The Locate Literal is finished. A helper is needed to handle the
     * next node.
     */
    public void finishedLocateLiteral() {
        createHelper();
    }

    /**
     * INTERNAL
     * The Locate Variable is finished
     */
    public void finishedLocateVariable() {
        finishedExpression();
    }

    /**
     * Finish the >, >=, <, <= clause(s) by passing control to my parent constructor
     */
    public void finishedNumericOther() {
        finishedExpression();
    }

    /**
     * Finish the IN clause by passing control to my parent constructor
     */
    public void finishedIn() {
        getCurrentNode().setComplete(true);
        finishedExpression();
    }

    /**
     * Finish the IN clause, that is the IN within the FROM.
     */
    public void finishedInClauseInFrom() {
    }

    /**
     * Finish the JOIN clause.
     */
    public void finishedJoinClause() {
    }

    /**
     * Finish the LENGTH clause by passing control to my parent constructor
     */
    public void finishedLength() {
        finishedExpression();
    }

    /**
     * Finish the LENGTH variable clause by passing control to my parent constructor
     */
    public void finishedLengthVariable() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * The LIKE node is now finished...
     */
    public void finishedLike() {
        finishedExpression();
    }

    /**
     * Finish the MEMBER-OF clause by passing control to my parent constructor
     */
    public void finishedMemberOf() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * The null node is now finished
     */
    public void finishedNull() {
        finishedExpression();
    }

    /**
     * Finish the OR clause by passing control to my parent constructor
     */
    public void finishedOr() {
        finishedExpression();
    }

    /**
     * Clear the current node as ther may be multiple order by items
     */
    public void finishedOrderByItem() {
        getParentConstructor().placeNode(getCurrentNode());
        setCurrentNode(null);
    }

    /**
     * Clear the current node as ther may be multiple group by items
     */
    public void finishedGroupByItem() {
        getParentConstructor().placeNode(getCurrentNode());
        setCurrentNode(null);
    }

    /**
     * INTERNAL
     * Finish the SELECT clause
     */
    public void finishedSelect() {
        finishedExpression();
    }

    /**
     * Finished a set_to tree branch. Add it to the set node
     */
    public void finishedEqualsAssignment() {
        finishedExpression();
    }

    /**
     * Finish the LOWER clause by passing control to my parent constructor
     */
    public void finishedLower() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the LOWER variable clause by passing control to my parent constructor
     */
    public void finishedLowerVariable() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the Single valued path expression of a MOD
     */
    public void finishedModSingleValuedPathExpression() {
        finishedExpression();
    }

    /**
     * INTERNAl
     * Finish the SQRT variable clause by passing control to my parent constructor
     */
    public void finishedSqrtVariable() {
        finishedExpression();
    }

    /**
     * Finish the SUBSTRING clause by passing control to my parent constructor
     */
    public void finishedSubstring() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the SUBSTRING variable clause by passing control to my parent constructor
     */
    public void finishedSubstringVariable() {
        finishedExpression();
    }

    /**
     * Finish the UPPER clause by passing control to my parent constructor
     */
    public void finishedUpper() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the UPPER variable clause by passing control to my parent constructor
     */
    public void finishedUpperVariable() {
        finishedExpression();
    }

    /**
     * Finish the TRIM clause by passing control to my parent constructor
     */
    public void finishedTrim() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the trim character by passing control to my parent constructor
     */
    public void finishedTrimChar() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Finish the trim variable by passing control to my parent constructor
     */
    public void finishedTrimVariable() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Return the parseTreeContext
     */
    public ParseTreeContext getParseTreeContext() {
        return getTreeConstructor().getContext();
    }

    /**
     * INTERNAL
     * getCurrentNode()
     * Answer the current node not yet fully populated
     */
    public Node getCurrentNode() {
        return currentNode;
    }

    /**
     * INTERNAL
     * Answer the NotNode
     */
    private NotNode getNotNode() {
        return notNode;
    }

    /**
     * INTERNAL
     * Return the parent constructor of this constructor
     */
    public ParseTreeNodeConstructor getParentConstructor() {
        return parentConstructor;
    }

    /**
     * INTERNAL
     * Return the parent parse tree
     */
    public EJBQLParseTree getParseTree() {
        if (hasParentConstructor()) {
            return getParentConstructor().getParseTree();
        } else {
            return (EJBQLParseTree)getTreeConstructor().getParseTree();
        }
    }

    public Node getSchemaIdentifierNode() {
        return schemaIdentifierNode;
    }

    public ParseTreeConstructor getTreeConstructor() {
        return treeConstructor;
    }

    /**
     * INTERNAL
     * Handle an 'ABS' on the input stream
     */
    public void handleAbs() {
        AbsNode absNode = new AbsNode();
        placeNode(absNode);
        createHelper();
    }

    /**
     * Event handler for the 'AND' token
     */
    public void handleAnd() {
        AndNode andNode = new AndNode();
        placeNode(andNode);
        createHelper();
    }

    /**
     * Event handler for the 'AND' after the 'BETWEEN' token
     */
    public void handleAndAfterBetween() {
        finishedConstructingNode();
    }

    /**
     * INTERNAL
     * Event handler for 'ASC'
     */
    public void handleAsc() {
        SortDirectionNode node = new SortDirectionNode();
        node.useAscending();
        placeNode(node);
    }

    /**
     * INTERNAL
     * Event handler for 'AVG'
     */
    public void handleAvg() {
        AggregateNode node = new AggregateNode();
        node.useAvg();
        placeNode(node);
        createHelper();
    }

    /**
     * Event handler for the 'BETWEEN' token
     */
    public void handleBetween() {
        BetweenNode betweenNode = new BetweenNode();
        placeNode(betweenNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'CONCAT' on the input stream
     */
    public void handleConcat() {
        ConcatNode concatNode = new ConcatNode();
        placeNode(concatNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'COUNT' on the input stream
     */
    public void handleCount() {
        CountNode node = new CountNode();
        placeNode(node);
        createHelper();
    }

    /**
     * Event handler for a COMMA after the 'CONCAT' token
     */
    public void handleCommaAfterConcat() {
        finishedConstructingNode();
    }

    /**
     * INTERNAL
     * Handle a 'DESC' on the input stream
     */
    public void handleDesc() {
        SortDirectionNode node = new SortDirectionNode();
        node.useDescending();
        placeNode(node);
    }

    /**
     * INTERNAL
     * Handle a DISTINCT on the input stream
     */
    public void handleDistinct() {
        getParseTree().setDistinctState(ObjectLevelReadQuery.USE_DISTINCT);
    }

    /**
     * INTERNAL
     * Handle a '/' on the input stream
     */
    public void handleDivide() {
        placeMultiplyOrDivideNode(new DivideNode());
    }

    /**
     * Event handler for the 'DOT' token
     */
    public void handleDot() {
        DotNode dotNode = new DotNode();
        placeNode(dotNode);
    }

    /**
     * INTERNAL
     * Event handler for the '=' token
     */
    public void handleEqualsComparison() {
        EqualsNode equalsNode = new EqualsNode();
        placeNode(equalsNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the escape token
     */
    public void handleEscape() {
        EscapeNode node = new EscapeNode();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the EMPTY token
     *
     * Place a new EmpyCollectionComparisonNode. Indicate to this node
     * if there is a NOT involved.
     */
    public void handleEmpty() {
        EmptyCollectionComparisonNode emptyNode = new EmptyCollectionComparisonNode();
        if (hasNotNode()) {
            setNotNode(null);
            emptyNode.indicateNot();
        }
        placeNode(emptyNode);
    }

    /**
     * INTERNAL
     * Handle a 'FALSE' being found on the input stream
     */
    public void handleFalse() {
        BooleanLiteralNode falseNode = new BooleanLiteralNode(false);
        placeNode(falseNode);
    }

    /**
     * INTERNAL
     * A float was foundon the input stream
     */
    public void handleFloat(Object theFloat) {
        FloatLiteralNode floatNode = new FloatLiteralNode(theFloat);
        placeNode(floatNode);
    }

    /**
     * INTERNAL
     * Handle a 'FROM' being found on the input stream.
     */
    public void handleFrom() {
        FromNode node = new FromNode();
        node.setContext(getParseTreeContext());
        placeNode(node);
    }

    /**
     * INTERNAL
     * Event handler for the '>' token
     */
    public void handleGreaterThan() {
        GreaterThanNode greaterThanNode = new GreaterThanNode();
        placeNode(greaterThanNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the ">=" token
     */
    public void handleGreaterThanEqualTo() {
        GreaterThanEqualToNode greaterThanEqualToNode = new GreaterThanEqualToNode();
        placeNode(greaterThanEqualToNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the 'GROUP BY' token. Create a new groupByNode and place it
     */
    public void handleGroupBy() {
        GroupByNode groupByNode = new GroupByNode();
        placeNode(groupByNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the 'HAVING' token. Create a new havingNode and place it
     */
    public void handleHaving() {
        HavingNode havingNode = new HavingNode();
        placeNode(havingNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the "IN" token
     */
    public void handleIn() {
        InNode inNode = new InNode();

        // if a NOT was encountered, indicate it in the IN node
        if (hasNotNode()) {
            inNode.indicateNot();
            setNotNode(null);
        }

        placeNode(inNode);
    }

    /**
     * INTERNAL
     * Event handler for an input parameter
     */
    public void handleInputParameter(String theParameter) {
        ParameterNode parameterNode = new ParameterNode(theParameter);
        placeNode(parameterNode);
        parameterNode.addParameterToTree();
    }

    /**
     * Event handler for an Integer
     */
    public void handleInteger(Integer newInteger) {
        IntegerLiteralNode integerNode = new IntegerLiteralNode(newInteger);
        placeNode(integerNode);
    }

    /**
     * INTERNAL
     * Event handler for a leftmost local variable. The node will have "leftMost" indicated.
     */
    public void handleLeftMostLocalVariable(String variableName) {
        placeNode(buildLeftMostLocalVariableNode(variableName));
    }

    /**
     * Event handler for the '(' token
     */
    public void handleLeftRoundBracket() {
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'LENGTH' on the input stream
     */
    public void handleLength() {
        LengthNode lengthNode = new LengthNode();
        placeNode(lengthNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the '<' token
     */
    public void handleLessThan() {
        LessThanNode lessThanNode = new LessThanNode();
        placeNode(lessThanNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the '<=' token
     */
    public void handleLessThanEqualTo() {
        LessThanEqualToNode lessThanEqualToNode = new LessThanEqualToNode();
        placeNode(lessThanEqualToNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a like being found on the input stream.
     * Create a new LikeNode and place it
     */
    public void handleLike() {
        LikeNode likeNode = new LikeNode();
        placeNode(likeNode);
    }

    /**
     * INTERNAL
     * Event handler for a local variable
     */
    public void handleLocalVariable(String variableName) {
        placeNode(buildLocalVariableNode(variableName));
    }

    /**
     * INTERNAL
     * Handle a LOCATE being found
     */
    public void handleLocate() {
        LocateNode node = new LocateNode();
        placeNode(node);
    }

    /**
     * INTERNAL
     * Handle a 'MAX' on the input stream
     */
    public void handleMax() {
        AggregateNode node = new AggregateNode();
        node.useMax();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'MEMBER-OF' on the input stream
     */
    public void handleMemberOf() {
        MemberOfNode memberOfNode = new MemberOfNode();
        if (hasNotNode()) {
            setNotNode(null);
            memberOfNode.indicateNot();
        }
        placeNode(memberOfNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'MIN' on the input stream
     */
    public void handleMin() {
        AggregateNode node = new AggregateNode();
        node.useMin();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a '-' on the input stream
     */
    public void handleMinus() {
        placeNode(new MinusNode());
    }

    /**
     * INTERNAL
     * Handle a 'MOD' on the input stream
     */
    public void handleMod() {
        ModNode node = new ModNode();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a '*' on the input stream
     */
    public void handleMultiply() {
        placeMultiplyOrDivideNode(new MultiplyNode());
    }

    /**
     * INTERNAL
     * Event handler for the 'NOT' token
     */
    public void handleNot() {
        if (hasNotNode()) {
            // This is the second not operation in the same scope => skip
            setNotNode(null);
        }
        else {
            NotNode notNode = new NotNode();
            setNotNode(notNode);
        }
    }

    /**
     * INTERNAL
     * Handle a 'NULL' being found on the input stream
     */
    public void handleNull() {
        NullComparisonNode nullComparisonNode = new NullComparisonNode();
        placeNode(nullComparisonNode);
        //Handle "NOT NULL"
        if (hasNotNode()) {
            placeNode(getNotNode());
            setNotNode(null);
        }
    }

    /**
     * INTERNAL
     * Event handler for the 'OR' token. Create a new orNode and place it
     */
    public void handleOr() {
        OrNode orNode = new OrNode();
        placeNode(orNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the 'ORDER BY' token. Create a new orderByNode and place it
     */
    public void handleOrderBy() {
        OrderByNode orderByNode = new OrderByNode();
        placeNode(orderByNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Hanlde an order by item being found
     */
    public void handleOrderByItem() {
        OrderByItemNode node = new OrderByItemNode();
        placeNode(node);
    }

    /**
     * INTERNAL
     * Handle a '+' on the input stream
     */
    public void handlePlus() {
        placeNode(new PlusNode());
    }

    /**
     * Event handler for the ')' token
     */
    public void handleRightRoundBracket() {
        finishedExpression();
    }

    /**
     * INTERNAL
     * Handle a SELECT
     */
    public void handleSelect() {
        SelectNode node = new SelectNode();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a SET
     */
    public void handleSet() {
        SetNode setNode = new SetNode();
        setNode.placeNodeOnTree(getTreeConstructor().getParseTree());
        createHelper();
    }

    /**
     * INTERNAL
     * Event handler for the set_to token
     */
    public void handleEqualsAssignment() {
        EqualsAssignmentNode equalsAssignmentNode = new EqualsAssignmentNode();
        placeNode(equalsAssignmentNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'SQRT' on the input stream
     */
    public void handleSqrt() {
        SqrtNode sqrtNode = new SqrtNode();
        placeNode(sqrtNode);
        createHelper();
    }

    /**
     * INTERNAL:
     * Handle the start of a set_to branch
     */
    public void handleStartEqualsAssignment() {
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'SUM' on the input stream
     */
    public void handleSum() {
        AggregateNode node = new AggregateNode();
        node.useSum();
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a string literal being encountered on the input stream
     */
    public void handleString(String theString) {
        StringLiteralNode stringLiteralNode = new StringLiteralNode(theString);
        placeNode(stringLiteralNode);
    }

    /**
     * INTERNAL
     * Handle a 'SUBSTRING' on the input stream
     */
    public void handleSubstring() {
        SubstringNode substringNode = new SubstringNode();
        placeNode(substringNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'UPPER' on the input stream
     */
    public void handleUpper() {
        UpperNode upperNode = new UpperNode();
        placeNode(upperNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'LOWER' on the input stream
     */
    public void handleLower() {
        LowerNode lowerNode = new LowerNode();
        placeNode(lowerNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'TRIM' on the input stream
     */
    public void handleTrim() {
        TrimNode trimNode = new TrimNode();
        placeNode(trimNode);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a 'TRUE' being found on the input stream
     */
    public void handleTrue() {
        BooleanLiteralNode trueNode = new BooleanLiteralNode(true);
        placeNode(trueNode);
    }

    /**
     * INTERNAL
     * Handle an UPDATE
     */
    public void handleUpdate() {
        UpdateNode node = new UpdateNode();
        node.setContext(getParseTreeContext());
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a DELETE
     */
    public void handleDelete() {
        DeleteNode node = new DeleteNode();
        node.setContext(getParseTreeContext());
        placeNode(node);
        createHelper();
    }

    /**
     * INTERNAL
     * Handle a WHERE being encountered on the input stream
     * Clear out the variables from the FROM and SELECT
     */
    public void handleWhere() {
        setCurrentNode(null);
    }

    public boolean hasNotNode() {
        return getNotNode() != null;
    }

    public boolean hasParentConstructor() {
        return parentConstructor != null;
    }

    /**
     * Helper is done. Place its current node. Get rid of the helper.
     * If this set of brackets surrounds a + or -, then trigger the precedence override,
     * so that *, / don't take precedence any more.
     */
    public void helperDone(ParseTreeNodeConstructor child) {
        placeNode(child.getCurrentNode());
        this.getTreeConstructor().setNodeConstructor(this);
        checkForBracketsOverridingMultiplyOrDivide();
    }

    public void finishPendingNodes() {
        //take care of the NOT, if there is one
        if (hasNotNode()) {
            placeNode(getNotNode());
            setNotNode(null);
        }
    }

    /**
     * INTERNAL
     * Ask the node to put itself where it belongs in relation to the current root.
     * If there's no current node, make the new node the root and current node.
     */
    public void placeNode(Node newNode) {
        setLastPlacedNode(newNode);

        newNode.setNodeConstructor(this);
        if (getCurrentNode() != null) {
            getCurrentNode().placeNode(newNode);
        }

        // If the newNode should replace the current node then do it
        if (shouldReplaceCurrent(newNode)) {
            setCurrentNode(newNode);
            setRoot(newNode);
        }
    }

    /**
     * INTERNAL
     * We need to check for a precedence handling.
     * Consider:
     * a + b * c VS
     * (a + b) * c
     *
     * We need to set a trigger to indicate that the * does not take precedence in the second
     * case
     */
    public void checkForBracketsOverridingMultiplyOrDivide() {
        if (getCurrentNode().isPlusNode() || getCurrentNode().isMinusNode()) {
            bracketsOverrideMultiplyOrDivide = true;
        }
    }

    /**
     * INTERNAL
     * Place the MultiplyNode or DivideNode taking into consideration the current node.
     * If the current node is a PlusNode or a Minus Node, then we must adjust for precedence,
     * unless brackets override.
     * To adjust for precendence, we steal the node that should have rightfully been the Multiply or Divide Node's,
     * and create a helper to finish the Multiply or Divide Node.
     * If the current node is something else, place normally.
     */
    public void placeMultiplyOrDivideNode(Node multiplyOrDivideNode) {
        if (bracketsOverrideMultiplyOrDivide) {
            placeNode(multiplyOrDivideNode);
            return;
        }
        if (getCurrentNode().isPlusNode() || getCurrentNode().isMinusNode()) {
            multiplyOrDivideNode.stealNodeFrom(getCurrentNode());
            createHelper().placeNode(multiplyOrDivideNode);
        } else {
            placeNode(multiplyOrDivideNode);
        }
    }

    public void setCurrentNode(Node newCurrentNode) {
        currentNode = newCurrentNode;
    }

    public void setLastPlacedNode(Node newLastPlacedNode) {
        lastPlacedNode = newLastPlacedNode;
    }

    private void setNotNode(NotNode newNotNode) {
        notNode = newNotNode;
    }

    public void setParentConstructor(ParseTreeNodeConstructor newParentConstructor) {
        parentConstructor = newParentConstructor;
    }

    /**
     * INTERNAL
     * Set the root node to the passed node. Ignore if I have a parent (which mean I'm a helper)
     */
    public void setRoot(Node theNode) {
        if (hasParentConstructor()) {
            return;
        }
        getTreeConstructor().setRoot(theNode);
    }

    public void setTreeConstructor(ParseTreeConstructor newTreeConstructor) {
        treeConstructor = newTreeConstructor;
    }

    /**
     * INTERNAL
     * Answer true if there's no current node yet, or the current node is now within
     * the subtree of the newNode.
     */
    public boolean shouldReplaceCurrent(Node newNode) {
        return (getCurrentNode() == null) || (newNode.containsNode(getCurrentNode()));
    }
}