drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From par...@apache.org
Subject [1/6] drill git commit: DRILL-2463: Implement JDBC mapping of SQL NULL for ResultSet.getXxx() methods.
Date Fri, 27 Mar 2015 17:49:19 GMT
Repository: drill
Updated Branches:
  refs/heads/master ee94a37e5 -> 20efb2fbc


DRILL-2463: Implement JDBC mapping of SQL NULL for ResultSet.getXxx() methods.

- Added test Drill2463GetNullsFailedWithAssertionsBugTest.
- Implemented JDBC mapping of NULL for ResultSet.getXxx() methods:
  - Fixed was-always-false isNull(...) for Nullable... SqlAccessor
    implementations in SqlAccessors template.
  - Handled NULL mapping for primitive types in AvaticaDrillSqlAccessor.
  - Handled NULL mapping for object types in Nullable... SqlAccessor
    implementations in SqlAccessors template.
- Related miscellaneous changes:
  - Added "rename this" TODO.  [config.fmpp]
  - Added documentation.  [SqlAccessor]
  - Edited comment.  [BoundCheckingAccessor]


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/83ebf4f3
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/83ebf4f3
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/83ebf4f3

Branch: refs/heads/master
Commit: 83ebf4f3e12ae42ba771e7c1cdd833a27ecf9b35
Parents: ee94a37
Author: dbarclay <dbarclay@maprtech.com>
Authored: Tue Mar 17 20:45:31 2015 -0700
Committer: Parth Chandra <pchandra@maprtech.com>
Committed: Fri Mar 27 10:19:14 2015 -0700

----------------------------------------------------------------------
 exec/java-exec/src/main/codegen/config.fmpp     |   1 +
 .../main/codegen/templates/SqlAccessors.java    | 199 ++++++++++++++-----
 .../vector/accessor/BoundCheckingAccessor.java  |   4 +-
 .../drill/exec/vector/accessor/SqlAccessor.java |  83 +++++---
 .../drill/jdbc/AvaticaDrillSqlAccessor.java     |  37 +++-
 ...2463GetNullsFailedWithAssertionsBugTest.java | 122 ++++++++++++
 6 files changed, 360 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/java-exec/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/codegen/config.fmpp b/exec/java-exec/src/main/codegen/config.fmpp
