This is an automated email from the ASF dual-hosted git repository.

stoty pushed a commit to branch 5.1
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.1 by this push:
     new 87f8216fe0 PHOENIX-6881 Implement the applicable Date/Time features 
from JDBC 4.2
87f8216fe0 is described below

commit 87f8216fe00058825b34848c0d0ddc94bf5bcdc6
Author: Istvan Toth <st...@apache.org>
AuthorDate: Tue Feb 28 15:55:00 2023 +0100

    PHOENIX-6881 Implement the applicable Date/Time features from JDBC 4.2
---
 .../java/org/apache/phoenix/end2end/Array1IT.java  |  50 +++++
 .../org/apache/phoenix/end2end/DateTimeIT.java     | 215 ++++++++++++++++++++-
 .../org/apache/phoenix/end2end/GetSetObjectIT.java | 183 ++++++++++++++++++
 .../apache/phoenix/compile/ColumnProjector.java    |  14 +-
 .../phoenix/compile/ExpressionProjector.java       |  23 +++
 .../phoenix/jdbc/PhoenixPreparedStatement.java     |  85 +++++---
 .../org/apache/phoenix/jdbc/PhoenixResultSet.java  |  32 ++-
 .../phoenix/schema/types/PArrayDataType.java       |  15 +-
 .../apache/phoenix/schema/types/PBinaryBase.java   |  14 ++
 .../org/apache/phoenix/schema/types/PBoolean.java  |  14 ++
 .../org/apache/phoenix/schema/types/PChar.java     |  13 ++
 .../org/apache/phoenix/schema/types/PDataType.java |  34 +++-
 .../phoenix/schema/types/PDataTypeFactory.java     |  47 +++++
 .../org/apache/phoenix/schema/types/PDate.java     |  42 ++++
 .../apache/phoenix/schema/types/PNumericType.java  |  14 ++
 .../org/apache/phoenix/schema/types/PTime.java     |  43 ++++-
 .../apache/phoenix/schema/types/PTimestamp.java    |  40 +++-
 .../apache/phoenix/schema/types/PUnsignedDate.java |  13 +-
 .../apache/phoenix/schema/types/PUnsignedTime.java |  12 +-
 .../org/apache/phoenix/schema/types/PVarchar.java  |  12 ++
 .../java/org/apache/phoenix/util/DateUtil.java     |   4 +-
 21 files changed, 867 insertions(+), 52 deletions(-)

diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
index 85958f86cd..f5e9587783 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Array1IT.java
@@ -23,6 +23,7 @@ import static 
org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.sql.Array;
 import java.sql.Connection;
@@ -31,6 +32,7 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.Properties;
 
 import org.apache.phoenix.query.BaseTest;
@@ -1002,4 +1004,52 @@ public class Array1IT extends ArrayIT {
         conn.close();
     }
 
+    @Test
+    public void testGetSetObject() throws SQLException {
+        String table = generateUniqueName();
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        String[] s = new String[] { "1", "2" };
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement insertStmt =
+                        conn.prepareStatement("upsert into " + table + "(k, a) 
values (?,?)")) {
+            stmt.executeUpdate(
+                "CREATE TABLE  " + table + "  ( k VARCHAR PRIMARY KEY, a 
VARCHAR(5) ARRAY)");
+            PhoenixArray phoenixArray = (PhoenixArray) 
conn.createArrayOf("VARCHAR", s);
+
+            insertStmt.setString(1, "a");
+            insertStmt.setArray(2, phoenixArray);
+            insertStmt.executeUpdate();
+            insertStmt.setString(1, "b");
+            insertStmt.setObject(2, phoenixArray);
+            insertStmt.executeUpdate();
+            conn.commit();
+            try {
+                insertStmt.setString(1, "c");
+                insertStmt.setObject(2, s);
+                insertStmt.executeUpdate();
+                conn.commit();
+                fail("must only accept java.sql.Array objects");
+            } catch (Exception e) {
+                // FIXME we should be throwing an SQLException
+                // Success
+            }
+
+            ResultSet rs = stmt.executeQuery("select * from " + table);
+            int rows = 0;
+            while (rs.next()) {
+                rows++;
+                assertEquals(phoenixArray, rs.getObject(2));
+                assertEquals(phoenixArray, rs.getObject(2, 
java.sql.Array.class));
+                assertEquals(phoenixArray, rs.getArray(2));
+                try {
+                    rs.getObject(2, String[].class);
+                    fail("must only accept java.sql.Array subclasses");
+                } catch (SQLException e) {
+                    // Success
+                }
+            }
+            assertEquals(2, rows);
+        }
+    }
 }
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
index 61378fbc3d..af6594afd5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DateTimeIT.java
@@ -53,13 +53,14 @@ import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.Format;
 import java.time.LocalDate;
+import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
+import java.util.List;
 import java.util.Properties;
 import java.util.TimeZone;
-import java.util.List;
-import java.util.Arrays;
 
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
@@ -69,6 +70,7 @@ import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PDate;
 import org.apache.phoenix.schema.types.PTime;
