blob: ee23a0bb986dfdf6fa8b517978744da960b809de [file] [log] [blame]
/*
* 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.phoenix.compile;
import static org.apache.phoenix.schema.PTable.QualifierEncodingScheme.TWO_BYTE_QUALIFIERS;
import static org.apache.phoenix.util.TestUtil.ATABLE_NAME;
import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
import static org.apache.phoenix.util.TestUtil.and;
import static org.apache.phoenix.util.TestUtil.assertDegenerate;
import static org.apache.phoenix.util.TestUtil.bindParams;
import static org.apache.phoenix.util.TestUtil.columnComparison;
import static org.apache.phoenix.util.TestUtil.constantComparison;
import static org.apache.phoenix.util.TestUtil.in;
import static org.apache.phoenix.util.TestUtil.multiEncodedKVFilter;
import static org.apache.phoenix.util.TestUtil.not;
import static org.apache.phoenix.util.TestUtil.or;
import static org.apache.phoenix.util.TestUtil.singleKVFilter;
import static org.junit.Assert.assertArrayEquals;
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 java.math.BigDecimal;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.KeyValueColumnExpression;
import org.apache.phoenix.expression.LiteralExpression;
import org.apache.phoenix.expression.RowKeyColumnExpression;
import org.apache.phoenix.expression.function.SubstrFunction;
import org.apache.phoenix.filter.RowKeyComparisonFilter;
import org.apache.phoenix.filter.SkipScanFilter;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
import org.apache.phoenix.query.BaseConnectionlessQueryTest;
import org.apache.phoenix.query.KeyRange;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.schema.ColumnRef;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.RowKeyValueAccessor;
import org.apache.phoenix.schema.SaltingUtil;
import org.apache.phoenix.schema.TableRef;
import org.apache.phoenix.schema.types.PChar;
import org.apache.phoenix.schema.types.PLong;
import org.apache.phoenix.schema.types.PVarchar;
import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.DateUtil;
import org.apache.phoenix.util.NumberUtil;
import org.apache.phoenix.util.PropertiesUtil;
import org.apache.phoenix.util.SchemaUtil;
import org.apache.phoenix.util.StringUtil;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
public class WhereCompilerTest extends BaseConnectionlessQueryTest {
private PhoenixPreparedStatement newPreparedStatement(PhoenixConnection pconn, String query) throws SQLException {
PhoenixPreparedStatement pstmt = new PhoenixPreparedStatement(pconn, query);
assertRoundtrip(query);
return pstmt;
}
@Test
public void testSingleEqualFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer=0";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(constantComparison(
CompareOp.EQUAL,
A_INTEGER,
0)),
filter);
}
@Test
public void testOrPKWithAndPKAndNotPK() throws SQLException {
String query = "select * from bugTable where ID = 'i1' or (ID = 'i2' and company = 'c3')";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
pconn.createStatement().execute("create table bugTable(ID varchar primary key,company varchar)");
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
Expression idExpression = new ColumnRef(plan.getTableRef(), plan.getTableRef().getTable().getColumnForColumnName("ID").getPosition()).newColumnExpression();
Expression id = new RowKeyColumnExpression(idExpression,new RowKeyValueAccessor(plan.getTableRef().getTable().getPKColumns(),0));
Expression company = new KeyValueColumnExpression(plan.getTableRef().getTable().getColumnForColumnName("COMPANY"));
// FilterList has no equals implementation
assertTrue(filter instanceof FilterList);
FilterList filterList = (FilterList)filter;
assertEquals(FilterList.Operator.MUST_PASS_ALL, filterList.getOperator());
assertEquals(
Arrays.asList(
new SkipScanFilter(
ImmutableList.of(Arrays.asList(
pointRange("i1"),
pointRange("i2"))),
SchemaUtil.VAR_BINARY_SCHEMA),
singleKVFilter(
or(constantComparison(CompareOp.EQUAL,id,"i1"),
and(constantComparison(CompareOp.EQUAL,id,"i2"),
constantComparison(CompareOp.EQUAL,company,"c3"))))),
filterList.getFilters());
}
@Test
public void testAndPKAndNotPK() throws SQLException {
String query = "select * from bugTable where ID = 'i2' and company = 'c3'";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
pconn.createStatement().execute("create table bugTable(ID varchar primary key,company varchar)");
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
PColumn column = plan.getTableRef().getTable().getColumnForColumnName("COMPANY");
assertEquals(
singleKVFilter(constantComparison(
CompareOp.EQUAL,
new KeyValueColumnExpression(column),
"c3")),
filter);
}
@Test
public void testSingleFixedFullPkSalted() throws SQLException {
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
pconn.createStatement().execute("CREATE TABLE t (k bigint not null primary key, v varchar) SALT_BUCKETS=20");
String query = "select * from t where k=" + 1;
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertNull(filter);
byte[] key = new byte[PLong.INSTANCE.getByteSize() + 1];
PLong.INSTANCE.toBytes(1L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PLong.INSTANCE.getByteSize(), 20);
byte[] expectedStartKey = key;
byte[] expectedEndKey = ByteUtil.nextKey(key);
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
assertArrayEquals(expectedStartKey, startKey);
assertArrayEquals(expectedEndKey, stopKey);
}
@Test
public void testSingleVariableFullPkSalted() throws SQLException {
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
pconn.createStatement().execute("CREATE TABLE t (k varchar primary key, v varchar) SALT_BUCKETS=20");
String query = "select * from t where k='a'";
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertNull(filter);
byte[] key = new byte[2];
PVarchar.INSTANCE.toBytes("a", key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, 1, 20);
byte[] expectedStartKey = key;
byte[] expectedEndKey = ByteUtil.nextKey(ByteUtil.concat(key, QueryConstants.SEPARATOR_BYTE_ARRAY));
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
assertTrue(Bytes.compareTo(expectedStartKey, startKey) == 0);
assertTrue(Bytes.compareTo(expectedEndKey, stopKey) == 0);
}
@Test
public void testMultiFixedFullPkSalted() throws SQLException {
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
pconn.createStatement().execute("CREATE TABLE t (k bigint not null primary key, v varchar) SALT_BUCKETS=20");
String query = "select * from t where k in (1,3)";
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
byte[] key = new byte[PLong.INSTANCE.getByteSize() + 1];
PLong.INSTANCE.toBytes(1L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PLong.INSTANCE.getByteSize(), 20);
byte[] startKey1 = key;
key = new byte[PLong.INSTANCE.getByteSize() + 1];
PLong.INSTANCE.toBytes(3L, key, 1);
key[0] = SaltingUtil.getSaltingByte(key, 1, PLong.INSTANCE.getByteSize(), 20);
byte[] startKey2 = key;
byte[] startKey = scan.getStartRow();
byte[] stopKey = scan.getStopRow();
// Due to salting byte, the 1 key may be after the 3 key
byte[] expectedStartKey;
byte[] expectedEndKey;
List<List<KeyRange>> expectedRanges = Collections.singletonList(
Arrays.asList(KeyRange.getKeyRange(startKey1),
KeyRange.getKeyRange(startKey2)));
if (Bytes.compareTo(startKey1, startKey2) > 0) {
expectedStartKey = startKey2;
expectedEndKey = startKey1;
Collections.reverse(expectedRanges.get(0));
} else {
expectedStartKey = startKey1;
expectedEndKey = startKey2;
}
assertEquals(0,startKey.length);
assertEquals(0,stopKey.length);
assertNotNull(filter);
assertTrue(filter instanceof SkipScanFilter);
SkipScanFilter skipScanFilter = (SkipScanFilter)filter;
assertEquals(1,skipScanFilter.getSlots().size());
assertEquals(2,skipScanFilter.getSlots().get(0).size());
assertArrayEquals(expectedStartKey, skipScanFilter.getSlots().get(0).get(0).getLowerRange());
assertArrayEquals(expectedEndKey, skipScanFilter.getSlots().get(0).get(1).getLowerRange());
StatementContext context = plan.getContext();
ScanRanges scanRanges = context.getScanRanges();
List<List<KeyRange>> ranges = scanRanges.getRanges();
assertEquals(expectedRanges, ranges);
}
@Test
public void testMultiColumnEqualFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_string=b_string";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
multiEncodedKVFilter(columnComparison(
CompareOp.EQUAL,
A_STRING,
B_STRING), TWO_BYTE_QUALIFIERS),
filter);
}
@Test
public void testCollapseFunctionToNull() throws SQLException {
String query = "select * from atable where substr(entity_id,null) = 'foo'";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertNull(filter);
assertArrayEquals(scan.getStartRow(),KeyRange.EMPTY_RANGE.getLowerRange());
assertArrayEquals(scan.getStopRow(),KeyRange.EMPTY_RANGE.getUpperRange());
}
@Test
public void testAndFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id=? and a_integer=0 and a_string='foo'";
List<Object> binds = Arrays.<Object>asList(tenantId);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
multiEncodedKVFilter(and(
constantComparison(
CompareOp.EQUAL,
A_INTEGER,
0),
constantComparison(
CompareOp.EQUAL,
A_STRING,
"foo")), TWO_BYTE_QUALIFIERS),
filter);
}
@Test
public void testRHSLiteral() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and 0 >= a_integer";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(constantComparison(
CompareOp.LESS_OR_EQUAL,
A_INTEGER,
0)),
filter);
}
@Test
public void testToDateFilter() throws Exception {
String tenantId = "000000000000001";
String dateStr = "2012-01-01 12:00:00";
String query = "select * from atable where organization_id='" + tenantId + "' and a_date >= to_date('" + dateStr + "')";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
Date date = DateUtil.parseDate(dateStr);
assertEquals(
singleKVFilter(constantComparison(
CompareOp.GREATER_OR_EQUAL,
A_DATE,
date)),
filter);
}
private void helpTestToNumberFilter(String toNumberClause, BigDecimal expectedDecimal) throws Exception {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and x_decimal >= " + toNumberClause;
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(constantComparison(
CompareOp.GREATER_OR_EQUAL,
X_DECIMAL,
expectedDecimal)),
filter);
}
private void helpTestToNumberFilterWithNoPattern(String stringValue) throws Exception {
String toNumberClause = "to_number('" + stringValue + "')";
BigDecimal expectedDecimal = NumberUtil.normalize(new BigDecimal(stringValue));
helpTestToNumberFilter(toNumberClause, expectedDecimal);
}
@Test
public void testToNumberFilterWithInteger() throws Exception {
String stringValue = "123";
helpTestToNumberFilterWithNoPattern(stringValue);
}
@Test
public void testToNumberFilterWithDecimal() throws Exception {
String stringValue = "123.33";
helpTestToNumberFilterWithNoPattern(stringValue);
}
@Test
public void testToNumberFilterWithNegativeDecimal() throws Exception {
String stringValue = "-123.33";
helpTestToNumberFilterWithNoPattern(stringValue);
}
@Test
public void testToNumberFilterWithPatternParam() throws Exception {
String toNumberClause = "to_number('!1.23333E2', '!0.00000E0')";
BigDecimal expectedDecimal = NumberUtil.normalize(new BigDecimal("123.333"));
helpTestToNumberFilter(toNumberClause, expectedDecimal);
}
@Test(expected=AssertionError.class) // compileStatement() fails because zero rows are found by to_number()
public void testToNumberFilterWithPatternParamNegativeTest() throws Exception {
String toNumberClause = "to_number('$123.33', '000.00')"; // no currency sign in pattern param
BigDecimal expectedDecimal = NumberUtil.normalize(new BigDecimal("123.33"));
helpTestToNumberFilter(toNumberClause, expectedDecimal);
}
@Test
public void testRowKeyFilter() throws SQLException {
String keyPrefix = "foo";
String query = "select * from atable where substr(entity_id,1,3)=?";
List<Object> binds = Arrays.<Object>asList(keyPrefix);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
new RowKeyComparisonFilter(
constantComparison(CompareOp.EQUAL,
new SubstrFunction(
Arrays.<Expression>asList(
new RowKeyColumnExpression(ENTITY_ID,new RowKeyValueAccessor(ATABLE.getPKColumns(),1)),
LiteralExpression.newConstant(1),
LiteralExpression.newConstant(3))
),
keyPrefix),
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES),
filter);
}
@Test
public void testPaddedRowKeyFilter() throws SQLException {
String keyPrefix = "fo";
String query = "select * from atable where entity_id=?";
List<Object> binds = Arrays.<Object>asList(keyPrefix);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
assertEquals(0,scan.getStartRow().length);
assertEquals(0,scan.getStopRow().length);
assertNotNull(scan.getFilter());
}
@Test
public void testPaddedStartStopKey() throws SQLException {
String tenantId = "000000000000001";
String keyPrefix = "fo";
String query = "select * from atable where organization_id=? AND entity_id=?";
List<Object> binds = Arrays.<Object>asList(tenantId,keyPrefix);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] expectedStartRow = ByteUtil.concat(Bytes.toBytes(tenantId), StringUtil.padChar(Bytes.toBytes(keyPrefix), 15));
assertArrayEquals(expectedStartRow,scan.getStartRow());
assertArrayEquals(ByteUtil.nextKey(expectedStartRow),scan.getStopRow());
}
@Test
public void testDegenerateRowKeyFilter() throws SQLException {
String keyPrefix = "foobar";
String query = "select * from atable where substr(entity_id,1,3)=?";
List<Object> binds = Arrays.<Object>asList(keyPrefix);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
// Degenerate b/c "foobar" is more than 3 characters
assertDegenerate(plan.getContext());
}
@Test
public void testDegenerateBiggerThanMaxLengthVarchar() throws SQLException {
byte[] tooBigValue = new byte[101];
Arrays.fill(tooBigValue, (byte)50);
String aString = (String) PVarchar.INSTANCE.toObject(tooBigValue);
String query = "select * from atable where a_string=?";
List<Object> binds = Arrays.<Object>asList(aString);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
// Degenerate b/c a_string length is 100
assertDegenerate(plan.getContext());
}
@Test
public void testOrFilter() throws SQLException {
String tenantId = "000000000000001";
String keyPrefix = "foo";
int aInt = 2;
String query = "select * from atable where organization_id=? and (substr(entity_id,1,3)=? or a_integer=?)";
List<Object> binds = Arrays.<Object>asList(tenantId, keyPrefix, aInt);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
bindParams(pstmt, binds);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter( // single b/c one column is a row key column
or(
constantComparison(
CompareOp.EQUAL,
new SubstrFunction(Arrays.<Expression> asList(
new RowKeyColumnExpression(
ENTITY_ID,
new RowKeyValueAccessor(ATABLE.getPKColumns(), 1)),
LiteralExpression.newConstant(1),
LiteralExpression.newConstant(3))),
keyPrefix),
constantComparison(
CompareOp.EQUAL,
A_INTEGER,
aInt))),
filter);
}
@Test
public void testTypeMismatch() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer > 'foo'";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
try {
pstmt.optimizeQuery();
fail();
} catch (SQLException e) {
assertTrue(e.getMessage().contains("Type mismatch"));
}
}
@Test
public void testAndFalseFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer=0 and 2=3";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
assertDegenerate(plan.getContext());
}
@Test
public void testFalseFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and 2=3";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
assertDegenerate(plan.getContext());
}
@Test
public void testTrueFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and 2<=2";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
assertNull(scan.getFilter());
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testAndTrueFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer=0 and 2<3";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(constantComparison(
CompareOp.EQUAL,
A_INTEGER,
0)),
filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testOrFalseFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and (a_integer=0 or 3!=3)";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(constantComparison(
CompareOp.EQUAL,
A_INTEGER,
0)),
filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testOrTrueFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and (a_integer=0 or 3>2)";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertNull(filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testInFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_string IN ('a','b')";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(in(
A_STRING,
"a",
"b")),
filter);
}
@Test
public void testInListFilter() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String query = String.format("select * from %s where organization_id IN ('%s','%s','%s')",
ATABLE_NAME, tenantId1, tenantId3, tenantId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId1);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = PVarchar.INSTANCE.toBytes(tenantId3);
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.of(Arrays.asList(
pointRange(tenantId1),
pointRange(tenantId2),
pointRange(tenantId3))),
plan.getTableRef().getTable().getRowKeySchema()),
filter);
}
@Test @Ignore("OR not yet optimized")
public void testOr2InFilter() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String query = String.format("select * from %s where organization_id='%s' OR organization_id='%s' OR organization_id='%s'",
ATABLE_NAME, tenantId1, tenantId3, tenantId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.of(Arrays.asList(
pointRange(tenantId1),
pointRange(tenantId2),
pointRange(tenantId3))),
plan.getTableRef().getTable().getRowKeySchema()),
filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId1);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = PVarchar.INSTANCE.toBytes(tenantId3);
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testSecondPkColInListFilter() throws SQLException {
String tenantId = "000000000000001";
String entityId1 = "00000000000000X";
String entityId2 = "00000000000000Y";
String query = String.format("select * from %s where organization_id='%s' AND entity_id IN ('%s','%s')",
ATABLE_NAME, tenantId, entityId1, entityId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId + entityId1);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = PVarchar.INSTANCE.toBytes(tenantId + entityId2);
assertArrayEquals(ByteUtil.concat(stopRow, QueryConstants.SEPARATOR_BYTE_ARRAY), scan.getStopRow());
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.of(
Arrays.asList(
pointRange(tenantId,entityId1),
pointRange(tenantId,entityId2))),
SchemaUtil.VAR_BINARY_SCHEMA),
filter);
}
@Test
public void testInListWithAnd1GTEFilter() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String entityId1 = "00000000000000X";
String entityId2 = "00000000000000Y";
String query = String.format("select * from %s where organization_id IN ('%s','%s','%s') AND entity_id>='%s' AND entity_id<='%s'",
ATABLE_NAME, tenantId1, tenantId3, tenantId2, entityId1, entityId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.of(
Arrays.asList(
pointRange(tenantId1),
pointRange(tenantId2),
pointRange(tenantId3)),
Arrays.asList(PChar.INSTANCE.getKeyRange(
Bytes.toBytes(entityId1),
true,
Bytes.toBytes(entityId2),
true))),
plan.getTableRef().getTable().getRowKeySchema()),
filter);
}
@Test
public void testInListWithAnd1Filter() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String entityId = "00000000000000X";
String query = String.format("select * from %s where organization_id IN ('%s','%s','%s') AND entity_id='%s'",
ATABLE_NAME, tenantId1, tenantId3, tenantId2, entityId);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.of(
Arrays.asList(
pointRange(tenantId1, entityId),
pointRange(tenantId2, entityId),
pointRange(tenantId3, entityId))),
SchemaUtil.VAR_BINARY_SCHEMA),
filter);
}
@Test
public void testInListWithAnd1FilterScankey() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String entityId = "00000000000000X";
String query = String.format("select * from %s where organization_id IN ('%s','%s','%s') AND entity_id='%s'",
ATABLE_NAME, tenantId1, tenantId3, tenantId2, entityId);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] startRow = ByteUtil.concat(PVarchar.INSTANCE.toBytes(tenantId1), PVarchar.INSTANCE.toBytes(entityId));
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = ByteUtil.concat(PVarchar.INSTANCE.toBytes(tenantId3), PVarchar.INSTANCE.toBytes(entityId));
assertArrayEquals(ByteUtil.concat(stopRow, QueryConstants.SEPARATOR_BYTE_ARRAY), scan.getStopRow());
// TODO: validate scan ranges
}
private static KeyRange pointRange(String... ids) {
byte[] theKey = ByteUtil.EMPTY_BYTE_ARRAY;
for (String id : ids) {
theKey = ByteUtil.concat(theKey, Bytes.toBytes(id));
}
return pointRange(theKey);
}
private static KeyRange pointRange(byte[] bytes) {
return KeyRange.POINT.apply(bytes);
}
@Test
public void testInListWithAnd2Filter() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String entityId1 = "00000000000000X";
String entityId2 = "00000000000000Y";
String query = String.format("select * from %s where organization_id IN ('%s','%s') AND entity_id IN ('%s', '%s')",
ATABLE_NAME, tenantId1, tenantId2, entityId1, entityId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
new SkipScanFilter(
ImmutableList.<List<KeyRange>>of(ImmutableList.of(
pointRange(tenantId1, entityId1),
pointRange(tenantId1, entityId2),
pointRange(tenantId2, entityId1),
pointRange(tenantId2, entityId2))),
SchemaUtil.VAR_BINARY_SCHEMA),
filter);
}
@Test
public void testPartialRangeFilter() throws SQLException {
// I know these id's are ridiculous, but users can write queries that look like this
String tenantId1 = "001";
String tenantId2 = "02";
String query = String.format("select * from %s where organization_id > '%s' AND organization_id < '%s'",
ATABLE_NAME, tenantId1, tenantId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
assertNull(scan.getFilter());
byte[] wideLower = ByteUtil.nextKey(StringUtil.padChar(Bytes.toBytes(tenantId1), 15));
byte[] wideUpper = StringUtil.padChar(Bytes.toBytes(tenantId2), 15);
assertArrayEquals(wideLower, scan.getStartRow());
assertArrayEquals(wideUpper, scan.getStopRow());
}
@Test
public void testInListWithAnd2FilterScanKey() throws SQLException {
String tenantId1 = "000000000000001";
String tenantId2 = "000000000000002";
String tenantId3 = "000000000000003";
String entityId1 = "00000000000000X";
String entityId2 = "00000000000000Y";
String query = String.format("select * from %s where organization_id IN ('%s','%s','%s') AND entity_id IN ('%s', '%s')",
ATABLE_NAME, tenantId1, tenantId3, tenantId2, entityId1, entityId2);
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
byte[] startRow = ByteUtil.concat(PVarchar.INSTANCE.toBytes(tenantId1), PVarchar.INSTANCE.toBytes(entityId1));
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = ByteUtil.concat(PVarchar.INSTANCE.toBytes(tenantId3), PVarchar.INSTANCE.toBytes(entityId2));
assertArrayEquals(ByteUtil.concat(stopRow, QueryConstants.SEPARATOR_BYTE_ARRAY), scan.getStopRow());
// TODO: validate scan ranges
}
@Test
public void testBetweenFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer between 0 and 10";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(and(
constantComparison(
CompareOp.GREATER_OR_EQUAL,
A_INTEGER,
0),
constantComparison(
CompareOp.LESS_OR_EQUAL,
A_INTEGER,
10))),
filter);
}
@Test
public void testNotBetweenFilter() throws SQLException {
String tenantId = "000000000000001";
String query = "select * from atable where organization_id='" + tenantId + "' and a_integer not between 0 and 10";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
assertEquals(
singleKVFilter(not(and(
constantComparison(
CompareOp.GREATER_OR_EQUAL,
A_INTEGER,
0),
constantComparison(
CompareOp.LESS_OR_EQUAL,
A_INTEGER,
10)))).toString(),
filter.toString());
}
@Test
public void testTenantConstraintsAddedToScan() throws SQLException {
String tenantTypeId = "5678";
String tenantId = "000000000000123";
String url = getUrl(tenantId);
createTestTable(getUrl(), "create table base_table_for_tenant_filter_test (tenant_id char(15) not null, type_id char(4) not null, " +
"id char(5) not null, a_integer integer, a_string varchar(100) constraint pk primary key (tenant_id, type_id, id)) multi_tenant=true");
createTestTable(url, "create view tenant_filter_test (tenant_col integer) AS SELECT * FROM BASE_TABLE_FOR_TENANT_FILTER_TEST WHERE type_id= '" + tenantTypeId + "'");
String query = "select * from tenant_filter_test where a_integer=0 and a_string='foo'";
PhoenixConnection pconn = DriverManager.getConnection(url, PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
PTable table = plan.getTableRef().getTable();
Expression aInteger = new ColumnRef(new TableRef(table), table.getColumnForColumnName("A_INTEGER").getPosition()).newColumnExpression();
Expression aString = new ColumnRef(new TableRef(table), table.getColumnForColumnName("A_STRING").getPosition()).newColumnExpression();
assertEquals(
multiEncodedKVFilter(and(
constantComparison(
CompareOp.EQUAL,
aInteger,
0),
constantComparison(
CompareOp.EQUAL,
aString,
"foo")), TWO_BYTE_QUALIFIERS),
filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId + tenantTypeId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testTenantConstraintsAddedToScanWithNullTenantTypeId() throws SQLException {
String tenantId = "000000000000123";
createTestTable(getUrl(), "create table base_table_for_tenant_filter_test (tenant_id char(15) not null, " +
"id char(5) not null, a_integer integer, a_string varchar(100) constraint pk primary key (tenant_id, id)) multi_tenant=true");
createTestTable(getUrl(tenantId), "create view tenant_filter_test (tenant_col integer) AS SELECT * FROM BASE_TABLE_FOR_TENANT_FILTER_TEST");
String query = "select * from tenant_filter_test where a_integer=0 and a_string='foo'";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(tenantId), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Filter filter = scan.getFilter();
PTable table = plan.getTableRef().getTable();
Expression aInteger = new ColumnRef(new TableRef(table), table.getColumnForColumnName("A_INTEGER").getPosition()).newColumnExpression();
Expression aString = new ColumnRef(new TableRef(table), table.getColumnForColumnName("A_STRING").getPosition()).newColumnExpression();
assertEquals(
multiEncodedKVFilter(and(
constantComparison(
CompareOp.EQUAL,
aInteger,
0),
constantComparison(
CompareOp.EQUAL,
aString,
"foo")), TWO_BYTE_QUALIFIERS),
filter);
byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
assertArrayEquals(startRow, scan.getStartRow());
byte[] stopRow = startRow;
assertArrayEquals(ByteUtil.nextKey(stopRow), scan.getStopRow());
}
@Test
public void testScanCaching_Default() throws SQLException {
String query = "select * from atable where a_integer=0";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
Configuration config = HBaseConfiguration.create();
int defaultScannerCacheSize = config.getInt(HConstants.HBASE_CLIENT_SCANNER_CACHING,
HConstants.DEFAULT_HBASE_CLIENT_SCANNER_CACHING);
assertEquals(defaultScannerCacheSize, pstmt.getFetchSize());
assertEquals(defaultScannerCacheSize, scan.getCaching());
}
@Test
public void testScanCaching_CustomFetchSizeOnStatement() throws SQLException {
String query = "select * from atable where a_integer=0";
PhoenixConnection pconn = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)).unwrap(PhoenixConnection.class);
PhoenixPreparedStatement pstmt = newPreparedStatement(pconn, query);
final int FETCH_SIZE = 25;
pstmt.setFetchSize(FETCH_SIZE);
QueryPlan plan = pstmt.optimizeQuery();
Scan scan = plan.getContext().getScan();
assertEquals(FETCH_SIZE, pstmt.getFetchSize());
assertEquals(FETCH_SIZE, scan.getCaching());
}
}