package net.sourceforge.mayfly.evaluation.select; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; import junitx.framework.ObjectAssert; import net.sourceforge.mayfly.datastore.Schema; import net.sourceforge.mayfly.datastore.StringCell; import net.sourceforge.mayfly.evaluation.ResultRow; import net.sourceforge.mayfly.evaluation.ResultRows; import net.sourceforge.mayfly.evaluation.Value; import net.sourceforge.mayfly.evaluation.ValueList; import net.sourceforge.mayfly.evaluation.command.Command; import net.sourceforge.mayfly.evaluation.condition.And; import net.sourceforge.mayfly.evaluation.condition.Equal; import net.sourceforge.mayfly.evaluation.condition.Greater; import net.sourceforge.mayfly.evaluation.condition.Or; import net.sourceforge.mayfly.evaluation.condition.True; import net.sourceforge.mayfly.evaluation.expression.Maximum; import net.sourceforge.mayfly.evaluation.expression.SingleColumn; import net.sourceforge.mayfly.evaluation.expression.literal.IntegerLiteral; import net.sourceforge.mayfly.evaluation.from.FromTable; import net.sourceforge.mayfly.evaluation.from.InnerJoin; import net.sourceforge.mayfly.util.ImmutableList; import net.sourceforge.mayfly.util.L; import net.sourceforge.mayfly.util.MayflyAssert; public class SelectTest { @Test public void testExecuteSimpleJoin() throws Exception { ImmutableList fooColumns = new L().append("colA").append("colB").asImmutable(); ImmutableList barColumns = new L().append("colX").append("colY").asImmutable(); Evaluator evaluator = new StoreEvaluator( new Schema() .createTable("foo", fooColumns) .addRow("foo", fooColumns, makeValues("1a", "1b")) .addRow("foo", fooColumns, makeValues("2a", "2b")) .createTable("bar", barColumns) .addRow("bar", barColumns, makeValues("1a", "1b")) .addRow("bar", barColumns, makeValues("2a", "2b")) .addRow("bar", barColumns, makeValues("3a", "3b")) ); ResultRows rows = query(evaluator, "select * from foo, bar"); assertEquals(6, rows.rowCount()); assertRow("foo", "colA", "1a", "foo", "colB", "1b", "bar", "colX", "1a", "bar", "colY", "1b", rows.row(0)); assertRow("foo", "colA", "1a", "foo", "colB", "1b", "bar", "colX", "2a", "bar", "colY", "2b", rows.row(1)); assertRow("foo", "colA", "1a", "foo", "colB", "1b", "bar", "colX", "3a", "bar", "colY", "3b", rows.row(2)); assertRow("foo", "colA", "2a", "foo", "colB", "2b", "bar", "colX", "1a", "bar", "colY", "1b", rows.row(3)); assertRow("foo", "colA", "2a", "foo", "colB", "2b", "bar", "colX", "2a", "bar", "colY", "2b", rows.row(4)); assertRow("foo", "colA", "2a", "foo", "colB", "2b", "bar", "colX", "3a", "bar", "colY", "3b", rows.row(5)); } private void assertRow(String alias1, String column1, String value1, String alias2, String column2, String value2, String alias3, String column3, String value3, String alias4, String column4, String value4, ResultRow row) { assertEquals(4, row.size()); assertRowElement(alias1, column1, value1, row.element(0)); assertRowElement(alias2, column2, value2, row.element(1)); assertRowElement(alias3, column3, value3, row.element(2)); assertRowElement(alias4, column4, value4, row.element(3)); } private void assertRow(String alias1, String column1, String value1, String alias2, String column2, String value2, ResultRow row) { assertEquals(2, row.size()); assertRowElement(alias1, column1, value1, row.element(0)); assertRowElement(alias2, column2, value2, row.element(1)); } private void assertRowElement(String alias, String column, String value, ResultRow.Element element) { assertEquals(alias, element.column().tableOrAlias()); assertEquals(column, element.column().columnName()); assertEquals(value, element.value.asString()); } private ResultRows query(Evaluator evaluator, String sql) { Select select = (Select) Command.fromSql(sql); OptimizedSelect optimized = select.plan(evaluator); return optimized.query(); } @Test public void testSmallerJoin() throws Exception { Evaluator evaluator = new StoreEvaluator( new Schema() .createTable("foo", "colA") .addRow("foo", new ImmutableList().with("colA"), ValueList.singleton(new StringCell("1a"))) .createTable("bar", "colX") .addRow("bar", new ImmutableList().with("colX"), ValueList.singleton(new StringCell("barXValue"))) ); ResultRows rows = query(evaluator, "select * from foo, bar"); assertRow("foo", "colA", "1a", "bar", "colX", "barXValue", rows.singleRow()); } @Test public void testSimpleWhere() throws Exception { ImmutableList columnNames = new ImmutableList().with("colA").with("colB"); Evaluator evaluator = new StoreEvaluator( new Schema() .createTable("foo", columnNames) .addRow("foo", columnNames, makeValues("1a", "1b")) .addRow("foo", columnNames, makeValues("2a", "xx")) .addRow("foo", columnNames, makeValues("3a", "xx")) ); ResultRows rows = query(evaluator, "select * from foo where colB = 'xx'"); assertEquals(2, rows.rowCount()); assertRow("foo", "colA", "2a", "foo", "colB", "xx", rows.row(0)); assertRow("foo", "colA", "3a", "foo", "colB", "xx", rows.row(1)); } private ValueList makeValues(String firstStringValue, String secondStringValue) { return ValueList .singleton(new StringCell(firstStringValue)) .with(new Value(new StringCell(secondStringValue))); } @Test public void testMakeJoinsExplicit() throws Exception { Select select = (Select) Select.fromSql("select * from foo, bar"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo") .createTable("bar") )); InnerJoin join = (InnerJoin) planned.from; assertEquals("foo", ((FromTable) join.left).tableName); assertEquals("bar", ((FromTable) join.right).tableName); } @Test public void testLeftAssociative() throws Exception { // At least for now, we don't try to pick the optimal order of // joins; we just take the listed order in a left-associative way. Select select = (Select) Select.fromSql("select * from foo, bar, baz"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo") .createTable("bar") .createTable("baz") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; assertEquals("foo", ((FromTable) firstJoin.left).tableName); assertEquals("bar", ((FromTable) firstJoin.right).tableName); assertEquals("baz", ((FromTable) join.right).tableName); } @Test public void testTransformWhereToOn() throws Exception { Select select = (Select) Select.fromSql( "select * from foo, bar, baz where foo.id = bar.id"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; Equal on = (Equal) firstJoin.condition; MayflyAssert.assertColumn("foo", "id", on.leftSide); MayflyAssert.assertColumn("bar", "id", on.rightSide); ObjectAssert.assertInstanceOf(True.class, planned.where); } @Ignore @Test public void testAlsoWillTransformWhereToOnForExplicitJoin() throws Exception { Select select = (Select) Select.fromSql( "select * from (foo cross join bar) cross join baz where foo.id = bar.id"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; Equal on = (Equal) firstJoin.condition; MayflyAssert.assertColumn("foo", "id", on.leftSide); MayflyAssert.assertColumn("bar", "id", on.rightSide); ObjectAssert.assertInstanceOf(True.class, planned.where); } @Test public void testCanMove() throws Exception { assertTrue(Planner.canMove( new Equal( new SingleColumn("foo", "a"), new SingleColumn("b")), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testCannotMoveNoColumn() throws Exception { assertFalse(Planner.canMove( new Equal( new SingleColumn("foo", "a"), new SingleColumn("c")), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testCannotMoveNoTable() throws Exception { assertFalse(Planner.canMove( new Equal( new SingleColumn("foo", "a"), new SingleColumn("baz", "c")), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testCheckComplexExpressionUnmovable() throws Exception { assertFalse(Planner.canMove( new Or( new Equal(new IntegerLiteral(5), new IntegerLiteral(5)), new Equal( new SingleColumn("foo", "a"), new SingleColumn("baz", "c")) ), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testCheckComplexExpressionMovable() throws Exception { assertTrue(Planner.canMove( new Or( new Equal(new IntegerLiteral(5), new IntegerLiteral(5)), new Equal( new SingleColumn("foo", "a"), new SingleColumn("bar", "b")) ), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testCannotMoveAggregate() throws Exception { // The problem is that we don't yet have fully functional // machinery for taking max(a) and knowing that "a" is // foo.a and not some other a. assertFalse(Planner.canMove( new Equal( new Maximum(new SingleColumn("a"), "max", false), new IntegerLiteral(5)), new FromTable("foo"), new FromTable("bar"), new StoreEvaluator(new Schema() .createTable("foo", "a") .createTable("bar", "b")) )); } @Test public void testFullDummyRow() throws Exception { Select select = (Select) Select.fromSql( "select * from foo, bar, baz"); ResultRow dummyRow = select.planner().dummyRow( 0, new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); assertEquals(3, dummyRow.size()); MayflyAssert.assertColumn("foo", "id", dummyRow.expression(0)); } @Test public void testMoveLeftSideOfAnd() throws Exception { Select select = (Select) Select.fromSql( "select * from foo, bar, baz " + "where foo.id = bar.id and (bar.id = 5 or baz.id = 7)"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; Equal on = (Equal) firstJoin.condition; MayflyAssert.assertColumn("foo", "id", on.leftSide); MayflyAssert.assertColumn("bar", "id", on.rightSide); Or movedToLastJoin = (Or) join.condition; Equal barEquals5 = (Equal) movedToLastJoin.leftSide; MayflyAssert.assertColumn("bar", "id", barEquals5.leftSide); Equal bazEquals7 = (Equal) movedToLastJoin.rightSide; MayflyAssert.assertColumn("baz", "id", bazEquals7.leftSide); ObjectAssert.assertInstanceOf(True.class, planned.where); } @Test public void testMoveRightSideOfAnd() throws Exception { Select select = (Select) Select.fromSql( "select * from foo, bar, baz " + "where foo.id = baz.id and (bar.id = 5 or foo.id = 7)"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; Or on = (Or) firstJoin.condition; MayflyAssert.assertColumn("bar", "id", ((Equal)on.leftSide).leftSide); MayflyAssert.assertColumn("foo", "id", ((Equal)on.rightSide).leftSide); Equal movedToLastJoin = (Equal) join.condition; MayflyAssert.assertColumn("foo", "id", movedToLastJoin.leftSide); MayflyAssert.assertColumn("baz", "id", movedToLastJoin.rightSide); ObjectAssert.assertInstanceOf(True.class, planned.where); } @Test public void testMoveAlmostEverything() throws Exception { Select select = (Select) Select.fromSql( "select * from foo, bar, baz " + "where foo.id = bar.id and bar.id > 5 and baz.id = 9 and foo.id > 7"); OptimizedSelect planned = select.plan( new StoreEvaluator(new Schema() .createTable("foo", "id") .createTable("bar", "id") .createTable("baz", "id") )); InnerJoin join = (InnerJoin) planned.from; InnerJoin firstJoin = (InnerJoin) join.left; And on = (And) firstJoin.condition; MayflyAssert.assertColumn("foo", "id", ((Greater)on.leftSide).leftSide); And secondLevel = (And) on.rightSide; MayflyAssert.assertColumn("foo", "id", ((Equal)secondLevel.leftSide).leftSide); MayflyAssert.assertColumn("bar", "id", ((Greater)secondLevel.rightSide).leftSide); Equal movedToLastJoin = (Equal) join.condition; MayflyAssert.assertColumn("baz", "id", movedToLastJoin.leftSide); ObjectAssert.assertInstanceOf(True.class, planned.where); } }