@@ -2157,7 +2159,7 @@ public class DateTimeIT extends ParallelStatsDisabledIT {
         Connection conn1 = DriverManager.getConnection(getUrl(), props);
 
         String tableName = generateUniqueName();
-        String ddl = "CREATE TABLE IF NOT EXISTS " + tableName +
+        String ddl = "CREATE TABLE " + tableName +
                 " (k1 INTEGER PRIMARY KEY," +
                 " v_date DATE," +
                 " v_time TIME," +
@@ -2212,4 +2214,211 @@ public class DateTimeIT extends ParallelStatsDisabledIT 
{
         assertTrue(formatter instanceof FastDateFormat);
         assertEquals(((FastDateFormat)formatter).getTimeZone().getID(), 
timeZoneId);
     }
+
+    @Test
+    public void testJdbc42ApiWithoutDisplacement() throws SQLException {
+        testJdbc42Api(false);
+    }
+
+    @Test
+    public void testJdbc42ApiWithDisplacement() throws SQLException {
+        testJdbc42Api(true);
+    }
+
+    private void testJdbc42Api(boolean withDisplacement) throws SQLException {
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        ZoneId testZoneId;
+        if (withDisplacement) {
+            testZoneId = ZoneOffset.systemDefault();
+            props.setProperty(QueryServices.APPLY_TIME_ZONE_DISPLACMENT_ATTRIB,
+                Boolean.TRUE.toString());
+        } else {
+            testZoneId = ZoneId.of("UTC");
+        }
+
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+
+            String tableName = generateUniqueName();
+            String ddl =
+                    "CREATE TABLE " + tableName
+                            + " (ID INTEGER PRIMARY KEY, D DATE, T TIME, S 
TIMESTAMP, "
+                            + "UD UNSIGNED_DATE, UT UNSIGNED_TIME, US 
UNSIGNED_TIMESTAMP)";
+            java.time.LocalDateTime localDateTime = 
java.time.LocalDateTime.now();
+            java.time.LocalDateTime localDateTimeMs =
+                    localDateTime.withNano((localDateTime.getNano() / 1000000) 
* 1000000);
+            java.time.LocalDate localDate = localDateTime.toLocalDate();
+            java.time.LocalTime localTime = localDateTime.toLocalTime();
+            java.time.LocalTime localTimeMs =
+                    localTime.withNano((localDateTime.getNano() / 1000000) * 
1000000);
+
+            java.sql.Timestamp javaSqlTimestampFull =
+                    
java.sql.Timestamp.from(localDateTime.atZone(testZoneId).toInstant());
+            java.sql.Timestamp javaSqlTimestampFullMs =
+                    new java.sql.Timestamp(javaSqlTimestampFull.getTime());
+            java.sql.Timestamp javaSqlTimestampDatePart =
+                    java.sql.Timestamp
+                            
.from(localDate.atStartOfDay().atZone(testZoneId).toInstant());
+            java.sql.Timestamp javaSqlTimestampTimePart =
+                    java.sql.Timestamp.from(
+                        
localTime.atDate(DateUtil.LD_EPOCH).atZone(testZoneId).toInstant());
+            java.sql.Timestamp javaSqlTimestampTimePartMs =
+                    new java.sql.Timestamp(javaSqlTimestampTimePart.getTime());
+
+            java.util.Date javaUtilDateFull = new 
java.util.Date(javaSqlTimestampFull.getTime());
+            java.util.Date javaUtilDateDatePart =
+                    new java.util.Date(javaSqlTimestampDatePart.getTime());
+            java.util.Date javaUtilDateTimePart =
+                    new java.util.Date(javaSqlTimestampTimePart.getTime());
+
+            java.sql.Date javaSqlDateFull = new 
java.sql.Date(javaSqlTimestampFull.getTime());
+            java.sql.Time javaSqlTimeFull = new 
java.sql.Time(javaSqlTimestampFull.getTime());
+
+            // Differ by date
+            String dml = "UPSERT INTO " + tableName + " VALUES (?, ?, ?, ?, ?, 
?, ?)";
+            try (Statement stmt = conn.createStatement();
+                    PreparedStatement ps = conn.prepareStatement(dml)) {
+                stmt.executeUpdate(ddl);
+
+                ps.setInt(1, 1);
+                ps.setObject(2, localDateTime);
+                ps.setObject(3, localDateTime);
+                ps.setObject(4, localDateTime);
+                ps.setObject(5, localDateTime);
+                ps.setObject(6, localDateTime);
+                ps.setObject(7, localDateTime);
+                assertEquals(1, ps.executeUpdate());
+
+                ps.setInt(1, 2);
+                ps.setObject(2, localDate);
+                ps.setObject(3, localDate);
+                ps.setObject(4, localDate);
+                ps.setObject(5, localDate);
+                ps.setObject(6, localDate);
+                ps.setObject(7, localDate);
+                assertEquals(1, ps.executeUpdate());
+
+                ps.setInt(1, 3);
+                ps.setObject(2, localTime);
+                ps.setObject(3, localTime);
+                ps.setObject(4, localTime);
+                ps.setObject(5, localTime);
+                ps.setObject(6, localTime);
+                ps.setObject(7, localTime);
+                assertEquals(1, ps.executeUpdate());
+
+                ps.setInt(1, 4);
+                ps.setObject(2, javaSqlTimestampFull);
+                ps.setObject(3, javaSqlTimestampFull);
+                ps.setObject(4, javaSqlTimestampFull);
+                ps.setObject(5, javaSqlTimestampFull);
+                ps.setObject(6, javaSqlTimestampFull);
+                ps.setObject(7, javaSqlTimestampFull);
+                assertEquals(1, ps.executeUpdate());
+
+                ps.setInt(1, 5);
+                ps.setObject(2, javaSqlDateFull);
+                ps.setObject(3, javaSqlDateFull);
+                ps.setObject(4, javaSqlDateFull);
+                ps.setObject(5, javaSqlDateFull);
+                ps.setObject(6, javaSqlDateFull);
+                ps.setObject(7, javaSqlDateFull);
+                assertEquals(1, ps.executeUpdate());
+
+                ps.setInt(1, 6);
+                ps.setObject(2, javaSqlTimeFull);
+                ps.setObject(3, javaSqlTimeFull);
+                ps.setObject(4, javaSqlTimeFull);
+                ps.setObject(5, javaSqlTimeFull);
+                ps.setObject(6, javaSqlTimeFull);
+                ps.setObject(7, javaSqlTimeFull);
+                assertEquals(1, ps.executeUpdate());
+
+                conn.commit();
+                ResultSet rs = stmt.executeQuery("select * from " + tableName 
+ " order by id");
+                assertTrue(rs.next());
+                assertEquals(1, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    if (i == 4 || i == 7) {
+                        // Nanos
+                        assertEquals(javaSqlTimestampFull, rs.getTimestamp(i));
+                        assertEquals(localDateTime, rs.getObject(i, 
java.time.LocalDateTime.class));
+                        // Non-standard extensions
+                        assertEquals(localTime, rs.getObject(i, 
java.time.LocalTime.class));
+                    } else {
+                        // Millis
+                        assertEquals(javaSqlDateFull, rs.getTimestamp(i));
+                        assertEquals(localDateTimeMs,
+                            rs.getObject(i, java.time.LocalDateTime.class));
+                        // Non-standard extension
+                        assertEquals(localTimeMs, rs.getObject(i, 
java.time.LocalTime.class));
+
+                    }
+                    // Non-standard extension
+                    assertEquals(localDate, rs.getObject(i, 
java.time.LocalDate.class));
+                    assertEquals(javaUtilDateFull, rs.getObject(i, 
java.util.Date.class));
+                }
+                // Make sure name based getObject() works
+                assertEquals(localDateTimeMs, rs.getObject("D", 
java.time.LocalDateTime.class));
+                assertEquals(localDateTimeMs, rs.getObject("T", 
java.time.LocalDateTime.class));
+                assertEquals(localDateTime, rs.getObject("S", 
java.time.LocalDateTime.class));
+                assertEquals(localDateTimeMs, rs.getObject("UD", 
java.time.LocalDateTime.class));
+                assertEquals(localDateTimeMs, rs.getObject("UT", 
java.time.LocalDateTime.class));
+                assertEquals(localDateTime, rs.getObject("US", 
java.time.LocalDateTime.class));
+
+                assertTrue(rs.next());
+                assertEquals(2, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    assertEquals(javaSqlTimestampDatePart, rs.getTimestamp(i));
+                    assertEquals(javaUtilDateDatePart, rs.getObject(i, 
java.util.Date.class));
+                }
+                assertEquals(localDate, rs.getObject(2, 
java.time.LocalDate.class));
+                assertEquals(localDate, rs.getObject(5, 
java.time.LocalDate.class));
+
+                assertTrue(rs.next());
+                assertEquals(3, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    if (i == 4 || i == 7) {
+                        assertEquals(javaSqlTimestampTimePart, 
rs.getTimestamp(i));
+                        // Can read nanos from timestamp field
+                        assertEquals(localTime, rs.getObject(i, 
java.time.LocalTime.class));
+                    } else {
+                        // Millis
+                        assertEquals(javaSqlTimestampTimePartMs, 
rs.getTimestamp(i));
+                    }
+                    assertEquals(javaUtilDateTimePart, rs.getObject(i, 
java.util.Date.class));
+                }
+                assertEquals(localTimeMs, rs.getObject(3, 
java.time.LocalTime.class));
+                assertEquals(localTimeMs, rs.getObject(6, 
java.time.LocalTime.class));
+
+                assertTrue(rs.next());
+                assertEquals(4, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    if (i == 4 || i == 7) {
+                        // Nanos
+                        assertEquals(javaSqlTimestampFull,
+                            rs.getObject(i, java.sql.Timestamp.class));
+                    } else {
+                        // Millis
+                        assertEquals(javaSqlTimestampFullMs,
+                            rs.getObject(i, java.sql.Timestamp.class));
+                    }
+                }
+
+                assertTrue(rs.next());
+                assertEquals(5, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    assertEquals(javaSqlDateFull, rs.getObject(i, 
java.sql.Date.class));
+                }
+
+                assertTrue(rs.next());
+                assertEquals(6, rs.getInt(1));
+                for (int i = 2; i <= 7; i++) {
+                    // java.sql.time doesn't consider fractional seconds when 
comparing or in
+                    // toString, so we compare the millis values.
+                    assertEquals(javaSqlTimeFull.getTime(),
+                        rs.getObject(i, java.sql.Time.class).getTime());
+                }
+            }
+        }
+    }
 }
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetObjectIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetObjectIT.java
new file mode 100644
index 0000000000..4d3a9dc80e
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetObjectIT.java
@@ -0,0 +1,183 @@
+/*
+ * 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.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.expression.function.SqrtFunction;
+import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * End to end tests for {@link SqrtFunction}
+ */
+@Category(ParallelStatsDisabledTest.class)
+public class GetSetObjectIT extends ParallelStatsDisabledIT {
+
+    // Temporals are tested in DateTimeIT
+    // Arrays are tested in Array1IT
+
+    @Test
+    public void testNonNumeric() throws SQLException {
+        Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES);
+        String tableName = generateUniqueName();
+
+        byte[] bytes = { 1, 2 };
+        String characters = "11";
+
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement insertStmt =
+                        conn.prepareStatement("upsert into " + tableName
+                                + "(id, binary_col, varbinary_col, 
boolean_col, char_col, varchar_col) values (?,?,?,?,?,?)")) {
+            stmt.executeUpdate("CREATE TABLE " + tableName
+                    + " (ID INTEGER PRIMARY KEY, BINARY_COL BINARY(2), 
VARBINARY_COL VARBINARY, BOOLEAN_COL BOOLEAN, CHAR_COL CHAR(2), VARCHAR_COL 
VARCHAR)");
+            insertStmt.setInt(1, 1);
+            // The type specific setters also delegate to these
+            insertStmt.setObject(2, bytes);
+            insertStmt.setObject(3, bytes);
+            insertStmt.setObject(4, Boolean.TRUE);
+            insertStmt.setObject(5, characters);
+            insertStmt.setObject(6, characters);
+            insertStmt.executeUpdate();
+            conn.commit();
+
+            ResultSet rs = stmt.executeQuery("select * from " + tableName);
+            while (rs.next()) {
+                assertArrayEquals(bytes, rs.getBytes(2));
+                assertArrayEquals(bytes, (byte[]) rs.getObject(2));
+                assertArrayEquals(bytes, rs.getObject(2, byte[].class));
+                try {
+                    rs.getObject(2, Long.class);
+                    fail("We only implement byte[]");
+                } catch (SQLException e) {
+                    
assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), e.getErrorCode());
+                }
+                assertArrayEquals(bytes, rs.getBytes(3));
+                assertArrayEquals(bytes, (byte[]) rs.getObject(3));
+                assertArrayEquals(bytes, rs.getObject(3, byte[].class));
+                try {
+                    rs.getObject(3, Long.class);
+                    fail("We only implement byte[]");
+                } catch (SQLException e) {
+                    
assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), e.getErrorCode());
+                }
+                assertEquals(Boolean.TRUE, rs.getBoolean(4));
+                assertEquals(Boolean.TRUE, rs.getObject(4));
+                assertEquals(Boolean.TRUE, rs.getObject(4, Boolean.class));
+                try {
+                    assertEquals(Boolean.TRUE, rs.getObject(3, Long.class));
+                    fail("We only implement Boolean");
+                } catch (SQLException e) {
+                    
assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), e.getErrorCode());
+                }
+                assertEquals(characters, rs.getString(5));
+                assertEquals(characters, rs.getObject(5));
+                assertEquals(characters, rs.getObject(5, String.class));
+                try {
+                    assertEquals(Boolean.TRUE, rs.getObject(5, Boolean.class));
+                    fail("We only implement String");
+                } catch (SQLException e) {
+                    
assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), e.getErrorCode());
+                }
+                assertEquals(characters, rs.getString(6));
+                assertEquals(characters, rs.getObject(6));
+                assertEquals(characters, rs.getObject(6, String.class));
+                try {
+                    assertEquals(Boolean.TRUE, rs.getObject(6, Boolean.class));
+                    fail("We only implement String");
+                } catch (SQLException e) {
+                    
assertEquals(SQLExceptionCode.TYPE_MISMATCH.getErrorCode(), e.getErrorCode());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testNumeric() throws SQLException {
+        Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES);
+        String tableName = generateUniqueName();
+
+        try (Connection conn = DriverManager.getConnection(getUrl(), props);
+                Statement stmt = conn.createStatement();
+                PreparedStatement insertStmt =
+                        conn.prepareStatement("upsert into " + tableName
+                                + "(id, TINY_COL, SMALL_COL, INT_COL, BIG_COL, 
FLOAT_COL, DOUBLE_COL, DECIMAL_COL) values (?,?,?,?,?,?,?,?)")) {
+            stmt.executeUpdate("CREATE TABLE " + tableName
+                    + " (ID INTEGER PRIMARY KEY, TINY_COL TINYINT, SMALL_COL 
SMALLINT, INT_COL INTEGER, BIG_COL BIGINT, FLOAT_COL FLOAT, DOUBLE_COL DOUBLE, 
DECIMAL_COL DECIMAL)");
+            insertStmt.setInt(1, 1);
+            // The type specific setters also delegate to these
+            insertStmt.setObject(2, Byte.valueOf("1"));
+            insertStmt.setObject(3, Short.valueOf("1"));
+            insertStmt.setObject(4, Integer.valueOf("1"));
+            insertStmt.setObject(5, Long.valueOf("1"));
+            insertStmt.setObject(6, Float.valueOf("1.0"));
+            insertStmt.setObject(7, Double.valueOf("1.0"));
+            insertStmt.setObject(8, BigDecimal.ONE);
+            insertStmt.executeUpdate();
+            conn.commit();
+
+            ResultSet rs = stmt.executeQuery("select * from " + tableName);
+            assertTrue(rs.next());
+            assertEquals(Byte.valueOf("1"), rs.getObject(2));
+            assertEquals(Short.valueOf("1"), rs.getObject(3));
+            assertEquals(Integer.valueOf("1"), rs.getObject(4));
+            assertEquals(Long.valueOf("1"), rs.getObject(5));
+            assertEquals(Float.valueOf("1.0"), rs.getObject(6));
+            assertEquals(Double.valueOf("1.0"), rs.getObject(7));
+            assertEquals(BigDecimal.ONE, rs.getObject(8));
+            for (int i = 2; i <= 8; i++) {
+                assertEquals(Byte.valueOf("1"), Byte.valueOf(rs.getByte(i)));
+                assertEquals(Byte.valueOf("1"), rs.getObject(i, Byte.class));
+                assertEquals(Short.valueOf("1"), 
Short.valueOf(rs.getShort(i)));
+                assertEquals(Short.valueOf("1"), rs.getObject(i, Short.class));
+                assertEquals(Integer.valueOf("1"), 
Integer.valueOf(rs.getInt(i)));
+                assertEquals(Integer.valueOf("1"), rs.getObject(i, 
Integer.class));
+                assertEquals(Long.valueOf("1"), Long.valueOf(rs.getLong(i)));
+                assertEquals(Long.valueOf("1"), rs.getObject(i, Long.class));
+                assertEquals(Float.valueOf("1.0"), 
Float.valueOf(rs.getFloat(i)));
+                assertEquals(Float.valueOf("1.0"), rs.getObject(i, 
Float.class));
+                assertEquals(Double.valueOf("1.0"), 
Double.valueOf(rs.getDouble(i)));
+                assertEquals(Double.valueOf("1.0"), rs.getObject(i, 
Double.class));
+                assertTrue(BigDecimal.ONE.abs().compareTo(rs.getBigDecimal(i)) 
== 0);
+                assertTrue(BigDecimal.ONE.abs().compareTo(rs.getObject(i, 
BigDecimal.class)) == 0);
+                try {
+                    rs.getObject(i, Boolean.class);
+                    fail("Non-numeric types are not supported");
+                } catch (SQLException e) {
+                    // Expected
+                }
+            }
+        }
+    }
+}
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ColumnProjector.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ColumnProjector.java
index 934d73c90d..88c795685c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ColumnProjector.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ColumnProjector.java
@@ -63,6 +63,18 @@ public interface ColumnProjector {
      * @throws SQLException
      */
     Object getValue(Tuple tuple, PDataType type, ImmutableBytesWritable ptr) 
throws SQLException;
-    
+
+    /**
+     * Get the value of the column, coercing it if necessary to the specified 
type
+     * @param tuple the row containing the column
+     * @param type the type to which to coerce the binary value
+     * @param ptr used to retrieve the value
+     * @param jdbcType The java type to convert to, for rs.getObject()
+     * @return the object representation of the column value.
+     * @throws SQLException
+     */
+    Object getValue(Tuple tuple, PDataType type, ImmutableBytesWritable ptr, 
Class jdbcType)
+            throws SQLException;
+
     boolean isCaseSensitive();
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionProjector.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionProjector.java
index b333ac65f2..39349b0fbe 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionProjector.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionProjector.java
@@ -83,6 +83,29 @@ public class ExpressionProjector implements ColumnProjector {
         }
     }
 
