# HG changeset patch # User Severin Gehwolf # Date 1372430785 -7200 # Node ID fe3f66cc83e754692e96ba80578c0558b839bcae # Parent 4ba43dcff78e5f242c89f7c6028f1258c76426bf Implement prepared statements (Part 2). Reviewed-by: ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-June/007215.html diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java Fri Jun 28 15:27:50 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/core/QueuedStorageTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -562,6 +562,13 @@ Thread.currentThread().interrupt(); } } + + @Override + public PreparedStatement prepareStatement(StatementDescriptor desc) + throws DescriptorParsingException { + // not implemented + return null; + } } } diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/AgentInfoDAOTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/AgentInfoDAOTest.java Fri Jun 28 15:27:50 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/AgentInfoDAOTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -54,11 +54,15 @@ import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.DescriptorParsingException; import com.redhat.thermostat.storage.core.HostRef; import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.PreparedStatement; import com.redhat.thermostat.storage.core.Query; 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.StatementExecutionException; import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.core.Update; import com.redhat.thermostat.storage.dao.AgentInfoDAO; @@ -116,17 +120,18 @@ assertTrue(keys.contains(AgentInfoDAO.CONFIG_LISTEN_ADDRESS)); } + @SuppressWarnings({ "unchecked", "rawtypes" }) @Test - public void verifyGetAllAgentInformationWithOneAgentInStorage() { - @SuppressWarnings("unchecked") - Cursor agentCursor = mock(Cursor.class); + public void verifyGetAllAgentInformationWithOneAgentInStorage() + throws DescriptorParsingException, StatementExecutionException { + Cursor agentCursor = mock(Cursor.class); when(agentCursor.hasNext()).thenReturn(true).thenReturn(false); when(agentCursor.next()).thenReturn(agent1).thenReturn(null); Storage storage = mock(Storage.class); - Query query = mock(Query.class); - when(query.execute()).thenReturn(agentCursor); - when(storage.createQuery(any(Category.class))).thenReturn(query); + PreparedStatement stmt = mock(PreparedStatement.class); + when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(stmt); + when(stmt.executeQuery()).thenReturn(agentCursor); AgentInfoDAOImpl dao = new AgentInfoDAOImpl(storage); List allAgentInfo = dao.getAllAgentInformation(); @@ -138,26 +143,29 @@ assertEquals(expected, result); } + @SuppressWarnings({ "unchecked", "rawtypes" }) @Test - public void verifyGetAliveAgent() { - @SuppressWarnings("unchecked") - Cursor agentCursor = mock(Cursor.class); + public void verifyGetAliveAgent() throws DescriptorParsingException, StatementExecutionException { + Cursor agentCursor = mock(Cursor.class); when(agentCursor.hasNext()).thenReturn(true).thenReturn(false); when(agentCursor.next()).thenReturn(agent1).thenReturn(null); Query query = mock(Query.class); Storage storage = mock(Storage.class); + PreparedStatement stmt = mock(PreparedStatement.class); + when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(stmt); + when(stmt.executeQuery()).thenReturn(agentCursor); when(storage.createQuery(any(Category.class))).thenReturn(query); when(query.execute()).thenReturn(agentCursor); AgentInfoDAO dao = new AgentInfoDAOImpl(storage); List aliveAgents = dao.getAliveAgents(); - verify(storage).createQuery(AgentInfoDAO.CATEGORY); - Expression expr = factory.equalTo(AgentInfoDAO.ALIVE_KEY, Boolean.TRUE); - verify(query).where(eq(expr)); - verify(query).execute(); - verifyNoMoreInteractions(query); + verify(storage).prepareStatement(any(StatementDescriptor.class)); + verify(stmt).executeQuery(); + verify(stmt).setString(eq(0), eq(AgentInfoDAOImpl.ALIVE_KEY.getName())); + verify(stmt).setBoolean(eq(1), eq(true)); + verifyNoMoreInteractions(stmt); assertEquals(1, aliveAgents.size()); diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -0,0 +1,469 @@ +/* + * 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 + * . + * + * 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.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.Pair; +import com.redhat.thermostat.storage.core.Cursor; +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.query.BinaryComparisonExpression; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.BinaryLogicalExpression; +import com.redhat.thermostat.storage.query.BinaryLogicalOperator; +import com.redhat.thermostat.storage.query.Expression; +import com.redhat.thermostat.storage.query.LiteralExpression; + +public class ParsedStatementTest { + + private Query statement; + + @Before + public void setup() { + statement = new TestQuery(); + } + + @After + public void tearDown() { + statement = null; + } + + @SuppressWarnings("rawtypes") + @Test + public void canPatchWhereAndExpr() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? AND c = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); + BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); + leftEqual.setOperator(BinaryComparisonOperator.EQUALS); + rightEqual.setOperator(BinaryComparisonOperator.EQUALS); + and.setLeftChild(leftEqual); + and.setRightChild(rightEqual); + TerminalNode a = new TerminalNode(leftEqual); + Key aKey = new Key("a", false); + a.setValue(aKey); + TerminalNode b = new TerminalNode(leftEqual); + UnfinishedValueNode patchB = new UnfinishedValueNode(); + patchB.setParameterIndex(0); + patchB.setType(String.class); + b.setValue(patchB); + leftEqual.setLeftChild(a); + leftEqual.setRightChild(b); + TerminalNode c = new TerminalNode(rightEqual); + c.setValue("c"); + rightEqual.setLeftChild(c); + TerminalNode d = new TerminalNode(rightEqual); + UnfinishedValueNode dPatch = new UnfinishedValueNode(); + dPatch.setParameterIndex(1); + dPatch.setType(Integer.class); + d.setValue(dPatch); + rightEqual.setRightChild(d); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(2); + preparedStatement.setString(0, "test1"); + preparedStatement.setInt(1, 2); + // finally test the patching + Query query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryLogicalExpression); + BinaryLogicalExpression andFinal = (BinaryLogicalExpression)expectedExpression; + assertEquals(BinaryLogicalOperator.AND, andFinal.getOperator()); + assertTrue(andFinal.getLeftOperand() instanceof BinaryComparisonExpression); + assertTrue(andFinal.getRightOperand() instanceof BinaryComparisonExpression); + BinaryComparisonExpression left = (BinaryComparisonExpression)andFinal.getLeftOperand(); + BinaryComparisonExpression right = (BinaryComparisonExpression)andFinal.getRightOperand(); + assertEquals(BinaryComparisonOperator.EQUALS, left.getOperator()); + assertEquals(BinaryComparisonOperator.EQUALS, right.getOperator()); + assertTrue(left.getLeftOperand() instanceof LiteralExpression); + assertTrue(left.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)left.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)left.getRightOperand(); + assertEquals(aKey, leftLiteral1.getValue()); + assertEquals("test1", rightLiteral1.getValue()); + LiteralExpression leftLiteral2 = (LiteralExpression)right.getLeftOperand(); + LiteralExpression rightLiteral2 = (LiteralExpression)right.getRightOperand(); + assertEquals("c", leftLiteral2.getValue()); + // right literal value should have been patched to a "d" + assertEquals(2, rightLiteral2.getValue()); + } + + @SuppressWarnings("rawtypes") + @Test + public void canPatchBasicWhereEquals() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + a.setValue(new Key("a", false)); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(0); + bPatch.setType(boolean.class); + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(1); + preparedStatement.setBoolean(0, true); + // finally test the patching + Query query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key("a", false), leftLiteral1.getValue()); + // this should have gotten patched to a "b" + assertEquals(true, rightLiteral1.getValue()); + // now do it again with a different value + preparedStatement = new PreparedStatementImpl(1); + preparedStatement.setBoolean(0, false); + query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + q = (TestQuery)query; + expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key("a", false), leftLiteral1.getValue()); + assertEquals(false, rightLiteral1.getValue()); + } + + @SuppressWarnings("rawtypes") + @Test + public void canPatchBasicWhereEqualsLHSKeyAndRHSValue() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE ?s = ?b + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + UnfinishedValueNode aPatch = new UnfinishedValueNode(); + aPatch.setLHS(true); + aPatch.setType(String.class); + aPatch.setParameterIndex(0); + a.setValue(aPatch); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(1); + bPatch.setType(boolean.class); + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(2); + preparedStatement.setString(0, "a"); + preparedStatement.setBoolean(1, true); + // finally test the patching + Query query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key("a", false), leftLiteral1.getValue()); + // this should have gotten patched to a "b" + assertEquals(true, rightLiteral1.getValue()); + // now do it again with a different value + preparedStatement = new PreparedStatementImpl(2); + preparedStatement.setString(0, "a"); + preparedStatement.setBoolean(1, false); + query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + q = (TestQuery)query; + expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key("a", false), leftLiteral1.getValue()); + assertEquals(false, rightLiteral1.getValue()); + } + + @Test + public void canPatchBasicLimit() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + LimitExpression limitExpnToPatch = new LimitExpression(); + UnfinishedLimitValue unfinished = new UnfinishedLimitValue(); + unfinished.setParameterIndex(0); + limitExpnToPatch.setValue(unfinished); + suffixExpn.setLimitExpn(limitExpnToPatch); + suffixExpn.setSortExpn(null); + suffixExpn.setWhereExpn(null); + parsedStmt.setSuffixExpression(suffixExpn); + // set the value for the one unfinished param + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(1); + preparedStatement.setInt(0, 3); + @SuppressWarnings("rawtypes") + Query query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + assertEquals(3, q.limitVal); + } + + @SuppressWarnings("rawtypes") + @Test + public void canPatchBasicSort() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + // SORT ? ASC, b DSC + SortExpression sortExpn = new SortExpression(); + SortMember member = new SortMember(); + member.setDirection(SortDirection.ASCENDING); + UnfinishedSortKey unfinished = new UnfinishedSortKey(); + unfinished.setParameterIndex(0); + member.setSortKey(unfinished); + SortMember member2 = new SortMember(); + member2.setDirection(SortDirection.DESCENDING); + member2.setSortKey("b"); + sortExpn.addMember(member); + sortExpn.addMember(member2); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(sortExpn); + suffixExpn.setWhereExpn(null); + parsedStmt.setSuffixExpression(suffixExpn); + // set the value for the one unfinished param + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(1); + preparedStatement.setString(0, "a"); + Query query = (Query)parsedStmt.patchQuery(preparedStatement); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + List> actualSorts = q.sorts; + assertEquals(2, actualSorts.size()); + Pair first = actualSorts.get(0); + Key firstKeyActual = (Key)first.getFirst(); + Key expectedFirst = new Key("a", false); + assertEquals(expectedFirst, firstKeyActual); + assertEquals(SortDirection.ASCENDING, first.getSecond()); + Pair second = actualSorts.get(1); + Key secondKeyActual = (Key)second.getFirst(); + Key expectedSecond = new Key("b", false); + assertEquals(expectedSecond, secondKeyActual); + assertEquals(SortDirection.DESCENDING, second.getSecond()); + } + + @SuppressWarnings("rawtypes") + @Test + public void failPatchWithWrongType() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE 'a' = ?s AND 'c' = ?i + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); + BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); + leftEqual.setOperator(BinaryComparisonOperator.EQUALS); + rightEqual.setOperator(BinaryComparisonOperator.EQUALS); + and.setLeftChild(leftEqual); + and.setRightChild(rightEqual); + TerminalNode a = new TerminalNode(leftEqual); + Key aKey = new Key("a", false); + a.setValue(aKey); + TerminalNode b = new TerminalNode(leftEqual); + UnfinishedValueNode patchB = new UnfinishedValueNode(); + patchB.setType(String.class); + patchB.setParameterIndex(0); + b.setValue(patchB); + leftEqual.setLeftChild(a); + leftEqual.setRightChild(b); + TerminalNode c = new TerminalNode(rightEqual); + Key cKey = new Key("c", false); + c.setValue(cKey); + rightEqual.setLeftChild(c); + TerminalNode d = new TerminalNode(rightEqual); + UnfinishedValueNode dPatch = new UnfinishedValueNode(); + dPatch.setParameterIndex(1); + dPatch.setType(Integer.class); + d.setValue(dPatch); + rightEqual.setRightChild(d); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(2); + preparedStatement.setString(0, "test1"); + preparedStatement.setString(1, "foo"); + // finally test the patching + try { + parsedStmt.patchQuery(preparedStatement); + fail("should have failed to patch param 1 with a string value (expected int)"); + } catch (IllegalPatchException e) { + // pass + assertTrue(e.getMessage().contains("invalid type when attempting to patch")); + } + } + + @Test + public void failPatchBasicEqualsIfIndexOutofBounds() { + // create the parsedStatementImpl we are going to use + ParsedStatement parsedStmt = new ParsedStatement(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + a.setValue("a"); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(1); // out of bounds + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(1); + preparedStatement.setString(0, "b"); + // this should fail + try { + parsedStmt.patchQuery(preparedStatement); + } catch (IllegalPatchException e) { + // pass + assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException); + } + } + + @SuppressWarnings("rawtypes") + private static class TestQuery implements Query { + + private Expression expr; + private List> sorts; + private int limitVal = -1; + + private TestQuery() { + sorts = new ArrayList<>(); + } + + @Override + public void where(Expression expr) { + this.expr = expr; + } + + @Override + public void sort(Key key, SortDirection direction) { + Pair sortPair = new Pair<>(key, direction); + sorts.add(sortPair); + } + + @Override + public void limit(int n) { + this.limitVal = n; + } + + @Override + public Cursor execute() { + // Not implemented + return null; + } + + } +} diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -0,0 +1,167 @@ +/* + * 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 + * . + * + * 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.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.StatementExecutionException; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.query.BinaryComparisonExpression; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.Expression; +import com.redhat.thermostat.storage.query.LiteralExpression; + +public class PreparedStatementImplTest { + + @Test + public void failToSetIndexOutOfBounds() { + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(2); + preparedStatement.setInt(1, 3); + preparedStatement.setString(0, "testing"); + try { + preparedStatement.setLong(3, 1); + fail("should have thrown exception"); + } catch (IllegalArgumentException e) { + // pass + } + try { + preparedStatement.setInt(4, 1); + fail("should have thrown exception"); + } catch (IllegalArgumentException e) { + // pass + } + try { + preparedStatement.setString(10, "ignored"); + fail("should have thrown exception"); + } catch (IllegalArgumentException e) { + // pass + } + try { + preparedStatement.setStringList(3, new String[] { "ignored" }); + fail("should have thrown exception"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void canDoParsingPatchingAndExecution() throws Exception { + String queryString = "QUERY foo WHERE 'a' = ?s"; + StatementDescriptor desc = mock(StatementDescriptor.class); + when(desc.getQueryDescriptor()).thenReturn(queryString); + Category mockCategory = mock(Category.class); + when(desc.getCategory()).thenReturn(mockCategory); + when(mockCategory.getName()).thenReturn("foo"); + Storage storage = mock(Storage.class); + StubQuery stmt = new StubQuery(); + when(storage.createQuery(any(Category.class))).thenReturn(stmt); + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(storage, desc); + preparedStatement.setString(0, "foo"); + preparedStatement.executeQuery(); + assertTrue(stmt.called); + LiteralExpression o1 = new LiteralExpression<>(new Key("a", false)); + LiteralExpression o2 = new LiteralExpression<>("foo"); + BinaryComparisonExpression binComp = new BinaryComparisonExpression( + o1, BinaryComparisonOperator.EQUALS, o2); + assertEquals(binComp, stmt.expr); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void failExecutionWithWronglyTypedParams() throws Exception { + String queryString = "QUERY foo WHERE 'a' = ?b"; + StatementDescriptor desc = mock(StatementDescriptor.class); + when(desc.getQueryDescriptor()).thenReturn(queryString); + Category mockCategory = mock(Category.class); + when(desc.getCategory()).thenReturn(mockCategory); + when(mockCategory.getName()).thenReturn("foo"); + Storage storage = mock(Storage.class); + StubQuery stmt = new StubQuery(); + when(storage.createQuery(any(Category.class))).thenReturn(stmt); + PreparedStatementImpl preparedStatement = new PreparedStatementImpl(storage, desc); + preparedStatement.setString(0, "foo"); + try { + preparedStatement.executeQuery(); + fail("Should have thrown SEE due to type mismatch. boolean vs. string"); + } catch (StatementExecutionException e) { + // pass + assertTrue(e.getMessage().contains("invalid type when attempting to patch")); + } + } + + @SuppressWarnings("rawtypes") + private static class StubQuery implements Query { + + private Expression expr; + private boolean called = false; + + @Override + public void where(Expression expr) { + this.expr = expr; + } + + @Override + public void sort(Key key, SortDirection direction) { + // nothing + } + + @Override + public void limit(int n) { + // nothing + } + + @Override + public Cursor execute() { + called = true; + return null; + } + + } +} diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -0,0 +1,802 @@ +/* + * 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 + * . + * + * 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.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +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.Query; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.Storage; +import com.redhat.thermostat.storage.core.Query.SortDirection; +import com.redhat.thermostat.storage.dao.AgentInfoDAO; +import com.redhat.thermostat.storage.internal.statement.BinaryExpressionNode; +import com.redhat.thermostat.storage.internal.statement.LimitExpression; +import com.redhat.thermostat.storage.internal.statement.NotBooleanExpressionNode; +import com.redhat.thermostat.storage.internal.statement.ParsedStatement; +import com.redhat.thermostat.storage.internal.statement.SortExpression; +import com.redhat.thermostat.storage.internal.statement.SortMember; +import com.redhat.thermostat.storage.internal.statement.StatementDescriptorParser; +import com.redhat.thermostat.storage.internal.statement.SuffixExpression; +import com.redhat.thermostat.storage.internal.statement.TerminalNode; +import com.redhat.thermostat.storage.internal.statement.UnfinishedLimitValue; +import com.redhat.thermostat.storage.internal.statement.UnfinishedSortKey; +import com.redhat.thermostat.storage.internal.statement.UnfinishedValueNode; +import com.redhat.thermostat.storage.internal.statement.WhereExpression; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.BinaryLogicalOperator; + +public class StatementDescriptorParserTest { + + private Storage storage; + @SuppressWarnings("rawtypes") + private Query mockQuery; + private StatementDescriptorParser parser; + + @SuppressWarnings("unchecked") + @Before + public void setup() { + storage = mock(Storage.class); + mockQuery = mock(Query.class); + when(storage.createQuery(any(AgentInfoDAO.CATEGORY.getClass()))).thenReturn(mockQuery); + } + + @After + public void teardown() { + storage = null; + mockQuery = null; + } + + @Test + public void testParseQuerySimple() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName(); + StatementDescriptor desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 testParseQuerySimpleWithLimit() throws DescriptorParsingException { + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i"; + StatementDescriptor desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 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()); + } + + @SuppressWarnings("rawtypes") + @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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 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 aKey = new Key("a", false); + 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 cKey = new Key("c", false); + 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 xKey = new Key("x", false); + 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())); + } + + @SuppressWarnings("rawtypes") + @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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 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 aKey = new Key("a", false); + 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 cKey = new Key("c", false); + 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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = (ParsedStatement)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 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", false); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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(), false); + 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())); + } + + @SuppressWarnings("rawtypes") + @Test + public void testParseSimpleWithAndOr() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s" + + " AND ?s < ?b OR 'a' = 'b'"; + StatementDescriptor desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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 aKey = new Key("a", false); + 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 cKey = new Key(Key.AGENT_ID.getName(), false); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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", false); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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", false); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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", false); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + ParsedStatement statement = null; + try { + statement = (ParsedStatement)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 rejectLimitWhichIsNotInt() { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT illegal"; + StatementDescriptor desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.parse(); + } catch (DescriptorParsingException e) { + // pass + assertTrue(e.getMessage().contains("Expected string value. Got term ->a<-")); + } + } + + @Test + public void rejectIllegalFreeParamType() throws DescriptorParsingException { + // ? should be one of '?i', '?s', '?b', '?s[' + String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE ? < 1"; + StatementDescriptor desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.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 desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.parse(); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectSimpleQueryWithInvalidComparison() throws DescriptorParsingException { + // <> is illegal + String descrString = "QUERY " + AgentInfoDAO.CATEGORY + " WHERE " + "'a' <> 'b'"; + StatementDescriptor desc = getDescriptor(descrString, AgentInfoDAO.CATEGORY); + parser = new StatementDescriptorParser(storage, desc); + try { + parser.parse(); + } catch (DescriptorParsingException e) { + // pass + } + } + + @Test + public void rejectInvalidDescriptorStringBadWhere() throws DescriptorParsingException { + String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " where '" + Key.AGENT_ID.getName() + "'= ?s"; + StatementDescriptor desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + 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 desc = getDescriptor(descString, AgentInfoDAO.CATEGORY); + 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")); + } + } + + private StatementDescriptor getDescriptor(final String desc, final Category category) { + return new StatementDescriptor() { + + @Override + public String getQueryDescriptor() { + return desc; + } + + @Override + public Category getCategory() { + return category; + } + }; + } +} diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressions.java Fri Jun 28 16:46:25 2013 +0200 @@ -0,0 +1,130 @@ +/* + * 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 + * . + * + * 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.Objects; + +import com.redhat.thermostat.storage.internal.statement.BinaryExpressionNode; +import com.redhat.thermostat.storage.internal.statement.Node; +import com.redhat.thermostat.storage.internal.statement.NotBooleanExpressionNode; +import com.redhat.thermostat.storage.internal.statement.TerminalNode; +import com.redhat.thermostat.storage.internal.statement.WhereExpression; + +/** + * Helper class for comparing where expression trees. + * + * @see StatementDescriptorParserTest + */ +public class WhereExpressions { + + /** + * Compares two where expression parse trees. + * + * @param a + * @param b + * @return true, if all nodes/values were equal. false otherwise. + */ + public static boolean equals(WhereExpression a, WhereExpression b) { + Node aRoot = a.getRoot(); + Node bRoot = b.getRoot(); + return recEquals(aRoot, bRoot); + } + + private static boolean recEquals(Node a, Node b) { + boolean retval = Objects.equals(a, b); + if (!retval) { + return false; + } + Node[] nextNodesA = getNextNodes(a); + Node[] nextNodesB = getNextNodes(b); + if (nextNodesA != null && nextNodesB != null) { + retval = retval && (nextNodesA.length == nextNodesB.length); + if (!retval) { + return false; + } + // verify nodes at this level are equal + for (int i = 0; i < nextNodesA.length; i++) { + retval = retval && Objects.equals(nextNodesA[i], nextNodesB[i]); + if (!retval) { + return false; + } + } + // recursively check child nodes + for (int i = 0; i < nextNodesA.length; i++) { + retval = retval && recEquals(nextNodesA[i], nextNodesB[i]); + if (!retval) { + return false; + } + } + return retval; + } + if (nextNodesA != null || nextNodesB != null) { + return false; + } + // all pass + return true; + } + + private static Node[] getNextNodes(Node node) { + if (node instanceof TerminalNode) { + return null; + } else if (node instanceof BinaryExpressionNode) { + List nodes = new ArrayList<>(2); + BinaryExpressionNode binNode = (BinaryExpressionNode)node; + if (binNode.getLeftChild() != null) { + nodes.add(binNode.getLeftChild()); + } + if (binNode.getRightChild() != null) { + nodes.add(binNode.getRightChild()); + } + if (nodes.size() == 0) { + return null; + } + return nodes.toArray(new Node[0]); + } else if (node instanceof NotBooleanExpressionNode || node instanceof Node) { + Node next = (Node)node.getValue(); + if (next == null) { + return null; + } + return new Node[] { next }; + } else { + return null; + } + } +} diff -r 4ba43dcff78e -r fe3f66cc83e7 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressionsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/WhereExpressionsTest.java Fri Jun 28 16:46:25 2013 +0200 @@ -0,0 +1,283 @@ +/* + * 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 + * . + * + * 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.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.redhat.thermostat.storage.internal.statement.BinaryExpressionNode; +import com.redhat.thermostat.storage.internal.statement.NotBooleanExpressionNode; +import com.redhat.thermostat.storage.internal.statement.TerminalNode; +import com.redhat.thermostat.storage.internal.statement.WhereExpression; + +public class WhereExpressionsTest { + + @Test + public void testEqualsSelf() { + WhereExpression expn1 = new WhereExpression(); + assertTrue(WhereExpressions.equals(expn1, expn1)); + } + + @Test + public void testEqualsEmpty() { + WhereExpression expn1 = new WhereExpression(); + WhereExpression expn2 = new WhereExpression(); + assertTrue(WhereExpressions.equals(expn1, expn2)); + } + + @Test + public void testEqualsSimpleTerminal() { + WhereExpression expn1 = new WhereExpression(); + WhereExpression expn2 = new WhereExpression(); + TerminalNode node = new TerminalNode(expn1.getRoot()); + node.setValue("testing"); + TerminalNode node2 = new TerminalNode(expn2.getRoot()); + node2.setValue("testing"); + expn1.getRoot().setValue(node); + expn2.getRoot().setValue(node2); + assertTrue(WhereExpressions.equals(expn1, expn2)); + } + + @Test + public void testEqualsSimpleTerminalWithDifferentValues() { + WhereExpression expn1 = new WhereExpression(); + WhereExpression expn2 = new WhereExpression(); + TerminalNode node = new TerminalNode(expn1.getRoot()); + node.setValue("test"); + TerminalNode node2 = new TerminalNode(expn2.getRoot()); + node2.setValue("other"); + expn1.getRoot().setValue(node); + expn2.getRoot().setValue(node2); + assertFalse(WhereExpressions.equals(expn1, expn2)); + } + + @Test + public void testEqualsComplexish() { + WhereExpression expn1 = new WhereExpression(); + BinaryExpressionNode binNode1 = new BinaryExpressionNode(expn1.getRoot()); + expn1.getRoot().setValue(binNode1); + binNode1.setOperator("OR"); + NotBooleanExpressionNode notNode1 = new NotBooleanExpressionNode(binNode1); + TerminalNode termNode1 = new TerminalNode(notNode1); + termNode1.setValue("testing"); + notNode1.setValue(termNode1); + binNode1.setLeftChild(notNode1); + BinaryExpressionNode and1 = new BinaryExpressionNode(binNode1); + binNode1.setRightChild(and1); + and1.setOperator("AND"); + BinaryExpressionNode left1 = new BinaryExpressionNode(and1); + left1.setOperator("="); + TerminalNode termNode1Equal = new TerminalNode(left1); + termNode1Equal.setValue("a"); + TerminalNode termNode1EqualOther = new TerminalNode(left1); + termNode1EqualOther.setValue("b"); + left1.setLeftChild(termNode1Equal); + left1.setRightChild(termNode1EqualOther); + and1.setLeftChild(left1); + BinaryExpressionNode right1 = new BinaryExpressionNode(and1); + right1.setOperator("<="); + TerminalNode termNode1LessEqual = new TerminalNode(right1); + termNode1LessEqual.setValue("x"); + TerminalNode termNode1LessEqualOther = new TerminalNode(right1); + termNode1LessEqualOther.setValue("y"); + right1.setLeftChild(termNode1LessEqual); + right1.setRightChild(termNode1LessEqualOther); + and1.setRightChild(right1); + + // now build the second and equal where expn + WhereExpression expn2 = new WhereExpression(); + BinaryExpressionNode binNode2 = new BinaryExpressionNode(expn2.getRoot()); + expn2.getRoot().setValue(binNode2); + binNode2.setOperator("OR"); + NotBooleanExpressionNode notNode2 = new NotBooleanExpressionNode(binNode2); + TerminalNode termNode2 = new TerminalNode(notNode2); + termNode2.setValue("testing"); + notNode2.setValue(termNode2); + binNode2.setLeftChild(notNode2); + BinaryExpressionNode and2 = new BinaryExpressionNode(binNode2); + binNode2.setRightChild(and2); + and2.setOperator("AND"); + BinaryExpressionNode left2 = new BinaryExpressionNode(and2); + left2.setOperator("="); + TerminalNode termNode2Equal = new TerminalNode(left2); + termNode2Equal.setValue("a"); + TerminalNode termNode2EqualOther = new TerminalNode(left2); + termNode2EqualOther.setValue("b"); + left2.setLeftChild(termNode2Equal); + left2.setRightChild(termNode2EqualOther); + and2.setLeftChild(left2); + BinaryExpressionNode right2 = new BinaryExpressionNode(and2); + right2.setOperator("<="); + TerminalNode termNode2LessEqual = new TerminalNode(right2); + termNode2LessEqual.setValue("x"); + TerminalNode termNode2LessEqualOther = new TerminalNode(right2); + termNode2LessEqualOther.setValue("y"); + right2.setLeftChild(termNode2LessEqual); + right2.setRightChild(termNode2LessEqualOther); + and2.setRightChild(right2); + assertTrue(WhereExpressions.equals(expn1, expn2)); + } + + @Test + public void testNotEqualsComplexish() { + WhereExpression expn1 = new WhereExpression(); + BinaryExpressionNode binNode1 = new BinaryExpressionNode(expn1.getRoot()); + expn1.getRoot().setValue(binNode1); + binNode1.setOperator("OR"); + NotBooleanExpressionNode notNode1 = new NotBooleanExpressionNode(binNode1); + TerminalNode termNode1 = new TerminalNode(notNode1); + termNode1.setValue("testing"); + notNode1.setValue(termNode1); + binNode1.setLeftChild(notNode1); + BinaryExpressionNode and1 = new BinaryExpressionNode(binNode1); + binNode1.setRightChild(and1); + and1.setOperator("AND"); + BinaryExpressionNode left1 = new BinaryExpressionNode(and1); + left1.setOperator("="); + TerminalNode termNode1Equal = new TerminalNode(left1); + termNode1Equal.setValue("d"); // should be "a" to make it equal + TerminalNode termNode1EqualOther = new TerminalNode(left1); + termNode1EqualOther.setValue("b"); + left1.setLeftChild(termNode1Equal); + left1.setRightChild(termNode1EqualOther); + and1.setLeftChild(left1); + BinaryExpressionNode right1 = new BinaryExpressionNode(and1); + right1.setOperator("<="); + TerminalNode termNode1LessEqual = new TerminalNode(right1); + termNode1LessEqual.setValue("x"); + TerminalNode termNode1LessEqualOther = new TerminalNode(right1); + termNode1LessEqualOther.setValue("y"); + right1.setLeftChild(termNode1LessEqual); + right1.setRightChild(termNode1LessEqualOther); + and1.setRightChild(right1); + + // now build the second and equal where expn + WhereExpression expn2 = new WhereExpression(); + BinaryExpressionNode binNode2 = new BinaryExpressionNode(expn2.getRoot()); + expn2.getRoot().setValue(binNode2); + binNode2.setOperator("OR"); + NotBooleanExpressionNode notNode2 = new NotBooleanExpressionNode(binNode2); + TerminalNode termNode2 = new TerminalNode(notNode2); + termNode2.setValue("testing"); + notNode2.setValue(termNode2); + binNode2.setLeftChild(notNode2); + BinaryExpressionNode and2 = new BinaryExpressionNode(binNode2); + binNode2.setRightChild(and2); + and2.setOperator("AND"); + BinaryExpressionNode left2 = new BinaryExpressionNode(and2); + left2.setOperator("="); + TerminalNode termNode2Equal = new TerminalNode(left2); + termNode2Equal.setValue("a"); + TerminalNode termNode2EqualOther = new TerminalNode(left2); + termNode2EqualOther.setValue("b"); + left2.setLeftChild(termNode2Equal); + left2.setRightChild(termNode2EqualOther); + and2.setLeftChild(left2); + BinaryExpressionNode right2 = new BinaryExpressionNode(and2); + right2.setOperator("<="); + TerminalNode termNode2LessEqual = new TerminalNode(right2); + termNode2LessEqual.setValue("x"); + TerminalNode termNode2LessEqualOther = new TerminalNode(right2); + termNode2LessEqualOther.setValue("y"); + right2.setLeftChild(termNode2LessEqual); + right2.setRightChild(termNode2LessEqualOther); + and2.setRightChild(right2); + assertFalse(WhereExpressions.equals(expn1, expn2)); + } + + @Test + public void testNotEqualsNotSameTreeAtAll() { + WhereExpression expn1 = new WhereExpression(); + BinaryExpressionNode binNode1 = new BinaryExpressionNode(expn1.getRoot()); + expn1.getRoot().setValue(binNode1); + binNode1.setOperator("OR"); + NotBooleanExpressionNode notNode1 = new NotBooleanExpressionNode(binNode1); + TerminalNode termNode1 = new TerminalNode(notNode1); + termNode1.setValue("testing"); + notNode1.setValue(termNode1); + binNode1.setLeftChild(notNode1); + BinaryExpressionNode and1 = new BinaryExpressionNode(binNode1); + binNode1.setRightChild(and1); + and1.setOperator("AND"); + BinaryExpressionNode left1 = new BinaryExpressionNode(and1); + left1.setOperator("="); + TerminalNode termNode1Equal = new TerminalNode(left1); + termNode1Equal.setValue("d"); // should be "a" to make it equal + TerminalNode termNode1EqualOther = new TerminalNode(left1); + termNode1EqualOther.setValue("b"); + left1.setLeftChild(termNode1Equal); + left1.setRightChild(termNode1EqualOther); + and1.setLeftChild(left1); + BinaryExpressionNode right1 = new BinaryExpressionNode(and1); + right1.setOperator("<="); + TerminalNode termNode1LessEqual = new TerminalNode(right1); + termNode1LessEqual.setValue("x"); + TerminalNode termNode1LessEqualOther = new TerminalNode(right1); + termNode1LessEqualOther.setValue("y"); + right1.setLeftChild(termNode1LessEqual); + right1.setRightChild(termNode1LessEqualOther); + and1.setRightChild(right1); + + // now build the second fairly different tree + WhereExpression expn2 = new WhereExpression(); + NotBooleanExpressionNode notNode2 = new NotBooleanExpressionNode(expn2.getRoot()); + BinaryExpressionNode and2 = new BinaryExpressionNode(notNode2); + notNode2.setValue(and2); + and2.setOperator("AND"); + BinaryExpressionNode left2 = new BinaryExpressionNode(and2); + left2.setOperator("="); + TerminalNode termNode2Equal = new TerminalNode(left2); + termNode2Equal.setValue("a"); + TerminalNode termNode2EqualOther = new TerminalNode(left2); + termNode2EqualOther.setValue("b"); + left2.setLeftChild(termNode2Equal); + left2.setRightChild(termNode2EqualOther); + and2.setLeftChild(left2); + BinaryExpressionNode right2 = new BinaryExpressionNode(and2); + right2.setOperator("<="); + TerminalNode termNode2LessEqual = new TerminalNode(right2); + termNode2LessEqual.setValue("x"); + TerminalNode termNode2LessEqualOther = new TerminalNode(right2); + termNode2LessEqualOther.setValue("y"); + right2.setLeftChild(termNode2LessEqual); + right2.setRightChild(termNode2LessEqualOther); + and2.setRightChild(right2); + assertFalse(WhereExpressions.equals(expn1, expn2)); + } +}