Mercurial > hg > release > thermostat-1.0
changeset 1276:5d3611202f8a
Rename StatementDescriptorParser => BasicDescriptorParser.
Reviewed-by: omajid
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-October/008412.html
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParser.java Fri Oct 04 18:13:31 2013 +0200 @@ -0,0 +1,887 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.internal.statement; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import com.redhat.thermostat.storage.core.Add; +import com.redhat.thermostat.storage.core.AggregateQuery.AggregateFunction; +import com.redhat.thermostat.storage.core.BackingStorage; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.DescriptorParsingException; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.ParsedStatement; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Query.SortDirection; +import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.storage.core.Replace; +import com.redhat.thermostat.storage.core.Statement; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.Update; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.BinaryLogicalOperator; + +/** + * A parser for the string representation of {@link StatementDescriptor}s. + * Tokens have to be separated by whitespace. + * + * This parser implements the following simple grammar for statement descriptors. + * It supports the following statement types: + * <ul> + * <li>QUERY (read)</li> + * <li>QUERY-COUNT (read)</li> + * <li>ADD (write)</li> + * <li>UPDATE (write)</li> + * <li>REPLACE (write)</li> + * <li>REMOVE (write)</li> + * </ul> + * + * <p><strong>Grammar:</strong></p> + * <pre> + * statementDesc := statementType category setList suffix + * statementType := 'QUERY' | 'QUERY-COUNT' | + * 'ADD' | 'REPLACE' | 'UPDATE' | + * 'REMOVE' + * category := string + * setList := 'SET' setValues | \empty + * setValues := valuePair valueList + * valuePair := term '=' term + * valueList := ',' setValues | \empty + * suffix := 'WHERE' where | + * 'SORT' sortCond | + * 'LIMIT' term | \empty + * where := whereExp sort limit + * whereExp := andCond orCond + * orCond := 'OR' whereExp | \empty + * sort := 'SORT' sortCond | \empty + * sortCond := sortPair sortList + * sortPair := term sortModifier + * sortModifier := 'ASC' | 'DSC' + * sortList := ',' sortCond | \empty + * limit := 'LIMIT' term | \empty + * andCond := condition andBody + * andBody := 'AND' whereExp | \empty + * condition := 'NOT' condition | compExp + * compExp := term compExpRHS + * term := freeParam | literal + * freeParam := '?s' | '?i' | '?l' | '?s[' | '?b' + * literal := sQuote string sQuote | int | long | boolean + * sQuote := \' + * boolean := <true> | <false> + * int := <literal-int> + * long := <literal-long>longPostFix + * longPostFix := 'l' | 'L' + * string := <literal-string-value> + * compExpRHS := '!=' term | '=' term | '<=' term | '>=' term | + * '<' term | '>' term + * </pre> + * + * This implements the following logic precedence rules (in this order of + * precedence): + * + * <ol> + * <li>NOT</li> + * <li>AND</li> + * <li>OR</li> + * </ol> + * + * NOTE: Comparison expressions have equal precedence. + */ +class BasicDescriptorParser<T extends Pojo> { + + private static final String TOKEN_DELIMS = " \t\r\n\f"; + private static final short IDX_QUERY = 0; + private static final short IDX_QUERY_COUNT = 1; + private static final short IDX_ADD = 2; + private static final short IDX_REPLACE = 3; + private static final short IDX_UPDATE = 4; + private static final short IDX_REMOVE = 5; + private static final String[] KNOWN_STATEMENT_TYPES = new String[] { + "QUERY", "QUERY-COUNT", "ADD", "REPLACE", "UPDATE", "REMOVE" + }; + private static final String SORTLIST_SEP = ","; + private static final String SETLIST_SEP = SORTLIST_SEP; + private static final String KEYWORD_SET = "SET"; + private static final String KEYWORD_WHERE = "WHERE"; + private static final String KEYWORD_SORT = "SORT"; + private static final String KEYWORD_LIMIT = "LIMIT"; + private static final String KEYWORD_ASC = "ASC"; + private static final String KEYWORD_DSC = "DSC"; + private static final String POJO_FREE_PARAMETER_TYPE = "?p"; + private static final char PARAM_PLACEHOLDER = '?'; + + private final String[] tokens; + private final StatementDescriptor<T> desc; + private final BackingStorage storage; + private int currTokenIndex; + private int placeHolderCount; + // the parsed statement + private ParsedStatementImpl<T> parsedStatement; + private SuffixExpression tree; + private SetList setList; + + BasicDescriptorParser(BackingStorage storage, StatementDescriptor<T> desc) { + this.tokens = getTokens(desc.getDescriptor()); + this.currTokenIndex = 0; + this.placeHolderCount = 0; + this.desc = desc; + this.storage = storage; + } + + private String[] getTokens(String str) { + StringTokenizer tokenizer = new StringTokenizer(str, TOKEN_DELIMS); + List<String> toks = new ArrayList<>(tokenizer.countTokens()); + while (tokenizer.hasMoreTokens()) { + toks.add(tokenizer.nextToken()); + } + return toks.toArray(new String[0]); + } + + public ParsedStatement<T> parse() throws DescriptorParsingException { + matchStatementType(); + matchCategory(); + // matched so far, create the raw statement + createStatement(); + this.setList = new SetList(); + matchSetList(setList); + this.tree = new SuffixExpression(); + matchSuffix(); + if (currTokenIndex != tokens.length) { + throw new DescriptorParsingException("Incomplete parse"); + } + parsedStatement.setNumFreeParams(placeHolderCount); + parsedStatement.setSetList(setList); + parsedStatement.setSuffixExpression(tree); + doSemanticAnalysis(); + return parsedStatement; + } + + private void doSemanticAnalysis() throws DescriptorParsingException { + // TODO: + // - Check that ADD/REPLACE specifies all keys judging by the Pojo + // model class. Not sure if good idea though, as this would likely + // introduce dep on beanutils. + Statement<T> stmt = parsedStatement.getRawStatement(); + if (stmt == null) { + // should never be null + throw new NullPointerException(); + } + if (stmt instanceof Add && tree.getWhereExpn() != null) { + String msg = "WHERE clause not allowed for ADD"; + throw new DescriptorParsingException(msg); + } + if (stmt instanceof Replace && tree.getWhereExpn() == null) { + String msg = "WHERE clause required for REPLACE"; + throw new DescriptorParsingException(msg); + } + if (stmt instanceof Update) { + if (tree.getWhereExpn() == null) { + // WHERE required for UPDATE + String msg = "WHERE clause required for UPDATE"; + throw new DescriptorParsingException(msg); + } + if (setList.getValues().size() == 0) { + // SET required for UPDATE + String msg = "SET list required for UPDATE"; + throw new DescriptorParsingException(msg); + } + } + if (stmt instanceof Remove && setList.getValues().size() > 0) { + String msg = "SET not allowed for REMOVE"; + throw new DescriptorParsingException(msg); + } + if (stmt instanceof Query) { + if (setList.getValues().size() > 0) { + // Must not have SET for QUERYs + String msg = "SET not allowed for QUERY/QUERY-COUNT"; + throw new DescriptorParsingException(msg); + } + } else { + // only queries can have sort/limit expressions + if (this.tree.getLimitExpn() != null || this.tree.getSortExpn() != null) { + String msg = "LIMIT/SORT only allowed for QUERY/QUERY-COUNT"; + throw new DescriptorParsingException(msg); + } + } + } + + /* + * Match set list for DML statements. + */ + private void matchSetList(final SetList setList) throws DescriptorParsingException { + if (tokens.length == currTokenIndex) { + // no set list + return; + } + if (tokens[currTokenIndex].equals(KEYWORD_SET)) { + currTokenIndex++; // SET + matchSetValues(setList); + } + // empty, proceed with suffix + } + + /* + * Match list of values in a SET expression + */ + private void matchSetValues(SetList setList) throws DescriptorParsingException { + matchValuePair(setList); + matchValueList(setList); + } + + /* + * Match more value pairs in a SET list + */ + private void matchValueList(SetList setList) throws DescriptorParsingException { + if (currTokenIndex == tokens.length) { + // empty + return; + } + if (tokens[currTokenIndex].equals(SETLIST_SEP)) { + currTokenIndex++; // , + matchSetValues(setList); + } + } + + /* + * Match one pair of values in a + */ + private void matchValuePair(SetList setList) throws DescriptorParsingException { + SetListValue value = new SetListValue(); + TerminalNode lval = new TerminalNode(null); + matchTerm(lval, true, true); + value.setKey(lval); + if (tokens[currTokenIndex].equals("=")) { + currTokenIndex++; // = + } else { + String msg = "Expected '=' after SET value LHS. Token was ->" + tokens[currTokenIndex] + "<-"; + throw new DescriptorParsingException(msg); + } + TerminalNode rval = new TerminalNode(null); + matchTerm(rval, false, true); + value.setValue(rval); + setList.addValue(value); + } + + /* + * Match optional suffixes. + */ + private void matchSuffix() throws DescriptorParsingException { + if (tokens.length == currTokenIndex) { + // no suffix + return; + } + if (tokens[currTokenIndex].equals(KEYWORD_WHERE)) { + currTokenIndex++; + WhereExpression expn = new WhereExpression(); + tree.setWhereExpn(expn); + matchWhereExp(expn.getRoot()); + matchSort(tree); + matchLimit(tree); + } else if (tokens[currTokenIndex].equals(KEYWORD_SORT)) { + // SORT token eaten up by matchSort() + matchSort(tree); + matchLimit(tree); + } else if (tokens[currTokenIndex].equals(KEYWORD_LIMIT)) { + // LIMIT token eaten up by matchLimit() + matchLimit(tree); + } else { + throw new DescriptorParsingException("Unexpected token: '" + + tokens[currTokenIndex] + "'. Expected one of " + + KEYWORD_WHERE + ", " + KEYWORD_SORT + ", " + KEYWORD_LIMIT); + } + } + + private void matchLimit(SuffixExpression tree) throws DescriptorParsingException { + if (currTokenIndex == tokens.length) { + // empty + return; + } else if (currTokenIndex < tokens.length) { + if (tokens[currTokenIndex].equals(KEYWORD_LIMIT)) { + LimitExpression node = new LimitExpression(); + tree.setLimitExpn(node); + currTokenIndex++; + matchTerm(node); + } + } else { + throw new DescriptorParsingException("Illegal statement descriptor: Reason LIMIT"); + } + } + + private void matchSort(SuffixExpression tree) throws DescriptorParsingException { + if (currTokenIndex < tokens.length + && tokens[currTokenIndex].equals(KEYWORD_SORT)) { + SortExpression sortExpn = new SortExpression(); + tree.setSortExpn(sortExpn); + currTokenIndex++; + matchSortList(sortExpn); + } + if (currTokenIndex > tokens.length) { + throw new DescriptorParsingException("Illegal statement descriptor."); + } + // empty + } + + private void matchSortList(SortExpression sortExpn) throws DescriptorParsingException { + matchSortPair(sortExpn); + matchSortListPreamble(sortExpn); + } + + private void matchSortListPreamble(SortExpression sortExpn) throws DescriptorParsingException { + if (currTokenIndex < tokens.length && tokens[currTokenIndex].equals(SORTLIST_SEP)) { + currTokenIndex++; // ',' token + matchSortList(sortExpn); + } + } + + private void matchSortPair(SortExpression expn) throws DescriptorParsingException { + SortMember member = new SortMember(); + matchTerm(member); + matchSortModifier(member); + // Add the member node to the list of the sort node + expn.addMember(member); + } + + private void matchSortModifier(SortMember member) throws DescriptorParsingException { + String msg = "Illegal statement decriptor: Reason SORT. Expected ASC or DSC"; + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException(msg); + } + if (tokens[currTokenIndex].equals(KEYWORD_ASC)) { + member.setDirection(SortDirection.ASCENDING); + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KEYWORD_DSC)) { + member.setDirection(SortDirection.DESCENDING); + currTokenIndex++; + } else { + throw new DescriptorParsingException(msg); + } + } + + private void matchWhereExp(Node node) throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException("Illegal where clause"); + } + assert(node != null); + matchAndCondition(node); + matchOrCondition(node); + } + + private void matchAndCondition(Node currNode) throws DescriptorParsingException { + matchCondition(currNode); + matchAndExpression(currNode); + } + + private void matchCondition(Node currNode) throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException("Illegal statement descriptor: Reason sort clause"); + } + if (tokens[currTokenIndex].equals(Operator.NOT.getName())) { + NotBooleanExpressionNode notNode = new NotBooleanExpressionNode(currNode); + if (currNode instanceof BinaryExpressionNode) { + BinaryExpressionNode currNodeExpr = (BinaryExpressionNode)currNode; + Node available = currNodeExpr.getLeftChild(); + if (available != null) { + currNodeExpr.setRightChild(notNode); + } else { + currNodeExpr.setLeftChild(notNode); + } + } else { + assert(currNode instanceof NotBooleanExpressionNode || currNode instanceof Node); + currNode.setValue(notNode); + } + currTokenIndex++; // NOT keyword + + matchCondition(notNode); + } else { + matchComparisionExpression(currNode); + } + } + + private void matchComparisionExpression(Node currNode) throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException("Illegal statement descriptor: Comparison expression"); + } + BinaryExpressionNode expr = new BinaryExpressionNode(currNode); + TerminalNode left = new TerminalNode(expr); + TerminalNode right = new TerminalNode(expr); + expr.setLeftChild(left); + expr.setRightChild(right); + + if (currNode instanceof BinaryExpressionNode) { + BinaryExpressionNode currNodeExpr = (BinaryExpressionNode)currNode; + Node available = currNodeExpr.getLeftChild(); + if (available == null) { + currNodeExpr.setLeftChild(expr); + } else { + assert(currNodeExpr.getRightChild() == null); + currNodeExpr.setRightChild(expr); + } + } else { + assert(currNode instanceof NotBooleanExpressionNode || currNode instanceof Node); + currNode.setValue(expr); + } + + matchTerm(left, true); + matchComparisonRHS(expr); + } + + private void matchComparisonRHS(BinaryExpressionNode currNode) throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + // boolean literals are not allowed + throw new DescriptorParsingException("Illegal statement descriptor: Boolean literals are not allowed!"); + } + if (tokens[currTokenIndex].equals(Operator.EQUALS.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.EQUALS); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else if (tokens[currTokenIndex].equals(Operator.LESS_THAN_OR_EQUAL_TO.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.LESS_THAN_OR_EQUAL_TO); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else if (tokens[currTokenIndex].equals(Operator.GREATER_THAN_OR_EQUAL_TO.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else if (tokens[currTokenIndex].equals(Operator.GREATER_THAN.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.GREATER_THAN); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else if (tokens[currTokenIndex].equals(Operator.LESS_THAN.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.LESS_THAN); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else if (tokens[currTokenIndex].equals(Operator.NOT_EQUAL_TO.getName())) { + currTokenIndex++; + currNode.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); + matchTerm((TerminalNode)currNode.getRightChild(), false); + } else { + throw new DescriptorParsingException("Illegal statement descriptor: Reason comparison expression!"); + } + } + + private void matchTerm(SortMember member) throws DescriptorParsingException { + String term = getTerm(); + if (term.charAt(0) == PARAM_PLACEHOLDER) { + assert(placeHolderCount > 0); + ensureValidType(term, "SORT"); + if (term.charAt(1) != 's') { + String msg = "Sort parameters only accept string types. Placeholder was: " + term; + throw new DescriptorParsingException(msg); + } + UnfinishedSortKey unfinishedKey = new UnfinishedSortKey(); + unfinishedKey.setParameterIndex(placeHolderCount - 1); + member.setSortKey(unfinishedKey); + return; + } + String stringTerm = getStringTerm(term); + member.setSortKey(stringTerm); + } + + /* + * Check if the free parameter type is valid in a given context. Currently, + * list and pojo free type parameters are invalid for LIMIT, SORT and WHERE. + * + * Currently, no list types and no Pojo types are allowed. + */ + private void ensureValidType(String term, String contextName) throws DescriptorParsingException { + if (term.length() > 2) { + // Don't allow list types for invalid contexts + // Only list type free variables have 3 characters + String format = "List free variable type not allowed in %s context"; + String msg = String.format(format, contextName); + throw new DescriptorParsingException(msg); + } + if (term.equals(POJO_FREE_PARAMETER_TYPE)) { + String format = "Pojo free variable type not allowed in %s context"; + String msg = String.format(format, contextName); + throw new DescriptorParsingException(msg); + } + } + + private void matchTerm(LimitExpression expn) throws DescriptorParsingException { + String term = getTerm(); + if (term.charAt(0) == PARAM_PLACEHOLDER) { + assert(placeHolderCount > 0); + ensureValidType(term, "LIMIT"); + if (term.charAt(1) != 'i') { + String msg = "Limit parameters only accept integer types. Placeholder was: " + term; + throw new DescriptorParsingException(msg); + } + UnfinishedLimitValue limitValue = new UnfinishedLimitValue(); + limitValue.setParameterIndex(placeHolderCount - 1); + expn.setValue(limitValue); + return; + } + int limitVal; + try { + limitVal = Integer.parseInt(term); + } catch (NumberFormatException e) { + throw new DescriptorParsingException("Invalid limit expression. '" + term + "' not an integer"); + } + expn.setValue(limitVal); + } + + /** + * Calls {@link #matchTerm(TerminalNode, boolean, boolean)} with a + * {@code false isSetListContext} parameter. + */ + private void matchTerm(TerminalNode node, boolean isLHS) throws DescriptorParsingException { + // default to false + matchTerm(node, isLHS, false); + } + + /** + * Match a terminal in a TerminalNode context. Only where expression, and + * set list context take this code path. + * + * @param node + * The terminal node which is parsed at this point. + * @param isLHS + * {@code true} if and only if the node is the left hand side of + * a binary expression. + * @param isSetListContext + * {@code true} if and only if the node is in a set list context + * or conversely NOT in a where expression context. + * @throws DescriptorParsingException + * If and error was encountered parsing the terminal string + * token. + */ + private void matchTerm(TerminalNode node, boolean isLHS, + boolean isSetListContext) throws DescriptorParsingException { + String term = getTerm(); + if (term.charAt(0) == PARAM_PLACEHOLDER) { + assert(placeHolderCount > 0); + if (!isSetListContext) { + ensureValidType(term, "WHERE"); + } + UnfinishedValueNode patchNode = new UnfinishedValueNode(); + patchNode.setParameterIndex(placeHolderCount - 1); + patchNode.setLHS(isLHS); + // figure out the expected type + FreeVarTypeToken expectedType = getType(term.substring(1)); + if (expectedType == null) { + throw new DescriptorParsingException("Unknown type of free parameter: '" + term + "'"); + } + patchNode.setType(expectedType.componentType); + patchNode.setArrayType(expectedType.isArrayType); + node.setValue(patchNode); + return; + } + // regular terminal. i.e. literal value + if (isLHS) { + // FIXME: In thermostat LHS of comparisons must be Key objects. I'm + // not sure if this restriction is very meaningful in a prepared + // statement context as the purpose of this was to ensure "type" + // compatibility between Key <=> value comparisons. + String stringTerm = getStringTerm(term); + Key<?> key = new Key<>(stringTerm); + node.setValue(key); + } else { + Object typedValue = getTypedValue(term); + node.setValue(typedValue); + } + } + + private Object getTypedValue(String term) throws DescriptorParsingException { + try { + String stringTerm = getStringTerm(term); + return stringTerm; + } catch (DescriptorParsingException e) { + // Must be integer (long/int) or boolean. First check for boolean, + // then for long and regular ints. + if (term.equals(Boolean.toString(false)) || term.equals(Boolean.toString(true))) { + boolean boolVal = Boolean.parseBoolean(term); + return boolVal; + } + // Next, parse long or int. + try { + int lastCharInTokenIndex = term.length() - 1; + // preceding l/L indicate long integer types. + if (term.charAt(lastCharInTokenIndex) == 'L' || term.charAt(lastCharInTokenIndex) == 'l') { + long longVal = Long.parseLong(term.substring(0, lastCharInTokenIndex)); + return longVal; + } + // must be integer or some invalid type + int intVal = Integer.parseInt(term); + return intVal; + } catch (NumberFormatException nfe) { + String msg = "Illegal terminal type. Token was ->" + term + "<-"; + throw new DescriptorParsingException(msg); + } + } + } + + private String getStringTerm(String term) throws DescriptorParsingException { + String errorMsg = + "Expected string value. Got term ->" + + term + + "<-" + + " Was the string properly quoted? Example: 'string'."; + if (term.charAt(0) != '\'' || term.charAt(term.length() - 1) != '\'') { + throw new DescriptorParsingException(errorMsg); + } + return term.substring(1, term.length() - 1); + } + + private FreeVarTypeToken getType(String term) { + if (term.equals("")) { + // illegal type + return null; + } + // free variable types can have 1 or 2 characters. + assert(term.length() > 0 && term.length() < 3); + char switchChar = term.charAt(0); + FreeVarTypeToken typeToken = null; + switch (switchChar) { + case 'i': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(Integer.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(Integer.class, true); + } + break; + } + case 'l': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(Long.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(Long.class, true); + } + break; + } + case 's': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(String.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(String.class, true); + } + break; + } + case 'b': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(Boolean.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(Boolean.class, true); + } + break; + } + case 'd': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(Double.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(Double.class, true); + } + break; + } + case 'p': { + if (term.length() == 1) { + typeToken = new FreeVarTypeToken(Pojo.class, false); + } else if (term.length() == 2 && term.charAt(1) == '[') { + typeToken = new FreeVarTypeToken(Pojo.class, true); + } + break; + } + default: + assert(typeToken == null); + break; + } + return typeToken; + } + + private String getTerm() throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException("Invalid where clause. Reason: term expected but not given!"); + } + if (tokens[currTokenIndex].charAt(0) == PARAM_PLACEHOLDER) { + placeHolderCount++; + } + String term = tokens[currTokenIndex]; + currTokenIndex++; + return term; + } + + private void matchAndExpression(Node currNode) throws DescriptorParsingException { + if (currTokenIndex < tokens.length && + tokens[currTokenIndex].equals(Operator.AND.getName())) { + currTokenIndex++; // AND keyword + + Node parent = currNode; + if (currNode instanceof BinaryExpressionNode || + currNode instanceof NotBooleanExpressionNode) { + parent = currNode.getParent(); + assert(parent != null); + } + BinaryExpressionNode and = new BinaryExpressionNode(parent); + and.setOperator(BinaryLogicalOperator.AND); + if (currNode instanceof BinaryExpressionNode || + currNode instanceof NotBooleanExpressionNode) { + currNode.setParent(and); + and.setLeftChild(currNode); + parent.setValue(and); + } else { + // Root node case + and.setLeftChild((Node)parent.getValue()); + parent.setValue(and); + } + // Note the current AND expression node at this point of parsing + // must be at the root of the entire expression. + assert(and.getParent().getParent() == null); + + matchWhereExp(and); + + } + // empty + } + + private void matchOrCondition(Node currNode) throws DescriptorParsingException { + if (currTokenIndex < tokens.length && + tokens[currTokenIndex].equals(Operator.OR.getName())) { + currTokenIndex++; // OR keyword + + Node parent = currNode; + if (currNode instanceof BinaryExpressionNode || + currNode instanceof NotBooleanExpressionNode) { + parent = currNode.getParent(); + assert(parent != null); + } + BinaryExpressionNode or = new BinaryExpressionNode(parent); + or.setOperator(BinaryLogicalOperator.OR); + if (currNode instanceof BinaryExpressionNode || + currNode instanceof NotBooleanExpressionNode) { + currNode.setParent(or); + or.setLeftChild(currNode); + parent.setValue(or); + } else { + // Root node case + or.setLeftChild((Node)parent.getValue()); + parent.setValue(or); + } + // Note the current OR expression node at this point of parsing + // must be at the root of the entire expression. + assert(or.getParent().getParent() == null); + + matchWhereExp(or); + } + // empty + } + + private void createStatement() { + // matchStatementType and matchCategory advanced currTokenIndex, + // lets use idx of 0 here. + final String statementType = tokens[0]; + if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_QUERY])) { + // regular query case + Query<T> query = storage.createQuery(desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(query); + } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_QUERY_COUNT])) { + // create aggregate count query + Query<T> query = storage.createAggregateQuery(AggregateFunction.COUNT, desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(query); + } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_ADD])) { + // create add + Add<T> add = storage.createAdd(desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(add); + } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_REPLACE])) { + // create replace + Replace<T> replace = storage.createReplace(desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(replace); + } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_UPDATE])) { + // create replace + Update<T> update = storage.createUpdate(desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(update); + } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_REMOVE])) { + // create remove + Remove<T> remove = storage.createRemove(desc.getCategory()); + this.parsedStatement = new ParsedStatementImpl<>(remove); + } else { + throw new IllegalStateException("Don't know how to create statement type '" + statementType + "'"); + } + } + + private void matchCategory() throws DescriptorParsingException { + if (currTokenIndex >= tokens.length) { + throw new DescriptorParsingException("Missing category name in descriptor: '" + desc.getDescriptor() + "'"); + } + Category<?> category = desc.getCategory(); + if (!tokens[currTokenIndex].equals(category.getName())) { + throw new DescriptorParsingException( + "Category mismatch in descriptor. Category from descriptor string: '" + + tokens[currTokenIndex] + + "'. Category name from category: '" + + category.getName() + "'."); + } + currTokenIndex++; + } + + private void matchStatementType() throws DescriptorParsingException { + if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_QUERY])) { + // QUERY + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_QUERY_COUNT])) { + // QUERY-COUNT + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_ADD])) { + // ADD + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_REPLACE])) { + // REPLACE + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_UPDATE])) { + // UPDATE + currTokenIndex++; + } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_REMOVE])) { + // REMOVE + currTokenIndex++; + } else { + throw new DescriptorParsingException("Unknown statement type: '" + tokens[currTokenIndex] + "'"); + } + } + + private static class FreeVarTypeToken { + + private final boolean isArrayType; + private final Class<?> componentType; + + private FreeVarTypeToken(Class<?> componentType, boolean isArrayType) { + this.isArrayType = isArrayType; + this.componentType = componentType; + } + } +}
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImpl.java Mon Oct 07 15:03:04 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImpl.java Fri Oct 04 18:13:31 2013 +0200 @@ -49,7 +49,7 @@ import com.redhat.thermostat.storage.query.Expression; /** - * Result object as returned by {@link StatementDescriptorParser#parse()}. + * Result object as returned by {@link BasicDescriptorParser#parse()}. * An instance of this plus a list of {@link PreparedParameter} should * be sufficient to patch up a prepared statement with its real values. *
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImpl.java Mon Oct 07 15:03:04 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImpl.java Fri Oct 04 18:13:31 2013 +0200 @@ -65,7 +65,7 @@ public PreparedStatementImpl(BackingStorage storage, StatementDescriptor<T> desc) throws DescriptorParsingException { this.desc = desc; - StatementDescriptorParser<T> parser = new StatementDescriptorParser<>(storage, desc); + BasicDescriptorParser<T> parser = new BasicDescriptorParser<>(storage, desc); this.parsedStatement = (ParsedStatementImpl<T>)parser.parse(); int numParams = parsedStatement.getNumParams(); params = new PreparedParameters(numParams);
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParser.java Mon Oct 07 15:03:04 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,887 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.storage.internal.statement; - -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -import com.redhat.thermostat.storage.core.Add; -import com.redhat.thermostat.storage.core.AggregateQuery.AggregateFunction; -import com.redhat.thermostat.storage.core.BackingStorage; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.DescriptorParsingException; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.ParsedStatement; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Query.SortDirection; -import com.redhat.thermostat.storage.core.Remove; -import com.redhat.thermostat.storage.core.Replace; -import com.redhat.thermostat.storage.core.Statement; -import com.redhat.thermostat.storage.core.StatementDescriptor; -import com.redhat.thermostat.storage.core.Update; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.storage.query.BinaryComparisonOperator; -import com.redhat.thermostat.storage.query.BinaryLogicalOperator; - -/** - * A parser for the string representation of {@link StatementDescriptor}s. - * Tokens have to be separated by whitespace. - * - * This parser implements the following simple grammar for statement descriptors. - * It supports the following statement types: - * <ul> - * <li>QUERY (read)</li> - * <li>QUERY-COUNT (read)</li> - * <li>ADD (write)</li> - * <li>UPDATE (write)</li> - * <li>REPLACE (write)</li> - * <li>REMOVE (write)</li> - * </ul> - * - * <p><strong>Grammar:</strong></p> - * <pre> - * statementDesc := statementType category setList suffix - * statementType := 'QUERY' | 'QUERY-COUNT' | - * 'ADD' | 'REPLACE' | 'UPDATE' | - * 'REMOVE' - * category := string - * setList := 'SET' setValues | \empty - * setValues := valuePair valueList - * valuePair := term '=' term - * valueList := ',' setValues | \empty - * suffix := 'WHERE' where | - * 'SORT' sortCond | - * 'LIMIT' term | \empty - * where := whereExp sort limit - * whereExp := andCond orCond - * orCond := 'OR' whereExp | \empty - * sort := 'SORT' sortCond | \empty - * sortCond := sortPair sortList - * sortPair := term sortModifier - * sortModifier := 'ASC' | 'DSC' - * sortList := ',' sortCond | \empty - * limit := 'LIMIT' term | \empty - * andCond := condition andBody - * andBody := 'AND' whereExp | \empty - * condition := 'NOT' condition | compExp - * compExp := term compExpRHS - * term := freeParam | literal - * freeParam := '?s' | '?i' | '?l' | '?s[' | '?b' - * literal := sQuote string sQuote | int | long | boolean - * sQuote := \' - * boolean := <true> | <false> - * int := <literal-int> - * long := <literal-long>longPostFix - * longPostFix := 'l' | 'L' - * string := <literal-string-value> - * compExpRHS := '!=' term | '=' term | '<=' term | '>=' term | - * '<' term | '>' term - * </pre> - * - * This implements the following logic precedence rules (in this order of - * precedence): - * - * <ol> - * <li>NOT</li> - * <li>AND</li> - * <li>OR</li> - * </ol> - * - * NOTE: Comparison expressions have equal precedence. - */ -class StatementDescriptorParser<T extends Pojo> { - - private static final String TOKEN_DELIMS = " \t\r\n\f"; - private static final short IDX_QUERY = 0; - private static final short IDX_QUERY_COUNT = 1; - private static final short IDX_ADD = 2; - private static final short IDX_REPLACE = 3; - private static final short IDX_UPDATE = 4; - private static final short IDX_REMOVE = 5; - private static final String[] KNOWN_STATEMENT_TYPES = new String[] { - "QUERY", "QUERY-COUNT", "ADD", "REPLACE", "UPDATE", "REMOVE" - }; - private static final String SORTLIST_SEP = ","; - private static final String SETLIST_SEP = SORTLIST_SEP; - private static final String KEYWORD_SET = "SET"; - private static final String KEYWORD_WHERE = "WHERE"; - private static final String KEYWORD_SORT = "SORT"; - private static final String KEYWORD_LIMIT = "LIMIT"; - private static final String KEYWORD_ASC = "ASC"; - private static final String KEYWORD_DSC = "DSC"; - private static final String POJO_FREE_PARAMETER_TYPE = "?p"; - private static final char PARAM_PLACEHOLDER = '?'; - - private final String[] tokens; - private final StatementDescriptor<T> desc; - private final BackingStorage storage; - private int currTokenIndex; - private int placeHolderCount; - // the parsed statement - private ParsedStatementImpl<T> parsedStatement; - private SuffixExpression tree; - private SetList setList; - - StatementDescriptorParser(BackingStorage storage, StatementDescriptor<T> desc) { - this.tokens = getTokens(desc.getDescriptor()); - this.currTokenIndex = 0; - this.placeHolderCount = 0; - this.desc = desc; - this.storage = storage; - } - - private String[] getTokens(String str) { - StringTokenizer tokenizer = new StringTokenizer(str, TOKEN_DELIMS); - List<String> toks = new ArrayList<>(tokenizer.countTokens()); - while (tokenizer.hasMoreTokens()) { - toks.add(tokenizer.nextToken()); - } - return toks.toArray(new String[0]); - } - - public ParsedStatement<T> parse() throws DescriptorParsingException { - matchStatementType(); - matchCategory(); - // matched so far, create the raw statement - createStatement(); - this.setList = new SetList(); - matchSetList(setList); - this.tree = new SuffixExpression(); - matchSuffix(); - if (currTokenIndex != tokens.length) { - throw new DescriptorParsingException("Incomplete parse"); - } - parsedStatement.setNumFreeParams(placeHolderCount); - parsedStatement.setSetList(setList); - parsedStatement.setSuffixExpression(tree); - doSemanticAnalysis(); - return parsedStatement; - } - - private void doSemanticAnalysis() throws DescriptorParsingException { - // TODO: - // - Check that ADD/REPLACE specifies all keys judging by the Pojo - // model class. Not sure if good idea though, as this would likely - // introduce dep on beanutils. - Statement<T> stmt = parsedStatement.getRawStatement(); - if (stmt == null) { - // should never be null - throw new NullPointerException(); - } - if (stmt instanceof Add && tree.getWhereExpn() != null) { - String msg = "WHERE clause not allowed for ADD"; - throw new DescriptorParsingException(msg); - } - if (stmt instanceof Replace && tree.getWhereExpn() == null) { - String msg = "WHERE clause required for REPLACE"; - throw new DescriptorParsingException(msg); - } - if (stmt instanceof Update) { - if (tree.getWhereExpn() == null) { - // WHERE required for UPDATE - String msg = "WHERE clause required for UPDATE"; - throw new DescriptorParsingException(msg); - } - if (setList.getValues().size() == 0) { - // SET required for UPDATE - String msg = "SET list required for UPDATE"; - throw new DescriptorParsingException(msg); - } - } - if (stmt instanceof Remove && setList.getValues().size() > 0) { - String msg = "SET not allowed for REMOVE"; - throw new DescriptorParsingException(msg); - } - if (stmt instanceof Query) { - if (setList.getValues().size() > 0) { - // Must not have SET for QUERYs - String msg = "SET not allowed for QUERY/QUERY-COUNT"; - throw new DescriptorParsingException(msg); - } - } else { - // only queries can have sort/limit expressions - if (this.tree.getLimitExpn() != null || this.tree.getSortExpn() != null) { - String msg = "LIMIT/SORT only allowed for QUERY/QUERY-COUNT"; - throw new DescriptorParsingException(msg); - } - } - } - - /* - * Match set list for DML statements. - */ - private void matchSetList(final SetList setList) throws DescriptorParsingException { - if (tokens.length == currTokenIndex) { - // no set list - return; - } - if (tokens[currTokenIndex].equals(KEYWORD_SET)) { - currTokenIndex++; // SET - matchSetValues(setList); - } - // empty, proceed with suffix - } - - /* - * Match list of values in a SET expression - */ - private void matchSetValues(SetList setList) throws DescriptorParsingException { - matchValuePair(setList); - matchValueList(setList); - } - - /* - * Match more value pairs in a SET list - */ - private void matchValueList(SetList setList) throws DescriptorParsingException { - if (currTokenIndex == tokens.length) { - // empty - return; - } - if (tokens[currTokenIndex].equals(SETLIST_SEP)) { - currTokenIndex++; // , - matchSetValues(setList); - } - } - - /* - * Match one pair of values in a - */ - private void matchValuePair(SetList setList) throws DescriptorParsingException { - SetListValue value = new SetListValue(); - TerminalNode lval = new TerminalNode(null); - matchTerm(lval, true, true); - value.setKey(lval); - if (tokens[currTokenIndex].equals("=")) { - currTokenIndex++; // = - } else { - String msg = "Expected '=' after SET value LHS. Token was ->" + tokens[currTokenIndex] + "<-"; - throw new DescriptorParsingException(msg); - } - TerminalNode rval = new TerminalNode(null); - matchTerm(rval, false, true); - value.setValue(rval); - setList.addValue(value); - } - - /* - * Match optional suffixes. - */ - private void matchSuffix() throws DescriptorParsingException { - if (tokens.length == currTokenIndex) { - // no suffix - return; - } - if (tokens[currTokenIndex].equals(KEYWORD_WHERE)) { - currTokenIndex++; - WhereExpression expn = new WhereExpression(); - tree.setWhereExpn(expn); - matchWhereExp(expn.getRoot()); - matchSort(tree); - matchLimit(tree); - } else if (tokens[currTokenIndex].equals(KEYWORD_SORT)) { - // SORT token eaten up by matchSort() - matchSort(tree); - matchLimit(tree); - } else if (tokens[currTokenIndex].equals(KEYWORD_LIMIT)) { - // LIMIT token eaten up by matchLimit() - matchLimit(tree); - } else { - throw new DescriptorParsingException("Unexpected token: '" - + tokens[currTokenIndex] + "'. Expected one of " - + KEYWORD_WHERE + ", " + KEYWORD_SORT + ", " + KEYWORD_LIMIT); - } - } - - private void matchLimit(SuffixExpression tree) throws DescriptorParsingException { - if (currTokenIndex == tokens.length) { - // empty - return; - } else if (currTokenIndex < tokens.length) { - if (tokens[currTokenIndex].equals(KEYWORD_LIMIT)) { - LimitExpression node = new LimitExpression(); - tree.setLimitExpn(node); - currTokenIndex++; - matchTerm(node); - } - } else { - throw new DescriptorParsingException("Illegal statement descriptor: Reason LIMIT"); - } - } - - private void matchSort(SuffixExpression tree) throws DescriptorParsingException { - if (currTokenIndex < tokens.length - && tokens[currTokenIndex].equals(KEYWORD_SORT)) { - SortExpression sortExpn = new SortExpression(); - tree.setSortExpn(sortExpn); - currTokenIndex++; - matchSortList(sortExpn); - } - if (currTokenIndex > tokens.length) { - throw new DescriptorParsingException("Illegal statement descriptor."); - } - // empty - } - - private void matchSortList(SortExpression sortExpn) throws DescriptorParsingException { - matchSortPair(sortExpn); - matchSortListPreamble(sortExpn); - } - - private void matchSortListPreamble(SortExpression sortExpn) throws DescriptorParsingException { - if (currTokenIndex < tokens.length && tokens[currTokenIndex].equals(SORTLIST_SEP)) { - currTokenIndex++; // ',' token - matchSortList(sortExpn); - } - } - - private void matchSortPair(SortExpression expn) throws DescriptorParsingException { - SortMember member = new SortMember(); - matchTerm(member); - matchSortModifier(member); - // Add the member node to the list of the sort node - expn.addMember(member); - } - - private void matchSortModifier(SortMember member) throws DescriptorParsingException { - String msg = "Illegal statement decriptor: Reason SORT. Expected ASC or DSC"; - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException(msg); - } - if (tokens[currTokenIndex].equals(KEYWORD_ASC)) { - member.setDirection(SortDirection.ASCENDING); - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KEYWORD_DSC)) { - member.setDirection(SortDirection.DESCENDING); - currTokenIndex++; - } else { - throw new DescriptorParsingException(msg); - } - } - - private void matchWhereExp(Node node) throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException("Illegal where clause"); - } - assert(node != null); - matchAndCondition(node); - matchOrCondition(node); - } - - private void matchAndCondition(Node currNode) throws DescriptorParsingException { - matchCondition(currNode); - matchAndExpression(currNode); - } - - private void matchCondition(Node currNode) throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException("Illegal statement descriptor: Reason sort clause"); - } - if (tokens[currTokenIndex].equals(Operator.NOT.getName())) { - NotBooleanExpressionNode notNode = new NotBooleanExpressionNode(currNode); - if (currNode instanceof BinaryExpressionNode) { - BinaryExpressionNode currNodeExpr = (BinaryExpressionNode)currNode; - Node available = currNodeExpr.getLeftChild(); - if (available != null) { - currNodeExpr.setRightChild(notNode); - } else { - currNodeExpr.setLeftChild(notNode); - } - } else { - assert(currNode instanceof NotBooleanExpressionNode || currNode instanceof Node); - currNode.setValue(notNode); - } - currTokenIndex++; // NOT keyword - - matchCondition(notNode); - } else { - matchComparisionExpression(currNode); - } - } - - private void matchComparisionExpression(Node currNode) throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException("Illegal statement descriptor: Comparison expression"); - } - BinaryExpressionNode expr = new BinaryExpressionNode(currNode); - TerminalNode left = new TerminalNode(expr); - TerminalNode right = new TerminalNode(expr); - expr.setLeftChild(left); - expr.setRightChild(right); - - if (currNode instanceof BinaryExpressionNode) { - BinaryExpressionNode currNodeExpr = (BinaryExpressionNode)currNode; - Node available = currNodeExpr.getLeftChild(); - if (available == null) { - currNodeExpr.setLeftChild(expr); - } else { - assert(currNodeExpr.getRightChild() == null); - currNodeExpr.setRightChild(expr); - } - } else { - assert(currNode instanceof NotBooleanExpressionNode || currNode instanceof Node); - currNode.setValue(expr); - } - - matchTerm(left, true); - matchComparisonRHS(expr); - } - - private void matchComparisonRHS(BinaryExpressionNode currNode) throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - // boolean literals are not allowed - throw new DescriptorParsingException("Illegal statement descriptor: Boolean literals are not allowed!"); - } - if (tokens[currTokenIndex].equals(Operator.EQUALS.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.EQUALS); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else if (tokens[currTokenIndex].equals(Operator.LESS_THAN_OR_EQUAL_TO.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.LESS_THAN_OR_EQUAL_TO); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else if (tokens[currTokenIndex].equals(Operator.GREATER_THAN_OR_EQUAL_TO.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else if (tokens[currTokenIndex].equals(Operator.GREATER_THAN.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.GREATER_THAN); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else if (tokens[currTokenIndex].equals(Operator.LESS_THAN.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.LESS_THAN); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else if (tokens[currTokenIndex].equals(Operator.NOT_EQUAL_TO.getName())) { - currTokenIndex++; - currNode.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); - matchTerm((TerminalNode)currNode.getRightChild(), false); - } else { - throw new DescriptorParsingException("Illegal statement descriptor: Reason comparison expression!"); - } - } - - private void matchTerm(SortMember member) throws DescriptorParsingException { - String term = getTerm(); - if (term.charAt(0) == PARAM_PLACEHOLDER) { - assert(placeHolderCount > 0); - ensureValidType(term, "SORT"); - if (term.charAt(1) != 's') { - String msg = "Sort parameters only accept string types. Placeholder was: " + term; - throw new DescriptorParsingException(msg); - } - UnfinishedSortKey unfinishedKey = new UnfinishedSortKey(); - unfinishedKey.setParameterIndex(placeHolderCount - 1); - member.setSortKey(unfinishedKey); - return; - } - String stringTerm = getStringTerm(term); - member.setSortKey(stringTerm); - } - - /* - * Check if the free parameter type is valid in a given context. Currently, - * list and pojo free type parameters are invalid for LIMIT, SORT and WHERE. - * - * Currently, no list types and no Pojo types are allowed. - */ - private void ensureValidType(String term, String contextName) throws DescriptorParsingException { - if (term.length() > 2) { - // Don't allow list types for invalid contexts - // Only list type free variables have 3 characters - String format = "List free variable type not allowed in %s context"; - String msg = String.format(format, contextName); - throw new DescriptorParsingException(msg); - } - if (term.equals(POJO_FREE_PARAMETER_TYPE)) { - String format = "Pojo free variable type not allowed in %s context"; - String msg = String.format(format, contextName); - throw new DescriptorParsingException(msg); - } - } - - private void matchTerm(LimitExpression expn) throws DescriptorParsingException { - String term = getTerm(); - if (term.charAt(0) == PARAM_PLACEHOLDER) { - assert(placeHolderCount > 0); - ensureValidType(term, "LIMIT"); - if (term.charAt(1) != 'i') { - String msg = "Limit parameters only accept integer types. Placeholder was: " + term; - throw new DescriptorParsingException(msg); - } - UnfinishedLimitValue limitValue = new UnfinishedLimitValue(); - limitValue.setParameterIndex(placeHolderCount - 1); - expn.setValue(limitValue); - return; - } - int limitVal; - try { - limitVal = Integer.parseInt(term); - } catch (NumberFormatException e) { - throw new DescriptorParsingException("Invalid limit expression. '" + term + "' not an integer"); - } - expn.setValue(limitVal); - } - - /** - * Calls {@link #matchTerm(TerminalNode, boolean, boolean)} with a - * {@code false isSetListContext} parameter. - */ - private void matchTerm(TerminalNode node, boolean isLHS) throws DescriptorParsingException { - // default to false - matchTerm(node, isLHS, false); - } - - /** - * Match a terminal in a TerminalNode context. Only where expression, and - * set list context take this code path. - * - * @param node - * The terminal node which is parsed at this point. - * @param isLHS - * {@code true} if and only if the node is the left hand side of - * a binary expression. - * @param isSetListContext - * {@code true} if and only if the node is in a set list context - * or conversely NOT in a where expression context. - * @throws DescriptorParsingException - * If and error was encountered parsing the terminal string - * token. - */ - private void matchTerm(TerminalNode node, boolean isLHS, - boolean isSetListContext) throws DescriptorParsingException { - String term = getTerm(); - if (term.charAt(0) == PARAM_PLACEHOLDER) { - assert(placeHolderCount > 0); - if (!isSetListContext) { - ensureValidType(term, "WHERE"); - } - UnfinishedValueNode patchNode = new UnfinishedValueNode(); - patchNode.setParameterIndex(placeHolderCount - 1); - patchNode.setLHS(isLHS); - // figure out the expected type - FreeVarTypeToken expectedType = getType(term.substring(1)); - if (expectedType == null) { - throw new DescriptorParsingException("Unknown type of free parameter: '" + term + "'"); - } - patchNode.setType(expectedType.componentType); - patchNode.setArrayType(expectedType.isArrayType); - node.setValue(patchNode); - return; - } - // regular terminal. i.e. literal value - if (isLHS) { - // FIXME: In thermostat LHS of comparisons must be Key objects. I'm - // not sure if this restriction is very meaningful in a prepared - // statement context as the purpose of this was to ensure "type" - // compatibility between Key <=> value comparisons. - String stringTerm = getStringTerm(term); - Key<?> key = new Key<>(stringTerm); - node.setValue(key); - } else { - Object typedValue = getTypedValue(term); - node.setValue(typedValue); - } - } - - private Object getTypedValue(String term) throws DescriptorParsingException { - try { - String stringTerm = getStringTerm(term); - return stringTerm; - } catch (DescriptorParsingException e) { - // Must be integer (long/int) or boolean. First check for boolean, - // then for long and regular ints. - if (term.equals(Boolean.toString(false)) || term.equals(Boolean.toString(true))) { - boolean boolVal = Boolean.parseBoolean(term); - return boolVal; - } - // Next, parse long or int. - try { - int lastCharInTokenIndex = term.length() - 1; - // preceding l/L indicate long integer types. - if (term.charAt(lastCharInTokenIndex) == 'L' || term.charAt(lastCharInTokenIndex) == 'l') { - long longVal = Long.parseLong(term.substring(0, lastCharInTokenIndex)); - return longVal; - } - // must be integer or some invalid type - int intVal = Integer.parseInt(term); - return intVal; - } catch (NumberFormatException nfe) { - String msg = "Illegal terminal type. Token was ->" + term + "<-"; - throw new DescriptorParsingException(msg); - } - } - } - - private String getStringTerm(String term) throws DescriptorParsingException { - String errorMsg = - "Expected string value. Got term ->" - + term - + "<-" - + " Was the string properly quoted? Example: 'string'."; - if (term.charAt(0) != '\'' || term.charAt(term.length() - 1) != '\'') { - throw new DescriptorParsingException(errorMsg); - } - return term.substring(1, term.length() - 1); - } - - private FreeVarTypeToken getType(String term) { - if (term.equals("")) { - // illegal type - return null; - } - // free variable types can have 1 or 2 characters. - assert(term.length() > 0 && term.length() < 3); - char switchChar = term.charAt(0); - FreeVarTypeToken typeToken = null; - switch (switchChar) { - case 'i': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(Integer.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(Integer.class, true); - } - break; - } - case 'l': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(Long.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(Long.class, true); - } - break; - } - case 's': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(String.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(String.class, true); - } - break; - } - case 'b': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(Boolean.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(Boolean.class, true); - } - break; - } - case 'd': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(Double.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(Double.class, true); - } - break; - } - case 'p': { - if (term.length() == 1) { - typeToken = new FreeVarTypeToken(Pojo.class, false); - } else if (term.length() == 2 && term.charAt(1) == '[') { - typeToken = new FreeVarTypeToken(Pojo.class, true); - } - break; - } - default: - assert(typeToken == null); - break; - } - return typeToken; - } - - private String getTerm() throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException("Invalid where clause. Reason: term expected but not given!"); - } - if (tokens[currTokenIndex].charAt(0) == PARAM_PLACEHOLDER) { - placeHolderCount++; - } - String term = tokens[currTokenIndex]; - currTokenIndex++; - return term; - } - - private void matchAndExpression(Node currNode) throws DescriptorParsingException { - if (currTokenIndex < tokens.length && - tokens[currTokenIndex].equals(Operator.AND.getName())) { - currTokenIndex++; // AND keyword - - Node parent = currNode; - if (currNode instanceof BinaryExpressionNode || - currNode instanceof NotBooleanExpressionNode) { - parent = currNode.getParent(); - assert(parent != null); - } - BinaryExpressionNode and = new BinaryExpressionNode(parent); - and.setOperator(BinaryLogicalOperator.AND); - if (currNode instanceof BinaryExpressionNode || - currNode instanceof NotBooleanExpressionNode) { - currNode.setParent(and); - and.setLeftChild(currNode); - parent.setValue(and); - } else { - // Root node case - and.setLeftChild((Node)parent.getValue()); - parent.setValue(and); - } - // Note the current AND expression node at this point of parsing - // must be at the root of the entire expression. - assert(and.getParent().getParent() == null); - - matchWhereExp(and); - - } - // empty - } - - private void matchOrCondition(Node currNode) throws DescriptorParsingException { - if (currTokenIndex < tokens.length && - tokens[currTokenIndex].equals(Operator.OR.getName())) { - currTokenIndex++; // OR keyword - - Node parent = currNode; - if (currNode instanceof BinaryExpressionNode || - currNode instanceof NotBooleanExpressionNode) { - parent = currNode.getParent(); - assert(parent != null); - } - BinaryExpressionNode or = new BinaryExpressionNode(parent); - or.setOperator(BinaryLogicalOperator.OR); - if (currNode instanceof BinaryExpressionNode || - currNode instanceof NotBooleanExpressionNode) { - currNode.setParent(or); - or.setLeftChild(currNode); - parent.setValue(or); - } else { - // Root node case - or.setLeftChild((Node)parent.getValue()); - parent.setValue(or); - } - // Note the current OR expression node at this point of parsing - // must be at the root of the entire expression. - assert(or.getParent().getParent() == null); - - matchWhereExp(or); - } - // empty - } - - private void createStatement() { - // matchStatementType and matchCategory advanced currTokenIndex, - // lets use idx of 0 here. - final String statementType = tokens[0]; - if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_QUERY])) { - // regular query case - Query<T> query = storage.createQuery(desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(query); - } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_QUERY_COUNT])) { - // create aggregate count query - Query<T> query = storage.createAggregateQuery(AggregateFunction.COUNT, desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(query); - } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_ADD])) { - // create add - Add<T> add = storage.createAdd(desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(add); - } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_REPLACE])) { - // create replace - Replace<T> replace = storage.createReplace(desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(replace); - } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_UPDATE])) { - // create replace - Update<T> update = storage.createUpdate(desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(update); - } else if (statementType.equals(KNOWN_STATEMENT_TYPES[IDX_REMOVE])) { - // create remove - Remove<T> remove = storage.createRemove(desc.getCategory()); - this.parsedStatement = new ParsedStatementImpl<>(remove); - } else { - throw new IllegalStateException("Don't know how to create statement type '" + statementType + "'"); - } - } - - private void matchCategory() throws DescriptorParsingException { - if (currTokenIndex >= tokens.length) { - throw new DescriptorParsingException("Missing category name in descriptor: '" + desc.getDescriptor() + "'"); - } - Category<?> category = desc.getCategory(); - if (!tokens[currTokenIndex].equals(category.getName())) { - throw new DescriptorParsingException( - "Category mismatch in descriptor. Category from descriptor string: '" - + tokens[currTokenIndex] - + "'. Category name from category: '" - + category.getName() + "'."); - } - currTokenIndex++; - } - - private void matchStatementType() throws DescriptorParsingException { - if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_QUERY])) { - // QUERY - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_QUERY_COUNT])) { - // QUERY-COUNT - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_ADD])) { - // ADD - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_REPLACE])) { - // REPLACE - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_UPDATE])) { - // UPDATE - currTokenIndex++; - } else if (tokens[currTokenIndex].equals(KNOWN_STATEMENT_TYPES[IDX_REMOVE])) { - // REMOVE - currTokenIndex++; - } else { - throw new DescriptorParsingException("Unknown statement type: '" + tokens[currTokenIndex] + "'"); - } - } - - private static class FreeVarTypeToken { - - private final boolean isArrayType; - private final Class<?> componentType; - - private FreeVarTypeToken(Class<?> componentType, boolean isArrayType) { - this.isArrayType = isArrayType; - this.componentType = componentType; - } - } -}
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/WhereExpression.java Mon Oct 07 15:03:04 2013 -0400 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/internal/statement/WhereExpression.java Fri Oct 04 18:13:31 2013 +0200 @@ -44,7 +44,7 @@ * * @see Node * @see SuffixExpression - * @see StatementDescriptorParser + * @see BasicDescriptorParser */ class WhereExpression implements Printable, Patchable {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/BasicDescriptorParserTest.java Fri Oct 04 18:13:31 2013 +0200 @@ -0,0 +1,2010 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.internal.statement; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import com.redhat.thermostat.storage.core.Add; +import com.redhat.thermostat.storage.core.AggregateQuery; +import com.redhat.thermostat.storage.core.AggregateQuery.AggregateFunction; +import com.redhat.thermostat.storage.core.BackingStorage; +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.CategoryAdapter; +import com.redhat.thermostat.storage.core.DescriptorParsingException; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Query.SortDirection; +import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.storage.core.Replace; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.Update; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.AggregateCount; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.BinaryLogicalOperator; + +public class BasicDescriptorParserTest { + + private BackingStorage storage; + private Query<AgentInformation> mockQuery; + private BasicDescriptorParser<AgentInformation> parser; + private Add<AgentInformation> mockAdd; + private Update<AgentInformation> mockUpdate; + private Replace<AgentInformation> mockReplace; + private Remove<AgentInformation> mockRemove; + + @SuppressWarnings("unchecked") + @Before + public void setup() { + storage = mock(BackingStorage.class); + mockQuery = mock(Query.class); + when(storage.createQuery(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockQuery); + // setup for ADD + mockAdd = mock(Add.class); + when(storage.createAdd(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockAdd); + // setup for UPDATE + mockUpdate = mock(Update.class); + when(storage.createUpdate(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockUpdate); + // setup for REMOVE + mockRemove = mock(Remove.class); + when(storage.createRemove(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockRemove); + // setup for REPLACE + mockReplace = mock(Replace.class); + when(storage.createReplace(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockReplace); + } + + @After + public void teardown() { + storage = null; + mockQuery = null; + mockUpdate = null; + mockReplace = null; + mockAdd = null; + mockRemove = null; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testParseAggregateCount() throws DescriptorParsingException { + Query<AggregateCount> query = mock(AggregateQuery.class); + ArgumentCaptor<Category> captor = ArgumentCaptor.forClass(Category.class); + when(storage.createAggregateQuery(eq(AggregateFunction.COUNT), captor.capture())).thenReturn(query); + // first adapt from the target category in order to be able to produce the + // right aggregate query with a different result type. + CategoryAdapter<AgentInformation, AggregateCount> adapter = new CategoryAdapter<>(AgentInfoDAO.CATEGORY); + Category<AggregateCount> aggregateCategory = adapter.getAdapted(AggregateCount.class); + String descrString = "QUERY-COUNT " + aggregateCategory.getName() + " WHERE 'a' = 'b'"; + StatementDescriptor<AggregateCount> desc = new StatementDescriptor<>(aggregateCategory, descrString); + BasicDescriptorParser<AggregateCount> parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AggregateCount> statement = (ParsedStatementImpl<AggregateCount>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertTrue(statement.getRawStatement() instanceof AggregateQuery); + Category<AggregateCount> capturedCategory = captor.getValue(); + assertEquals(aggregateCategory, capturedCategory); + SuffixExpression expn = statement.getSuffixExpression(); + assertNotNull(expn); + WhereExpression where = expn.getWhereExpn(); + assertNotNull(where); + assertNull(expn.getSortExpn()); + assertNull(expn.getLimitExpn()); + } + + @Test + public void testParseQuerySimple() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName(); + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression tree = statement.getSuffixExpression(); + assertNull(tree.getLimitExpn()); + assertNull(tree.getSortExpn()); + assertNull(tree.getWhereExpn()); + } + + @Test + public void testParseLongInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003L"; + doTestType(descrString, 30000000003L, 0); + } + + @Test + public void testParseLongInWhere2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003l"; + doTestType(descrString, 30000000003L, 0); + } + + @Test + public void testParseLongInWhere3() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 3l"; + doTestType(descrString, 3L, 0); + } + + @Test + public void testParseLongInWhere4() throws DescriptorParsingException { + long val = Long.MIN_VALUE; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val + "l"; + doTestType(descrString, val, 0); + } + + @Test + public void testParseIntInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000"; + doTestType(descrString, 30000, 0); + } + + @Test + public void testParseIntInWhere2() throws DescriptorParsingException { + int val = Integer.MAX_VALUE - 1; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; + doTestType(descrString, val, 0); + } + + @Test + public void testParseIntInWhere3() throws DescriptorParsingException { + int val = Integer.MIN_VALUE; + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; + doTestType(descrString, val, 0); + } + + @Test + public void testParseBooleanInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != true"; + doTestType(descrString, true, 0); + } + + @Test + public void testParseBooleanInWhere2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != false"; + doTestType(descrString, false, 0); + } + + @Test + public void testParseStringInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'testing'"; + doTestType(descrString, "testing", 0); + } + + @Test + public void testParseStringTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?s"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(String.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseDoubleTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?d"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Double.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * String list free variable type in ADD. + */ + @Test + public void testParseStringListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?s["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(String.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * integer list free variable type in ADD. + */ + @Test + public void testParseDoubleListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?d["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Double.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * double list free variable type in ADD. + */ + @Test + public void testParseIntListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?i["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Integer.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * boolean list free variable type in ADD. + */ + @Test + public void testParseBooleanListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?b["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Boolean.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * long list free variable type in ADD. + */ + @Test + public void testParseLongListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?l["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Long.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * long list free variable type in ADD. + */ + @Test + public void testParsePojoTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support pojo type. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?p"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Pojo.class); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + /* + * SET clauses allow for setting list types. Test that it works for a + * Pojo list free variable type in ADD. + */ + @Test + public void testParsePojoListTypeFreeVarInSetlist() throws DescriptorParsingException { + // use ADD since WHERE should not support list types. + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?p["; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Pojo.class); + unfinished.setArrayType(true); + unfinished.setParameterIndex(0); + doListTypeTest(descString, unfinished); + } + + private void doListTypeTest(String descString, UnfinishedValueNode unfinished) { + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertTrue(statement.getRawStatement() instanceof Add); + SuffixExpression suffix = statement.getSuffixExpression(); + assertNotNull(suffix); + assertNull(suffix.getLimitExpn()); + assertNull(suffix.getSortExpn()); + assertNull(suffix.getWhereExpn()); + SetList list = statement.getSetList(); + assertNotNull(list); + List<SetListValue> mems = list.getValues(); + assertEquals(1, mems.size()); + SetListValue val = mems.get(0); + TerminalNode aKey = new TerminalNode(null); + aKey.setValue(new Key<>("a")); + assertEquals(aKey, val.getKey()); + TerminalNode aVal = new TerminalNode(null); + aVal.setValue(unfinished); + assertEquals(aVal, val.getValue()); + } + + @Test + public void testParseIntTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?i"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Integer.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseLongTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?l"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Long.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + @Test + public void testParseBooleanTypeFreeVarInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?b"; + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setLHS(false); + unfinished.setType(Boolean.class); + unfinished.setParameterIndex(0); + doTestType(descrString, unfinished, 1); + } + + private void doTestType(String strDesc, Object bVal, int expNumFreeVars) throws DescriptorParsingException { + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(expNumFreeVars, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression tree = statement.getSuffixExpression(); + assertNull(tree.getLimitExpn()); + assertNull(tree.getSortExpn()); + assertNotNull(tree.getWhereExpn()); + + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode notEquals = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(notEquals); + notEquals.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); + TerminalNode a = new TerminalNode(notEquals); + a.setValue(new Key<String>("a")); + TerminalNode b = new TerminalNode(notEquals); + b.setValue(bVal); + notEquals.setLeftChild(a); + notEquals.setRightChild(b); + + assertTrue(WhereExpressions.equals(expected, tree.getWhereExpn())); + } + + @Test + public void testParseNotEqualComparisonInWhere() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression tree = statement.getSuffixExpression(); + assertNull(tree.getLimitExpn()); + assertNull(tree.getSortExpn()); + assertNotNull(tree.getWhereExpn()); + + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode notEquals = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(notEquals); + notEquals.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); + TerminalNode a = new TerminalNode(notEquals); + a.setValue(new Key<String>("a")); + TerminalNode b = new TerminalNode(notEquals); + b.setValue("b"); + notEquals.setLeftChild(a); + notEquals.setRightChild(b); + + assertTrue(WhereExpressions.equals(expected, tree.getWhereExpn())); + } + + @Test + public void testParseQuerySimpleWithLimit() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(1, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNotNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNull(expn.getWhereExpn()); + LimitExpression limitExp = expn.getLimitExpn(); + assertTrue(limitExp.getValue() instanceof UnfinishedLimitValue); + UnfinishedLimitValue value = (UnfinishedLimitValue)limitExp.getValue(); + assertEquals(0, value.getParameterIndex()); + } + + @Test + public void testParseSortMultiple() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT 'a' ASC , 'b' DSC , 'c' ASC"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + SortExpression sortExpn = suffixExpn.getSortExpn(); + assertNotNull(sortExpn); + assertNull(suffixExpn.getLimitExpn()); + assertNull(suffixExpn.getWhereExpn()); + List<SortMember> list = sortExpn.getMembers(); + assertNotNull(list); + assertEquals(3, list.size()); + assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); + assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); + assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); + assertEquals("a", list.get(0).getSortKey()); + assertEquals("b", list.get(1).getSortKey()); + assertEquals("c", list.get(2).getSortKey()); + } + + @Test + public void testParseQueryWithMultipleConcunctions() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(1, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode and1 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(and1); + BinaryExpressionNode and2 = new BinaryExpressionNode(and1); + and1.setLeftChild(and2); + and1.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode equality1 = new BinaryExpressionNode(and2); + and2.setOperator(BinaryLogicalOperator.AND); + and2.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(and2); + and2.setRightChild(equality2); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(and1); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + and1.setRightChild(lessThan); + TerminalNode e = new TerminalNode(lessThan); + Key<Integer> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + UnfinishedValueNode f = new UnfinishedValueNode(); + f.setParameterIndex(0); + f.setType(Integer.class); + f.setLHS(false); + TerminalNode fReal = new TerminalNode(lessThan); + fReal.setValue(f); + lessThan.setRightChild(fReal); + + assertTrue( WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWithMultipleConcunctions2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < 'f' AND 'g' >= 'h'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode and1 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(and1); + BinaryExpressionNode and2 = new BinaryExpressionNode(and1); + and1.setLeftChild(and2); + BinaryExpressionNode and3 = new BinaryExpressionNode(and2); + and3.setOperator(BinaryLogicalOperator.AND); + and2.setLeftChild(and3); + and1.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode equality1 = new BinaryExpressionNode(and3); + and2.setOperator(BinaryLogicalOperator.AND); + and3.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(and3); + and3.setRightChild(equality2); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(and2); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + and2.setRightChild(lessThan); + TerminalNode e = new TerminalNode(lessThan); + Key<String> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + TerminalNode f = new TerminalNode(lessThan); + f.setValue("f"); + lessThan.setRightChild(f); + BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(and1); + greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + TerminalNode g = new TerminalNode(greaterOrEqual); + Key<String> gKey = new Key<>("g"); + g.setValue(gKey); + greaterOrEqual.setLeftChild(g); + TerminalNode h = new TerminalNode(greaterOrEqual); + h.setValue("h"); + greaterOrEqual.setRightChild(h); + and1.setRightChild(greaterOrEqual); + + assertTrue( WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWithMultipleDisjunctions() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(1, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode or1 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(or1); + BinaryExpressionNode or2 = new BinaryExpressionNode(or1); + or1.setLeftChild(or2); + or1.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode equality1 = new BinaryExpressionNode(or2); + or2.setOperator(BinaryLogicalOperator.OR); + or2.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(or2); + or2.setRightChild(equality2); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(or1); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + or1.setRightChild(lessThan); + TerminalNode e = new TerminalNode(lessThan); + Key<Integer> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + UnfinishedValueNode f = new UnfinishedValueNode(); + f.setParameterIndex(0); + f.setType(Integer.class); + f.setLHS(false); + TerminalNode fReal = new TerminalNode(lessThan); + fReal.setValue(f); + lessThan.setRightChild(fReal); + + assertTrue( WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWithMultipleDisjunctions2() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode or1 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(or1); + BinaryExpressionNode or2 = new BinaryExpressionNode(or1); + or1.setLeftChild(or2); + BinaryExpressionNode or3 = new BinaryExpressionNode(or2); + or3.setOperator(BinaryLogicalOperator.OR); + or2.setLeftChild(or3); + or1.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode equality1 = new BinaryExpressionNode(or3); + or2.setOperator(BinaryLogicalOperator.OR); + or3.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(or3); + or3.setRightChild(equality2); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + or2.setRightChild(lessThan); + TerminalNode e = new TerminalNode(lessThan); + Key<String> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + TerminalNode f = new TerminalNode(lessThan); + f.setValue("f"); + lessThan.setRightChild(f); + BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or1); + greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + TerminalNode g = new TerminalNode(greaterOrEqual); + Key<String> gKey = new Key<>("g"); + g.setValue(gKey); + greaterOrEqual.setLeftChild(g); + TerminalNode h = new TerminalNode(greaterOrEqual); + h.setValue("h"); + greaterOrEqual.setRightChild(h); + or1.setRightChild(greaterOrEqual); + + assertTrue( WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWithMultipleConDisjunctions() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode and3 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(and3); + and3.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode and2 = new BinaryExpressionNode(and3); + and2.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode and1 = new BinaryExpressionNode(and2); + and1.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode equality3 = new BinaryExpressionNode(and1); + equality3.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode x = new TerminalNode(equality3); + x.setValue(new Key<>("x")); + TerminalNode y = new TerminalNode(equality3); + y.setValue("y"); + equality3.setLeftChild(x); + equality3.setRightChild(y); + and1.setRightChild(equality3); + and3.setLeftChild(and2); + and2.setLeftChild(and1); + BinaryExpressionNode equality4 = new BinaryExpressionNode(and2); + equality4.setOperator(BinaryComparisonOperator.EQUALS); + and2.setRightChild(equality4); + TerminalNode u = new TerminalNode(equality4); + u.setValue(new Key<>("u")); + equality4.setLeftChild(u); + TerminalNode w = new TerminalNode(equality4); + w.setValue("w"); + equality4.setRightChild(w); + BinaryExpressionNode equality5 = new BinaryExpressionNode(and3); + equality5.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode s = new TerminalNode(equality5); + s.setValue(new Key<>("s")); + TerminalNode t = new TerminalNode(equality5); + t.setValue("t"); + equality5.setLeftChild(s); + equality5.setRightChild(t); + and3.setRightChild(equality5); + BinaryExpressionNode or3 = new BinaryExpressionNode(and1); + BinaryExpressionNode or2 = new BinaryExpressionNode(or3); + BinaryExpressionNode or1 = new BinaryExpressionNode(or2); + or3.setOperator(BinaryLogicalOperator.OR); + or2.setOperator(BinaryLogicalOperator.OR); + or1.setOperator(BinaryLogicalOperator.OR); + or3.setLeftChild(or2); + or2.setLeftChild(or1); + BinaryExpressionNode equality1 = new BinaryExpressionNode(or1); + or1.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(or1); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + or1.setRightChild(equality2); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + or2.setRightChild(lessThan); + or2.setLeftChild(or1); + TerminalNode e = new TerminalNode(lessThan); + Key<String> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + TerminalNode f = new TerminalNode(lessThan); + f.setValue("f"); + lessThan.setRightChild(f); + BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or3); + greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + TerminalNode g = new TerminalNode(greaterOrEqual); + Key<String> gKey = new Key<>("g"); + g.setValue(gKey); + greaterOrEqual.setLeftChild(g); + TerminalNode h = new TerminalNode(greaterOrEqual); + h.setValue("h"); + greaterOrEqual.setRightChild(h); + or3.setRightChild(greaterOrEqual); + or3.setLeftChild(or2); + and1.setLeftChild(or3); + + assertTrue(WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWithMultipleConDisjunctionsNegations() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR NOT 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND NOT 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression expn = statement.getSuffixExpression(); + assertNull(expn.getLimitExpn()); + assertNull(expn.getSortExpn()); + assertNotNull(expn.getWhereExpn()); + + WhereExpression where = expn.getWhereExpn(); + // build the expected expression tree + WhereExpression expected = new WhereExpression(); + BinaryExpressionNode and3 = new BinaryExpressionNode(expected.getRoot()); + expected.getRoot().setValue(and3); + and3.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode and2 = new BinaryExpressionNode(and3); + and2.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode and1 = new BinaryExpressionNode(and2); + and1.setOperator(BinaryLogicalOperator.AND); + NotBooleanExpressionNode not2 = new NotBooleanExpressionNode(and1); + BinaryExpressionNode equality3 = new BinaryExpressionNode(not2); + not2.setValue(equality3); + equality3.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode x = new TerminalNode(equality3); + x.setValue(new Key<String>("x")); + TerminalNode y = new TerminalNode(equality3); + y.setValue("y"); + equality3.setLeftChild(x); + equality3.setRightChild(y); + and1.setRightChild(not2); + and3.setLeftChild(and2); + and2.setLeftChild(and1); + BinaryExpressionNode equality4 = new BinaryExpressionNode(and2); + equality4.setOperator(BinaryComparisonOperator.EQUALS); + and2.setRightChild(equality4); + TerminalNode u = new TerminalNode(equality4); + u.setValue(new Key<String>("u")); + equality4.setLeftChild(u); + TerminalNode w = new TerminalNode(equality4); + w.setValue("w"); + equality4.setRightChild(w); + BinaryExpressionNode equality5 = new BinaryExpressionNode(and3); + equality5.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode s = new TerminalNode(equality5); + s.setValue(new Key<String>("s")); + TerminalNode t = new TerminalNode(equality5); + t.setValue("t"); + equality5.setLeftChild(s); + equality5.setRightChild(t); + and3.setRightChild(equality5); + BinaryExpressionNode or3 = new BinaryExpressionNode(and1); + BinaryExpressionNode or2 = new BinaryExpressionNode(or3); + BinaryExpressionNode or1 = new BinaryExpressionNode(or2); + or3.setOperator(BinaryLogicalOperator.OR); + or2.setOperator(BinaryLogicalOperator.OR); + or1.setOperator(BinaryLogicalOperator.OR); + or3.setLeftChild(or2); + or2.setLeftChild(or1); + BinaryExpressionNode equality1 = new BinaryExpressionNode(or1); + or1.setLeftChild(equality1); + equality1.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality1); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality1.setLeftChild(a); + TerminalNode b = new TerminalNode(equality1); + b.setValue("b"); + equality1.setRightChild(b); + BinaryExpressionNode equality2 = new BinaryExpressionNode(or1); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + NotBooleanExpressionNode not1 = new NotBooleanExpressionNode(or1); + not1.setValue(equality2); + or1.setRightChild(not1); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + or2.setRightChild(lessThan); + or2.setLeftChild(or1); + TerminalNode e = new TerminalNode(lessThan); + Key<String> eKey = new Key<>("e"); + e.setValue(eKey); + lessThan.setLeftChild(e); + TerminalNode f = new TerminalNode(lessThan); + f.setValue("f"); + lessThan.setRightChild(f); + BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or3); + greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + TerminalNode g = new TerminalNode(greaterOrEqual); + Key<String> gKey = new Key<>("g"); + g.setValue(gKey); + greaterOrEqual.setLeftChild(g); + TerminalNode h = new TerminalNode(greaterOrEqual); + h.setValue("h"); + greaterOrEqual.setRightChild(h); + or3.setRightChild(greaterOrEqual); + or3.setLeftChild(or2); + and1.setLeftChild(or3); + + assertTrue(WhereExpressions.equals(expected, where)); + } + + @Test + public void testParseQueryWhereAndSortMultiple() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' AND 'c' = ?s OR NOT 'x' >= ?i SORT 'a' ASC , 'b' DSC , 'c' ASC"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); + assertEquals(2, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNotNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + List<SortMember> list = suffixExpn.getSortExpn().getMembers(); + assertNotNull(list); + assertEquals(3, list.size()); + assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); + assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); + assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); + assertEquals("a", list.get(0).getSortKey()); + assertEquals("b", list.get(1).getSortKey()); + assertEquals("c", list.get(2).getSortKey()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(or); + or.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode and = new BinaryExpressionNode(or); + and.setOperator(BinaryLogicalOperator.AND); + or.setLeftChild(and); + NotBooleanExpressionNode not = new NotBooleanExpressionNode(or); + or.setRightChild(not); + BinaryExpressionNode unequality = new BinaryExpressionNode(and); + unequality.setOperator(BinaryComparisonOperator.LESS_THAN); + TerminalNode a = new TerminalNode(unequality); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + unequality.setLeftChild(a); + TerminalNode b = new TerminalNode(unequality); + b.setValue("b"); + unequality.setRightChild(b); + and.setLeftChild(unequality); + BinaryExpressionNode equality = new BinaryExpressionNode(and); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality.setLeftChild(c); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(0); + patch1.setLHS(false); + patch1.setType(String.class); + TerminalNode d = new TerminalNode(equality); + d.setValue(patch1); + equality.setRightChild(d); + and.setRightChild(equality); + BinaryExpressionNode greaterEqual = new BinaryExpressionNode(not); + not.setValue(greaterEqual); + greaterEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); + TerminalNode x = new TerminalNode(greaterEqual); + Key<Integer> xKey = new Key<>("x"); + x.setValue(xKey); + greaterEqual.setLeftChild(x); + UnfinishedValueNode patch2 = new UnfinishedValueNode(); + patch2.setParameterIndex(1); + patch2.setLHS(false); + patch2.setType(Integer.class); + TerminalNode y = new TerminalNode(greaterEqual); + y.setValue(patch2); + greaterEqual.setRightChild(y); + // finally assert equality + assertTrue( WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseQueryWhereOrSortMultiple() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' OR 'c' = ?s SORT 'a' ASC , ?s DSC , 'c' ASC"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); + assertEquals(2, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNotNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + SortExpression sortExp = suffixExpn.getSortExpn(); + List<SortMember> list = sortExp.getMembers(); + assertNotNull(list); + assertEquals(3, list.size()); + assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); + assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); + assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); + assertEquals("a", list.get(0).getSortKey()); + UnfinishedSortKey unfinished = new UnfinishedSortKey(); + unfinished.setParameterIndex(1); + assertEquals(unfinished, list.get(1).getSortKey()); + assertEquals("c", list.get(2).getSortKey()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(or); + or.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode unequality = new BinaryExpressionNode(or); + unequality.setOperator(BinaryComparisonOperator.LESS_THAN); + TerminalNode a = new TerminalNode(unequality); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + unequality.setLeftChild(a); + TerminalNode b = new TerminalNode(unequality); + b.setValue("b"); + unequality.setRightChild(b); + or.setLeftChild(unequality); + BinaryExpressionNode equality = new BinaryExpressionNode(or); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality); + Key<String> cKey = new Key<>("c"); + c.setValue(cKey); + equality.setLeftChild(c); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(0); + patch1.setLHS(false); + patch1.setType(String.class); + TerminalNode d = new TerminalNode(equality); + d.setValue(patch1); + equality.setRightChild(d); + or.setRightChild(equality); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseQuerySimpleWhereAndSimpleSort() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' SORT 'a' DSC"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNotNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + SortExpression sortExp = suffixExpn.getSortExpn(); + List<SortMember> list = sortExp.getMembers(); + assertNotNull(list); + assertEquals(1, list.size()); + assertEquals(SortDirection.DESCENDING, list.get(0).getDirection()); + assertEquals("a", list.get(0).getSortKey()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode unequality = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(unequality); + unequality.setOperator(BinaryComparisonOperator.LESS_THAN); + TerminalNode a = new TerminalNode(unequality); + @SuppressWarnings("rawtypes") + Key aKey = new Key("a"); + a.setValue(aKey); + unequality.setLeftChild(a); + TerminalNode b = new TerminalNode(unequality); + b.setValue("b"); + unequality.setRightChild(b); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseQuerySimpleWithOneWhere() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + fail(e.getMessage()); + } + assertEquals(1, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode equality = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(equality); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality); + @SuppressWarnings("rawtypes") + Key aKey = new Key(Key.AGENT_ID.getName()); + a.setValue(aKey); + equality.setLeftChild(a); + TerminalNode b = new TerminalNode(equality); + UnfinishedValueNode node = new UnfinishedValueNode(); + node.setParameterIndex(0); + node.setLHS(false); + node.setType(String.class); + b.setValue(node); + equality.setRightChild(b); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseSimpleWithAndOr() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s" + + " AND ?s < ?b OR 'a' = 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertEquals(3, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(or); + or.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode and = new BinaryExpressionNode(or); + and.setOperator(BinaryLogicalOperator.AND); + or.setLeftChild(and); + BinaryExpressionNode equality = new BinaryExpressionNode(and); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality); + Key<String> aKey = new Key<>("a"); + a.setValue(aKey); + equality.setLeftChild(a); + TerminalNode b = new TerminalNode(equality); + b.setValue("b"); + equality.setRightChild(b); + or.setRightChild(equality); + BinaryExpressionNode equality2 = new BinaryExpressionNode(and); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + Key<String> cKey = new Key<>(Key.AGENT_ID.getName()); + c.setValue(cKey); + equality2.setLeftChild(c); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(0); + patch1.setLHS(false); + patch1.setType(String.class); + TerminalNode d = new TerminalNode(equality2); + d.setValue(patch1); + equality2.setRightChild(d); + and.setLeftChild(equality2); + BinaryExpressionNode lessThan = new BinaryExpressionNode(and); + lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); + UnfinishedValueNode patch = new UnfinishedValueNode(); + patch.setParameterIndex(1); + patch.setType(String.class); + patch.setLHS(true); + TerminalNode x = new TerminalNode(lessThan); + x.setValue(patch); + lessThan.setLeftChild(x); + UnfinishedValueNode patch2 = new UnfinishedValueNode(); + patch2.setLHS(false); + patch2.setParameterIndex(2); + patch2.setType(Boolean.class); + TerminalNode y = new TerminalNode(lessThan); + y.setValue(patch2); + lessThan.setRightChild(y); + and.setRightChild(lessThan); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseSimpleWithAnd() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s AND ?s = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertEquals(2, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + // build the expected expression tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode equality = new BinaryExpressionNode(and); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality); + @SuppressWarnings("rawtypes") + Key aKey = new Key("a"); + a.setValue(aKey); + equality.setLeftChild(a); + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setParameterIndex(0); + unfinished.setLHS(false); + unfinished.setType(String.class); + TerminalNode b = new TerminalNode(equality); + b.setValue(unfinished); + equality.setRightChild(b); + and.setLeftChild(equality); + BinaryExpressionNode equality2 = new BinaryExpressionNode(and); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(1); + patch1.setType(String.class); + patch1.setLHS(true); + c.setValue(patch1); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + and.setRightChild(equality2); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseSimpleWithNotOR() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE NOT 'a' = ?s OR ?s = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertEquals(2, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + // build the expected parse tree + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode or = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(or); + or.setOperator(BinaryLogicalOperator.OR); + NotBooleanExpressionNode notNode = new NotBooleanExpressionNode(or); + BinaryExpressionNode comparison = new BinaryExpressionNode(notNode); + notNode.setValue(comparison); + or.setLeftChild(notNode); + comparison.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode rightCompTerm = new TerminalNode(comparison); + TerminalNode leftCompTerm = new TerminalNode(comparison); + @SuppressWarnings("rawtypes") + Key aKey = new Key("a"); + leftCompTerm.setValue(aKey); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(0); + patch1.setType(String.class); + patch1.setLHS(false); + rightCompTerm.setValue(patch1); + comparison.setLeftChild(leftCompTerm); + comparison.setRightChild(rightCompTerm); + BinaryExpressionNode otherComparison = new BinaryExpressionNode(or); + otherComparison.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode leftUnfinished = new TerminalNode(otherComparison); + UnfinishedValueNode patch2 = new UnfinishedValueNode(); + patch2.setParameterIndex(1); + patch2.setLHS(true); + patch2.setType(String.class); + leftUnfinished.setValue(patch2); + TerminalNode other = new TerminalNode(otherComparison); + other.setValue("d"); + otherComparison.setLeftChild(leftUnfinished); + otherComparison.setRightChild(other); + or.setRightChild(otherComparison); + assertTrue(WhereExpressions.equals(expn, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseSimpleWithOr() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s OR ?s = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertEquals(2, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getLimitExpn()); + assertNotNull(suffixExpn.getWhereExpn()); + // build the expected parse tree + WhereExpression where = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(where.getRoot()); + where.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.OR); + BinaryExpressionNode equality = new BinaryExpressionNode(and); + equality.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(equality); + @SuppressWarnings("rawtypes") + Key aKey = new Key("a"); + a.setValue(aKey); + equality.setLeftChild(a); + UnfinishedValueNode unfinished = new UnfinishedValueNode(); + unfinished.setParameterIndex(0); + unfinished.setType(String.class); + unfinished.setLHS(false); + TerminalNode b = new TerminalNode(equality); + b.setValue(unfinished); + equality.setRightChild(b); + and.setLeftChild(equality); + BinaryExpressionNode equality2 = new BinaryExpressionNode(and); + equality2.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode c = new TerminalNode(equality2); + UnfinishedValueNode patch1 = new UnfinishedValueNode(); + patch1.setParameterIndex(1); + patch1.setType(String.class); + patch1.setLHS(true); + c.setValue(patch1); + equality2.setLeftChild(c); + TerminalNode d = new TerminalNode(equality2); + d.setValue("d"); + equality2.setRightChild(d); + and.setRightChild(equality2); + assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); + } + + @Test + public void testParseSimpleWithLimit() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT 1"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertEquals(0, statement.getNumParams()); + assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); + SuffixExpression suffixExpn = statement.getSuffixExpression(); + assertNull(suffixExpn.getSortExpn()); + assertNull(suffixExpn.getWhereExpn()); + assertNotNull(suffixExpn.getLimitExpn()); + assertEquals(1, suffixExpn.getLimitExpn().getValue()); + } + + @Test + public void testParseAddBasic() throws DescriptorParsingException { + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = ?s"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertTrue(statement.getRawStatement() instanceof Add); + SuffixExpression suffix = statement.getSuffixExpression(); + assertNotNull(suffix); + WhereExpression where = suffix.getWhereExpn(); + LimitExpression limit = suffix.getLimitExpn(); + SortExpression sort = suffix.getSortExpn(); + assertNull(where); + assertNull(limit); + assertNull(sort); + SetList setList = statement.getSetList(); + assertNotNull(setList); + List<SetListValue> valuesList = setList.getValues(); + assertEquals(2, valuesList.size()); + SetListValue first = valuesList.get(0); + SetListValue second = valuesList.get(1); + TerminalNode a = new TerminalNode(null); + a.setValue(new Key<>("a")); + TerminalNode b = new TerminalNode(null); + b.setValue("b"); + SetListValue firstExpected = new SetListValue(); + firstExpected.setKey(a); + firstExpected.setValue(b); + assertEquals(firstExpected, first); + TerminalNode c = new TerminalNode(null); + c.setValue(new Key<>("c")); + UnfinishedValueNode dVal = new UnfinishedValueNode(); + dVal.setType(String.class); + dVal.setLHS(false); + dVal.setParameterIndex(0); + TerminalNode d = new TerminalNode(null); + d.setValue(dVal); + SetListValue secondExpected = new SetListValue(); + secondExpected.setKey(c); + secondExpected.setValue(d); + assertEquals(secondExpected, second); + } + + @Test + public void testParseUpdateBasic() throws DescriptorParsingException { + String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = ?s WHERE 'foo' != ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + ParsedStatementImpl<AgentInformation> statement = null; + try { + statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); + } catch (DescriptorParsingException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + assertTrue(statement.getRawStatement() instanceof Update); + SuffixExpression suffix = statement.getSuffixExpression(); + assertNotNull(suffix); + WhereExpression where = suffix.getWhereExpn(); + LimitExpression limit = suffix.getLimitExpn(); + SortExpression sort = suffix.getSortExpn(); + assertNotNull(where); + assertNull(limit); + assertNull(sort); + SetList setList = statement.getSetList(); + assertNotNull(setList); + List<SetListValue> valuesList = setList.getValues(); + assertEquals(2, valuesList.size()); + SetListValue first = valuesList.get(0); + SetListValue second = valuesList.get(1); + TerminalNode a = new TerminalNode(null); + a.setValue(new Key<>("a")); + TerminalNode b = new TerminalNode(null); + b.setValue("b"); + SetListValue firstExpected = new SetListValue(); + firstExpected.setKey(a); + firstExpected.setValue(b); + assertEquals(firstExpected, first); + TerminalNode c = new TerminalNode(null); + c.setValue(new Key<>("c")); + UnfinishedValueNode dVal = new UnfinishedValueNode(); + dVal.setType(String.class); + dVal.setLHS(false); + dVal.setParameterIndex(0); + TerminalNode d = new TerminalNode(null); + d.setValue(dVal); + SetListValue secondExpected = new SetListValue(); + secondExpected.setKey(c); + secondExpected.setValue(d); + assertEquals(secondExpected, second); + // Build expected where expn + WhereExpression expectedWhere = new WhereExpression(); + BinaryExpressionNode equality = new BinaryExpressionNode(expectedWhere.getRoot()); + expectedWhere.getRoot().setValue(equality); + equality.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); + TerminalNode foo = new TerminalNode(equality); + @SuppressWarnings("rawtypes") + Key fooKey = new Key("foo"); + foo.setValue(fooKey); + equality.setLeftChild(foo); + TerminalNode fooVal = new TerminalNode(equality); + UnfinishedValueNode node = new UnfinishedValueNode(); + node.setParameterIndex(1); + node.setLHS(false); + node.setType(Integer.class); + fooVal.setValue(node); + equality.setRightChild(fooVal); + assertTrue(WhereExpressions.equals(expectedWhere, where)); + } + + // TODO: Add basic parse tests for REMOVE/REPLACE + + @Test + public void rejectLongValAsIntType() throws DescriptorParsingException { + // 30000000003 > Integer.MAX_VALUE; needs to be preceded by 'l/L' + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Illegal terminal type.")); + } + } + + @Test + public void rejectLimitWhichIsNotInt() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT illegal"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + assertEquals("Invalid limit expression. 'illegal' not an integer", e.getMessage()); + } + } + + @Test + public void rejectLHSnotString() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE a < 1"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Expected string value. Got term ->a<-")); + } + } + + /* + * At this point list types don't make sense in WHERE. + * What should "'a' != [ 'a', 'b' ]" evaluate to? + * How about this? "'key' > [ 1, 2 ]" + * + * The only reasonable use case in WHERE would be 'a' IN [ 'a', 'b' ... ]. + * We don't support this in a prepared context at this point. Should this + * change in future, this test needs to be carefully re-crafted. + */ + @Test + public void rejectListTypesAsFreeVarInInvalidContext() throws DescriptorParsingException { + List<String> illegalDescs = new ArrayList<>(); + + // Where should not support list types/Pojos. + String[] illegalWheres = new String[] { + " WHERE 'a' = ?s[", + " WHERE 'a' = ?i[", + " WHERE 'a' = ?p[", + " WHERE 'a' = ?d[", + }; + + // Make sure we test for QUERY, QUERY-COUNT, REPLACE, UPDATE, REMOVE + // i.e. all that accept a WHERE. + String basicQuery = "QUERY " + AgentInfoDAO.CATEGORY.getName(); + String basicQueryCount = "QUERY-COUNT " + AgentInfoDAO.CATEGORY.getName(); + // note SET clause is legal + String basicUpdate = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?s["; + // note SET clause is legal + String basicReplace = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?i["; + String basicRemove = "REMOVE " + AgentInfoDAO.CATEGORY.getName(); + for (String where: illegalWheres) { + illegalDescs.add(basicQuery + where); + illegalDescs.add(basicQueryCount + where); + illegalDescs.add(basicUpdate + where); + illegalDescs.add(basicReplace + where); + illegalDescs.add(basicRemove + where); + } + + // Test all illegal descs involving WHERE + doIllegalDescsTest(illegalDescs, "WHERE", "List"); + + // Test limit too + illegalDescs.clear(); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i["); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?s["); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?p["); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?d["); + + doIllegalDescsTest(illegalDescs, "LIMIT", "List"); + + // Test sort + illegalDescs.clear(); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?i[ ASC"); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?s[ ASC"); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?p[ ASC"); + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?d[ ASC"); + + doIllegalDescsTest(illegalDescs, "SORT", "List"); + } + + /* + * Pojos don't make sense in a WHERE, LIMIT and SORT clause. This test makes + * sure we reject this right away. + * + */ + @Test + public void rejectPojoTypesAsFreeVarInInvalidContext() throws DescriptorParsingException { + List<String> illegalDescs = new ArrayList<>(); + + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?p"); + doIllegalDescsTest(illegalDescs, "WHERE", "Pojo"); + illegalDescs.clear(); + + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?p"); + doIllegalDescsTest(illegalDescs, "LIMIT", "Pojo"); + illegalDescs.clear(); + + illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?p DSC"); + doIllegalDescsTest(illegalDescs, "SORT", "Pojo"); + } + + private void doIllegalDescsTest(List<String> descs, String errorMsgContext, String type) { + for (String strDesc : descs) { + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail(strDesc + " should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertEquals(strDesc + " did not provide correct error message.", + type + " free variable type not allowed in " + errorMsgContext + " context", e.getMessage()); + } + } + } + + @Test + public void rejectIllegalFreeParamType() throws DescriptorParsingException { + // ? should be one of '?i', '?l', '?s', '?b', '?s[' + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE ? < 1"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertEquals("Unknown type of free parameter: '?'", e.getMessage()); + } + } + + @Test + public void rejectParseQueryWhereAndSortMultipleIllegalSortModifier() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'somekey' < 2 AND 'c' = ?s OR 'a' >= ?i SORT 'a' ASC , 'b' ILLEGAL , 'c' ASC"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Expected ASC or DSC")); + } + } + + @Test + public void rejectParseQueryWhereBoolTerm() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE true AND false"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectQueryWithParenthesis() throws DescriptorParsingException { + // We don't allow parenthesized expressions. This is due to mongodb not + // allowing this. + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE NOT ( 'a' = 'b' AND 'c' = 'd' )"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectSimpleQueryWithMissingSpaces() throws DescriptorParsingException { + // we require a space before every operator/keyword + String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a'='b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectSimpleQueryWithMissingSpaces2() throws DescriptorParsingException { + // we require a space before every operator/keyword + String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a' ='b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectSimpleQueryWithInvalidComparison() throws DescriptorParsingException { + // <> is illegal + String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a' <> 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("should not parse"); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectInvalidDescriptorStringBadWhere() throws DescriptorParsingException { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " where '" + Key.AGENT_ID.getName() + "'= ?s"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("lower case where not allowed in descriptor. Should have rejected."); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Unexpected token: 'where'")); + } + } + + @Test + public void rejectInvalidDescriptorStringBadStatementType() throws DescriptorParsingException { + String descString = "UNKNOWN some-unknown-category WHERE 1 = ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("UNKNOWN not a valid statement type"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Unknown statement type")); + } + } + + @Test + public void rejectInvalidDescriptorStringCategoryMismatch() throws DescriptorParsingException { + String descString = "QUERY some-unknown-category WHERE 1 = ?i"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("category names in descriptor and Category object did not match!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Category mismatch")); + } + } + + @Test + public void rejectInvalidDescriptorStringBadSortNoArg() throws DescriptorParsingException { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?i SORT"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("category names in descriptor and Category object did not match!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Invalid where clause")); + } + } + + @Test + public void rejectInvalidDescriptorStringBadWhereNoArg() throws DescriptorParsingException { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE SORT"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("category names in descriptor and Category object did not match!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("SORT")); + assertTrue(e.getMessage().contains("Expected string value")); + } + } + + @Test + public void rejectAddWithInvalidSetList() throws DescriptorParsingException { + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = , 'c' = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("Invalid SET values list."); + } catch (DescriptorParsingException e) { + // pass + assertEquals("Illegal terminal type. Token was ->,<-", e.getMessage()); + } + } + + @Test + public void rejectAddWithWhere() throws DescriptorParsingException { + String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd' WHERE 'a' = 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("ADD operation does not support WHERE clauses!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("WHERE clause not allowed for ADD")); + } + } + + @Test + public void rejectReplaceWithoutWhere() throws DescriptorParsingException { + String descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("REPLACE operation requires WHERE clause, and should not parse!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("WHERE clause required for REPLACE")); + } + } + + @Test + public void rejectUpdateWithoutWhere() throws DescriptorParsingException { + String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("UPDATE operation requires WHERE clause, and should not parse!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("WHERE clause required for UPDATE")); + } + } + + @Test + public void rejectRemoveWithSetList() throws DescriptorParsingException { + String descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("REMOVE does not allow SET list, and should not parse!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("SET not allowed for REMOVE")); + } + } + + @Test + public void rejectQueryWithSetList() throws DescriptorParsingException { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("QUERY does not allow SET list, and should not parse!"); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("SET not allowed for QUERY")); + } + } + + + private void doRejectWriteSortLimitTest(String descString, String failmsg) { + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail(failmsg); + } catch (DescriptorParsingException e) { + // pass + assertEquals("LIMIT/SORT only allowed for QUERY/QUERY-COUNT", e.getMessage()); + } + } + + @Test + public void rejectWritesWithSortLimit() throws DescriptorParsingException { + // Update rejects + String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' SORT 'a' DSC"; + String failmsg = "SORT in UPDATE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' LIMIT 1"; + failmsg = "LIMIT in UPDATE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + + // Remove rejects + descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' LIMIT 1"; + failmsg = "LIMIT in REMOVE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' SORT 'a' ASC"; + failmsg = "SORT in REMOVE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + + // Replace rejects + descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' LIMIT 1"; + failmsg = "LIMIT in REPLACE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' SORT 'a' ASC"; + failmsg = "SORT in REPLACE is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + + // Add rejects + descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' LIMIT 1"; + failmsg = "LIMIT in ADD is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' SORT 'a' ASC"; + failmsg = "SORT in ADD is not allowed, and should not parse!"; + doRejectWriteSortLimitTest(descString, failmsg); + } + + @Test + public void rejectUpdateWithoutSet() throws DescriptorParsingException { + String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b'"; + StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); + parser = new BasicDescriptorParser<>(storage, desc); + try { + parser.parse(); + fail("UPDATE requires SET list, and should not parse!"); + } catch (DescriptorParsingException e) { + // pass + assertEquals("SET list required for UPDATE", e.getMessage()); + } + } + + // TODO: add tests where set list does not match all props of Pojo class for + // add/replace + +}
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Mon Oct 07 15:03:04 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2010 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.storage.internal.statement; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import com.redhat.thermostat.storage.core.Add; -import com.redhat.thermostat.storage.core.AggregateQuery; -import com.redhat.thermostat.storage.core.AggregateQuery.AggregateFunction; -import com.redhat.thermostat.storage.core.BackingStorage; -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.CategoryAdapter; -import com.redhat.thermostat.storage.core.DescriptorParsingException; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Query.SortDirection; -import com.redhat.thermostat.storage.core.Remove; -import com.redhat.thermostat.storage.core.Replace; -import com.redhat.thermostat.storage.core.StatementDescriptor; -import com.redhat.thermostat.storage.core.Update; -import com.redhat.thermostat.storage.dao.AgentInfoDAO; -import com.redhat.thermostat.storage.model.AgentInformation; -import com.redhat.thermostat.storage.model.AggregateCount; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.storage.query.BinaryComparisonOperator; -import com.redhat.thermostat.storage.query.BinaryLogicalOperator; - -public class StatementDescriptorParserTest { - - private BackingStorage storage; - private Query<AgentInformation> mockQuery; - private StatementDescriptorParser<AgentInformation> parser; - private Add<AgentInformation> mockAdd; - private Update<AgentInformation> mockUpdate; - private Replace<AgentInformation> mockReplace; - private Remove<AgentInformation> mockRemove; - - @SuppressWarnings("unchecked") - @Before - public void setup() { - storage = mock(BackingStorage.class); - mockQuery = mock(Query.class); - when(storage.createQuery(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockQuery); - // setup for ADD - mockAdd = mock(Add.class); - when(storage.createAdd(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockAdd); - // setup for UPDATE - mockUpdate = mock(Update.class); - when(storage.createUpdate(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockUpdate); - // setup for REMOVE - mockRemove = mock(Remove.class); - when(storage.createRemove(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockRemove); - // setup for REPLACE - mockReplace = mock(Replace.class); - when(storage.createReplace(eq(AgentInfoDAO.CATEGORY))).thenReturn(mockReplace); - } - - @After - public void teardown() { - storage = null; - mockQuery = null; - mockUpdate = null; - mockReplace = null; - mockAdd = null; - mockRemove = null; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testParseAggregateCount() throws DescriptorParsingException { - Query<AggregateCount> query = mock(AggregateQuery.class); - ArgumentCaptor<Category> captor = ArgumentCaptor.forClass(Category.class); - when(storage.createAggregateQuery(eq(AggregateFunction.COUNT), captor.capture())).thenReturn(query); - // first adapt from the target category in order to be able to produce the - // right aggregate query with a different result type. - CategoryAdapter<AgentInformation, AggregateCount> adapter = new CategoryAdapter<>(AgentInfoDAO.CATEGORY); - Category<AggregateCount> aggregateCategory = adapter.getAdapted(AggregateCount.class); - String descrString = "QUERY-COUNT " + aggregateCategory.getName() + " WHERE 'a' = 'b'"; - StatementDescriptor<AggregateCount> desc = new StatementDescriptor<>(aggregateCategory, descrString); - StatementDescriptorParser<AggregateCount> parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AggregateCount> statement = (ParsedStatementImpl<AggregateCount>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertTrue(statement.getRawStatement() instanceof AggregateQuery); - Category<AggregateCount> capturedCategory = captor.getValue(); - assertEquals(aggregateCategory, capturedCategory); - SuffixExpression expn = statement.getSuffixExpression(); - assertNotNull(expn); - WhereExpression where = expn.getWhereExpn(); - assertNotNull(where); - assertNull(expn.getSortExpn()); - assertNull(expn.getLimitExpn()); - } - - @Test - public void testParseQuerySimple() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName(); - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression tree = statement.getSuffixExpression(); - assertNull(tree.getLimitExpn()); - assertNull(tree.getSortExpn()); - assertNull(tree.getWhereExpn()); - } - - @Test - public void testParseLongInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003L"; - doTestType(descrString, 30000000003L, 0); - } - - @Test - public void testParseLongInWhere2() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003l"; - doTestType(descrString, 30000000003L, 0); - } - - @Test - public void testParseLongInWhere3() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 3l"; - doTestType(descrString, 3L, 0); - } - - @Test - public void testParseLongInWhere4() throws DescriptorParsingException { - long val = Long.MIN_VALUE; - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val + "l"; - doTestType(descrString, val, 0); - } - - @Test - public void testParseIntInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000"; - doTestType(descrString, 30000, 0); - } - - @Test - public void testParseIntInWhere2() throws DescriptorParsingException { - int val = Integer.MAX_VALUE - 1; - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; - doTestType(descrString, val, 0); - } - - @Test - public void testParseIntInWhere3() throws DescriptorParsingException { - int val = Integer.MIN_VALUE; - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != " + val; - doTestType(descrString, val, 0); - } - - @Test - public void testParseBooleanInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != true"; - doTestType(descrString, true, 0); - } - - @Test - public void testParseBooleanInWhere2() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != false"; - doTestType(descrString, false, 0); - } - - @Test - public void testParseStringInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'testing'"; - doTestType(descrString, "testing", 0); - } - - @Test - public void testParseStringTypeFreeVarInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?s"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(String.class); - unfinished.setParameterIndex(0); - doTestType(descrString, unfinished, 1); - } - - @Test - public void testParseDoubleTypeFreeVarInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?d"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Double.class); - unfinished.setParameterIndex(0); - doTestType(descrString, unfinished, 1); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * String list free variable type in ADD. - */ - @Test - public void testParseStringListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?s["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(String.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * integer list free variable type in ADD. - */ - @Test - public void testParseDoubleListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?d["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Double.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * double list free variable type in ADD. - */ - @Test - public void testParseIntListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?i["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Integer.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * boolean list free variable type in ADD. - */ - @Test - public void testParseBooleanListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?b["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Boolean.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * long list free variable type in ADD. - */ - @Test - public void testParseLongListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?l["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Long.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * long list free variable type in ADD. - */ - @Test - public void testParsePojoTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support pojo type. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?p"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Pojo.class); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - /* - * SET clauses allow for setting list types. Test that it works for a - * Pojo list free variable type in ADD. - */ - @Test - public void testParsePojoListTypeFreeVarInSetlist() throws DescriptorParsingException { - // use ADD since WHERE should not support list types. - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?p["; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Pojo.class); - unfinished.setArrayType(true); - unfinished.setParameterIndex(0); - doListTypeTest(descString, unfinished); - } - - private void doListTypeTest(String descString, UnfinishedValueNode unfinished) { - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertTrue(statement.getRawStatement() instanceof Add); - SuffixExpression suffix = statement.getSuffixExpression(); - assertNotNull(suffix); - assertNull(suffix.getLimitExpn()); - assertNull(suffix.getSortExpn()); - assertNull(suffix.getWhereExpn()); - SetList list = statement.getSetList(); - assertNotNull(list); - List<SetListValue> mems = list.getValues(); - assertEquals(1, mems.size()); - SetListValue val = mems.get(0); - TerminalNode aKey = new TerminalNode(null); - aKey.setValue(new Key<>("a")); - assertEquals(aKey, val.getKey()); - TerminalNode aVal = new TerminalNode(null); - aVal.setValue(unfinished); - assertEquals(aVal, val.getValue()); - } - - @Test - public void testParseIntTypeFreeVarInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?i"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Integer.class); - unfinished.setParameterIndex(0); - doTestType(descrString, unfinished, 1); - } - - @Test - public void testParseLongTypeFreeVarInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?l"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Long.class); - unfinished.setParameterIndex(0); - doTestType(descrString, unfinished, 1); - } - - @Test - public void testParseBooleanTypeFreeVarInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != ?b"; - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setLHS(false); - unfinished.setType(Boolean.class); - unfinished.setParameterIndex(0); - doTestType(descrString, unfinished, 1); - } - - private void doTestType(String strDesc, Object bVal, int expNumFreeVars) throws DescriptorParsingException { - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(expNumFreeVars, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression tree = statement.getSuffixExpression(); - assertNull(tree.getLimitExpn()); - assertNull(tree.getSortExpn()); - assertNotNull(tree.getWhereExpn()); - - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode notEquals = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(notEquals); - notEquals.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); - TerminalNode a = new TerminalNode(notEquals); - a.setValue(new Key<String>("a")); - TerminalNode b = new TerminalNode(notEquals); - b.setValue(bVal); - notEquals.setLeftChild(a); - notEquals.setRightChild(b); - - assertTrue(WhereExpressions.equals(expected, tree.getWhereExpn())); - } - - @Test - public void testParseNotEqualComparisonInWhere() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression tree = statement.getSuffixExpression(); - assertNull(tree.getLimitExpn()); - assertNull(tree.getSortExpn()); - assertNotNull(tree.getWhereExpn()); - - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode notEquals = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(notEquals); - notEquals.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); - TerminalNode a = new TerminalNode(notEquals); - a.setValue(new Key<String>("a")); - TerminalNode b = new TerminalNode(notEquals); - b.setValue("b"); - notEquals.setLeftChild(a); - notEquals.setRightChild(b); - - assertTrue(WhereExpressions.equals(expected, tree.getWhereExpn())); - } - - @Test - public void testParseQuerySimpleWithLimit() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(1, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNotNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNull(expn.getWhereExpn()); - LimitExpression limitExp = expn.getLimitExpn(); - assertTrue(limitExp.getValue() instanceof UnfinishedLimitValue); - UnfinishedLimitValue value = (UnfinishedLimitValue)limitExp.getValue(); - assertEquals(0, value.getParameterIndex()); - } - - @Test - public void testParseSortMultiple() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT 'a' ASC , 'b' DSC , 'c' ASC"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - SortExpression sortExpn = suffixExpn.getSortExpn(); - assertNotNull(sortExpn); - assertNull(suffixExpn.getLimitExpn()); - assertNull(suffixExpn.getWhereExpn()); - List<SortMember> list = sortExpn.getMembers(); - assertNotNull(list); - assertEquals(3, list.size()); - assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); - assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); - assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); - assertEquals("a", list.get(0).getSortKey()); - assertEquals("b", list.get(1).getSortKey()); - assertEquals("c", list.get(2).getSortKey()); - } - - @Test - public void testParseQueryWithMultipleConcunctions() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(1, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode and1 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(and1); - BinaryExpressionNode and2 = new BinaryExpressionNode(and1); - and1.setLeftChild(and2); - and1.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode equality1 = new BinaryExpressionNode(and2); - and2.setOperator(BinaryLogicalOperator.AND); - and2.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(and2); - and2.setRightChild(equality2); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(and1); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - and1.setRightChild(lessThan); - TerminalNode e = new TerminalNode(lessThan); - Key<Integer> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - UnfinishedValueNode f = new UnfinishedValueNode(); - f.setParameterIndex(0); - f.setType(Integer.class); - f.setLHS(false); - TerminalNode fReal = new TerminalNode(lessThan); - fReal.setValue(f); - lessThan.setRightChild(fReal); - - assertTrue( WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWithMultipleConcunctions2() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < 'f' AND 'g' >= 'h'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode and1 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(and1); - BinaryExpressionNode and2 = new BinaryExpressionNode(and1); - and1.setLeftChild(and2); - BinaryExpressionNode and3 = new BinaryExpressionNode(and2); - and3.setOperator(BinaryLogicalOperator.AND); - and2.setLeftChild(and3); - and1.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode equality1 = new BinaryExpressionNode(and3); - and2.setOperator(BinaryLogicalOperator.AND); - and3.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(and3); - and3.setRightChild(equality2); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(and2); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - and2.setRightChild(lessThan); - TerminalNode e = new TerminalNode(lessThan); - Key<String> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - TerminalNode f = new TerminalNode(lessThan); - f.setValue("f"); - lessThan.setRightChild(f); - BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(and1); - greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - TerminalNode g = new TerminalNode(greaterOrEqual); - Key<String> gKey = new Key<>("g"); - g.setValue(gKey); - greaterOrEqual.setLeftChild(g); - TerminalNode h = new TerminalNode(greaterOrEqual); - h.setValue("h"); - greaterOrEqual.setRightChild(h); - and1.setRightChild(greaterOrEqual); - - assertTrue( WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWithMultipleDisjunctions() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(1, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode or1 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(or1); - BinaryExpressionNode or2 = new BinaryExpressionNode(or1); - or1.setLeftChild(or2); - or1.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode equality1 = new BinaryExpressionNode(or2); - or2.setOperator(BinaryLogicalOperator.OR); - or2.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(or2); - or2.setRightChild(equality2); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(or1); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - or1.setRightChild(lessThan); - TerminalNode e = new TerminalNode(lessThan); - Key<Integer> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - UnfinishedValueNode f = new UnfinishedValueNode(); - f.setParameterIndex(0); - f.setType(Integer.class); - f.setLHS(false); - TerminalNode fReal = new TerminalNode(lessThan); - fReal.setValue(f); - lessThan.setRightChild(fReal); - - assertTrue( WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWithMultipleDisjunctions2() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode or1 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(or1); - BinaryExpressionNode or2 = new BinaryExpressionNode(or1); - or1.setLeftChild(or2); - BinaryExpressionNode or3 = new BinaryExpressionNode(or2); - or3.setOperator(BinaryLogicalOperator.OR); - or2.setLeftChild(or3); - or1.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode equality1 = new BinaryExpressionNode(or3); - or2.setOperator(BinaryLogicalOperator.OR); - or3.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(or3); - or3.setRightChild(equality2); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - or2.setRightChild(lessThan); - TerminalNode e = new TerminalNode(lessThan); - Key<String> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - TerminalNode f = new TerminalNode(lessThan); - f.setValue("f"); - lessThan.setRightChild(f); - BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or1); - greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - TerminalNode g = new TerminalNode(greaterOrEqual); - Key<String> gKey = new Key<>("g"); - g.setValue(gKey); - greaterOrEqual.setLeftChild(g); - TerminalNode h = new TerminalNode(greaterOrEqual); - h.setValue("h"); - greaterOrEqual.setRightChild(h); - or1.setRightChild(greaterOrEqual); - - assertTrue( WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWithMultipleConDisjunctions() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode and3 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(and3); - and3.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode and2 = new BinaryExpressionNode(and3); - and2.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode and1 = new BinaryExpressionNode(and2); - and1.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode equality3 = new BinaryExpressionNode(and1); - equality3.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode x = new TerminalNode(equality3); - x.setValue(new Key<>("x")); - TerminalNode y = new TerminalNode(equality3); - y.setValue("y"); - equality3.setLeftChild(x); - equality3.setRightChild(y); - and1.setRightChild(equality3); - and3.setLeftChild(and2); - and2.setLeftChild(and1); - BinaryExpressionNode equality4 = new BinaryExpressionNode(and2); - equality4.setOperator(BinaryComparisonOperator.EQUALS); - and2.setRightChild(equality4); - TerminalNode u = new TerminalNode(equality4); - u.setValue(new Key<>("u")); - equality4.setLeftChild(u); - TerminalNode w = new TerminalNode(equality4); - w.setValue("w"); - equality4.setRightChild(w); - BinaryExpressionNode equality5 = new BinaryExpressionNode(and3); - equality5.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode s = new TerminalNode(equality5); - s.setValue(new Key<>("s")); - TerminalNode t = new TerminalNode(equality5); - t.setValue("t"); - equality5.setLeftChild(s); - equality5.setRightChild(t); - and3.setRightChild(equality5); - BinaryExpressionNode or3 = new BinaryExpressionNode(and1); - BinaryExpressionNode or2 = new BinaryExpressionNode(or3); - BinaryExpressionNode or1 = new BinaryExpressionNode(or2); - or3.setOperator(BinaryLogicalOperator.OR); - or2.setOperator(BinaryLogicalOperator.OR); - or1.setOperator(BinaryLogicalOperator.OR); - or3.setLeftChild(or2); - or2.setLeftChild(or1); - BinaryExpressionNode equality1 = new BinaryExpressionNode(or1); - or1.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(or1); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - or1.setRightChild(equality2); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - or2.setRightChild(lessThan); - or2.setLeftChild(or1); - TerminalNode e = new TerminalNode(lessThan); - Key<String> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - TerminalNode f = new TerminalNode(lessThan); - f.setValue("f"); - lessThan.setRightChild(f); - BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or3); - greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - TerminalNode g = new TerminalNode(greaterOrEqual); - Key<String> gKey = new Key<>("g"); - g.setValue(gKey); - greaterOrEqual.setLeftChild(g); - TerminalNode h = new TerminalNode(greaterOrEqual); - h.setValue("h"); - greaterOrEqual.setRightChild(h); - or3.setRightChild(greaterOrEqual); - or3.setLeftChild(or2); - and1.setLeftChild(or3); - - assertTrue(WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWithMultipleConDisjunctionsNegations() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR NOT 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND NOT 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression expn = statement.getSuffixExpression(); - assertNull(expn.getLimitExpn()); - assertNull(expn.getSortExpn()); - assertNotNull(expn.getWhereExpn()); - - WhereExpression where = expn.getWhereExpn(); - // build the expected expression tree - WhereExpression expected = new WhereExpression(); - BinaryExpressionNode and3 = new BinaryExpressionNode(expected.getRoot()); - expected.getRoot().setValue(and3); - and3.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode and2 = new BinaryExpressionNode(and3); - and2.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode and1 = new BinaryExpressionNode(and2); - and1.setOperator(BinaryLogicalOperator.AND); - NotBooleanExpressionNode not2 = new NotBooleanExpressionNode(and1); - BinaryExpressionNode equality3 = new BinaryExpressionNode(not2); - not2.setValue(equality3); - equality3.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode x = new TerminalNode(equality3); - x.setValue(new Key<String>("x")); - TerminalNode y = new TerminalNode(equality3); - y.setValue("y"); - equality3.setLeftChild(x); - equality3.setRightChild(y); - and1.setRightChild(not2); - and3.setLeftChild(and2); - and2.setLeftChild(and1); - BinaryExpressionNode equality4 = new BinaryExpressionNode(and2); - equality4.setOperator(BinaryComparisonOperator.EQUALS); - and2.setRightChild(equality4); - TerminalNode u = new TerminalNode(equality4); - u.setValue(new Key<String>("u")); - equality4.setLeftChild(u); - TerminalNode w = new TerminalNode(equality4); - w.setValue("w"); - equality4.setRightChild(w); - BinaryExpressionNode equality5 = new BinaryExpressionNode(and3); - equality5.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode s = new TerminalNode(equality5); - s.setValue(new Key<String>("s")); - TerminalNode t = new TerminalNode(equality5); - t.setValue("t"); - equality5.setLeftChild(s); - equality5.setRightChild(t); - and3.setRightChild(equality5); - BinaryExpressionNode or3 = new BinaryExpressionNode(and1); - BinaryExpressionNode or2 = new BinaryExpressionNode(or3); - BinaryExpressionNode or1 = new BinaryExpressionNode(or2); - or3.setOperator(BinaryLogicalOperator.OR); - or2.setOperator(BinaryLogicalOperator.OR); - or1.setOperator(BinaryLogicalOperator.OR); - or3.setLeftChild(or2); - or2.setLeftChild(or1); - BinaryExpressionNode equality1 = new BinaryExpressionNode(or1); - or1.setLeftChild(equality1); - equality1.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality1); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality1.setLeftChild(a); - TerminalNode b = new TerminalNode(equality1); - b.setValue("b"); - equality1.setRightChild(b); - BinaryExpressionNode equality2 = new BinaryExpressionNode(or1); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - NotBooleanExpressionNode not1 = new NotBooleanExpressionNode(or1); - not1.setValue(equality2); - or1.setRightChild(not1); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - BinaryExpressionNode lessThan = new BinaryExpressionNode(or2); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - or2.setRightChild(lessThan); - or2.setLeftChild(or1); - TerminalNode e = new TerminalNode(lessThan); - Key<String> eKey = new Key<>("e"); - e.setValue(eKey); - lessThan.setLeftChild(e); - TerminalNode f = new TerminalNode(lessThan); - f.setValue("f"); - lessThan.setRightChild(f); - BinaryExpressionNode greaterOrEqual = new BinaryExpressionNode(or3); - greaterOrEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - TerminalNode g = new TerminalNode(greaterOrEqual); - Key<String> gKey = new Key<>("g"); - g.setValue(gKey); - greaterOrEqual.setLeftChild(g); - TerminalNode h = new TerminalNode(greaterOrEqual); - h.setValue("h"); - greaterOrEqual.setRightChild(h); - or3.setRightChild(greaterOrEqual); - or3.setLeftChild(or2); - and1.setLeftChild(or3); - - assertTrue(WhereExpressions.equals(expected, where)); - } - - @Test - public void testParseQueryWhereAndSortMultiple() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' AND 'c' = ?s OR NOT 'x' >= ?i SORT 'a' ASC , 'b' DSC , 'c' ASC"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); - assertEquals(2, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNotNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - List<SortMember> list = suffixExpn.getSortExpn().getMembers(); - assertNotNull(list); - assertEquals(3, list.size()); - assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); - assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); - assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); - assertEquals("a", list.get(0).getSortKey()); - assertEquals("b", list.get(1).getSortKey()); - assertEquals("c", list.get(2).getSortKey()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(or); - or.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode and = new BinaryExpressionNode(or); - and.setOperator(BinaryLogicalOperator.AND); - or.setLeftChild(and); - NotBooleanExpressionNode not = new NotBooleanExpressionNode(or); - or.setRightChild(not); - BinaryExpressionNode unequality = new BinaryExpressionNode(and); - unequality.setOperator(BinaryComparisonOperator.LESS_THAN); - TerminalNode a = new TerminalNode(unequality); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - unequality.setLeftChild(a); - TerminalNode b = new TerminalNode(unequality); - b.setValue("b"); - unequality.setRightChild(b); - and.setLeftChild(unequality); - BinaryExpressionNode equality = new BinaryExpressionNode(and); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality.setLeftChild(c); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(0); - patch1.setLHS(false); - patch1.setType(String.class); - TerminalNode d = new TerminalNode(equality); - d.setValue(patch1); - equality.setRightChild(d); - and.setRightChild(equality); - BinaryExpressionNode greaterEqual = new BinaryExpressionNode(not); - not.setValue(greaterEqual); - greaterEqual.setOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL_TO); - TerminalNode x = new TerminalNode(greaterEqual); - Key<Integer> xKey = new Key<>("x"); - x.setValue(xKey); - greaterEqual.setLeftChild(x); - UnfinishedValueNode patch2 = new UnfinishedValueNode(); - patch2.setParameterIndex(1); - patch2.setLHS(false); - patch2.setType(Integer.class); - TerminalNode y = new TerminalNode(greaterEqual); - y.setValue(patch2); - greaterEqual.setRightChild(y); - // finally assert equality - assertTrue( WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseQueryWhereOrSortMultiple() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' OR 'c' = ?s SORT 'a' ASC , ?s DSC , 'c' ASC"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>) parser.parse(); - assertEquals(2, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNotNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - SortExpression sortExp = suffixExpn.getSortExpn(); - List<SortMember> list = sortExp.getMembers(); - assertNotNull(list); - assertEquals(3, list.size()); - assertEquals(SortDirection.ASCENDING, list.get(0).getDirection()); - assertEquals(SortDirection.DESCENDING, list.get(1).getDirection()); - assertEquals(SortDirection.ASCENDING, list.get(2).getDirection()); - assertEquals("a", list.get(0).getSortKey()); - UnfinishedSortKey unfinished = new UnfinishedSortKey(); - unfinished.setParameterIndex(1); - assertEquals(unfinished, list.get(1).getSortKey()); - assertEquals("c", list.get(2).getSortKey()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(or); - or.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode unequality = new BinaryExpressionNode(or); - unequality.setOperator(BinaryComparisonOperator.LESS_THAN); - TerminalNode a = new TerminalNode(unequality); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - unequality.setLeftChild(a); - TerminalNode b = new TerminalNode(unequality); - b.setValue("b"); - unequality.setRightChild(b); - or.setLeftChild(unequality); - BinaryExpressionNode equality = new BinaryExpressionNode(or); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality); - Key<String> cKey = new Key<>("c"); - c.setValue(cKey); - equality.setLeftChild(c); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(0); - patch1.setLHS(false); - patch1.setType(String.class); - TerminalNode d = new TerminalNode(equality); - d.setValue(patch1); - equality.setRightChild(d); - or.setRightChild(equality); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseQuerySimpleWhereAndSimpleSort() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' SORT 'a' DSC"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNotNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - SortExpression sortExp = suffixExpn.getSortExpn(); - List<SortMember> list = sortExp.getMembers(); - assertNotNull(list); - assertEquals(1, list.size()); - assertEquals(SortDirection.DESCENDING, list.get(0).getDirection()); - assertEquals("a", list.get(0).getSortKey()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode unequality = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(unequality); - unequality.setOperator(BinaryComparisonOperator.LESS_THAN); - TerminalNode a = new TerminalNode(unequality); - @SuppressWarnings("rawtypes") - Key aKey = new Key("a"); - a.setValue(aKey); - unequality.setLeftChild(a); - TerminalNode b = new TerminalNode(unequality); - b.setValue("b"); - unequality.setRightChild(b); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseQuerySimpleWithOneWhere() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - fail(e.getMessage()); - } - assertEquals(1, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode equality = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(equality); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality); - @SuppressWarnings("rawtypes") - Key aKey = new Key(Key.AGENT_ID.getName()); - a.setValue(aKey); - equality.setLeftChild(a); - TerminalNode b = new TerminalNode(equality); - UnfinishedValueNode node = new UnfinishedValueNode(); - node.setParameterIndex(0); - node.setLHS(false); - node.setType(String.class); - b.setValue(node); - equality.setRightChild(b); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseSimpleWithAndOr() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s" + - " AND ?s < ?b OR 'a' = 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertEquals(3, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode or = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(or); - or.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode and = new BinaryExpressionNode(or); - and.setOperator(BinaryLogicalOperator.AND); - or.setLeftChild(and); - BinaryExpressionNode equality = new BinaryExpressionNode(and); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality); - Key<String> aKey = new Key<>("a"); - a.setValue(aKey); - equality.setLeftChild(a); - TerminalNode b = new TerminalNode(equality); - b.setValue("b"); - equality.setRightChild(b); - or.setRightChild(equality); - BinaryExpressionNode equality2 = new BinaryExpressionNode(and); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - Key<String> cKey = new Key<>(Key.AGENT_ID.getName()); - c.setValue(cKey); - equality2.setLeftChild(c); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(0); - patch1.setLHS(false); - patch1.setType(String.class); - TerminalNode d = new TerminalNode(equality2); - d.setValue(patch1); - equality2.setRightChild(d); - and.setLeftChild(equality2); - BinaryExpressionNode lessThan = new BinaryExpressionNode(and); - lessThan.setOperator(BinaryComparisonOperator.LESS_THAN); - UnfinishedValueNode patch = new UnfinishedValueNode(); - patch.setParameterIndex(1); - patch.setType(String.class); - patch.setLHS(true); - TerminalNode x = new TerminalNode(lessThan); - x.setValue(patch); - lessThan.setLeftChild(x); - UnfinishedValueNode patch2 = new UnfinishedValueNode(); - patch2.setLHS(false); - patch2.setParameterIndex(2); - patch2.setType(Boolean.class); - TerminalNode y = new TerminalNode(lessThan); - y.setValue(patch2); - lessThan.setRightChild(y); - and.setRightChild(lessThan); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseSimpleWithAnd() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s AND ?s = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertEquals(2, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - // build the expected expression tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(and); - and.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode equality = new BinaryExpressionNode(and); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality); - @SuppressWarnings("rawtypes") - Key aKey = new Key("a"); - a.setValue(aKey); - equality.setLeftChild(a); - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setParameterIndex(0); - unfinished.setLHS(false); - unfinished.setType(String.class); - TerminalNode b = new TerminalNode(equality); - b.setValue(unfinished); - equality.setRightChild(b); - and.setLeftChild(equality); - BinaryExpressionNode equality2 = new BinaryExpressionNode(and); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(1); - patch1.setType(String.class); - patch1.setLHS(true); - c.setValue(patch1); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - and.setRightChild(equality2); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseSimpleWithNotOR() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE NOT 'a' = ?s OR ?s = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertEquals(2, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - // build the expected parse tree - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode or = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(or); - or.setOperator(BinaryLogicalOperator.OR); - NotBooleanExpressionNode notNode = new NotBooleanExpressionNode(or); - BinaryExpressionNode comparison = new BinaryExpressionNode(notNode); - notNode.setValue(comparison); - or.setLeftChild(notNode); - comparison.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode rightCompTerm = new TerminalNode(comparison); - TerminalNode leftCompTerm = new TerminalNode(comparison); - @SuppressWarnings("rawtypes") - Key aKey = new Key("a"); - leftCompTerm.setValue(aKey); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(0); - patch1.setType(String.class); - patch1.setLHS(false); - rightCompTerm.setValue(patch1); - comparison.setLeftChild(leftCompTerm); - comparison.setRightChild(rightCompTerm); - BinaryExpressionNode otherComparison = new BinaryExpressionNode(or); - otherComparison.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode leftUnfinished = new TerminalNode(otherComparison); - UnfinishedValueNode patch2 = new UnfinishedValueNode(); - patch2.setParameterIndex(1); - patch2.setLHS(true); - patch2.setType(String.class); - leftUnfinished.setValue(patch2); - TerminalNode other = new TerminalNode(otherComparison); - other.setValue("d"); - otherComparison.setLeftChild(leftUnfinished); - otherComparison.setRightChild(other); - or.setRightChild(otherComparison); - assertTrue(WhereExpressions.equals(expn, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseSimpleWithOr() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s OR ?s = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertEquals(2, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getLimitExpn()); - assertNotNull(suffixExpn.getWhereExpn()); - // build the expected parse tree - WhereExpression where = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(where.getRoot()); - where.getRoot().setValue(and); - and.setOperator(BinaryLogicalOperator.OR); - BinaryExpressionNode equality = new BinaryExpressionNode(and); - equality.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(equality); - @SuppressWarnings("rawtypes") - Key aKey = new Key("a"); - a.setValue(aKey); - equality.setLeftChild(a); - UnfinishedValueNode unfinished = new UnfinishedValueNode(); - unfinished.setParameterIndex(0); - unfinished.setType(String.class); - unfinished.setLHS(false); - TerminalNode b = new TerminalNode(equality); - b.setValue(unfinished); - equality.setRightChild(b); - and.setLeftChild(equality); - BinaryExpressionNode equality2 = new BinaryExpressionNode(and); - equality2.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode c = new TerminalNode(equality2); - UnfinishedValueNode patch1 = new UnfinishedValueNode(); - patch1.setParameterIndex(1); - patch1.setType(String.class); - patch1.setLHS(true); - c.setValue(patch1); - equality2.setLeftChild(c); - TerminalNode d = new TerminalNode(equality2); - d.setValue("d"); - equality2.setRightChild(d); - and.setRightChild(equality2); - assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); - } - - @Test - public void testParseSimpleWithLimit() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT 1"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertEquals(0, statement.getNumParams()); - assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); - SuffixExpression suffixExpn = statement.getSuffixExpression(); - assertNull(suffixExpn.getSortExpn()); - assertNull(suffixExpn.getWhereExpn()); - assertNotNull(suffixExpn.getLimitExpn()); - assertEquals(1, suffixExpn.getLimitExpn().getValue()); - } - - @Test - public void testParseAddBasic() throws DescriptorParsingException { - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = ?s"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertTrue(statement.getRawStatement() instanceof Add); - SuffixExpression suffix = statement.getSuffixExpression(); - assertNotNull(suffix); - WhereExpression where = suffix.getWhereExpn(); - LimitExpression limit = suffix.getLimitExpn(); - SortExpression sort = suffix.getSortExpn(); - assertNull(where); - assertNull(limit); - assertNull(sort); - SetList setList = statement.getSetList(); - assertNotNull(setList); - List<SetListValue> valuesList = setList.getValues(); - assertEquals(2, valuesList.size()); - SetListValue first = valuesList.get(0); - SetListValue second = valuesList.get(1); - TerminalNode a = new TerminalNode(null); - a.setValue(new Key<>("a")); - TerminalNode b = new TerminalNode(null); - b.setValue("b"); - SetListValue firstExpected = new SetListValue(); - firstExpected.setKey(a); - firstExpected.setValue(b); - assertEquals(firstExpected, first); - TerminalNode c = new TerminalNode(null); - c.setValue(new Key<>("c")); - UnfinishedValueNode dVal = new UnfinishedValueNode(); - dVal.setType(String.class); - dVal.setLHS(false); - dVal.setParameterIndex(0); - TerminalNode d = new TerminalNode(null); - d.setValue(dVal); - SetListValue secondExpected = new SetListValue(); - secondExpected.setKey(c); - secondExpected.setValue(d); - assertEquals(secondExpected, second); - } - - @Test - public void testParseUpdateBasic() throws DescriptorParsingException { - String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = ?s WHERE 'foo' != ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatementImpl<AgentInformation> statement = null; - try { - statement = (ParsedStatementImpl<AgentInformation>)parser.parse(); - } catch (DescriptorParsingException e) { - e.printStackTrace(); - fail(e.getMessage()); - } - assertTrue(statement.getRawStatement() instanceof Update); - SuffixExpression suffix = statement.getSuffixExpression(); - assertNotNull(suffix); - WhereExpression where = suffix.getWhereExpn(); - LimitExpression limit = suffix.getLimitExpn(); - SortExpression sort = suffix.getSortExpn(); - assertNotNull(where); - assertNull(limit); - assertNull(sort); - SetList setList = statement.getSetList(); - assertNotNull(setList); - List<SetListValue> valuesList = setList.getValues(); - assertEquals(2, valuesList.size()); - SetListValue first = valuesList.get(0); - SetListValue second = valuesList.get(1); - TerminalNode a = new TerminalNode(null); - a.setValue(new Key<>("a")); - TerminalNode b = new TerminalNode(null); - b.setValue("b"); - SetListValue firstExpected = new SetListValue(); - firstExpected.setKey(a); - firstExpected.setValue(b); - assertEquals(firstExpected, first); - TerminalNode c = new TerminalNode(null); - c.setValue(new Key<>("c")); - UnfinishedValueNode dVal = new UnfinishedValueNode(); - dVal.setType(String.class); - dVal.setLHS(false); - dVal.setParameterIndex(0); - TerminalNode d = new TerminalNode(null); - d.setValue(dVal); - SetListValue secondExpected = new SetListValue(); - secondExpected.setKey(c); - secondExpected.setValue(d); - assertEquals(secondExpected, second); - // Build expected where expn - WhereExpression expectedWhere = new WhereExpression(); - BinaryExpressionNode equality = new BinaryExpressionNode(expectedWhere.getRoot()); - expectedWhere.getRoot().setValue(equality); - equality.setOperator(BinaryComparisonOperator.NOT_EQUAL_TO); - TerminalNode foo = new TerminalNode(equality); - @SuppressWarnings("rawtypes") - Key fooKey = new Key("foo"); - foo.setValue(fooKey); - equality.setLeftChild(foo); - TerminalNode fooVal = new TerminalNode(equality); - UnfinishedValueNode node = new UnfinishedValueNode(); - node.setParameterIndex(1); - node.setLHS(false); - node.setType(Integer.class); - fooVal.setValue(node); - equality.setRightChild(fooVal); - assertTrue(WhereExpressions.equals(expectedWhere, where)); - } - - // TODO: Add basic parse tests for REMOVE/REPLACE - - @Test - public void rejectLongValAsIntType() throws DescriptorParsingException { - // 30000000003 > Integer.MAX_VALUE; needs to be preceded by 'l/L' - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 30000000003"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Illegal terminal type.")); - } - } - - @Test - public void rejectLimitWhichIsNotInt() { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT illegal"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - assertEquals("Invalid limit expression. 'illegal' not an integer", e.getMessage()); - } - } - - @Test - public void rejectLHSnotString() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE a < 1"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Expected string value. Got term ->a<-")); - } - } - - /* - * At this point list types don't make sense in WHERE. - * What should "'a' != [ 'a', 'b' ]" evaluate to? - * How about this? "'key' > [ 1, 2 ]" - * - * The only reasonable use case in WHERE would be 'a' IN [ 'a', 'b' ... ]. - * We don't support this in a prepared context at this point. Should this - * change in future, this test needs to be carefully re-crafted. - */ - @Test - public void rejectListTypesAsFreeVarInInvalidContext() throws DescriptorParsingException { - List<String> illegalDescs = new ArrayList<>(); - - // Where should not support list types/Pojos. - String[] illegalWheres = new String[] { - " WHERE 'a' = ?s[", - " WHERE 'a' = ?i[", - " WHERE 'a' = ?p[", - " WHERE 'a' = ?d[", - }; - - // Make sure we test for QUERY, QUERY-COUNT, REPLACE, UPDATE, REMOVE - // i.e. all that accept a WHERE. - String basicQuery = "QUERY " + AgentInfoDAO.CATEGORY.getName(); - String basicQueryCount = "QUERY-COUNT " + AgentInfoDAO.CATEGORY.getName(); - // note SET clause is legal - String basicUpdate = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?s["; - // note SET clause is legal - String basicReplace = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = ?i["; - String basicRemove = "REMOVE " + AgentInfoDAO.CATEGORY.getName(); - for (String where: illegalWheres) { - illegalDescs.add(basicQuery + where); - illegalDescs.add(basicQueryCount + where); - illegalDescs.add(basicUpdate + where); - illegalDescs.add(basicReplace + where); - illegalDescs.add(basicRemove + where); - } - - // Test all illegal descs involving WHERE - doIllegalDescsTest(illegalDescs, "WHERE", "List"); - - // Test limit too - illegalDescs.clear(); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i["); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?s["); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?p["); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?d["); - - doIllegalDescsTest(illegalDescs, "LIMIT", "List"); - - // Test sort - illegalDescs.clear(); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?i[ ASC"); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?s[ ASC"); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?p[ ASC"); - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?d[ ASC"); - - doIllegalDescsTest(illegalDescs, "SORT", "List"); - } - - /* - * Pojos don't make sense in a WHERE, LIMIT and SORT clause. This test makes - * sure we reject this right away. - * - */ - @Test - public void rejectPojoTypesAsFreeVarInInvalidContext() throws DescriptorParsingException { - List<String> illegalDescs = new ArrayList<>(); - - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?p"); - doIllegalDescsTest(illegalDescs, "WHERE", "Pojo"); - illegalDescs.clear(); - - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?p"); - doIllegalDescsTest(illegalDescs, "LIMIT", "Pojo"); - illegalDescs.clear(); - - illegalDescs.add("QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT ?p DSC"); - doIllegalDescsTest(illegalDescs, "SORT", "Pojo"); - } - - private void doIllegalDescsTest(List<String> descs, String errorMsgContext, String type) { - for (String strDesc : descs) { - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, strDesc); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail(strDesc + " should not parse"); - } catch (DescriptorParsingException e) { - // pass - assertEquals(strDesc + " did not provide correct error message.", - type + " free variable type not allowed in " + errorMsgContext + " context", e.getMessage()); - } - } - } - - @Test - public void rejectIllegalFreeParamType() throws DescriptorParsingException { - // ? should be one of '?i', '?l', '?s', '?b', '?s[' - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE ? < 1"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - assertEquals("Unknown type of free parameter: '?'", e.getMessage()); - } - } - - @Test - public void rejectParseQueryWhereAndSortMultipleIllegalSortModifier() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'somekey' < 2 AND 'c' = ?s OR 'a' >= ?i SORT 'a' ASC , 'b' ILLEGAL , 'c' ASC"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Expected ASC or DSC")); - } - } - - @Test - public void rejectParseQueryWhereBoolTerm() throws DescriptorParsingException { - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE true AND false"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - } - } - - @Test - public void rejectQueryWithParenthesis() throws DescriptorParsingException { - // We don't allow parenthesized expressions. This is due to mongodb not - // allowing this. - String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE NOT ( 'a' = 'b' AND 'c' = 'd' )"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - } - } - - @Test - public void rejectSimpleQueryWithMissingSpaces() throws DescriptorParsingException { - // we require a space before every operator/keyword - String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a'='b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - } - } - - @Test - public void rejectSimpleQueryWithMissingSpaces2() throws DescriptorParsingException { - // we require a space before every operator/keyword - String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a' ='b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - } - } - - @Test - public void rejectSimpleQueryWithInvalidComparison() throws DescriptorParsingException { - // <> is illegal - String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a' <> 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("should not parse"); - } catch (DescriptorParsingException e) { - // pass - } - } - - @Test - public void rejectInvalidDescriptorStringBadWhere() throws DescriptorParsingException { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " where '" + Key.AGENT_ID.getName() + "'= ?s"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("lower case where not allowed in descriptor. Should have rejected."); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Unexpected token: 'where'")); - } - } - - @Test - public void rejectInvalidDescriptorStringBadStatementType() throws DescriptorParsingException { - String descString = "UNKNOWN some-unknown-category WHERE 1 = ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("UNKNOWN not a valid statement type"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Unknown statement type")); - } - } - - @Test - public void rejectInvalidDescriptorStringCategoryMismatch() throws DescriptorParsingException { - String descString = "QUERY some-unknown-category WHERE 1 = ?i"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("category names in descriptor and Category object did not match!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Category mismatch")); - } - } - - @Test - public void rejectInvalidDescriptorStringBadSortNoArg() throws DescriptorParsingException { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?i SORT"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("category names in descriptor and Category object did not match!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("Invalid where clause")); - } - } - - @Test - public void rejectInvalidDescriptorStringBadWhereNoArg() throws DescriptorParsingException { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE SORT"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("category names in descriptor and Category object did not match!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("SORT")); - assertTrue(e.getMessage().contains("Expected string value")); - } - } - - @Test - public void rejectAddWithInvalidSetList() throws DescriptorParsingException { - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = , 'c' = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("Invalid SET values list."); - } catch (DescriptorParsingException e) { - // pass - assertEquals("Illegal terminal type. Token was ->,<-", e.getMessage()); - } - } - - @Test - public void rejectAddWithWhere() throws DescriptorParsingException { - String descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd' WHERE 'a' = 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("ADD operation does not support WHERE clauses!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("WHERE clause not allowed for ADD")); - } - } - - @Test - public void rejectReplaceWithoutWhere() throws DescriptorParsingException { - String descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("REPLACE operation requires WHERE clause, and should not parse!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("WHERE clause required for REPLACE")); - } - } - - @Test - public void rejectUpdateWithoutWhere() throws DescriptorParsingException { - String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' , 'c' = 'd'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("UPDATE operation requires WHERE clause, and should not parse!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("WHERE clause required for UPDATE")); - } - } - - @Test - public void rejectRemoveWithSetList() throws DescriptorParsingException { - String descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("REMOVE does not allow SET list, and should not parse!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("SET not allowed for REMOVE")); - } - } - - @Test - public void rejectQueryWithSetList() throws DescriptorParsingException { - String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("QUERY does not allow SET list, and should not parse!"); - } catch (DescriptorParsingException e) { - // pass - assertTrue(e.getMessage().contains("SET not allowed for QUERY")); - } - } - - - private void doRejectWriteSortLimitTest(String descString, String failmsg) { - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail(failmsg); - } catch (DescriptorParsingException e) { - // pass - assertEquals("LIMIT/SORT only allowed for QUERY/QUERY-COUNT", e.getMessage()); - } - } - - @Test - public void rejectWritesWithSortLimit() throws DescriptorParsingException { - // Update rejects - String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' SORT 'a' DSC"; - String failmsg = "SORT in UPDATE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' LIMIT 1"; - failmsg = "LIMIT in UPDATE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - - // Remove rejects - descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' LIMIT 1"; - failmsg = "LIMIT in REMOVE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - descString = "REMOVE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' SORT 'a' ASC"; - failmsg = "SORT in REMOVE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - - // Replace rejects - descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' LIMIT 1"; - failmsg = "LIMIT in REPLACE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - descString = "REPLACE " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' WHERE 'a' = 'b' SORT 'a' ASC"; - failmsg = "SORT in REPLACE is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - - // Add rejects - descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' LIMIT 1"; - failmsg = "LIMIT in ADD is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - descString = "ADD " + AgentInfoDAO.CATEGORY.getName() + " SET 'a' = 'b' SORT 'a' ASC"; - failmsg = "SORT in ADD is not allowed, and should not parse!"; - doRejectWriteSortLimitTest(descString, failmsg); - } - - @Test - public void rejectUpdateWithoutSet() throws DescriptorParsingException { - String descString = "UPDATE " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b'"; - StatementDescriptor<AgentInformation> desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); - parser = new StatementDescriptorParser<>(storage, desc); - try { - parser.parse(); - fail("UPDATE requires SET list, and should not parse!"); - } catch (DescriptorParsingException e) { - // pass - assertEquals("SET list required for UPDATE", e.getMessage()); - } - } - - // TODO: add tests where set list does not match all props of Pojo class for - // add/replace - -}
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressions.java Mon Oct 07 15:03:04 2013 -0400 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressions.java Fri Oct 04 18:13:31 2013 +0200 @@ -49,7 +49,7 @@ /** * Helper class for comparing where expression trees. * - * @see StatementDescriptorParserTest + * @see BasicDescriptorParserTest */ public class WhereExpressions {