+    @Override
+    public final Object getValue(Tuple tuple, PDataType type, 
ImmutableBytesWritable ptr,
+            Class jdbcType) throws SQLException {
+        try {
+            Expression expression = getExpression();
+            if (!expression.evaluate(tuple, ptr)) {
+                return null;
+            }
+            if (ptr.getLength() == 0) {
+                return null;
+            }
+            return type.toObject(ptr, expression.getDataType(), 
expression.getSortOrder(),
+                expression.getMaxLength(), expression.getScale(), jdbcType);
+        } catch (RuntimeException e) {
+            // FIXME: Expression.evaluate does not throw SQLException
+            // so this will unwrap throws from that.
+            if (e.getCause() instanceof SQLException) {
+                throw (SQLException) e.getCause();
+            }
+            throw e;
+        }
+    }
+
     @Override
     public boolean isCaseSensitive() {
         return isCaseSensitive;
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
index d1a775d9b5..d955777cd8 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixPreparedStatement.java
@@ -39,6 +39,7 @@ import java.sql.SQLFeatureNotSupportedException;
 import java.sql.SQLXML;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
@@ -369,15 +370,19 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setDate(int parameterIndex, Date x, Calendar cal) throws 
SQLException {
+        setParameter(parameterIndex, processDate(x, cal));
+    }
+
+    private java.sql.Date processDate(Date x, Calendar cal) {
         if (x != null) { // Since Date is mutable, make a copy
             if (connection.isApplyTimeZoneDisplacement()) {
-                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+                return DateUtil.applyInputDisplacement(x, cal.getTimeZone());
             } else {
                 // Since Date is mutable, make a copy
-                x = new Date(x.getTime());
+                return new Date(x.getTime());
             }
         }
-        setParameter(parameterIndex, x);
+        return x;
     }
 
     @Override
@@ -442,30 +447,12 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setObject(int parameterIndex, Object o) throws SQLException {
-        if (o instanceof java.util.Date) {
-            // TODO add java.time when implemented
-            if (connection.isApplyTimeZoneDisplacement()) {
-                if (o instanceof java.sql.Timestamp) {
-                    o =
-                            
DateUtil.applyInputDisplacement((java.sql.Timestamp) o,
-                                localCalendar.getTimeZone());
-                } else if (o instanceof java.sql.Time) {
-                    o =
-                            DateUtil.applyInputDisplacement((java.sql.Time) o,
-                                localCalendar.getTimeZone());
-                } else if (o instanceof java.sql.Date) {
-                    o =
-                            DateUtil.applyInputDisplacement((java.sql.Date) o,
-                                localCalendar.getTimeZone());
-                }
-            }
-            // FIXME if we make a copy in setDate() from temporals, why not 
here ?
-        }
-        setParameter(parameterIndex, o);
+        setParameter(parameterIndex, processObject(o));
     }
 
     @Override
     public void setObject(int parameterIndex, Object o, int targetSqlType) 
throws SQLException {
+        o = processObject(o);
         PDataType targetType = PDataType.fromTypeId(targetSqlType);
         if (o != null) {
             PDataType sourceType = PDataType.fromLiteral(o);
@@ -511,15 +498,19 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setTime(int parameterIndex, Time x, Calendar cal) throws 
SQLException {
+        setParameter(parameterIndex, processTime(x, cal));
+    }
+
+    private java.sql.Time processTime(Time x, Calendar cal) {
         if (x != null) {
             if (connection.isApplyTimeZoneDisplacement()) {
-                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+                return DateUtil.applyInputDisplacement(x, cal.getTimeZone());
             } else {
-                // Since Date is mutable, make a copy
-                x = new Time(x.getTime());
+                // Since Time is mutable, make a copy
+                return new Time(x.getTime());
             }
         }
-        setParameter(parameterIndex, x);
+        return x;
     }
 
     @Override
@@ -529,16 +520,20 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
 
     @Override
     public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) 
throws SQLException {
+        setParameter(parameterIndex, processTimestamp(x, cal));
+    }
+
+    private java.sql.Timestamp processTimestamp(Timestamp x, Calendar cal) {
         if (x != null) {
             if (connection.isApplyTimeZoneDisplacement()) {
-                x = DateUtil.applyInputDisplacement(x, cal.getTimeZone());
+                return DateUtil.applyInputDisplacement(x, cal.getTimeZone());
             } else {
                 int nanos = x.getNanos();
                 x = new Timestamp(x.getTime());
                 x.setNanos(nanos);
             }
         }
-        setParameter(parameterIndex, x);
+        return x;
     }
 
     @Override
@@ -550,4 +545,36 @@ public class PhoenixPreparedStatement extends 
PhoenixStatement implements Prepar
     public void setUnicodeStream(int parameterIndex, InputStream x, int 
length) throws SQLException {
         throw new SQLFeatureNotSupportedException();
     }
+
+    // Convert objects to their canonical forms as expected by the Phoenix 
type system and apply
+    // TZ displacement
+    private Object processObject(Object o) {
+        // We cannot use the direct conversions, as those work in the local TZ.
+        if (o instanceof java.time.temporal.Temporal) {
+            if (o instanceof java.time.LocalDateTime) {
+                return java.sql.Timestamp.from(((java.time.LocalDateTime) 
o).toInstant(ZoneOffset.UTC));
+            } else if (o instanceof java.time.LocalDate) {
+                // java.sql.Date.from() is inherited from j.u.Date.from() and 
returns j.u.Date
+                return new java.sql.Date(((java.time.LocalDate) 
o).atStartOfDay()
+                        .toInstant(ZoneOffset.UTC).toEpochMilli());
+            } else if (o instanceof java.time.LocalTime) {
+                // preserve nanos if writing to timestamp
+                return java.sql.Timestamp.from(
+                    ((java.time.LocalTime) 
o).atDate(DateUtil.LD_EPOCH).toInstant(ZoneOffset.UTC));
+            }
+        } else if (o instanceof java.util.Date) {
+            if (o instanceof java.sql.Date) {
+                return processDate((java.sql.Date) o, localCalendar);
+            } else if (o instanceof java.sql.Timestamp) {
+                return processTimestamp((java.sql.Timestamp) o, localCalendar);
+            } else if (o instanceof java.sql.Time) {
+                return processTime((java.sql.Time) o, localCalendar);
+            } else {
+                // We could use Timestamp, we don't have millis, and don't 
differentiate anyway
+                return processDate(new java.sql.Date(((java.util.Date) 
o).getTime()), localCalendar)
+                        .getTime();
+            }
+        }
+        return o;
+    }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
index 55a6de250b..b2275b3d94 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
@@ -759,7 +759,8 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
             return DateUtil.applyOutputDisplacement(value, cal.getTimeZone());
         } else {
             return value;
-        }    }
+        }
+    }
 
     @Override
     public Timestamp getTimestamp(String columnLabel, Calendar cal) throws 
