You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2017/04/24 22:12:58 UTC

[4/4] calcite-avatica git commit: [CALCITE-1756] Differentiate between implicitly null and explicitly null TypedValues

[CALCITE-1756] Differentiate between implicitly null and explicitly null TypedValues

Noticed that a test should have been failing in the ARRAY work. The null
TypedValue was getting lost in the serialization. We need to handle
these two cases separately to ensure that the "intent" is preserved.


Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica/commit/557f15cc
Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica/tree/557f15cc
Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica/diff/557f15cc

Branch: refs/heads/master
Commit: 557f15cc74ade0e4bd509838b263f85a7b3d0215
Parents: dd65a2b
Author: Josh Elser <el...@apache.org>
Authored: Mon Apr 17 11:40:55 2017 -0400
Committer: Josh Elser <el...@apache.org>
Committed: Mon Apr 24 17:59:24 2017 -0400

----------------------------------------------------------------------
 .../apache/calcite/avatica/proto/Common.java    | 185 ++++++++++++++-----
 .../apache/calcite/avatica/remote/Service.java  |   4 +-
 .../calcite/avatica/remote/TypedValue.java      |  20 +-
 core/src/main/protobuf/common.proto             |   3 +
 .../remote/ProtobufTranslationImplTest.java     |  12 ++
 5 files changed, 164 insertions(+), 60 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/557f15cc/core/src/main/java/org/apache/calcite/avatica/proto/Common.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/proto/Common.java b/core/src/main/java/org/apache/calcite/avatica/proto/Common.java
index 50450f4..3bc10b5 100644
--- a/core/src/main/java/org/apache/calcite/avatica/proto/Common.java
+++ b/core/src/main/java/org/apache/calcite/avatica/proto/Common.java
@@ -14596,6 +14596,15 @@ public final class Common {
      * <code>optional .Rep component_type = 9;</code>
      */
     org.apache.calcite.avatica.proto.Common.Rep getComponentType();
+
+    /**
+     * <pre>
+     * Differentiate between explicitly null (user-set) and implicitly null
+     * </pre>
+     *
+     * <code>optional bool implicitly_null = 10;</code>
+     */
+    boolean getImplicitlyNull();
   }
   /**
    * <pre>
@@ -14622,6 +14631,7 @@ public final class Common {
       null_ = false;
       arrayValue_ = java.util.Collections.emptyList();
       componentType_ = 0;
+      implicitlyNull_ = false;
     }
 
     @java.lang.Override
@@ -14701,6 +14711,11 @@ public final class Common {
               componentType_ = rawValue;
               break;
             }
+            case 80: {
+
+              implicitlyNull_ = input.readBool();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -14938,6 +14953,19 @@ public final class Common {
       return result == null ? org.apache.calcite.avatica.proto.Common.Rep.UNRECOGNIZED : result;
     }
 
+    public static final int IMPLICITLY_NULL_FIELD_NUMBER = 10;
+    private boolean implicitlyNull_;
+    /**
+     * <pre>
+     * Differentiate between explicitly null (user-set) and implicitly null
+     * </pre>
+     *
+     * <code>optional bool implicitly_null = 10;</code>
+     */
+    public boolean getImplicitlyNull() {
+      return implicitlyNull_;
+    }
+
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
       byte isInitialized = memoizedIsInitialized;
@@ -14977,6 +15005,9 @@ public final class Common {
       if (componentType_ != org.apache.calcite.avatica.proto.Common.Rep.PRIMITIVE_BOOLEAN.getNumber()) {
         output.writeEnum(9, componentType_);
       }
+      if (implicitlyNull_ != false) {
+        output.writeBool(10, implicitlyNull_);
+      }
     }
 
     public int getSerializedSize() {
@@ -15019,6 +15050,10 @@ public final class Common {
         size += com.google.protobuf.CodedOutputStream
           .computeEnumSize(9, componentType_);
       }
+      if (implicitlyNull_ != false) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBoolSize(10, implicitlyNull_);
+      }
       memoizedSize = size;
       return size;
     }
