/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.test.db; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; import org.h2.test.TestBase; import org.h2.util.StatementBuilder; /** * Test for optimizer hint SET FORCE_JOIN_ORDER. * * @author Sergi Vladykin */ public class TestOptimizerHints extends TestBase { /** * Run just this test. * * @param a ignored */ public static void main(String[] a) throws Exception { TestBase.createCaller().init().test(); } @Override public void test() throws Exception { deleteDb("testOptimizerHints"); Connection conn = getConnection("testOptimizerHints;FORCE_JOIN_ORDER=1"); Statement s = conn.createStatement(); s.execute("create table t1(id int unique)"); s.execute("create table t2(id int unique, t1_id int)"); s.execute("create table t3(id int unique)"); s.execute("create table t4(id int unique, t2_id int, t3_id int)"); String plan; plan = plan(s, "select * from t1, t2 where t1.id = t2.t1_id"); assertContains(plan, "INNER JOIN PUBLIC.T2"); plan = plan(s, "select * from t2, t1 where t1.id = t2.t1_id"); assertContains(plan, "INNER JOIN PUBLIC.T1"); plan = plan(s, "select * from t2, t1 where t1.id = 1"); assertContains(plan, "INNER JOIN PUBLIC.T1"); plan = plan(s, "select * from t2, t1 where t1.id = t2.t1_id and t2.id = 1"); assertContains(plan, "INNER JOIN PUBLIC.T1"); plan = plan(s, "select * from t1, t2 where t1.id = t2.t1_id and t2.id = 1"); assertContains(plan, "INNER JOIN PUBLIC.T2"); checkPlanComma(s, "t1", "t2", "t3", "t4"); checkPlanComma(s, "t4", "t2", "t3", "t1"); checkPlanComma(s, "t2", "t1", "t3", "t4"); checkPlanComma(s, "t1", "t4", "t3", "t2"); checkPlanComma(s, "t2", "t1", "t4", "t3"); checkPlanComma(s, "t4", "t3", "t2", "t1"); boolean on = false; boolean left = false; checkPlanJoin(s, on, left, "t1", "t2", "t3", "t4"); checkPlanJoin(s, on, left, "t4", "t2", "t3", "t1"); checkPlanJoin(s, on, left, "t2", "t1", "t3", "t4"); checkPlanJoin(s, on, left, "t1", "t4", "t3", "t2"); checkPlanJoin(s, on, left, "t2", "t1", "t4", "t3"); checkPlanJoin(s, on, left, "t4", "t3", "t2", "t1"); on = false; left = true; checkPlanJoin(s, on, left, "t1", "t2", "t3", "t4"); checkPlanJoin(s, on, left, "t4", "t2", "t3", "t1"); checkPlanJoin(s, on, left, "t2", "t1", "t3", "t4"); checkPlanJoin(s, on, left, "t1", "t4", "t3", "t2"); checkPlanJoin(s, on, left, "t2", "t1", "t4", "t3"); checkPlanJoin(s, on, left, "t4", "t3", "t2", "t1"); on = true; left = false; checkPlanJoin(s, on, left, "t1", "t2", "t3", "t4"); checkPlanJoin(s, on, left, "t4", "t2", "t3", "t1"); checkPlanJoin(s, on, left, "t2", "t1", "t3", "t4"); checkPlanJoin(s, on, left, "t1", "t4", "t3", "t2"); checkPlanJoin(s, on, left, "t2", "t1", "t4", "t3"); checkPlanJoin(s, on, left, "t4", "t3", "t2", "t1"); on = true; left = true; checkPlanJoin(s, on, left, "t1", "t2", "t3", "t4"); checkPlanJoin(s, on, left, "t4", "t2", "t3", "t1"); checkPlanJoin(s, on, left, "t2", "t1", "t3", "t4"); checkPlanJoin(s, on, left, "t1", "t4", "t3", "t2"); checkPlanJoin(s, on, left, "t2", "t1", "t4", "t3"); checkPlanJoin(s, on, left, "t4", "t3", "t2", "t1"); s.close(); conn.close(); deleteDb("testOptimizerHints"); } private void checkPlanComma(Statement s, String ... t) throws SQLException { StatementBuilder from = new StatementBuilder(); for (String table : t) { from.appendExceptFirst(", "); from.append(table); } String plan = plan(s, "select 1 from " + from.toString() + " where t1.id = t2.t1_id " + "and t2.id = t4.t2_id and t3.id = t4.t3_id"); int prev = plan.indexOf("FROM PUBLIC." + t[0].toUpperCase()); for (int i = 1; i < t.length; i++) { int next = plan.indexOf("INNER JOIN PUBLIC." + t[i].toUpperCase()); assertTrue("Wrong plan for : " + Arrays.toString(t) + "\n" + plan, next > prev); prev = next; } } private void checkPlanJoin(Statement s, boolean on, boolean left, String... t) throws SQLException { StatementBuilder from = new StatementBuilder(); for (int i = 0; i < t.length; i++) { if (i != 0) { if (left) { from.append(" left join "); } else { from.append(" inner join "); } } from.append(t[i]); if (on && i != 0) { from.append(" on 1=1 "); } } String plan = plan(s, "select 1 from " + from.toString() + " where t1.id = t2.t1_id " + "and t2.id = t4.t2_id and t3.id = t4.t3_id"); int prev = plan.indexOf("FROM PUBLIC." + t[0].toUpperCase()); for (int i = 1; i < t.length; i++) { int next = plan.indexOf( (!left ? "INNER JOIN PUBLIC." : on ? "LEFT OUTER JOIN PUBLIC." : "PUBLIC.") + t[i].toUpperCase()); if (prev > next) { System.err.println(plan); fail("Wrong plan for : " + Arrays.toString(t) + "\n" + plan); } prev = next; } } /** * @param s Statement. * @param query Query. * @return Plan. * @throws SQLException If failed. */ private String plan(Statement s, String query) throws SQLException { ResultSet rs = s.executeQuery("explain " + query); assertTrue(rs.next()); String plan = rs.getString(1); rs.close(); return plan; } }