SQLException {
@@ -1390,15 +1391,36 @@ public class PhoenixResultSet implements ResultSet, 
SQLCloseable {
     @SuppressWarnings("unchecked")
     @Override
     public <T> T getObject(int columnIndex, Class<T> type) throws SQLException 
{
-        return (T) getObject(columnIndex); // Just ignore type since we only 
support built-in types
+        if (type.equals(String.class)) {
+            // Special case, the connection specific formatter is not 
available in the Type system
+            return (T) getString(columnIndex);
+        } else if (java.util.Date.class.isAssignableFrom(type)) {
+            // The displacement handling code is in the specific getters
+            if (java.sql.Timestamp.class.isAssignableFrom(type)) {
+                return (T) getTimestamp(columnIndex);
+            } else if (java.sql.Date.class.isAssignableFrom(type)) {
+                return (T) getDate(columnIndex);
+            } else if (java.sql.Time.class.isAssignableFrom(type)) {
+                return (T) getTime(columnIndex);
+            } else if (java.util.Date.class.equals(type)) {
+                return (T) new java.util.Date(getDate(columnIndex).getTime());
+            }
+        }
+        checkCursorState();
+        ColumnProjector projector = 
getRowProjector().getColumnProjector(columnIndex - 1);
+
+        Object value =
+                projector.getValue(currentRow, 
projector.getExpression().getDataType(), ptr, type);
+
+        wasNull = (value == null);
+        return (T) value;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public <T> T getObject(String columnLabel, Class<T> type) throws 
SQLException {
-        return (T) getObject(columnLabel); // Just ignore type since we only 
support built-in types
+        return getObject(findColumn(columnLabel), type);
     }
-    
+
     @VisibleForTesting
     public ResultIterator getUnderlyingIterator() {
         return scanner;
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
index 497c90fdae..9646ee68ea 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
@@ -33,15 +33,14 @@ import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.ConstraintViolationException;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.ValueSchema;
+import org.apache.phoenix.thirdparty.com.google.common.base.Objects;
+import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TrustedByteArrayOutputStream;
 
 import edu.umd.cs.findbugs.annotations.SuppressWarnings;
 
-import org.apache.phoenix.thirdparty.com.google.common.base.Objects;
-import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
-
 /**
  * The datatype for PColummns that are Arrays. Any variable length array would 
follow the below order. Every element
  * would be seperated by a seperator byte '0'. Null elements are counted and 
once a first non null element appears we
@@ -388,6 +387,16 @@ public abstract class PArrayDataType<T> extends 
PDataType<T> {
         return Math.abs(getSerializedOffset(bytes, arrayIndex, useShort, 
indexOffset, serializationVersion));
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        if (java.sql.Array.class.isAssignableFrom(jdbcType)) {
+            return toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
        static int getSerializedOffset(byte[] bytes, int arrayIndex, boolean 
useShort, int indexOffset, byte serializationVersion) {
                int offset;
         if (useShort) {
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java
index 562875da5e..178e331eaa 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java
@@ -17,6 +17,8 @@
  */
 package org.apache.phoenix.schema.types;
 
+import java.sql.SQLException;
+
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
@@ -112,4 +114,16 @@ public abstract class PBinaryBase extends 
PDataType<byte[]> {
         }
         return true;
     }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        if (jdbcType == byte[].class) {
+            return toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        } else {
+            // TODO we could duplicate getString() for String type
+            throw newMismatchException(actualType, jdbcType);
+        }
+    }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
index f827f36514..35e38486b0 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBoolean.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.schema.types;
 
 import java.math.BigDecimal;
+import java.sql.SQLException;
 import java.sql.Types;
 
 import org.apache.phoenix.schema.SortOrder;
@@ -85,6 +86,19 @@ public class PBoolean extends PDataType<Boolean> {
         return null;
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        // FIXME according to the JDBC spec, we should support all these types:
+        // TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, 
NUMERIC, BIT,
+        // BOOLEAN, CHAR, VARCHAR, LONGVARCHAR
+        if (Boolean.class.isAssignableFrom(jdbcType)) {
+            return toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
     @Override
     public boolean isCoercibleTo(PDataType targetType) {
         return super.isCoercibleTo(targetType) || 
targetType.equals(PBinary.INSTANCE);
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
index 4f63003b33..b36768d5c1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PChar.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.schema.types;
 
+import java.sql.SQLException;
 import java.sql.Types;
 import java.text.Format;
 import java.util.Arrays;
@@ -123,6 +124,17 @@ public class PChar extends PDataType<String> {
       return s;
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        if (String.class.isAssignableFrom(jdbcType)) {
+            //We don't actually get here, we shortcut the String case in 
ResultSet
+            return toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
     @Override
     public Object toObject(Object object, PDataType actualType) {
       if (equalsAny(actualType, PVarchar.INSTANCE, this)) {
@@ -230,4 +242,5 @@ public class PChar extends PDataType<String> {
     public Object getSampleValue(Integer maxLength, Integer arrayLength) {
       return PVarchar.INSTANCE.getSampleValue(maxLength, arrayLength);
     }
+
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
index 5fa706a24f..cfa8a56141 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataType.java
@@ -289,6 +289,11 @@ public abstract class PDataType<T> implements DataType<T>, 
Comparable<PDataType<
                 .setMessage(source + " cannot be coerced to " + 
target).build().buildException());
     }
 
+    protected static SQLException newMismatchException(PDataType source, Class 
target) {
+        return new SQLExceptionInfo.Builder(SQLExceptionCode.TYPE_MISMATCH)
+                .setMessage(source + " cannot be retrieved as " + 
target).build().buildException();
+    }
+
     protected static RuntimeException newIllegalDataException() {
         return new IllegalDataException(new 
SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA).build()
                 .buildException());
@@ -857,6 +862,7 @@ public abstract class PDataType<T> implements DataType<T>, 
Comparable<PDataType<
         return (date == null || date.getTime() >= 0);
     }
 
+    //FIXME this is misnamed
     protected static void throwIfNonNegativeDate(java.util.Date date) {
         if (!isNonNegativeDate(date)) { throw newIllegalDataException("Value 
may not be negative(" + date + ")"); }
     }
@@ -865,6 +871,7 @@ public abstract class PDataType<T> implements DataType<T>, 
Comparable<PDataType<
         return v == null || v.longValue() >= 0;
     }
 
+    //FIXME this is misnamed
     protected static void throwIfNonNegativeNumber(Number v) {
         if (!isNonNegativeNumber(v)) { throw newIllegalDataException("Value 
may not be negative(" + v + ")"); }
     }
@@ -962,6 +969,10 @@ public abstract class PDataType<T> implements DataType<T>, 
Comparable<PDataType<
     public abstract Object toObject(byte[] bytes, int offset, int length, 
PDataType actualType, SortOrder sortOrder,
             Integer maxLength, Integer scale);
 
+    public abstract Object toObject(byte[] bytes, int offset, int length, 
PDataType actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException;
+
     @SuppressWarnings("unchecked")
     @Override
     public T decode(PositionedByteRange pbr) {
@@ -1005,6 +1016,13 @@ public abstract class PDataType<T> implements 
DataType<T>, Comparable<PDataType<
         return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), 
actualType, sortOrder, maxLength, scale);
     }
 
+    public final Object toObject(ImmutableBytesWritable ptr, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), 
actualType, sortOrder,
+            maxLength, scale, jdbcType);
+    }
+
     public final Object toObject(ImmutableBytesWritable ptr, SortOrder 
sortOrder, Integer maxLength, Integer scale) {
         return this.toObject(ptr.get(), ptr.getOffset(), ptr.getLength(), 
this, sortOrder, maxLength, scale);
     }
@@ -1147,15 +1165,20 @@ public abstract class PDataType<T> implements 
DataType<T>, Comparable<PDataType<
         return KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, 
upperInclusive);
     }
 
+    //TODO this could be improved by some lookup tables instead of iterating 
over all types
     public static PDataType fromLiteral(Object value) {
-        if (value == null) { return null; }
+        if (value == null) {
+            return null;
+        }
         for (PDataType type : PDataType.values()) {
             if (type.isArrayType()) {
                 if (value instanceof PhoenixArray) {
                     PhoenixArray arr = (PhoenixArray)value;
                     if ((type.getSqlType() == arr.baseType.sqlType + 
PDataType.ARRAY_TYPE_BASE)
-                            && type.getJavaClass().isInstance(value)) { return 
type; }
-                } else {
+                            && type.getJavaClass().isInstance(value)) {
+                        return type;
+                    }
+                } else if (value instanceof Array) {
                     Array arr = (Array) value;
                     try {
                         // Does the array's component type make sense for what 
we were told it is
@@ -1165,7 +1188,9 @@ public abstract class PDataType<T> implements 
DataType<T>, Comparable<PDataType<
                     } catch (SQLException e) { /* Passthrough to fail */ }
                 }
             } else {
-                if (type.getJavaClass().isInstance(value)) { return type; }
+                if (type.getJavaClass().isInstance(value)) {
+                    return type;
+                }
             }
         }
         throw new UnsupportedOperationException("Unsupported literal value [" 
+ value + "] of type "
@@ -1191,4 +1216,5 @@ public abstract class PDataType<T> implements 
DataType<T>, Comparable<PDataType<
         Preconditions.checkArgument(arrayType.isArrayType(), "Not a phoenix 
array type");
         return fromTypeId(arrayType.getSqlType() - ARRAY_TYPE_BASE);
     }
+
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
index 45a96573ee..3da8b2ef21 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDataTypeFactory.java
@@ -33,6 +33,9 @@ public class PDataTypeFactory {
   private final PDataType[] orderedTypes;
   private final SortedSet<PDataType> types;
   private final Map<Class<? extends PDataType>, PDataType> classToInstance;
+  private final Map<Class, PDataType> javaClassToInstance;
+  private final Map<Class, PDataType> javaClassToUnsignedInstance;
+  private final SortedSet<PDataType> unsignedtypes;
 
   public static PDataTypeFactory getInstance() {
     if (INSTANCE == null) {
@@ -48,6 +51,12 @@ public class PDataTypeFactory {
         return Integer.compare(o1.ordinal(), o2.ordinal());
       }
     });    // TODO: replace with ServiceLoader or some other plugin system
+    unsignedtypes = new TreeSet<>(new Comparator<PDataType>() {
+        @Override
+        public int compare(PDataType o1, PDataType o2) {
+          return Integer.compare(o1.ordinal(), o2.ordinal());
+        }
+      });    // TODO: replace with ServiceLoader or some other plugin system
     types.add(PBinary.INSTANCE);
     types.add(PBinaryArray.INSTANCE);
     types.add(PChar.INSTANCE);
@@ -75,23 +84,41 @@ public class PDataTypeFactory {
     types.add(PTinyint.INSTANCE);
     types.add(PTinyintArray.INSTANCE);
     types.add(PUnsignedDate.INSTANCE);
+    unsignedtypes.add(PUnsignedDate.INSTANCE);
     types.add(PUnsignedDateArray.INSTANCE);
+    unsignedtypes.add(PUnsignedDateArray.INSTANCE);
     types.add(PUnsignedDouble.INSTANCE);
+    unsignedtypes.add(PUnsignedDouble.INSTANCE);
     types.add(PUnsignedDoubleArray.INSTANCE);
+    unsignedtypes.add(PUnsignedDoubleArray.INSTANCE);
     types.add(PUnsignedFloat.INSTANCE);
+    unsignedtypes.add(PUnsignedFloat.INSTANCE);
     types.add(PUnsignedFloatArray.INSTANCE);
+    unsignedtypes.add(PUnsignedFloatArray.INSTANCE);
     types.add(PUnsignedInt.INSTANCE);
+    unsignedtypes.add(PUnsignedInt.INSTANCE);
     types.add(PUnsignedIntArray.INSTANCE);
+    unsignedtypes.add(PUnsignedIntArray.INSTANCE);
     types.add(PUnsignedLong.INSTANCE);
+    unsignedtypes.add(PUnsignedLong.INSTANCE);
     types.add(PUnsignedLongArray.INSTANCE);
+    unsignedtypes.add(PUnsignedLongArray.INSTANCE);
     types.add(PUnsignedSmallint.INSTANCE);
+    unsignedtypes.add(PUnsignedSmallint.INSTANCE);
     types.add(PUnsignedSmallintArray.INSTANCE);
+    unsignedtypes.add(PUnsignedSmallintArray.INSTANCE);
     types.add(PUnsignedTime.INSTANCE);
+    unsignedtypes.add(PUnsignedTime.INSTANCE);
     types.add(PUnsignedTimeArray.INSTANCE);
+    unsignedtypes.add(PUnsignedTimeArray.INSTANCE);
     types.add(PUnsignedTimestamp.INSTANCE);
+    unsignedtypes.add(PUnsignedTimestamp.INSTANCE);
     types.add(PUnsignedTimestampArray.INSTANCE);
+    unsignedtypes.add(PUnsignedTimestampArray.INSTANCE);
     types.add(PUnsignedTinyint.INSTANCE);
+    unsignedtypes.add(PUnsignedTinyint.INSTANCE);
     types.add(PUnsignedTinyintArray.INSTANCE);
+    unsignedtypes.add(PUnsignedTinyintArray.INSTANCE);
     types.add(PVarbinary.INSTANCE);
     types.add(PVarbinaryArray.INSTANCE);
     types.add(PVarchar.INSTANCE);
@@ -101,6 +128,18 @@ public class PDataTypeFactory {
     for (PDataType t : types) {
       classToInstance.put(t.getClass(), t);
     }
+    javaClassToInstance = new HashMap<>(types.size());
+    for (PDataType t : types) {
+        Class javaClass = t.getJavaClass();
+        // The first match
+        javaClassToInstance.putIfAbsent(javaClass, t);
+    }
+    javaClassToUnsignedInstance = new HashMap<>(types.size());
+    for (PDataType t : unsignedtypes) {
+        Class javaClass = t.getJavaClass();
+        // The first match
+        javaClassToInstance.putIfAbsent(javaClass, t);
+    }
     orderedTypes = types.toArray(new PDataType[types.size()]);
   }
 
@@ -115,4 +154,12 @@ public class PDataTypeFactory {
   public PDataType instanceFromClass(Class<? extends PDataType> clazz) {
     return classToInstance.get(clazz);
   }
+
+  public PDataType instanceFromJavaClass(Class clazz, PDataType actualType) {
+      if (unsignedtypes.contains(actualType)) {
+          return javaClassToUnsignedInstance.get(clazz);
+      } else {
+          return javaClassToInstance.get(clazz);
+      }
+  }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
index 3dd6c6903d..c98a8eb4af 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PDate.java
@@ -19,8 +19,10 @@ package org.apache.phoenix.schema.types;
 
 import java.math.BigDecimal;
 import java.sql.Date;
+import java.sql.SQLException;
 import java.sql.Types;
 import java.text.Format;
+import java.time.ZoneOffset;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -94,6 +96,46 @@ public class PDate extends PDataType<Date> {
         return null;
     }
 
+    // Keep this in sync with PUnsignedDate
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        java.sql.Date sqlDate =
+                toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        return dateToClass(sqlDate, actualType, jdbcType);
+    }
+
+    Object dateToClass(java.sql.Date sqlDate, PDataType actualType, Class 
jdbcType)
+            throws SQLException {
+        // FIXME java.time.Local conversions use ISO chronology, unlike the 
rest of Phoenix.
+        if (jdbcType == java.time.LocalDate.class) {
+            // FIXME this does a lot of unnecessary computation.
+            return java.time.LocalDateTime
+                    
.ofInstant(java.time.Instant.ofEpochMilli(sqlDate.getTime()), ZoneOffset.UTC)
+                    .toLocalDate();
+        } else if (jdbcType == java.time.LocalDateTime.class) {
+            // This is NOT JDBC compliant, but is useful because Dates are 
really Timestamps.
+            // We cannot use toInstant(), as that nulls the time fields.
+            return java.time.LocalDateTime
+                    
.ofInstant(java.time.Instant.ofEpochMilli(sqlDate.getTime()), ZoneOffset.UTC);
+        } else if (jdbcType == java.time.LocalTime.class) {
+            // This is NOT JDBC compliant, but is useful because Dates are 
really Timestamps.
+            return java.time.LocalDateTime
+                    
.ofInstant(java.time.Instant.ofEpochMilli(sqlDate.getTime()), ZoneOffset.UTC)
+                    .toLocalTime();
+        } else if (jdbcType == java.sql.Date.class) {
+            return sqlDate;
+        } else if (jdbcType == java.sql.Time.class) {
+            return new java.sql.Time(sqlDate.getTime());
+        } else if (jdbcType == java.sql.Timestamp.class) {
+            return new java.sql.Timestamp(sqlDate.getTime());
+        } else if (jdbcType == java.util.Date.class) {
+            return new java.util.Date(sqlDate.getTime());
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
     @Override
     public boolean isCastableTo(PDataType targetType) {
         return super.isCastableTo(targetType) ||
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PNumericType.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PNumericType.java
index 826d9add39..3e18d2d761 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PNumericType.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PNumericType.java
@@ -17,6 +17,8 @@
  */
 package org.apache.phoenix.schema.types;
 
+import java.sql.SQLException;
+
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.schema.SortOrder;
 
@@ -49,4 +51,16 @@ public abstract class PNumericType<T> extends PDataType<T> {
             ImmutableBytesWritable outPtr) {
         abs(ptr.get(), ptr.getOffset(), ptr.getLength(), sortOrder, outPtr);
     }
+
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        PDataType pType = 
PDataTypeFactory.getInstance().instanceFromJavaClass(jdbcType, this);
+        if (pType == null || 
!PNumericType.class.isAssignableFrom(pType.getClass())) {
+            throw newMismatchException(actualType, jdbcType);
+        } else {
+            return pType.toObject(bytes, offset, length, actualType, 
sortOrder, maxLength, scale);
+        }
+    }
 }
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTime.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTime.java
index 03afae9fc1..e0bcd9232a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTime.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTime.java
@@ -18,9 +18,11 @@
 package org.apache.phoenix.schema.types;
 
 import java.math.BigDecimal;
+import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Types;
 import java.text.Format;
+import java.time.ZoneOffset;
 
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.schema.SortOrder;
@@ -53,7 +55,7 @@ public class PTime extends PDataType<Time> {
     if (equalsAny(actualType, PTimestamp.INSTANCE, 
PUnsignedTimestamp.INSTANCE, PDate.INSTANCE,
         PUnsignedDate.INSTANCE, PTime.INSTANCE, PUnsignedTime.INSTANCE, 
PLong.INSTANCE,
         PUnsignedLong.INSTANCE)) {
-      return new java.sql.Time(actualType.getCodec().decodeLong(b, o, 
sortOrder));
+      return new java.sql.Time(DateUtil.getCodecFor(actualType).decodeLong(b, 
o, sortOrder));
     } else if (actualType == PDecimal.INSTANCE) {
       BigDecimal bd = (BigDecimal) actualType.toObject(b, o, l, actualType, 
sortOrder);
       return new java.sql.Time(bd.longValueExact());
@@ -83,6 +85,45 @@ public class PTime extends PDataType<Time> {
     return throwConstraintViolationException(actualType, this);
   }
 
+  @Override
+  public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+          SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+          throws SQLException {
+      java.sql.Time sqlTime =
+              toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+      return timeToClass(sqlTime, actualType, jdbcType);
+  }
+
+  Object timeToClass(java.sql.Time sqlTime, PDataType actualType, Class 
jdbcType)
+          throws SQLException {
+      if (jdbcType == java.time.LocalTime.class) {
+          // FIXME this does a lot of unnecessary computation.
+          return java.time.LocalDateTime
+                  
.ofInstant(java.time.Instant.ofEpochMilli(sqlTime.getTime()), ZoneOffset.UTC)
+                  .toLocalTime();
+      } else if (jdbcType == java.time.LocalDateTime.class) {
+          // This is NOT JDBC compliant
+          // We cannot use toInstant(), as that nulls the time fields.
+          return java.time.LocalDateTime
+                  
.ofInstant(java.time.Instant.ofEpochMilli(sqlTime.getTime()), ZoneOffset.UTC);
+      } else if (jdbcType == java.time.LocalDate.class) {
+          // This is NOT JDBC compliant
+          // FIXME this does a lot of unnecessary computation.
+          return java.time.LocalDateTime
+                  
.ofInstant(java.time.Instant.ofEpochMilli(sqlTime.getTime()), ZoneOffset.UTC)
+                  .toLocalDate();
+      } else if (jdbcType == java.sql.Time.class) {
+          return sqlTime;
+      } else if (jdbcType == java.sql.Date.class) {
+          return new java.sql.Date(sqlTime.getTime());
+      } else if (jdbcType == java.sql.Timestamp.class) {
+          return new java.sql.Timestamp(sqlTime.getTime());
+      } else if (jdbcType == java.util.Date.class) {
+          return new java.util.Date(sqlTime.getTime());
+      }
+      throw newMismatchException(actualType, jdbcType);
+  }
+
   @Override
   public boolean isCastableTo(PDataType targetType) {
     return PDate.INSTANCE.isCastableTo(targetType);
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java
index a0a5e27a69..4d26807a9e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PTimestamp.java
@@ -18,9 +18,11 @@
 package org.apache.phoenix.schema.types;
 
 import java.math.BigDecimal;
+import java.sql.SQLException;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.Format;
+import java.time.ZoneOffset;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -28,11 +30,10 @@ import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.schema.IllegalDataException;
 import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.DateUtil;
 
-import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
-
 public class PTimestamp extends PDataType<Timestamp> {
     public static final int MAX_NANOS_VALUE_EXCLUSIVE = 1000000;
     public static final PTimestamp INSTANCE = new PTimestamp();
@@ -166,6 +167,41 @@ public class PTimestamp extends PDataType<Timestamp> {
         return null;
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        java.sql.Timestamp sqlTs =
+                toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        return dateToClass(sqlTs, actualType, jdbcType);
+    }
+
+    Object dateToClass(java.sql.Timestamp sqlTs, PDataType actualType, Class 
jdbcType)
+            throws SQLException {
+        // FIXME java.time.Local conversions use ISO chronology, unlike the 
rest of Phoenix.
+        if (jdbcType == java.time.LocalDateTime.class) {
+            return java.time.LocalDateTime.ofInstant(sqlTs.toInstant(), 
ZoneOffset.UTC);
+        } else if (jdbcType == java.time.LocalTime.class) {
+            // This is NOT JDBC compliant
+            // This preserves nanos
+            return java.time.LocalDateTime.ofInstant(sqlTs.toInstant(), 
ZoneOffset.UTC)
+                    .toLocalTime();
+        } else if (jdbcType == java.time.LocalDate.class) {
+            // This is NOT JDBC compliant
+            return java.time.LocalDateTime.ofInstant(sqlTs.toInstant(), 
ZoneOffset.UTC)
+                    .toLocalDate();
+        } else if (jdbcType == java.sql.Timestamp.class) {
+            return sqlTs;
+        } else if (jdbcType == java.sql.Date.class) {
+            return new java.sql.Date(sqlTs.getTime());
+        } else if (jdbcType == java.util.Date.class) {
+            return new java.util.Date(sqlTs.getTime());
+        } else if (jdbcType == java.sql.Time.class) {
+            return new java.sql.Time(sqlTs.getTime());
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
     @Override
     public boolean isCastableTo(PDataType targetType) {
         return PDate.INSTANCE.isCastableTo(targetType);
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDate.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDate.java
index 863b0d18d0..ee21fa832e 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDate.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedDate.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.schema.types;
 
 import java.sql.Date;
+import java.sql.SQLException;
 import java.sql.Types;
 import java.text.Format;
 
@@ -61,12 +62,22 @@ public class PUnsignedDate extends PDataType<Date> {
     }
 
     @Override
-    public Object toObject(byte[] b, int o, int l, PDataType actualType, 
SortOrder sortOrder, Integer maxLength, Integer scale) {
+    public Date toObject(byte[] b, int o, int l, PDataType actualType, 
SortOrder sortOrder,
+            Integer maxLength, Integer scale) {
         Date d = (Date) PDate.INSTANCE.toObject(b, o, l, actualType, 
sortOrder);
         throwIfNonNegativeDate(d);
         return d;
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        java.sql.Date sqlDate =
+                toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        return PDate.INSTANCE.dateToClass(sqlDate, actualType, jdbcType);
+    }
+
     @Override
     public boolean isCastableTo(PDataType targetType) {
         return PDate.INSTANCE.isCastableTo(targetType);
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTime.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTime.java
index db0ba0caa8..fd07ec485e 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTime.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PUnsignedTime.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.schema.types;
 
+import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Types;
 import java.text.Format;
@@ -43,13 +44,22 @@ public class PUnsignedTime extends PDataType<Time> {
   }
 
   @Override
-  public Object toObject(byte[] b, int o, int l, PDataType actualType, 
SortOrder sortOrder,
+  public Time toObject(byte[] b, int o, int l, PDataType actualType, SortOrder 
sortOrder,
       Integer maxLength, Integer scale) {
     java.sql.Time t = (java.sql.Time) PTime.INSTANCE.toObject(b, o, l, 
actualType, sortOrder);
     throwIfNonNegativeDate(t);
     return t;
   }
 
+  @Override
+  public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+          SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+          throws SQLException {
+      java.sql.Time sqlTime =
+              toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+      return PTime.INSTANCE.timeToClass(sqlTime, actualType, jdbcType);
+  }
+
   @Override
   public Object toObject(Object object, PDataType actualType) {
     java.sql.Time t = (java.sql.Time) PTime.INSTANCE.toObject(object, 
actualType);
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarchar.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
index 7c74281260..ce357104df 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.schema.types;
 
+import java.sql.SQLException;
 import java.sql.Types;
 import java.text.Format;
 
@@ -72,6 +73,17 @@ public class PVarchar extends PDataType<String> {
         return Bytes.toString(bytes, offset, length);
     }
 
+    @Override
+    public Object toObject(byte[] bytes, int offset, int length, PDataType 
actualType,
+            SortOrder sortOrder, Integer maxLength, Integer scale, Class 
jdbcType)
+            throws SQLException {
+        if (String.class.isAssignableFrom(jdbcType)) {
+            //We don't actually get here, we shortcut the String case in 
ResultSet
+            return toObject(bytes, offset, length, actualType, sortOrder, 
maxLength, scale);
+        }
+        throw newMismatchException(actualType, jdbcType);
+    }
+
     @Override
     public Object toObject(Object object, PDataType actualType) {
         if (equalsAny(actualType, this, PChar.INSTANCE)) {
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java 
b/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
index fefdc8d3ad..5bd7027202 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/DateUtil.java
@@ -72,8 +72,7 @@ public class DateUtil {
     public static final String DEFAULT_TIMESTAMP_FORMAT = 
DEFAULT_MS_DATE_FORMAT;
     public static final Format DEFAULT_TIMESTAMP_FORMATTER = 
DEFAULT_MS_DATE_FORMATTER;
 
-    //Caching for performance. We don't expect the default TZ to changed after 
startup
-    //private static final java.util.TimeZone LOCAL_TIME_ZONE = 
TimeZone.getDefault();
+    public static final java.time.LocalDate LD_EPOCH = 
java.time.LocalDate.of(1970, 1, 1);
 
     private static final DateTimeFormatter JULIAN_DATE_TIME_FORMATTER = new 
DateTimeFormatterBuilder()
         .append(ISODateTimeFormat.dateParser())
@@ -87,6 +86,7 @@ public class DateUtil {
     }
 
     @NonNull
+    // FIXME why don't we just set these codecs in the Types ?
     public static PDataCodec getCodecFor(PDataType type) {
         PDataCodec codec = type.getCodec();
         if (codec != null) {

Reply via email to