@@ -15053,6 +15088,8 @@ public final class Common {
       result = result && getArrayValueList()
           .equals(other.getArrayValueList());
       result = result && componentType_ == other.componentType_;
+      result = result && (getImplicitlyNull()
+          == other.getImplicitlyNull());
       return result;
     }
 
@@ -15087,6 +15124,9 @@ public final class Common {
       }
       hash = (37 * hash) + COMPONENT_TYPE_FIELD_NUMBER;
       hash = (53 * hash) + componentType_;
+      hash = (37 * hash) + IMPLICITLY_NULL_FIELD_NUMBER;
+      hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(
+          getImplicitlyNull());
       hash = (29 * hash) + unknownFields.hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -15232,6 +15272,8 @@ public final class Common {
         }
         componentType_ = 0;
 
+        implicitlyNull_ = false;
+
         return this;
       }
 
@@ -15273,6 +15315,7 @@ public final class Common {
           result.arrayValue_ = arrayValueBuilder_.build();
         }
         result.componentType_ = componentType_;
+        result.implicitlyNull_ = implicitlyNull_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -15366,6 +15409,9 @@ public final class Common {
         if (other.componentType_ != 0) {
           setComponentTypeValue(other.getComponentTypeValue());
         }
+        if (other.getImplicitlyNull() != false) {
+          setImplicitlyNull(other.getImplicitlyNull());
+        }
         onChanged();
         return this;
       }
@@ -16114,6 +16160,44 @@ public final class Common {
         onChanged();
         return this;
       }
