Repository: phoenix Updated Branches: refs/heads/3.0 3640902c3 -> 9c85887af
PHOENIX-788 Support cast from/to DATE/TIME/TIMESTAMP to/from LONG/UNSIGNED_LONG Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/9c85887a Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/9c85887a Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/9c85887a Branch: refs/heads/3.0 Commit: 9c85887af4d48a7bac45171ddd2a33fbd310a440 Parents: 3640902 Author: James Taylor <jtay...@salesforce.com> Authored: Mon Jul 28 11:48:51 2014 -0700 Committer: James Taylor <jtay...@salesforce.com> Committed: Mon Jul 28 11:48:51 2014 -0700 ---------------------------------------------------------------------- .../org/apache/phoenix/end2end/BaseQueryIT.java | 196 +++++++++++++++++++ .../apache/phoenix/end2end/CaseStatementIT.java | 2 +- .../apache/phoenix/end2end/CastAndCoerceIT.java | 98 +++++++++- .../end2end/ClientTimeArithmeticQueryIT.java | 2 +- .../org/apache/phoenix/end2end/GroupByIT.java | 2 +- .../org/apache/phoenix/end2end/NotQueryIT.java | 2 +- .../org/apache/phoenix/end2end/QueryIT.java | 149 +------------- .../org/apache/phoenix/end2end/ScanQueryIT.java | 2 +- .../function/CeilDecimalExpression.java | 17 +- .../function/FloorDecimalExpression.java | 17 +- .../function/RoundDecimalExpression.java | 24 ++- .../org/apache/phoenix/parse/CastParseNode.java | 18 +- .../org/apache/phoenix/parse/CeilParseNode.java | 2 +- .../apache/phoenix/parse/FloorParseNode.java | 2 +- .../apache/phoenix/parse/RoundParseNode.java | 6 +- .../org/apache/phoenix/schema/PDataType.java | 15 ++ .../RoundFloorCeilExpressionsUnitTests.java | 5 +- 17 files changed, 383 insertions(+), 176 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseQueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseQueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseQueryIT.java new file mode 100644 index 0000000..f87e86c --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseQueryIT.java @@ -0,0 +1,196 @@ +/* + * 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.end2end; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.hbase.index.write.IndexWriterUtils; +import org.apache.phoenix.query.QueryServices; +import org.apache.phoenix.util.ByteUtil; +import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.PropertiesUtil; +import org.apache.phoenix.util.ReadOnlyProps; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + + + +/** + * + * Basic tests for Phoenix JDBC implementation + * + * + * @since 0.1 + */ + +@Category(ClientManagedTimeTest.class) +@RunWith(Parameterized.class) +public abstract class BaseQueryIT extends BaseClientManagedTimeIT { + protected static final String tenantId = getOrganizationId(); + protected static final String ATABLE_INDEX_NAME = "ATABLE_IDX"; + protected static final long BATCH_SIZE = 3; + + @BeforeClass + @Shadower(classBeingShadowed = BaseClientManagedTimeIT.class) + public static void doSetup() throws Exception { + int targetQueryConcurrency = 2; + int maxQueryConcurrency = 3; + Map<String,String> props = Maps.newHashMapWithExpectedSize(5); + props.put(QueryServices.QUEUE_SIZE_ATTRIB, Integer.toString(100)); + props.put(QueryServices.MAX_QUERY_CONCURRENCY_ATTRIB, Integer.toString(maxQueryConcurrency)); + props.put(QueryServices.TARGET_QUERY_CONCURRENCY_ATTRIB, Integer.toString(targetQueryConcurrency)); + props.put(IndexWriterUtils.HTABLE_THREAD_KEY, Integer.toString(100)); + // Make a small batch size to test multiple calls to reserve sequences + props.put(QueryServices.SEQUENCE_CACHE_SIZE_ATTRIB, Long.toString(BATCH_SIZE)); + + // Must update config before starting server + setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator())); + } + + protected long ts; + protected Date date; + private String indexDDL; + + public BaseQueryIT(String indexDDL) { + this.indexDDL = indexDDL; + } + + @Before + public void initTable() throws Exception { + ts = nextTimestamp(); + initATableValues(tenantId, getDefaultSplits(tenantId), date=new Date(System.currentTimeMillis()), ts); + if (indexDDL != null && indexDDL.length() > 0) { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts)); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.createStatement().execute(indexDDL); + } + } + + @Parameters(name="{0}") + public static Collection<Object> data() { + List<Object> testCases = Lists.newArrayList(); + testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer DESC) INCLUDE (" + + " A_STRING, " + " B_STRING, " + " A_DATE)" }); + testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer, a_string) INCLUDE (" + + " B_STRING, " + " A_DATE)" }); + testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer) INCLUDE (" + + " A_STRING, " + " B_STRING, " + " A_DATE)" }); + testCases.add(new String[] { "" }); + return testCases; + } + + protected void assertValueEqualsResultSet(ResultSet rs, List<Object> expectedResults) throws SQLException { + List<List<Object>> nestedExpectedResults = Lists.newArrayListWithExpectedSize(expectedResults.size()); + for (Object expectedResult : expectedResults) { + nestedExpectedResults.add(Arrays.asList(expectedResult)); + } + assertValuesEqualsResultSet(rs, nestedExpectedResults); + } + + /** + * Asserts that we find the expected values in the result set. We don't know the order, since we don't always + * have an order by and we're going through indexes, but we assert that each expected result occurs once as + * expected (in any order). + */ + protected void assertValuesEqualsResultSet(ResultSet rs, List<List<Object>> expectedResults) throws SQLException { + int expectedCount = expectedResults.size(); + int count = 0; + List<List<Object>> actualResults = Lists.newArrayList(); + List<Object> errorResult = null; + while (rs.next() && errorResult == null) { + List<Object> result = Lists.newArrayList(); + for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { + result.add(rs.getObject(i+1)); + } + if (!expectedResults.contains(result)) { + errorResult = result; + } + actualResults.add(result); + count++; + } + assertTrue("Could not find " + errorResult + " in expected results: " + expectedResults + " with actual results: " + actualResults, errorResult == null); + assertEquals(count, expectedCount); + } + + protected void assertOneOfValuesEqualsResultSet(ResultSet rs, List<List<Object>>... expectedResultsArray) throws SQLException { + List<List<Object>> results = Lists.newArrayList(); + while (rs.next()) { + List<Object> result = Lists.newArrayList(); + for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { + result.add(rs.getObject(i+1)); + } + results.add(result); + } + for (int j = 0; j < expectedResultsArray.length; j++) { + List<List<Object>> expectedResults = expectedResultsArray[j]; + Set<List<Object>> expectedResultsSet = Sets.newHashSet(expectedResults); + int count = 0; + boolean brokeEarly = false; + for (List<Object> result : results) { + if (!expectedResultsSet.contains(result)) { + brokeEarly = true; + break; + } + count++; + } + if (!brokeEarly && count == expectedResults.size()) { + return; + } + } + fail("Unable to find " + results + " in " + Arrays.asList(expectedResultsArray)); + } + + protected static boolean compare(CompareOp op, ImmutableBytesWritable lhsOutPtr, ImmutableBytesWritable rhsOutPtr) { + int compareResult = Bytes.compareTo(lhsOutPtr.get(), lhsOutPtr.getOffset(), lhsOutPtr.getLength(), rhsOutPtr.get(), rhsOutPtr.getOffset(), rhsOutPtr.getLength()); + return ByteUtil.compare(op, compareResult); + } + + + private static AtomicInteger runCount = new AtomicInteger(0); + protected static int nextRunCount() { + return runCount.getAndAdd(1); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CaseStatementIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CaseStatementIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CaseStatementIT.java index 57838a0..6354de3 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CaseStatementIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CaseStatementIT.java @@ -56,7 +56,7 @@ import com.google.common.collect.Lists; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class CaseStatementIT extends QueryIT { +public class CaseStatementIT extends BaseQueryIT { public CaseStatementIT(String indexDDL) { super(indexDDL); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CastAndCoerceIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CastAndCoerceIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CastAndCoerceIT.java index 5f1fa7b..7a0c3e3 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CastAndCoerceIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CastAndCoerceIT.java @@ -19,6 +19,7 @@ */ package org.apache.phoenix.end2end; +import static org.apache.phoenix.util.TestUtil.ROW1; import static org.apache.phoenix.util.TestUtil.ROW7; import static org.apache.phoenix.util.TestUtil.ROW9; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; @@ -44,7 +45,7 @@ import org.junit.runners.Parameterized.Parameters; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class CastAndCoerceIT extends QueryIT { +public class CastAndCoerceIT extends BaseQueryIT { public CastAndCoerceIT(String indexDDL) { super(indexDDL); @@ -165,4 +166,99 @@ public class CastAndCoerceIT extends QueryIT { } } + + @Test + public void testCoerceDateToBigInt() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + PreparedStatement statement; + ResultSet rs; + String query; + long dateAsLong; + BigDecimal dateAsDecimal; + String url; + Connection conn; + url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + (ts + 3); + conn = DriverManager.getConnection(url, props); + conn.setAutoCommit(true); + conn.createStatement().execute("UPSERT INTO ATABLE(organization_id,entity_id,a_time,a_timestamp) SELECT organization_id,entity_id,a_date,a_date FROM ATABLE"); + + url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + (ts + 5); + conn = DriverManager.getConnection(url, props); + try { + query = "SELECT entity_id, CAST(a_date AS BIGINT) FROM ATABLE WHERE organization_id=? AND a_date IS NOT NULL LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + dateAsLong = rs.getLong(2); + assertFalse(rs.next()); + + query = "SELECT entity_id FROM ATABLE WHERE organization_id=? AND a_date = CAST(? AS DATE) LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + statement.setLong(2, dateAsLong); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + assertFalse(rs.next()); + + query = "SELECT entity_id, CAST(a_time AS BIGINT) FROM ATABLE WHERE organization_id=? AND a_time IS NOT NULL LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + dateAsLong = rs.getLong(2); + assertFalse(rs.next()); + + query = "SELECT entity_id FROM ATABLE WHERE organization_id=? AND a_time = CAST(? AS TIME) LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + statement.setLong(2, dateAsLong); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + assertFalse(rs.next()); + + query = "SELECT entity_id, CAST(a_timestamp AS DECIMAL) FROM ATABLE WHERE organization_id=? AND a_timestamp IS NOT NULL LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + dateAsDecimal = rs.getBigDecimal(2); + assertFalse(rs.next()); + + query = "SELECT entity_id FROM ATABLE WHERE organization_id=? AND a_timestamp = CAST(? AS TIMESTAMP) LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + statement.setBigDecimal(2, dateAsDecimal); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + assertFalse(rs.next()); + + + query = "SELECT entity_id, CAST(a_timestamp AS BIGINT) FROM ATABLE WHERE organization_id=? AND a_timestamp IS NOT NULL LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + dateAsLong = rs.getLong(2); + assertFalse(rs.next()); + + query = "SELECT entity_id FROM ATABLE WHERE organization_id=? AND a_timestamp = CAST(? AS TIMESTAMP) LIMIT 1"; + statement = conn.prepareStatement(query); + statement.setString(1, tenantId); + statement.setLong(2, dateAsLong); + rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(ROW1, rs.getString(1)); + assertFalse(rs.next()); + } finally { + conn.close(); + } + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java index f5eb7d6..9be6fc1 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientTimeArithmeticQueryIT.java @@ -59,7 +59,7 @@ import com.google.common.collect.Lists; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class ClientTimeArithmeticQueryIT extends QueryIT { +public class ClientTimeArithmeticQueryIT extends BaseQueryIT { public ClientTimeArithmeticQueryIT(String indexDDL) { super(indexDDL); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByIT.java index fce93ea..6002d58 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByIT.java @@ -54,7 +54,7 @@ import com.google.common.collect.Lists; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class GroupByIT extends QueryIT { +public class GroupByIT extends BaseQueryIT { public GroupByIT(String indexDDL) { super(indexDDL); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/NotQueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/NotQueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/NotQueryIT.java index 0e6562c..d6f0c13 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/NotQueryIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/NotQueryIT.java @@ -53,7 +53,7 @@ import com.google.common.primitives.Floats; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class NotQueryIT extends QueryIT { +public class NotQueryIT extends BaseQueryIT { public NotQueryIT(String indexDDL) { super(indexDDL); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java index 0f469bc..dcbe59e 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java @@ -43,43 +43,25 @@ import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; import java.util.Properties; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.util.Bytes; -import org.apache.phoenix.hbase.index.write.IndexWriterUtils; import org.apache.phoenix.jdbc.PhoenixConnection; -import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.schema.ConstraintViolationException; import org.apache.phoenix.schema.PDataType; import org.apache.phoenix.schema.SequenceNotFoundException; import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.ReadOnlyProps; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; @@ -92,121 +74,10 @@ import com.google.common.collect.Sets; */ @Category(ClientManagedTimeTest.class) -@RunWith(Parameterized.class) -public class QueryIT extends BaseClientManagedTimeIT { - protected static final String tenantId = getOrganizationId(); - protected static final String ATABLE_INDEX_NAME = "ATABLE_IDX"; - protected static final long BATCH_SIZE = 3; - - @BeforeClass - @Shadower(classBeingShadowed = BaseClientManagedTimeIT.class) - public static void doSetup() throws Exception { - int targetQueryConcurrency = 2; - int maxQueryConcurrency = 3; - Map<String,String> props = Maps.newHashMapWithExpectedSize(5); - props.put(QueryServices.QUEUE_SIZE_ATTRIB, Integer.toString(100)); - props.put(QueryServices.MAX_QUERY_CONCURRENCY_ATTRIB, Integer.toString(maxQueryConcurrency)); - props.put(QueryServices.TARGET_QUERY_CONCURRENCY_ATTRIB, Integer.toString(targetQueryConcurrency)); - props.put(IndexWriterUtils.HTABLE_THREAD_KEY, Integer.toString(100)); - // Make a small batch size to test multiple calls to reserve sequences - props.put(QueryServices.SEQUENCE_CACHE_SIZE_ATTRIB, Long.toString(BATCH_SIZE)); - - // Must update config before starting server - setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator())); - } - - protected long ts; - private Date date; - private String indexDDL; +public class QueryIT extends BaseQueryIT { public QueryIT(String indexDDL) { - this.indexDDL = indexDDL; - } - - @Before - public void initTable() throws Exception { - ts = nextTimestamp(); - initATableValues(tenantId, getDefaultSplits(tenantId), date=new Date(System.currentTimeMillis()), ts); - if (indexDDL != null && indexDDL.length() > 0) { - Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts)); - Connection conn = DriverManager.getConnection(getUrl(), props); - conn.createStatement().execute(indexDDL); - } - } - - @Parameters(name="{0}") - public static Collection<Object> data() { - List<Object> testCases = Lists.newArrayList(); - testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer DESC) INCLUDE (" - + " A_STRING, " + " B_STRING, " + " A_DATE)" }); - testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer, a_string) INCLUDE (" - + " B_STRING, " + " A_DATE)" }); - testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer) INCLUDE (" - + " A_STRING, " + " B_STRING, " + " A_DATE)" }); - testCases.add(new String[] { "" }); - return testCases; - } - - protected void assertValueEqualsResultSet(ResultSet rs, List<Object> expectedResults) throws SQLException { - List<List<Object>> nestedExpectedResults = Lists.newArrayListWithExpectedSize(expectedResults.size()); - for (Object expectedResult : expectedResults) { - nestedExpectedResults.add(Arrays.asList(expectedResult)); - } - assertValuesEqualsResultSet(rs, nestedExpectedResults); - } - - /** - * Asserts that we find the expected values in the result set. We don't know the order, since we don't always - * have an order by and we're going through indexes, but we assert that each expected result occurs once as - * expected (in any order). - */ - protected void assertValuesEqualsResultSet(ResultSet rs, List<List<Object>> expectedResults) throws SQLException { - int expectedCount = expectedResults.size(); - int count = 0; - List<List<Object>> actualResults = Lists.newArrayList(); - List<Object> errorResult = null; - while (rs.next() && errorResult == null) { - List<Object> result = Lists.newArrayList(); - for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { - result.add(rs.getObject(i+1)); - } - if (!expectedResults.contains(result)) { - errorResult = result; - } - actualResults.add(result); - count++; - } - assertTrue("Could not find " + errorResult + " in expected results: " + expectedResults + " with actual results: " + actualResults, errorResult == null); - assertEquals(count, expectedCount); - } - - protected void assertOneOfValuesEqualsResultSet(ResultSet rs, List<List<Object>>... expectedResultsArray) throws SQLException { - List<List<Object>> results = Lists.newArrayList(); - while (rs.next()) { - List<Object> result = Lists.newArrayList(); - for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { - result.add(rs.getObject(i+1)); - } - results.add(result); - } - for (int j = 0; j < expectedResultsArray.length; j++) { - List<List<Object>> expectedResults = expectedResultsArray[j]; - Set<List<Object>> expectedResultsSet = Sets.newHashSet(expectedResults); - int count = 0; - boolean brokeEarly = false; - for (List<Object> result : results) { - if (!expectedResultsSet.contains(result)) { - brokeEarly = true; - break; - } - count++; - } - if (!brokeEarly && count == expectedResults.size()) { - return; - } - } - fail("Unable to find " + results + " in " + Arrays.asList(expectedResultsArray)); + super(indexDDL); } @Test @@ -325,7 +196,7 @@ public class QueryIT extends BaseClientManagedTimeIT { } } - public void testNoStringValue(String value) throws Exception { + private void testNoStringValue(String value) throws Exception { String url = getUrl() + ";" + PhoenixRuntime.CURRENT_SCN_ATTRIB + "=" + (ts + 1); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection upsertConn = DriverManager.getConnection(url, props); @@ -476,15 +347,6 @@ public class QueryIT extends BaseClientManagedTimeIT { conn.close(); } - - - - private static boolean compare(CompareOp op, ImmutableBytesWritable lhsOutPtr, ImmutableBytesWritable rhsOutPtr) { - int compareResult = Bytes.compareTo(lhsOutPtr.get(), lhsOutPtr.getOffset(), lhsOutPtr.getLength(), rhsOutPtr.get(), rhsOutPtr.getOffset(), rhsOutPtr.getLength()); - return ByteUtil.compare(op, compareResult); - } - - @Test public void testDateInList() throws Exception { String query = "SELECT entity_id FROM ATABLE WHERE a_date IN (?,?) AND a_integer < 4"; @@ -846,11 +708,6 @@ public class QueryIT extends BaseClientManagedTimeIT { } } - private static AtomicInteger runCount = new AtomicInteger(0); - private static int nextRunCount() { - return runCount.getAndAdd(1); - } - @Test public void testSplitWithCachedMeta() throws Exception { // Tests that you don't get an ambiguous column exception when using the same alias as the column name http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ScanQueryIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ScanQueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ScanQueryIT.java index fcb16e5..f817c2b 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ScanQueryIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ScanQueryIT.java @@ -62,7 +62,7 @@ import com.google.common.primitives.Floats; @Category(ClientManagedTimeTest.class) @RunWith(Parameterized.class) -public class ScanQueryIT extends QueryIT { +public class ScanQueryIT extends BaseQueryIT { @Parameters(name="{0}") public static Collection<Object> data() { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java index e07acb3..5e3f2f6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CeilDecimalExpression.java @@ -21,11 +21,12 @@ import java.math.RoundingMode; import java.sql.SQLException; import java.util.List; -import com.google.common.collect.Lists; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.LiteralExpression; import org.apache.phoenix.schema.PDataType; +import com.google.common.collect.Lists; + /** * * Class encapsulating the CEIL operation on a {@link org.apache.phoenix.schema.PDataType#DECIMAL} @@ -37,7 +38,7 @@ public class CeilDecimalExpression extends RoundDecimalExpression { public CeilDecimalExpression() {} - public CeilDecimalExpression(List<Expression> children) { + private CeilDecimalExpression(List<Expression> children) { super(children); } @@ -54,6 +55,18 @@ public class CeilDecimalExpression extends RoundDecimalExpression { return new CeilDecimalExpression(expressions); } + public static Expression create(List<Expression> exprs) throws SQLException { + Expression expr = exprs.get(0); + if (expr.getDataType().isCoercibleTo(PDataType.LONG)) { + return expr; + } + if (exprs.size() == 1) { + Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true); + exprs = Lists.newArrayList(expr, scaleExpr); + } + return new CeilDecimalExpression(exprs); + } + /** * Creates a {@link CeilDecimalExpression} with a default scale of 0 used for rounding. * http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java index 4b2a708..539dbfa 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/FloorDecimalExpression.java @@ -21,11 +21,12 @@ import java.math.RoundingMode; import java.sql.SQLException; import java.util.List; -import com.google.common.collect.Lists; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.LiteralExpression; import org.apache.phoenix.schema.PDataType; +import com.google.common.collect.Lists; + /** * * Class encapsulating the FLOOR operation on @@ -38,7 +39,7 @@ public class FloorDecimalExpression extends RoundDecimalExpression { public FloorDecimalExpression() {} - public FloorDecimalExpression(List<Expression> children) { + private FloorDecimalExpression(List<Expression> children) { super(children); } @@ -55,6 +56,18 @@ public class FloorDecimalExpression extends RoundDecimalExpression { return new FloorDecimalExpression(expressions); } + public static Expression create(List<Expression> exprs) throws SQLException { + Expression expr = exprs.get(0); + if (expr.getDataType().isCoercibleTo(PDataType.LONG)) { + return expr; + } + if (exprs.size() == 1) { + Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true); + exprs = Lists.newArrayList(expr, scaleExpr); + } + return new FloorDecimalExpression(exprs); + } + /** * Creates a {@link FloorDecimalExpression} with a default scale of 0 used for rounding. * http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java index be03682..64f957e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RoundDecimalExpression.java @@ -27,14 +27,14 @@ import java.util.List; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.io.WritableUtils; - -import com.google.common.collect.Lists; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.LiteralExpression; import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.schema.PDataType; import org.apache.phoenix.schema.tuple.Tuple; +import com.google.common.collect.Lists; + /** * * Class encapsulating the process for rounding off a column/literal of @@ -69,9 +69,21 @@ public class RoundDecimalExpression extends ScalarFunction { return create(expr, 0); } + public static Expression create(List<Expression> exprs) throws SQLException { + Expression expr = exprs.get(0); + if (expr.getDataType().isCoercibleTo(PDataType.LONG)) { + return expr; + } + if (exprs.size() == 1) { + Expression scaleExpr = LiteralExpression.newConstant(0, PDataType.INTEGER, true); + exprs = Lists.newArrayList(expr, scaleExpr); + } + return new RoundDecimalExpression(exprs); + } + public RoundDecimalExpression() {} - public RoundDecimalExpression(List<Expression> children) { + protected RoundDecimalExpression(List<Expression> children) { super(children); LiteralExpression scaleChild = (LiteralExpression)children.get(1); PDataType scaleType = scaleChild.getDataType(); @@ -92,9 +104,9 @@ public class RoundDecimalExpression extends ScalarFunction { public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression childExpr = children.get(0); if(childExpr.evaluate(tuple, ptr)) { - BigDecimal value = (BigDecimal)PDataType.DECIMAL.toObject(ptr, getDataType(), childExpr.getSortOrder()); + BigDecimal value = (BigDecimal)PDataType.DECIMAL.toObject(ptr, childExpr.getDataType(), childExpr.getSortOrder()); BigDecimal scaledValue = value.setScale(scale, getRoundingMode()); - ptr.set(getDataType().toBytes(scaledValue)); + ptr.set(PDataType.DECIMAL.toBytes(scaledValue)); return true; } return false; @@ -102,7 +114,7 @@ public class RoundDecimalExpression extends ScalarFunction { @Override public PDataType getDataType() { - return children.get(0).getDataType(); + return PDataType.DECIMAL; } protected RoundingMode getRoundingMode() { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java index e4c7424..a5c5c03 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CastParseNode.java @@ -78,17 +78,23 @@ public class CastParseNode extends UnaryParseNode { return scale; } + // TODO: don't repeat this ugly cast logic (maybe use isCastable in the last else block. public static Expression convertToRoundExpressionIfNeeded(PDataType fromDataType, PDataType targetDataType, List<Expression> expressions) throws SQLException { Expression firstChildExpr = expressions.get(0); if(fromDataType == targetDataType) { return firstChildExpr; - } else if(fromDataType == PDataType.DECIMAL && targetDataType.isCoercibleTo(PDataType.LONG)) { - return new RoundDecimalExpression(expressions); - } else if((fromDataType == PDataType.TIMESTAMP || fromDataType == PDataType.UNSIGNED_TIMESTAMP) && targetDataType.isCoercibleTo(PDataType.DATE)) { +// } else if((fromDataType == PDataType.DATE || fromDataType == PDataType.UNSIGNED_DATE) && targetDataType.isCoercibleTo(PDataType.LONG)) { +// return firstChildExpr; +// } else if(fromDataType.isCoercibleTo(PDataType.LONG) && (targetDataType == PDataType.DATE || targetDataType == PDataType.UNSIGNED_DATE)) { +// return firstChildExpr; + } else if((fromDataType == PDataType.DECIMAL || fromDataType == PDataType.TIMESTAMP || fromDataType == PDataType.UNSIGNED_TIMESTAMP) && targetDataType.isCoercibleTo(PDataType.LONG)) { + return RoundDecimalExpression.create(expressions); + } else if((fromDataType == PDataType.DECIMAL || fromDataType == PDataType.TIMESTAMP || fromDataType == PDataType.UNSIGNED_TIMESTAMP) && targetDataType.isCoercibleTo(PDataType.DATE)) { return RoundTimestampExpression.create(expressions); - } else if(!fromDataType.isCoercibleTo(targetDataType)) { - throw TypeMismatchException.newException(fromDataType, targetDataType, firstChildExpr.toString()); + } else if(fromDataType.isCastableTo(targetDataType)) { + return firstChildExpr; + } else { + throw TypeMismatchException.newException(fromDataType, targetDataType, firstChildExpr.toString()); } - return firstChildExpr; } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/parse/CeilParseNode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/CeilParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/CeilParseNode.java index c10ba87..08d7feb 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/CeilParseNode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/CeilParseNode.java @@ -57,7 +57,7 @@ public class CeilParseNode extends FunctionParseNode { } else if (firstChildDataType == PDataType.TIMESTAMP || firstChildDataType == PDataType.UNSIGNED_TIMESTAMP) { return CeilTimestampExpression.create(children); } else if(firstChildDataType.isCoercibleTo(PDataType.DECIMAL)) { - return new CeilDecimalExpression(children); + return CeilDecimalExpression.create(children); } else { throw TypeMismatchException.newException(firstChildDataType, "1"); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/parse/FloorParseNode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/FloorParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/FloorParseNode.java index 5030931..8da17ba 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/FloorParseNode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/FloorParseNode.java @@ -57,7 +57,7 @@ public class FloorParseNode extends FunctionParseNode { if(firstChildDataType.isCoercibleTo(PDataType.TIMESTAMP)) { return FloorDateExpression.create(children); } else if(firstChildDataType.isCoercibleTo(PDataType.DECIMAL)) { - return new FloorDecimalExpression(children); + return FloorDecimalExpression.create(children); } else { throw TypeMismatchException.newException(firstChildDataType, "1"); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java index ea9977a..5260f88 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RoundParseNode.java @@ -55,11 +55,11 @@ public class RoundParseNode extends FunctionParseNode { final PDataType firstChildDataType = firstChild.getDataType(); if(firstChildDataType.isCoercibleTo(PDataType.DATE)) { - return RoundDateExpression.create(children); // FIXME: remove cast + return RoundDateExpression.create(children); } else if (firstChildDataType.isCoercibleTo(PDataType.TIMESTAMP)) { - return RoundTimestampExpression.create(children); // FIXME: remove cast + return RoundTimestampExpression.create(children); } else if(firstChildDataType.isCoercibleTo(PDataType.DECIMAL)) { - return new RoundDecimalExpression(children); + return RoundDecimalExpression.create(children); } else { throw TypeMismatchException.newException(firstChildDataType, "1"); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java index c7697bf..bf0ba08 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PDataType.java @@ -389,6 +389,12 @@ public enum PDataType { case DECIMAL: BigDecimal d = (BigDecimal)object; return d.longValueExact(); + case DATE: + case UNSIGNED_DATE: + case TIME: + case UNSIGNED_TIME: + java.util.Date date = (java.util.Date)object; + return date.getTime(); default: return throwConstraintViolationException(actualType,this); } @@ -412,6 +418,10 @@ public enum PDataType { case UNSIGNED_FLOAT: case DOUBLE: case UNSIGNED_DOUBLE: + case DATE: + case UNSIGNED_DATE: + case TIME: + case UNSIGNED_TIME: return actualType.getCodec().decodeLong(b, o, sortOrder); case DECIMAL: BigDecimal bd = (BigDecimal)actualType.toObject(b, o, l, actualType, sortOrder); @@ -472,6 +482,11 @@ public enum PDataType { } @Override + public boolean isCastableTo(PDataType targetType) { + return super.isCastableTo(targetType) || targetType.isCoercibleTo(TIMESTAMP); + } + + @Override public boolean isFixedWidth() { return true; } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9c85887a/phoenix-core/src/test/java/org/apache/phoenix/expression/RoundFloorCeilExpressionsUnitTests.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/RoundFloorCeilExpressionsUnitTests.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/RoundFloorCeilExpressionsUnitTests.java index 068670e..c92fed4 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/expression/RoundFloorCeilExpressionsUnitTests.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/RoundFloorCeilExpressionsUnitTests.java @@ -27,8 +27,6 @@ import java.util.ArrayList; import java.util.List; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.junit.Test; - import org.apache.phoenix.expression.function.CeilDateExpression; import org.apache.phoenix.expression.function.CeilDecimalExpression; import org.apache.phoenix.expression.function.FloorDateExpression; @@ -39,6 +37,7 @@ import org.apache.phoenix.expression.function.TimeUnit; import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.schema.PDataType; import org.apache.phoenix.util.DateUtil; +import org.junit.Test; /** * @@ -115,7 +114,7 @@ public class RoundFloorCeilExpressionsUnitTests { exprs.add(bd); exprs.add(scale); try { - new RoundDecimalExpression(exprs); + RoundDecimalExpression.create(exprs); fail("Evaluation should have failed because only an INTEGER is allowed for second param in a RoundDecimalExpression"); } catch(IllegalDataException e) {