blob: 7ddffc6c9ad006c47ada160243647ed2ad878382 [file]
/*
* Class org.apache.derbyTesting.functionTests.tests.lang.DynamicLikeOptimizationTest
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.derbyTesting.functionTests.tests.lang;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.RuntimeStatisticsParser;
import org.apache.derbyTesting.junit.SQLUtilities;
import org.apache.derbyTesting.junit.TestConfiguration;
/**
* Test the dynamic like optimization.
*
* <p><b>NOTE:</b> the metadata test does a bunch of likes with parameters.
*/
public class DynamicLikeOptimizationTest extends BaseJDBCTestCase {
/** All rows in the cei table. */
private static final Object[][] CEI_ROWS = {
{ 0, "Alarms", "AlarmDisk999" },
{ 1, "Alarms", "AlarmFS-usr" },
{ 2, "Alarms", "AlarmPower" },
{ 3, "Alert", "AlertBattery" },
{ 4, "Alert", "AlertUPS" },
{ 5, "Warning", "WarnIntrusion" },
{ 6, "Warning", "WarnUnlockDoor" },
{ 7, "Warning", "Warn%Unlock%Door" },
{ 8, "Warning", "W_Unlock_Door" },
};
public DynamicLikeOptimizationTest(String name) {
super(name);
}
public static Test suite() {
BaseTestSuite tests =
new BaseTestSuite("DynamicLikeOptimizationTest");
tests.addTestSuite(DynamicLikeOptimizationTest.class);
tests.addTest(TestConfiguration.clientServerDecorator(
new BaseTestSuite(DynamicLikeOptimizationTest.class)));
return new CleanDatabaseTestSetup(tests) {
protected void decorateSQL(Statement stmt) throws SQLException {
stmt.executeUpdate("create table t1(c11 int)");
stmt.executeUpdate("insert into t1 values 1");
stmt.executeUpdate("create table test(id char(10), " +
"c10 char(10), vc10 varchar(10))");
PreparedStatement insert = getConnection().prepareStatement(
"insert into test values (?,?,?)");
String[] values = {
"asdf", "asdg", "aasdf", "%foobar", "foo%bar", "foo_bar"
};
for (int i = 0; i < values.length; i++) {
for (int j = 1; j <= 3; j++) {
insert.setString(j, values[i]);
}
insert.executeUpdate();
}
insert.setString(1, "V-NULL");
insert.setString(2, null);
insert.setString(3, null);
insert.executeUpdate();
insert.setString(1, "MAX_CHAR");
insert.setString(2, "\uFA2D");
insert.setString(3, "\uFA2D");
insert.executeUpdate();
insert.close();
stmt.executeUpdate(
"create table likeable(match_me varchar(10), " +
"pattern varchar(10), esc varchar(1))");
stmt.executeUpdate(
"insert into likeable values " +
"('foo%bar', 'fooZ%bar', 'Z'), " +
"('foo%bar', '%Z%ba_', 'Z')," +
"('foo%bar', 'fooZ%baZ', 'Z')");
stmt.executeUpdate(
"create table cei(id int, name varchar(192) not null, " +
"source varchar(252) not null)");
PreparedStatement cei = getConnection().prepareStatement(
"insert into cei values (?,?,?)");
for (int i = 0; i < CEI_ROWS.length; i++) {
for (int j = 0; j < CEI_ROWS[i].length; j++) {
cei.setObject(j+1, CEI_ROWS[i][j]);
}
cei.executeUpdate();
}
cei.close();
}
};
}
protected void setUp() throws SQLException {
getConnection().setAutoCommit(false);
}
public void testSimpleLikePredicates() throws SQLException {
PreparedStatement ps =
prepareStatement("select 1 from t1 where 'asdf' like ?");
// queries that expect one row
String[] one = { "%", "%f", "asd%", "_%", "%_", "%asdf" };
for (int i = 0; i < one.length; i++) {
ps.setString(1, one[i]);
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
}
// queries that expect empty result set
String[] empty = { "", "%g", "_asdf", null };
for (int i = 0; i < empty.length; i++) {
ps.setObject(1, empty[i], Types.VARCHAR);
JDBC.assertEmpty(ps.executeQuery());
}
ps.close();
}
public void testEscapeSyntax() throws SQLException {
PreparedStatement ps =
prepareStatement("select 1 from t1 where '%foobar' " +
"like 'Z%foobar' escape ?");
// match: optimize to LIKE and ==
ps.setString(1, "Z");
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
// invalid escape strings
String[][] invalid = {
{ "raZ", "22019" },
{ "", "22019" },
{ null, "22501" },
};
for (int i = 0; i < invalid.length; i++) {
ps.setObject(1, invalid[i][0], Types.VARCHAR);
try {
ps.executeQuery();
fail();
} catch (SQLException e) {
assertSQLState(invalid[i][1], e);
}
}
// no match, wrong char
ps.setString(1, "%");
JDBC.assertEmpty(ps.executeQuery());
ps.close();
}
public void testWildcardAsEscape() throws SQLException {
Statement s = createStatement();
JDBC.assertSingleValueResultSet(
s.executeQuery(
"select 1 from t1 where '%foobar' like '%%foobar' escape '%'"),
"1");
JDBC.assertSingleValueResultSet(
s.executeQuery(
"select 1 from t1 where '_foobar' like '__foobar' escape '_'"),
"1");
s.close();
}
public void testEscapeSyntax2() throws SQLException {
PreparedStatement ps = prepareStatement(
"select 1 from t1 where '%foobar' like ? escape ?");
ps.setString(1, "Z%foobar");
ps.setString(2, "Z");
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
ps.setString(2, "");
try {
ps.executeQuery();
fail();
} catch (SQLException e) {
assertSQLState("22019", e);
}
ps.close();
}
public void testEscapeSyntax3() throws SQLException {
PreparedStatement ps = prepareStatement(
"select 1 from t1 where '%foobar' like ? escape 'Z'");
ps.setString(1, "x%foobar");
JDBC.assertEmpty(ps.executeQuery());
ps.setString(1, "Z%foobar");
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
ps.close();
}
public void testEscapeSyntax4() throws SQLException {
PreparedStatement ps = prepareStatement(
"select 1 from t1 where '%foobar' like ? escape '$'");
ps.setString(1, "$%f%bar");
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
ps.close();
}
public void testEscapeSyntax5() throws SQLException {
PreparedStatement ps = prepareStatement(
"select 1 from t1 where 'Z%foobar' like ? escape 'Z'");
ps.setString(1, "ZZZ%foo%a_");
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
ps.close();
}
public void testLikeWithHighestValidCharacter() throws SQLException {
// \uFA2D - the highest valid character according to
// Character.isDefined() of JDK 1.4;
PreparedStatement ps =
prepareStatement("select 1 from t1 where '\uFA2D' like ?");
String[] match = { "%", "_", "\uFA2D" };
for (int i = 0; i < match.length; i++) {
ps.setString(1, match[i]);
JDBC.assertSingleValueResultSet(ps.executeQuery(), "1");
}
ps.setString(1, "");
JDBC.assertEmpty(ps.executeQuery());
ps.close();
}
public void testGeneratedPredicatesCHAR() throws SQLException {
PreparedStatement ps =
prepareStatement("select id from test where c10 like ?");
String[][][] expected = {
/* null */ { },
/* 1 */ { },
/* "" */ { },
/* % */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* %f */ { },
/* %g */ { },
/* asd% */ { {"asdf"},{"asdg"} },
/* _% */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* %_ */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* _asdf */ { },
/* _asdf % */ { {"aasdf"} },
/* %asdf */ { },
};
testGeneratedPredicates(ps, expected);
}
public void testGeneratedPredicatesVARCHAR() throws SQLException {
PreparedStatement ps =
prepareStatement("select id from test where vc10 like ?");
String[][][] expected = {
/* null */ { },
/* 1 */ { },
/* "" */ { },
/* % */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* %f */ { {"asdf"}, {"aasdf"} },
/* %g */ { {"asdg"} },
/* asd% */ { {"asdf"},{"asdg"} },
/* _% */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* %_ */ { {"MAX_CHAR"}, {"asdf"}, {"asdg"}, {"aasdf"},
{"%foobar"}, {"foo%bar"}, {"foo_bar"} },
/* _asdf */ { {"aasdf"} },
/* _asdf % */ { },
/* %asdf */ { {"asdf"}, {"aasdf"} },
};
testGeneratedPredicates(ps, expected);
}
/**
* Helper method for <code>testGeneratedPredicates*</code>. Executes a
* prepared statement with different parameter values and compares result
* to an array of expected rows.
*
* @param ps the prepared statement to execute
* @param rows array of expected rows to be returned for the different
* executions
*/
private void testGeneratedPredicates(PreparedStatement ps,
String[][][] rows)
throws SQLException {
Object[] args = {
null, 1, "", "%", "%f", "%g", "asd%", "_%", "%_",
"_asdf", "_asdf %", "%asdf"
};
assertEquals(args.length, rows.length);
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
ps.setString(1, null);
} else {
ps.setObject(1, args[i]);
}
JDBC.assertUnorderedResultSet(ps.executeQuery(), rows[i]);
}
ps.close();
}
public void testStringAndPatternAndEscapeFromTable() throws SQLException {
PreparedStatement ps =
prepareStatement("select match_me from likeable " +
"where match_me like pattern escape esc");
// In embedded, the first two should go fine, third one should fail
// because the escape character is not followed by _ or %. In
// client/server mode, executeQuery() should fail because of
// pre-fetching. (This test only works correctly if the rows are
// returned in the insert order, which happens to be the case but is
// not guaranteed.)
ResultSet rs = null;
boolean twoSuccessful = false;
try {
rs = ps.executeQuery();
assertTrue(rs.next());
assertTrue(rs.next());
twoSuccessful = true;
rs.next();
fail();
} catch (SQLException e) {
assertSQLState("22025", e);
assertTrue((usingEmbedded() && twoSuccessful) ||
(usingDerbyNetClient() && (rs == null)));
}
if (rs != null) {
rs.close();
}
PreparedStatement del = prepareStatement("delete from likeable");
assertEquals(3, del.executeUpdate());
PreparedStatement ins = prepareStatement("insert into likeable " +
"values (?, ?, ?)");
ins.setString(1, "foo%bar");
ins.setString(2, "foo%bar");
ins.setString(3, null);
ins.executeUpdate();
try {
JDBC.assertDrainResults(ps.executeQuery());
fail();
} catch (SQLException e) {
assertSQLState("22501", e);
}
assertEquals(1, del.executeUpdate());
ins.setString(3, "");
ins.executeUpdate();
try {
JDBC.assertDrainResults(ps.executeQuery());
fail();
} catch (SQLException e) {
assertSQLState("22019", e);
}
ps.close();
del.close();
ins.close();
}
/**
* Test defect 6002/6039.
*/
public void testEscapeWithBackslash() throws SQLException {
PreparedStatement ps = prepareStatement(
"select id, name, source from cei where " +
"(name LIKE ? escape '\\') and (source like ? escape '\\') " +
"order by source asc, name asc");
HashMap<String[], Object[][]> inputOutput =
new HashMap<String[], Object[][]>();
inputOutput.put(
new String[] {"%", "%"},
new Object[][] {
CEI_ROWS[0], CEI_ROWS[1], CEI_ROWS[2], CEI_ROWS[3], CEI_ROWS[4],
CEI_ROWS[8], CEI_ROWS[7], CEI_ROWS[5], CEI_ROWS[6]
});
inputOutput.put(
new String[] {"Alarms", "AlarmDisk%"},
new Object[][] { CEI_ROWS[0] });
inputOutput.put(
new String[] {"A%", "%"},
new Object[][] {
CEI_ROWS[0], CEI_ROWS[1], CEI_ROWS[2], CEI_ROWS[3], CEI_ROWS[4],
});
inputOutput.put(
new String[] {"%", "___rm%"},
new Object[][] { CEI_ROWS[0], CEI_ROWS[1], CEI_ROWS[2] });
inputOutput.put(
new String[] {"Warning", "%oor"},
new Object[][] { CEI_ROWS[8], CEI_ROWS[7], CEI_ROWS[6] });
inputOutput.put(
new String[] {"Warning", "Warn\\%Unlock\\%Door"},
new Object[][] { CEI_ROWS[7] });
inputOutput.put(
new String[] {"Warning", "%\\%Unlo%"},
new Object[][] { CEI_ROWS[7] });
inputOutput.put(
new String[] {"Warning", "W\\_Unloc%"},
new Object[][] { CEI_ROWS[8] });
inputOutput.put(
new String[] {"Warning", "_\\_Unlock\\_Door"},
new Object[][] { CEI_ROWS[8] });
inputOutput.put(
new String[] {"W%", "Warn\\%Unlock\\%Door"},
new Object[][] { CEI_ROWS[7] });
inputOutput.put(
new String[] {"%ing", "W\\_Unlock\\_%Door"},
new Object[][] { CEI_ROWS[8] });
inputOutput.put(new String[] {"Bogus", "Name"}, new Object[][] {});
for (Map.Entry<String[], Object[][]> entry : inputOutput.entrySet()) {
String[] args = entry.getKey();
Object[][] rows = entry.getValue();
ps.setObject(1, args[0]);
ps.setObject(2, args[1]);
JDBC.assertFullResultSet(ps.executeQuery(), rows, false);
}
ps.close();
}
/**
* Test that % matches tab characters (DERBY-1262).
*/
public void testTabs() throws SQLException {
Statement s = createStatement();
s.executeUpdate("insert into test values " +
"('asd\tp', 'asd\tp', 'asd\tp'), " +
"('ase\tp', 'ase\tg', 'ase\tg')");
String[][] expected = { {"asdf"}, {"asdg"}, {"asd\tp"} };
JDBC.assertUnorderedResultSet(
s.executeQuery("select c10 from test where c10 like 'asd%'"),
expected);
PreparedStatement ps =
prepareStatement("select c10 from test where c10 like ?");
ps.setString(1, "asd%");
JDBC.assertUnorderedResultSet(ps.executeQuery(), expected);
s.close();
ps.close();
}
/**
* Test that it is possible to escape an escape character that is before
* the first wildcard (% or _) in the pattern (DERBY-1386).
*/
public void testEscapedEscapeCharacterPrecedingFirstWildcard()
throws SQLException {
Statement s = createStatement();
s.executeUpdate("insert into test values " +
"('abc#def', 'abc#def', 'abc#def'), " +
"('abc\\def', 'abc\\def', 'abc\\def')");
PreparedStatement[] ps = {
prepareStatement("select id from test where c10 like ? escape ?"),
prepareStatement("select id from test where vc10 like ? escape ?")
};
String[][] inputOutput = {
{ "abc##%", "#", "abc#def", "abc#def" },
{ "abc\\\\%", "\\", "abc\\def", "abc\\def" },
{ "abc##_ef", "#", null, "abc#def" },
{ "abc\\\\_ef", "\\", null, "abc\\def" },
};
for (int i = 0; i < inputOutput.length; i++) {
for (int j = 0; j < ps.length; j++) {
ps[j].setString(1, inputOutput[i][0]);
ps[j].setString(2, inputOutput[i][1]);
ResultSet rs = ps[j].executeQuery();
String expected = inputOutput[i][2+j];
if (expected == null) {
JDBC.assertEmpty(rs);
} else {
JDBC.assertSingleValueResultSet(rs, expected);
}
}
}
s.close();
ps[0].close();
ps[1].close();
}
/**
* Test that dynamic like optimization is performed. That is, the LIKE
* predicate is rewritten to &gt;=, &lt; and LIKE.
*/
public void testDynamicLikeOptimization() throws SQLException {
Statement s = createStatement();
ResultSet rs = s.executeQuery(
"VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY('derby.database.collation')");
if (rs.next()){
if (rs.getString(1).equals("TERRITORY_BASED")) {
rs.close();
s.close();
return;
}
}
s.execute("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
PreparedStatement ps =
prepareStatement("select id from test where vc10 like ?");
ps.setString(1, "%");
JDBC.assertDrainResults(ps.executeQuery());
RuntimeStatisticsParser p = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(p.hasGreaterThanOrEqualQualifier());
assertTrue(p.hasLessThanQualifier());
s.close();
ps.close();
}
public void testCast() throws SQLException {
Statement s = createStatement();
JDBC.assertSingleValueResultSet(
s.executeQuery("select 1 from t1 where 'asdf' like " +
"cast('%f' as varchar(2))"),
"1");
JDBC.assertEmpty(s.executeQuery("select 1 from t1 where 'asdf' like " +
"cast(null as char)"));
JDBC.assertSingleValueResultSet(
s.executeQuery("select 1 from t1 where '%foobar' like 'Z%foobar' " +
"escape cast('Z' as varchar(1))"),
"1");
// quoted values clause should not match anything
JDBC.assertEmpty(s.executeQuery(
"select vc10 from test where vc10 like " +
"'values cast(null as varchar(1))'"));
JDBC.assertEmpty(s.executeQuery(
"select id from test where c10 like " +
"cast ('%f' as varchar(2))"));
JDBC.assertUnorderedResultSet(s.executeQuery(
"select id from test where vc10 like " +
"cast ('%f' as varchar(2))"),
new String[][] { {"asdf"}, {"aasdf"} });
s.close();
}
}