+
+      private boolean implicitlyNull_ ;
+      /**
+       * <pre>
+       * Differentiate between explicitly null (user-set) and implicitly null
+       * </pre>
+       *
+       * <code>optional bool implicitly_null = 10;</code>
+       */
+      public boolean getImplicitlyNull() {
+        return implicitlyNull_;
+      }
+      /**
+       * <pre>
+       * Differentiate between explicitly null (user-set) and implicitly null
+       * </pre>
+       *
+       * <code>optional bool implicitly_null = 10;</code>
+       */
+      public Builder setImplicitlyNull(boolean value) {
+        
+        implicitlyNull_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <pre>
+       * Differentiate between explicitly null (user-set) and implicitly null
+       * </pre>
+       *
+       * <code>optional bool implicitly_null = 10;</code>
+       */
+      public Builder clearImplicitlyNull() {
+        
+        implicitlyNull_ = false;
+        onChanged();
+        return this;
+      }
       public final Builder setUnknownFields(
           final com.google.protobuf.UnknownFieldSet unknownFields) {
         return this;
@@ -18770,60 +18854,61 @@ public final class Common {
       " \001(\014\"\207\001\n\013ColumnValue\022\032\n\005value\030\001 \003(\0132\013.Ty",
       "pedValue\022 \n\013array_value\030\002 \003(\0132\013.TypedVal" +
       "ue\022\027\n\017has_array_value\030\003 \001(\010\022!\n\014scalar_va" +
-      "lue\030\004 \001(\0132\013.TypedValue\"\331\001\n\nTypedValue\022\022\n" +
+      "lue\030\004 \001(\0132\013.TypedValue\"\362\001\n\nTypedValue\022\022\n" +
       "\004type\030\001 \001(\0162\004.Rep\022\022\n\nbool_value\030\002 \001(\010\022\024\n" +
       "\014string_value\030\003 \001(\t\022\024\n\014number_value\030\004 \001(" +
       "\022\022\023\n\013bytes_value\030\005 \001(\014\022\024\n\014double_value\030\006" +
       " \001(\001\022\014\n\004null\030\007 \001(\010\022 \n\013array_value\030\010 \003(\0132" +
       "\013.TypedValue\022\034\n\016component_type\030\t \001(\0162\004.R" +
-      "ep\"\246\002\n\031MetaDataOperationArgument\022\024\n\014stri" +
-      "ng_value\030\001 \001(\t\022\022\n\nbool_value\030\002 \001(\010\022\021\n\tin",
-      "t_value\030\003 \001(\021\022\033\n\023string_array_values\030\004 \003" +
-      "(\t\022\030\n\020int_array_values\030\005 \003(\021\0225\n\004type\030\006 \001" +
-      "(\0162\'.MetaDataOperationArgument.ArgumentT" +
-      "ype\"^\n\014ArgumentType\022\n\n\006STRING\020\000\022\010\n\004BOOL\020" +
-      "\001\022\007\n\003INT\020\002\022\023\n\017REPEATED_STRING\020\003\022\020\n\014REPEA" +
-      "TED_INT\020\004\022\010\n\004NULL\020\005\"\260\001\n\nQueryState\022\030\n\004ty" +
-      "pe\030\001 \001(\0162\n.StateType\022\013\n\003sql\030\002 \001(\t\022\036\n\002op\030" +
-      "\003 \001(\0162\022.MetaDataOperation\022(\n\004args\030\004 \003(\0132" +
-      "\032.MetaDataOperationArgument\022\020\n\010has_args\030" +
-      "\005 \001(\010\022\017\n\007has_sql\030\006 \001(\010\022\016\n\006has_op\030\007 \001(\010*\237",
-      "\001\n\rStatementType\022\n\n\006SELECT\020\000\022\n\n\006INSERT\020\001" +
-      "\022\n\n\006UPDATE\020\002\022\n\n\006DELETE\020\003\022\n\n\006UPSERT\020\004\022\t\n\005" +
-      "MERGE\020\005\022\r\n\tOTHER_DML\020\006\022\n\n\006CREATE\020\007\022\010\n\004DR" +
-      "OP\020\010\022\t\n\005ALTER\020\t\022\r\n\tOTHER_DDL\020\n\022\010\n\004CALL\020\013" +
-      "*\342\003\n\003Rep\022\025\n\021PRIMITIVE_BOOLEAN\020\000\022\022\n\016PRIMI" +
-      "TIVE_BYTE\020\001\022\022\n\016PRIMITIVE_CHAR\020\002\022\023\n\017PRIMI" +
-      "TIVE_SHORT\020\003\022\021\n\rPRIMITIVE_INT\020\004\022\022\n\016PRIMI" +
-      "TIVE_LONG\020\005\022\023\n\017PRIMITIVE_FLOAT\020\006\022\024\n\020PRIM" +
-      "ITIVE_DOUBLE\020\007\022\013\n\007BOOLEAN\020\010\022\010\n\004BYTE\020\t\022\r\n" +
-      "\tCHARACTER\020\n\022\t\n\005SHORT\020\013\022\013\n\007INTEGER\020\014\022\010\n\004",
-      "LONG\020\r\022\t\n\005FLOAT\020\016\022\n\n\006DOUBLE\020\017\022\017\n\013BIG_INT" +
-      "EGER\020\031\022\017\n\013BIG_DECIMAL\020\032\022\021\n\rJAVA_SQL_TIME" +
-      "\020\020\022\026\n\022JAVA_SQL_TIMESTAMP\020\021\022\021\n\rJAVA_SQL_D" +
-      "ATE\020\022\022\022\n\016JAVA_UTIL_DATE\020\023\022\017\n\013BYTE_STRING" +
-      "\020\024\022\n\n\006STRING\020\025\022\n\n\006NUMBER\020\026\022\n\n\006OBJECT\020\027\022\010" +
-      "\n\004NULL\020\030\022\t\n\005ARRAY\020\033\022\n\n\006STRUCT\020\034\022\014\n\010MULTI" +
-      "SET\020\035*^\n\010Severity\022\024\n\020UNKNOWN_SEVERITY\020\000\022" +
-      "\022\n\016FATAL_SEVERITY\020\001\022\022\n\016ERROR_SEVERITY\020\002\022" +
-      "\024\n\020WARNING_SEVERITY\020\003*\327\004\n\021MetaDataOperat" +
-      "ion\022\022\n\016GET_ATTRIBUTES\020\000\022\033\n\027GET_BEST_ROW_",
-      "IDENTIFIER\020\001\022\020\n\014GET_CATALOGS\020\002\022\036\n\032GET_CL" +
-      "IENT_INFO_PROPERTIES\020\003\022\031\n\025GET_COLUMN_PRI" +
-      "VILEGES\020\004\022\017\n\013GET_COLUMNS\020\005\022\027\n\023GET_CROSS_" +
-      "REFERENCE\020\006\022\025\n\021GET_EXPORTED_KEYS\020\007\022\030\n\024GE" +
-      "T_FUNCTION_COLUMNS\020\010\022\021\n\rGET_FUNCTIONS\020\t\022" +
-      "\025\n\021GET_IMPORTED_KEYS\020\n\022\022\n\016GET_INDEX_INFO" +
-      "\020\013\022\024\n\020GET_PRIMARY_KEYS\020\014\022\031\n\025GET_PROCEDUR" +
-      "E_COLUMNS\020\r\022\022\n\016GET_PROCEDURES\020\016\022\026\n\022GET_P" +
-      "SEUDO_COLUMNS\020\017\022\017\n\013GET_SCHEMAS\020\020\022\031\n\025GET_" +
-      "SCHEMAS_WITH_ARGS\020\021\022\024\n\020GET_SUPER_TABLES\020",
-      "\022\022\023\n\017GET_SUPER_TYPES\020\023\022\030\n\024GET_TABLE_PRIV" +
-      "ILEGES\020\024\022\016\n\nGET_TABLES\020\025\022\023\n\017GET_TABLE_TY" +
-      "PES\020\026\022\021\n\rGET_TYPE_INFO\020\027\022\014\n\010GET_UDTS\020\030\022\027" +
-      "\n\023GET_VERSION_COLUMNS\020\031*\"\n\tStateType\022\007\n\003" +
-      "SQL\020\000\022\014\n\010METADATA\020\001B\"\n org.apache.calcit" +
-      "e.avatica.protob\006proto3"
+      "ep\022\027\n\017implicitly_null\030\n \001(\010\"\246\002\n\031MetaData" +
+      "OperationArgument\022\024\n\014string_value\030\001 \001(\t\022",
+      "\022\n\nbool_value\030\002 \001(\010\022\021\n\tint_value\030\003 \001(\021\022\033" +
+      "\n\023string_array_values\030\004 \003(\t\022\030\n\020int_array" +
+      "_values\030\005 \003(\021\0225\n\004type\030\006 \001(\0162\'.MetaDataOp" +
+      "erationArgument.ArgumentType\"^\n\014Argument" +
+      "Type\022\n\n\006STRING\020\000\022\010\n\004BOOL\020\001\022\007\n\003INT\020\002\022\023\n\017R" +
+      "EPEATED_STRING\020\003\022\020\n\014REPEATED_INT\020\004\022\010\n\004NU" +
+      "LL\020\005\"\260\001\n\nQueryState\022\030\n\004type\030\001 \001(\0162\n.Stat" +
+      "eType\022\013\n\003sql\030\002 \001(\t\022\036\n\002op\030\003 \001(\0162\022.MetaDat" +
+      "aOperation\022(\n\004args\030\004 \003(\0132\032.MetaDataOpera" +
+      "tionArgument\022\020\n\010has_args\030\005 \001(\010\022\017\n\007has_sq",
+      "l\030\006 \001(\010\022\016\n\006has_op\030\007 \001(\010*\237\001\n\rStatementTyp" +
+      "e\022\n\n\006SELECT\020\000\022\n\n\006INSERT\020\001\022\n\n\006UPDATE\020\002\022\n\n" +
+      "\006DELETE\020\003\022\n\n\006UPSERT\020\004\022\t\n\005MERGE\020\005\022\r\n\tOTHE" +
+      "R_DML\020\006\022\n\n\006CREATE\020\007\022\010\n\004DROP\020\010\022\t\n\005ALTER\020\t" +
+      "\022\r\n\tOTHER_DDL\020\n\022\010\n\004CALL\020\013*\342\003\n\003Rep\022\025\n\021PRI" +
+      "MITIVE_BOOLEAN\020\000\022\022\n\016PRIMITIVE_BYTE\020\001\022\022\n\016" +
+      "PRIMITIVE_CHAR\020\002\022\023\n\017PRIMITIVE_SHORT\020\003\022\021\n" +
+      "\rPRIMITIVE_INT\020\004\022\022\n\016PRIMITIVE_LONG\020\005\022\023\n\017" +
+      "PRIMITIVE_FLOAT\020\006\022\024\n\020PRIMITIVE_DOUBLE\020\007\022" +
+      "\013\n\007BOOLEAN\020\010\022\010\n\004BYTE\020\t\022\r\n\tCHARACTER\020\n\022\t\n",
+      "\005SHORT\020\013\022\013\n\007INTEGER\020\014\022\010\n\004LONG\020\r\022\t\n\005FLOAT" +
+      "\020\016\022\n\n\006DOUBLE\020\017\022\017\n\013BIG_INTEGER\020\031\022\017\n\013BIG_D" +
+      "ECIMAL\020\032\022\021\n\rJAVA_SQL_TIME\020\020\022\026\n\022JAVA_SQL_" +
+      "TIMESTAMP\020\021\022\021\n\rJAVA_SQL_DATE\020\022\022\022\n\016JAVA_U" +
+      "TIL_DATE\020\023\022\017\n\013BYTE_STRING\020\024\022\n\n\006STRING\020\025\022" +
+      "\n\n\006NUMBER\020\026\022\n\n\006OBJECT\020\027\022\010\n\004NULL\020\030\022\t\n\005ARR" +
+      "AY\020\033\022\n\n\006STRUCT\020\034\022\014\n\010MULTISET\020\035*^\n\010Severi" +
+      "ty\022\024\n\020UNKNOWN_SEVERITY\020\000\022\022\n\016FATAL_SEVERI" +
+      "TY\020\001\022\022\n\016ERROR_SEVERITY\020\002\022\024\n\020WARNING_SEVE" +
+      "RITY\020\003*\327\004\n\021MetaDataOperation\022\022\n\016GET_ATTR",
+      "IBUTES\020\000\022\033\n\027GET_BEST_ROW_IDENTIFIER\020\001\022\020\n" +
+      "\014GET_CATALOGS\020\002\022\036\n\032GET_CLIENT_INFO_PROPE" +
+      "RTIES\020\003\022\031\n\025GET_COLUMN_PRIVILEGES\020\004\022\017\n\013GE" +
+      "T_COLUMNS\020\005\022\027\n\023GET_CROSS_REFERENCE\020\006\022\025\n\021" +
+      "GET_EXPORTED_KEYS\020\007\022\030\n\024GET_FUNCTION_COLU" +
+      "MNS\020\010\022\021\n\rGET_FUNCTIONS\020\t\022\025\n\021GET_IMPORTED" +
+      "_KEYS\020\n\022\022\n\016GET_INDEX_INFO\020\013\022\024\n\020GET_PRIMA" +
+      "RY_KEYS\020\014\022\031\n\025GET_PROCEDURE_COLUMNS\020\r\022\022\n\016" +
+      "GET_PROCEDURES\020\016\022\026\n\022GET_PSEUDO_COLUMNS\020\017" +
+      "\022\017\n\013GET_SCHEMAS\020\020\022\031\n\025GET_SCHEMAS_WITH_AR",
+      "GS\020\021\022\024\n\020GET_SUPER_TABLES\020\022\022\023\n\017GET_SUPER_" +
+      "TYPES\020\023\022\030\n\024GET_TABLE_PRIVILEGES\020\024\022\016\n\nGET" +
+      "_TABLES\020\025\022\023\n\017GET_TABLE_TYPES\020\026\022\021\n\rGET_TY" +
+      "PE_INFO\020\027\022\014\n\010GET_UDTS\020\030\022\027\n\023GET_VERSION_C" +
+      "OLUMNS\020\031*\"\n\tStateType\022\007\n\003SQL\020\000\022\014\n\010METADA" +
+      "TA\020\001B\"\n org.apache.calcite.avatica.proto" +
+      "b\006proto3"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
         new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
@@ -18914,7 +18999,7 @@ public final class Common {
     internal_static_TypedValue_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
         internal_static_TypedValue_descriptor,
-        new java.lang.String[] { "Type", "BoolValue", "StringValue", "NumberValue", "BytesValue", "DoubleValue", "Null", "ArrayValue", "ComponentType", });
+        new java.lang.String[] { "Type", "BoolValue", "StringValue", "NumberValue", "BytesValue", "DoubleValue", "Null", "ArrayValue", "ComponentType", "ImplicitlyNull", });
     internal_static_MetaDataOperationArgument_descriptor =
       getDescriptor().getMessageTypes().get(13);
     internal_static_MetaDataOperationArgument_fieldAccessorTable = new

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/557f15cc/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/Service.java b/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
index 8178ecd..b935b22 100644
--- a/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/Service.java
@@ -1045,7 +1045,7 @@ public interface Service {
       if (msg.getHasParameterValues()) {
         values = new ArrayList<>(msg.getParameterValuesCount());
         for (Common.TypedValue valueProto : msg.getParameterValuesList()) {
-          if (TypedValue.NULL_PROTO.equals(valueProto)) {
+          if (TypedValue.PROTO_IMPLICIT_NULL.equals(valueProto)) {
             values.add(null);
           } else {
             values.add(TypedValue.fromProto(valueProto));
@@ -1076,7 +1076,7 @@ public interface Service {
         builder.setHasParameterValues(true);
         for (TypedValue paramValue : parameterValues) {
           if (paramValue == null) {
-            builder.addParameterValues(TypedValue.NULL_PROTO);
+            builder.addParameterValues(TypedValue.PROTO_IMPLICIT_NULL);
           } else {
             builder.addParameterValues(paramValue.toProto());
           }

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/557f15cc/core/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java b/core/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
index 04268bb..363825b 100644
--- a/core/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
@@ -145,9 +145,12 @@ public class TypedValue {
   private static final FieldDescriptor BYTES_DESCRIPTOR = Common.TypedValue.getDescriptor()
       .findFieldByNumber(Common.TypedValue.BYTES_VALUE_FIELD_NUMBER);
 
-  public static final TypedValue NULL =
+  // If the user sets a `null` Object, it's explicitly null
+  public static final TypedValue EXPLICIT_NULL =
       new TypedValue(ColumnMetaData.Rep.OBJECT, null);
-  public static final Common.TypedValue NULL_PROTO = NULL.toProto();
+  // The user might also implicitly not set a value for a parameter.
+  public static final Common.TypedValue PROTO_IMPLICIT_NULL =
+      Common.TypedValue.newBuilder().setImplicitlyNull(true).build();
 
   /** Type of the value. */
   public final ColumnMetaData.Rep type;
@@ -194,7 +197,7 @@ public class TypedValue {
   public static TypedValue create(@JsonProperty("type") String type,
       @JsonProperty("value") Object value) {
     if (value == null) {
-      return NULL;
+      return EXPLICIT_NULL;
     }
     ColumnMetaData.Rep rep = ColumnMetaData.Rep.valueOf(type);
     return ofLocal(rep, serialToLocal(rep, value));
@@ -214,7 +217,7 @@ public class TypedValue {
   public static TypedValue ofJdbc(ColumnMetaData.Rep rep, Object value,
       Calendar calendar) {
     if (value == null) {
-      return NULL;
+      return EXPLICIT_NULL;
     }
     final Object serialValue;
     if (ColumnMetaData.Rep.ARRAY == rep) {
@@ -243,7 +246,7 @@ public class TypedValue {
    * deducing its type. */
   public static TypedValue ofJdbc(Object value, Calendar calendar) {
     if (value == null) {
-      return NULL;
+      return EXPLICIT_NULL;
     }
     final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(value.getClass());
     return new TypedValue(rep, jdbcToSerial(rep, value, calendar));
@@ -485,6 +488,9 @@ public class TypedValue {
    */
   public Common.TypedValue toProto() {
     final Common.TypedValue.Builder builder = Common.TypedValue.newBuilder();
+    // This isn't a static method, therefore we have a non-null TypedValue. Thus, this message
+    // cannot be implicitly null
+    builder.setImplicitlyNull(false);
 
     Common.Rep protoRep = type.toProto();
     // Protobuf has an explicit BIG_DECIMAL representation enum value.
@@ -896,9 +902,7 @@ public class TypedValue {
       }
 
       if (null == value) {
-        if (null != other.value) {
-          return false;
-        }
+        return null == other.value;
       }
 
       return value.equals(other.value);

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/557f15cc/core/src/main/protobuf/common.proto
----------------------------------------------------------------------
diff --git a/core/src/main/protobuf/common.proto b/core/src/main/protobuf/common.proto
index 63dbcc9..04d0a85 100644
--- a/core/src/main/protobuf/common.proto
+++ b/core/src/main/protobuf/common.proto
@@ -202,6 +202,9 @@ message TypedValue {
 
   repeated TypedValue array_value = 8; // The Array
   Rep component_type = 9; // If an Array, the representation for the array values
+
+  bool implicitly_null = 10; // Differentiate between explicitly null (user-set) and implicitly null
+                            // (un-set by the user)
 }
 
 // The severity of some unexpected outcome to an operation.

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/557f15cc/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java b/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
index ebb2df4..8069c84 100644
--- a/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
+++ b/core/src/test/java/org/apache/calcite/avatica/remote/ProtobufTranslationImplTest.java
@@ -45,6 +45,7 @@ import org.apache.calcite.avatica.remote.Service.DatabasePropertyRequest;
 import org.apache.calcite.avatica.remote.Service.DatabasePropertyResponse;
 import org.apache.calcite.avatica.remote.Service.ErrorResponse;
 import org.apache.calcite.avatica.remote.Service.ExecuteBatchResponse;
+import org.apache.calcite.avatica.remote.Service.ExecuteRequest;
 import org.apache.calcite.avatica.remote.Service.ExecuteResponse;
 import org.apache.calcite.avatica.remote.Service.FetchRequest;
 import org.apache.calcite.avatica.remote.Service.FetchResponse;
@@ -223,6 +224,17 @@ public class ProtobufTranslationImplTest<T> {
     List<String> commands = Arrays.asList("command1", "command2", "command3");
     requests.add(new PrepareAndExecuteBatchRequest("connectionId", 12345, commands));
 
+
+    List<ColumnMetaData> columns = Collections.emptyList();
+    List<AvaticaParameter> params = Collections.emptyList();
+    Meta.CursorFactory cursorFactory = Meta.CursorFactory.create(Style.LIST, Object.class,
+        Collections.<String>emptyList());
+    Signature signature = Signature.create(columns, "sql", params, cursorFactory,
+        Meta.StatementType.SELECT);
+    Meta.StatementHandle handle = new Meta.StatementHandle("1234", 1, signature);
+    requests.add(new ExecuteRequest(handle, Arrays.<TypedValue>asList((TypedValue) null), 10));
+    requests.add(new ExecuteRequest(handle, Arrays.asList(TypedValue.EXPLICIT_NULL), 10));
+
     return requests;
   }