index 5dc7360..8db120d 100644
--- a/exec/java-exec/src/main/codegen/config.fmpp
+++ b/exec/java-exec/src/main/codegen/config.fmpp
@@ -15,6 +15,7 @@
 # limitations under the License.
 
 data: {
+    # TODO:  Rename to ~valueVectorModesAndTypes for clarity.
     vv:                       tdd(../data/ValueVectorTypes.tdd),
 
     # Most types for comparison functions (for templates/ComparisonFunctions.java).

http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/java-exec/src/main/codegen/templates/SqlAccessors.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/codegen/templates/SqlAccessors.java b/exec/java-exec/src/main/codegen/templates/SqlAccessors.java
index 3039e5c..a838e11 100644
--- a/exec/java-exec/src/main/codegen/templates/SqlAccessors.java
+++ b/exec/java-exec/src/main/codegen/templates/SqlAccessors.java
@@ -32,150 +32,245 @@ package org.apache.drill.exec.vector.accessor;
 <#include "/@includes/vv_imports.ftl" />
 
 @SuppressWarnings("unused")
-public class ${name}Accessor extends AbstractSqlAccessor{
-  <#if mode == "Nullable">
+public class ${name}Accessor extends AbstractSqlAccessor {
+ <#if mode == "Nullable">
   private static final MajorType TYPE = Types.optional(MinorType.${minor.class?upper_case});
-  <#else>
+ <#else>
   private static final MajorType TYPE = Types.required(MinorType.${minor.class?upper_case});
-  </#if>
-  
+ </#if>
+
   private final ${name}Vector.Accessor ac;
-  
-  public ${name}Accessor(${name}Vector vector){
+
+  public ${name}Accessor(${name}Vector vector) {
     this.ac = vector.getAccessor();
   }
 
-  <#if minor.class != "TimeStamp" && minor.class != "Time" && minor.class
!= "Date">
-  public Object getObject(int index){
+  @Override
+  MajorType getType() {
+    return TYPE;
+  };
+
+  @Override
+  public boolean isNull(int index) {
+   <#if mode == "Nullable">
+    return ac.isNull(index);
+   <#else>
+    return false;
+   </#if>
+  }
+
+ <#if minor.class != "TimeStamp" && minor.class != "Time" && minor.class
!= "Date">
+  public Object getObject(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
     return ac.getObject(index);
   }
-  </#if>
-  
-  <#if type.major == "VarLen">
+ </#if>
+
+ <#if type.major == "VarLen">
 
-  @Override 
-  public InputStream getStream(int index){
+  @Override
+  public InputStream getStream(int index) {
+    <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
     ${name}Holder h = new ${name}Holder();
     ac.get(index, h);
     return new ByteBufInputStream(h.buffer.slice(h.start, h.end));
   }
-  
-  @Override 
-  public byte[] getBytes(int index){
+
+  @Override
+  public byte[] getBytes(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
     return ac.get(index);
   }
-  
+
   <#switch minor.class>
+
     <#case "VarBinary">
     public String getString(int index) {
+     <#if mode == "Nullable">
+      if (ac.isNull(index)) {
+        return null;
+      }
+     </#if>
       byte [] b = ac.get(index);
       return DrillStringUtils.toBinaryString(b);
     }
       <#break>
+
     <#case "VarChar">
-    @Override 
-    public InputStreamReader getReader(int index){
+    @Override
+    public InputStreamReader getReader(int index) {
+     <#if mode == "Nullable">
+      if (ac.isNull(index)) {
+        return null;
+      }
+     </#if>
       return new InputStreamReader(getStream(index), Charsets.UTF_8);
     }
-    
-    @Override 
-    public String getString(int index){
+
+    @Override
+    public String getString(int index) {
+     <#if mode == "Nullable">
+      if (ac.isNull(index)) {
+        return null;
+      }
+     </#if>
       return new String(getBytes(index), Charsets.UTF_8);
     }
-    
-    
       <#break>
+
     <#case "Var16Char">
-    @Override 
-    public InputStreamReader getReader(int index){
+    @Override
+    public InputStreamReader getReader(int index) {
+     <#if mode == "Nullable">
+      if (ac.isNull(index)) {
+        return null;
+      }
+     </#if>
       return new InputStreamReader(getStream(index), Charsets.UTF_16);
     }
-    
-    @Override 
-    public String getString(int index){
+
+    @Override
+    public String getString(int index) {
+     <#if mode == "Nullable">
+      if (ac.isNull(index)) {
+        return null;
+      }
+     </#if>
       return new String(getBytes(index), Charsets.UTF_16);
     }
-        
-    
       <#break>
-    <#default> 
+
+    <#default>
     This is uncompilable code
+
   </#switch>
 
-  <#else>
-  <#if minor.class == "Interval" || minor.class == "IntervalDay" || minor.class == "IntervalYear">
+ <#else> <#-- VarLen -->
+
+  <#if minor.class == "TimeStampTZ">
+
+  public Object getObject(int index) {
+    return getTimestamp(index);
+  }
+
+  @Override
+  public Timestamp getTimestamp(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
+    return new Timestamp(ac.getObject(index).getMillis());
+  }
+
+  <#elseif minor.class == "Interval" || minor.class == "IntervalDay" || minor.class ==
"IntervalYear">
+
   @Override
   public String getString(int index) {
-      return String.valueOf(ac.getAsStringBuilder(index));
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
+    return String.valueOf(ac.getAsStringBuilder(index));
   }
+
   <#elseif minor.class.startsWith("Decimal")>
+
   @Override
   public BigDecimal getBigDecimal(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
       return ac.getObject(index);
   }
   <#elseif minor.class == "Date">
+
   public Object getObject(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
     return getDate(index);
   }
 
   @Override
   public Date getDate(int index) {
-    <#if mode == "Nullable">
+   <#if mode == "Nullable">
     if (ac.isNull(index)) {
       return null;
     }
-    </#if>
+   </#if>
     org.joda.time.DateTime date = new org.joda.time.DateTime(ac.get(index), org.joda.time.DateTimeZone.UTC);
     date = date.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault());
     return new Date(date.getMillis());
   }
+
   <#elseif minor.class == "TimeStamp">
+
   public Object getObject(int index) {
+   <#if mode == "Nullable">
+    if (ac.isNull(index)) {
+      return null;
+    }
+   </#if>
     return getTimestamp(index);
   }
 
   @Override
   public Timestamp getTimestamp(int index) {
-    <#if mode == "Nullable">
+   <#if mode == "Nullable">
     if (ac.isNull(index)) {
       return null;
     }
-    </#if>
+   </#if>
     org.joda.time.DateTime date = new org.joda.time.DateTime(ac.get(index), org.joda.time.DateTimeZone.UTC);
     date = date.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault());
     return new Timestamp(date.getMillis());
   }
+
   <#elseif minor.class == "Time">
+
   public Object getObject(int index) {
     return getTime(index);
   }
 
   @Override
   public Time getTime(int index) {
-    <#if mode == "Nullable">
+   <#if mode == "Nullable">
     if (ac.isNull(index)) {
       return null;
     }
-    </#if>
+   </#if>
     org.joda.time.DateTime time = new org.joda.time.DateTime(ac.get(index), org.joda.time.DateTimeZone.UTC);
     time = time.withZoneRetainFields(org.joda.time.DateTimeZone.getDefault());
     return new TimePrintMillis(time.getMillis());
   }
+
   <#else>
+
   @Override
-  public ${javaType} get${javaType?cap_first}(int index){
+  public ${javaType} get${javaType?cap_first}(int index) {
     return ac.get(index);
   }
   </#if>
-  </#if>
-  
-  @Override
-  public boolean isNull(int index){
-    return false;
-  }
-  
-  @Override
-  MajorType getType(){return TYPE;};
+
+ </#if> <#-- not VarLen -->
 
 }
 

http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java
index 7e8da2c..c8d6cc7 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/BoundCheckingAccessor.java
@@ -130,8 +130,8 @@ public class BoundCheckingAccessor implements SqlAccessor {
    */
   @Override
   public Object getObject(int index) throws AbstractSqlAccessor.InvalidAccessException {
-    // in case some vectors have less values than others, callee invokes this method with
index >= #getValueCount
-    // this should still yield null.
+    // In case some vectors have fewer values than others, and callee invokes
+    // this method with index >= getValueCount(), this should still yield null.
     final ValueVector.Accessor accessor = vector.getAccessor();
     if (index < accessor.getValueCount()) {
       return delegate.getObject(index);

http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java
index b69ae54..6007bf4 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/vector/accessor/SqlAccessor.java
@@ -26,53 +26,90 @@ import java.sql.Timestamp;
 
 import org.apache.drill.exec.vector.accessor.AbstractSqlAccessor.InvalidAccessException;
 
-// TODO:  Doc.
-public interface SqlAccessor {
 
-  // TODO:  Document (renamed) index.
-  // TODO:  Rename ambiguous "index" (JDBC (1-based) column index? other index?)
-  // TODO:  Doc./Spec.:  What happens if index is invalid?
+// TODO:  Doc.:  Document more of basics of pattern of contracts for getXxx(...):
+// - What index is (especially since is not 1-based JDBC/SQL column index).
+// - What constitutes invalid access (that throws InvalidAccessException):
+//   - Does it include out-of-bound index values?  (The lack of "throws
+//     InvalidAccessException" on isNull(...) suggests no, but ...)
 
-  public abstract boolean isNull(int index);
+/**
+ * Column-data accessor that implements JDBC's Java-null--when--SQL-NULL mapping.
+ * <p>
+ *   When the requested value is logically a SQL NULL:
+ * </p>
+ * <li>
+ *   a get method that return primitive type throws an exception (callers are
+ *   responsible for calling {@link isNull} to check for null before calling
+ *   such methods)
+ * </li>
+ * <li>
+ *   a get method that returns a non-primitive type returns Java {@code null}
+ *   (the caller does not need to call {@link isNull} to check for nulls)
+ * </li>
+ */
+public interface SqlAccessor {
 
   // TODO:  Clean:  This interface refers to type InvalidAccessException
   // defined in class implementing this interface.
 
-  public abstract BigDecimal getBigDecimal(int index) throws InvalidAccessException;
+  /**
+   * Reports whether the logical value is a SQL NULL.
+   */
+  boolean isNull(int index);
+
+  /** (See {@link SqlAccessor class description}.) */
+  BigDecimal getBigDecimal(int index) throws InvalidAccessException;
 
-  public abstract boolean getBoolean(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  boolean getBoolean(int index) throws InvalidAccessException;
 
-  public abstract byte getByte(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  byte getByte(int index) throws InvalidAccessException;
 
-  public abstract byte[] getBytes(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  byte[] getBytes(int index) throws InvalidAccessException;
 
-  public abstract Date getDate(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  Date getDate(int index) throws InvalidAccessException;
 
-  public abstract double getDouble(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  double getDouble(int index) throws InvalidAccessException;
 
-  public abstract float getFloat(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  float getFloat(int index) throws InvalidAccessException;
 
-  public abstract char getChar(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  char getChar(int index) throws InvalidAccessException;
 
-  public abstract int getInt(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  int getInt(int index) throws InvalidAccessException;
 
-  public abstract long getLong(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  long getLong(int index) throws InvalidAccessException;
 
-  public abstract short getShort(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  short getShort(int index) throws InvalidAccessException;
 
-  public abstract InputStream getStream(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  InputStream getStream(int index) throws InvalidAccessException;
 
-  public abstract Reader getReader(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  Reader getReader(int index) throws InvalidAccessException;
 
   // TODO: Doc./Spec.:  What should happen if called on non-string type?  (Most
   // are convertible to string.  Does that result in error or conversion?)
   // Similar question for many other methods.
-  public abstract String getString(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  String getString(int index) throws InvalidAccessException;
 
-  public abstract Time getTime(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  Time getTime(int index) throws InvalidAccessException;
 
-  public abstract Timestamp getTimestamp(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  Timestamp getTimestamp(int index) throws InvalidAccessException;
 
-  public abstract Object getObject(int index) throws InvalidAccessException;
+  /** (See {@link SqlAccessor class description}.) */
+  Object getObject(int index) throws InvalidAccessException;
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/jdbc/src/main/java/org/apache/drill/jdbc/AvaticaDrillSqlAccessor.java
----------------------------------------------------------------------
diff --git a/exec/jdbc/src/main/java/org/apache/drill/jdbc/AvaticaDrillSqlAccessor.java b/exec/jdbc/src/main/java/org/apache/drill/jdbc/AvaticaDrillSqlAccessor.java
index 3702257..2b20f23 100644
--- a/exec/jdbc/src/main/java/org/apache/drill/jdbc/AvaticaDrillSqlAccessor.java
+++ b/exec/jdbc/src/main/java/org/apache/drill/jdbc/AvaticaDrillSqlAccessor.java
@@ -39,9 +39,14 @@ import net.hydromatic.avatica.Cursor.Accessor;
 
 import org.apache.drill.exec.vector.accessor.SqlAccessor;
 
+// TODO:  Revisit adding null check for non-primitive types to SqlAccessor's
+// contract and classes generated by SqlAccessor template (DRILL-xxxx).
 
-public class AvaticaDrillSqlAccessor implements Accessor{
-  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AvaticaDrillSqlAccessor.class);
+public class AvaticaDrillSqlAccessor implements Accessor {
+  private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AvaticaDrillSqlAccessor.class);
+
+  private final static byte PRIMITIVE_NUM_NULL_VALUE = 0;
+  private final static boolean BOOLEAN_NULL_VALUE = false;
 
   private SqlAccessor underlyingAccessor;
   private DrillCursor cursor;
@@ -78,37 +83,51 @@ public class AvaticaDrillSqlAccessor implements Accessor{
 
   @Override
   public boolean getBoolean() throws SQLException {
-    return underlyingAccessor.getBoolean(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? BOOLEAN_NULL_VALUE
+        : underlyingAccessor.getBoolean(getCurrentRecordNumber());
   }
 
   @Override
   public byte getByte() throws SQLException {
-    return underlyingAccessor.getByte(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getByte(getCurrentRecordNumber());
   }
 
   @Override
   public short getShort() throws SQLException {
-    return underlyingAccessor.getShort(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getShort(getCurrentRecordNumber());
   }
 
   @Override
   public int getInt() throws SQLException {
-    return underlyingAccessor.getInt(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getInt(getCurrentRecordNumber());
   }
 
   @Override
   public long getLong() throws SQLException {
-    return underlyingAccessor.getLong(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getLong(getCurrentRecordNumber());
   }
 
   @Override
   public float getFloat() throws SQLException {
-    return underlyingAccessor.getFloat(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getFloat(getCurrentRecordNumber());
   }
 
   @Override
   public double getDouble() throws SQLException {
-    return underlyingAccessor.getDouble(getCurrentRecordNumber());
+    return underlyingAccessor.isNull(getCurrentRecordNumber())
+        ? PRIMITIVE_NUM_NULL_VALUE
+        : underlyingAccessor.getDouble(getCurrentRecordNumber());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/drill/blob/83ebf4f3/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2463GetNullsFailedWithAssertionsBugTest.java
----------------------------------------------------------------------
diff --git a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2463GetNullsFailedWithAssertionsBugTest.java
b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2463GetNullsFailedWithAssertionsBugTest.java
new file mode 100644
index 0000000..c8346b1
--- /dev/null
+++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/Drill2463GetNullsFailedWithAssertionsBugTest.java
@@ -0,0 +1,122 @@
+/**
+ * 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.drill.jdbc.test;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.apache.drill.jdbc.Driver;
+import org.apache.drill.jdbc.JdbcTest;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+
+public class Drill2463GetNullsFailedWithAssertionsBugTest extends JdbcTest {
+
+  private static Connection connection;
+  private static Statement statement;
+
+  @BeforeClass
+  public static void setUpConnection() throws SQLException {
+    connection = new Driver().connect( "jdbc:drill:zk=local", null );
+    statement = connection.createStatement();
+  }
+
+  @AfterClass
+  public static void tearDownConnection() throws SQLException {
+    connection.close();
+  }
+
+  // Test primitive types vs. non-primitive types:
+
+  @Test
+  public void testGetPrimitiveTypeNullAsOwnType() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS INTEGER ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getInt(...) for NULL", rs.getInt( 1 ), equalTo( 0 ) );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+  @Test
+  public void testGetPrimitiveTypeNullAsObject() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS INTEGER ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getObject(...) for NULL", rs.getObject( 1 ), nullValue() );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+  @Test
+  public void testGetNonprimitiveTypeNullAsOwnType() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS VARCHAR) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getString(...) for NULL", rs.getString( 1 ), nullValue() );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+
+  // Test a few specifics
+
+  @Test
+  public void testGetBooleanNullAsOwnType() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS BOOLEAN ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getBoolean(...) for NULL", rs.getBoolean( 1 ), equalTo( false ) );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+  @Test
+  public void testGetBooleanNullAsObject() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS BOOLEAN ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getObject(...) for NULL", rs.getObject( 1 ), nullValue() );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+  @Test
+  public void testGetIntegerNullAsOwnType() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS INTEGER ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getInt(...) for NULL", rs.getInt( 1 ), equalTo( 0 ) );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+  @Test
+  public void testGetIntegerNullAsObject() throws Exception {
+    ResultSet rs = statement.executeQuery(
+        "SELECT CAST( NULL AS INTEGER ) FROM INFORMATION_SCHEMA.CATALOGS" );
+    assert rs.next();
+    assertThat( "getObject(...) for NULL", rs.getObject( 1 ), nullValue() );
+    assertThat( "wasNull", rs.wasNull(), equalTo( true ) );
+  }
+
+
+}


Mime
View raw message