You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/10/16 16:39:02 UTC

[3/4] incubator-calcite git commit: [CALCITE-705] DML in Avatica, and split Execute out from Fetch request (Yeong Wei)

[CALCITE-705] DML in Avatica, and split Execute out from Fetch request (Yeong Wei)

Add support for DML statements (INSERT, UPDATE, DELETE),
Statement.executeUpdate returns int update count.

Add new 'Execute' request that binds parameters. This used to occur
implicitly during first Fetch; Fetch no longer takes parameters.

Add statement type. If signature is null then defaults to
Meta.StatementType.SELECT.

Add testRemoteStatementInsert for JSON and PROTOBUF serialization test.

In JsonHandlerTest, replace testFetchRequestWithNumberParameter
with testExecuteRequestWithNumberParameter.

Cosmetic fix-ups by Julian Hyde.

Close apache/incubator-calcite#145
Close apache/incubator-calcite#154


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

Branch: refs/heads/master
Commit: cee8e8448c314753dacda8f880fac2bcab701f08
Parents: f5f83e5
Author: YeongWei <la...@persistent.my>
Authored: Thu May 28 04:57:36 2015 -0400
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Oct 16 00:58:24 2015 -0700

----------------------------------------------------------------------
 .../apache/calcite/avatica/jdbc/JdbcMeta.java   |  134 +-
 .../calcite/avatica/jdbc/JdbcResultSet.java     |   56 +-
 .../calcite/avatica/RemoteDriverTest.java       |   38 +-
 .../calcite/avatica/remote/RemoteMetaTest.java  |   18 +
 .../calcite/avatica/AvaticaConnection.java      |   76 +-
 .../avatica/AvaticaPreparedStatement.java       |   16 +-
 .../calcite/avatica/AvaticaStatement.java       |   26 +-
 .../java/org/apache/calcite/avatica/Meta.java   |   69 +-
 .../org/apache/calcite/avatica/MetaImpl.java    |   17 +-
 .../apache/calcite/avatica/proto/Common.java    |  376 +++-
 .../apache/calcite/avatica/proto/Requests.java  | 1644 +++++++++++-------
 .../calcite/avatica/remote/AbstractService.java |    2 +-
 .../calcite/avatica/remote/JsonService.java     |    8 +
 .../calcite/avatica/remote/LocalService.java    |   38 +-
 .../avatica/remote/MockProtobufService.java     |    4 +-
 .../calcite/avatica/remote/ProtobufService.java |    4 +
 .../avatica/remote/ProtobufTranslationImpl.java |    3 +
 .../calcite/avatica/remote/RemoteMeta.java      |   23 +-
 .../apache/calcite/avatica/remote/Service.java  |  156 +-
 .../calcite/avatica/util/AbstractCursor.java    |   10 +-
 avatica/src/main/protobuf/common.proto          |   17 +
 avatica/src/main/protobuf/requests.proto        |   11 +-
 .../avatica/remote/ProtobufHandlerTest.java     |    2 +-
 .../remote/ProtobufTranslationImplTest.java     |    5 +-
 .../calcite/avatica/test/JsonHandlerTest.java   |   67 +-
 .../calcite/jdbc/CalciteConnectionImpl.java     |   14 +-
 .../apache/calcite/jdbc/CalciteMetaImpl.java    |   43 +-
 .../org/apache/calcite/jdbc/CalcitePrepare.java |   20 +-
 .../org/apache/calcite/plan/RelOptUtil.java     |    7 +-
 .../calcite/prepare/CalcitePrepareImpl.java     |   38 +-
 .../calcite/jdbc/CalciteRemoteDriverTest.java   |  191 +-
 .../calcite/test/JdbcFrontLinqBackTest.java     |  119 +-
 32 files changed, 2397 insertions(+), 855 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
index 34288ce..3585185 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
@@ -17,10 +17,12 @@
 package org.apache.calcite.avatica.jdbc;
 
 import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.AvaticaPreparedStatement;
 import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.ConnectionPropertiesImpl;
 import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.MetaImpl;
 import org.apache.calcite.avatica.SqlType;
 import org.apache.calcite.avatica.remote.TypedValue;
 
@@ -65,6 +67,13 @@ public class JdbcMeta implements Meta {
   private static final String DEFAULT_CONN_ID =
       UUID.fromString("00000000-0000-0000-0000-000000000000").toString();
 
+  /** Special value for {@link Statement#getLargeMaxRows()} that means fetch
+   * an unlimited number of rows in a single batch.
+   *
+   * <p>Any other negative value will return an unlimited number of rows but
+   * will do it in the default batch size, namely 100. */
+  public static final long UNLIMITED_COUNT = -2L;
+
   // End of constants, start of member variables
 
   final Calendar calendar = Calendar.getInstance();
@@ -226,14 +235,16 @@ public class JdbcMeta implements Meta {
   }
 
   protected static Signature signature(ResultSetMetaData metaData,
-      ParameterMetaData parameterMetaData, String sql) throws  SQLException {
+      ParameterMetaData parameterMetaData, String sql,
+      Meta.StatementType statementType) throws  SQLException {
+    final CursorFactory cf = CursorFactory.LIST;  // because JdbcResultSet#frame
     return new Signature(columns(metaData), sql, parameters(parameterMetaData),
-        null, CursorFactory.LIST /* LIST because JdbcResultSet#frame */);
+        null, cf, statementType);
   }
 
   protected static Signature signature(ResultSetMetaData metaData)
       throws SQLException {
-    return signature(metaData, null, null);
+    return signature(metaData, null, null, null);
   }
 
   public Map<DatabaseProperty, Object> getDatabaseProperties() {
@@ -274,9 +285,9 @@ public class JdbcMeta implements Meta {
       Pat tableNamePattern, Pat columnNamePattern) {
     try {
       final ResultSet rs =
-          connection.getMetaData().getColumns(catalog, schemaPattern.s,
-              tableNamePattern.s, columnNamePattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+          connection.getMetaData().getColumns(
+              catalog, schemaPattern.s, tableNamePattern.s, columnNamePattern.s);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -286,7 +297,7 @@ public class JdbcMeta implements Meta {
     try {
       final ResultSet rs =
           connection.getMetaData().getSchemas(catalog, schemaPattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -295,7 +306,7 @@ public class JdbcMeta implements Meta {
   public MetaResultSet getCatalogs() {
     try {
       final ResultSet rs = connection.getMetaData().getCatalogs();
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -304,7 +315,7 @@ public class JdbcMeta implements Meta {
   public MetaResultSet getTableTypes() {
     try {
       final ResultSet rs = connection.getMetaData().getTableTypes();
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -314,9 +325,9 @@ public class JdbcMeta implements Meta {
       Pat procedureNamePattern) {
     try {
       final ResultSet rs =
-          connection.getMetaData().getProcedures(catalog, schemaPattern.s,
-              procedureNamePattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+          connection.getMetaData().getProcedures(
+              catalog, schemaPattern.s, procedureNamePattern.s);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -328,7 +339,7 @@ public class JdbcMeta implements Meta {
       final ResultSet rs =
           connection.getMetaData().getProcedureColumns(catalog,
               schemaPattern.s, procedureNamePattern.s, columnNamePattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -340,7 +351,7 @@ public class JdbcMeta implements Meta {
       final ResultSet rs =
           connection.getMetaData().getColumnPrivileges(catalog, schema,
               table, columnNamePattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -352,7 +363,7 @@ public class JdbcMeta implements Meta {
       final ResultSet rs =
           connection.getMetaData().getTablePrivileges(catalog,
               schemaPattern.s, tableNamePattern.s);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -368,7 +379,7 @@ public class JdbcMeta implements Meta {
       final ResultSet rs =
           connection.getMetaData().getBestRowIdentifier(catalog, schema,
               table, scope, nullable);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -382,7 +393,7 @@ public class JdbcMeta implements Meta {
     try {
       final ResultSet rs =
           connection.getMetaData().getVersionColumns(catalog, schema, table);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -396,7 +407,7 @@ public class JdbcMeta implements Meta {
     try {
       final ResultSet rs =
           connection.getMetaData().getPrimaryKeys(catalog, schema, table);
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -421,7 +432,7 @@ public class JdbcMeta implements Meta {
   public MetaResultSet getTypeInfo() {
     try {
       final ResultSet rs = connection.getMetaData().getTypeInfo();
-      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs);
+      return JdbcResultSet.create(DEFAULT_CONN_ID, -1, rs, UNLIMITED_COUNT);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -583,7 +594,7 @@ public class JdbcMeta implements Meta {
     } else if (e instanceof Error) {
       throw (Error) e;
     } else {
-      throw new RuntimeException(e);
+      throw new RuntimeException(e.getMessage());
     }
   }
 
@@ -593,10 +604,17 @@ public class JdbcMeta implements Meta {
       final Connection conn = getConnection(ch.id);
       final PreparedStatement statement = conn.prepareStatement(sql);
       final int id = statementIdGenerator.getAndIncrement();
+      Meta.StatementType statementType = null;
+      if (statement.isWrapperFor(AvaticaPreparedStatement.class)) {
+        final AvaticaPreparedStatement avaticaPreparedStatement;
+        avaticaPreparedStatement =
+            statement.unwrap(AvaticaPreparedStatement.class);
+        statementType = avaticaPreparedStatement.getStatementType();
+      }
       statementCache.put(id, new StatementInfo(statement));
       StatementHandle h = new StatementHandle(ch.id, id,
           signature(statement.getMetaData(), statement.getParameterMetaData(),
-              sql));
+              sql, statementType));
       if (LOG.isTraceEnabled()) {
         LOG.trace("prepared statement " + h);
       }
@@ -628,7 +646,7 @@ public class JdbcMeta implements Meta {
       if (info.resultSet == null) {
         // Create a special result set that just carries update count
         resultSets.add(
-            MetaResultSet.count(h.connectionId, h.id,
+            JdbcResultSet.count(h.connectionId, h.id,
                 AvaticaUtils.getLargeUpdateCount(statement)));
       } else {
         resultSets.add(
@@ -645,8 +663,7 @@ public class JdbcMeta implements Meta {
     }
   }
 
-  public Frame fetch(StatementHandle h, List<TypedValue> parameterValues,
-      long offset, int fetchMaxRowCount) {
+  public Frame fetch(StatementHandle h, long offset, int fetchMaxRowCount) {
     if (LOG.isTraceEnabled()) {
       LOG.trace("fetching " + h + " offset:" + offset + " fetchMaxRowCount:"
           + fetchMaxRowCount);
@@ -655,21 +672,6 @@ public class JdbcMeta implements Meta {
       final StatementInfo statementInfo = Objects.requireNonNull(
           statementCache.getIfPresent(h.id),
           "Statement not found, potentially expired. " + h);
-      if (statementInfo.resultSet == null || parameterValues != null) {
-        if (statementInfo.statement instanceof PreparedStatement) {
-          final PreparedStatement preparedStatement =
-              (PreparedStatement) statementInfo.statement;
-          if (parameterValues != null) {
-            for (int i = 0; i < parameterValues.size(); i++) {
-              TypedValue o = parameterValues.get(i);
-              preparedStatement.setObject(i + 1, o.toJdbc(calendar));
-            }
-          }
-          if (preparedStatement.execute()) {
-            statementInfo.resultSet = preparedStatement.getResultSet();
-          }
-        }
-      }
       if (statementInfo.resultSet == null) {
         return Frame.EMPTY;
       } else {
@@ -688,6 +690,60 @@ public class JdbcMeta implements Meta {
     return typeList.toArray(new String[typeList.size()]);
   }
 
+  @Override public ExecuteResult execute(StatementHandle h,
+      List<TypedValue> parameterValues, long maxRowCount) {
+    try {
+      if (MetaImpl.checkParameterValueHasNull(parameterValues)) {
+        throw new SQLException("exception while executing query: unbound parameter");
+      }
+
+      final StatementInfo statementInfo = Objects.requireNonNull(
+          statementCache.getIfPresent(h.id),
+          "Statement not found, potentially expired. " + h);
+      final List<MetaResultSet> resultSets = new ArrayList<>();
+      final PreparedStatement preparedStatement =
+          (PreparedStatement) statementInfo.statement;
+
+      if (parameterValues != null) {
+        for (int i = 0; i < parameterValues.size(); i++) {
+          TypedValue o = parameterValues.get(i);
+          preparedStatement.setObject(i + 1, o.toJdbc(calendar));
+        }
+      }
+
+      if (preparedStatement.execute()) {
+        final Meta.Frame frame;
+        final Signature signature2;
+        if (preparedStatement.isWrapperFor(AvaticaPreparedStatement.class)) {
+          signature2 = h.signature;
+        } else {
+          h.signature = signature(preparedStatement.getMetaData(),
+              preparedStatement.getParameterMetaData(), h.signature.sql,
+              Meta.StatementType.SELECT);
+          signature2 = h.signature;
+        }
+
+        statementInfo.resultSet = preparedStatement.getResultSet();
+        if (statementInfo.resultSet == null) {
+          frame = Frame.EMPTY;
+          resultSets.add(JdbcResultSet.empty(h.connectionId, h.id, signature2));
+        } else {
+          resultSets.add(
+              JdbcResultSet.create(h.connectionId, h.id,
+                  statementInfo.resultSet, maxRowCount, signature2));
+        }
+      } else {
+        resultSets.add(
+            JdbcResultSet.count(
+                h.connectionId, h.id, preparedStatement.getUpdateCount()));
+      }
+
+      return new ExecuteResult(resultSets);
+    } catch (SQLException e) {
+      throw propagate(e);
+    }
+  }
+
   /** All we know about a statement. */
   private static class StatementInfo {
     final Statement statement; // sometimes a PreparedStatement

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
index dc50405..d0a57ab 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica.jdbc;
 
+import org.apache.calcite.avatica.AvaticaStatement;
 import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.util.DateTimeUtils;
 
@@ -37,34 +38,77 @@ import java.util.List;
 class JdbcResultSet extends Meta.MetaResultSet {
   protected JdbcResultSet(String connectionId, int statementId,
       boolean ownStatement, Meta.Signature signature, Meta.Frame firstFrame) {
-    super(connectionId, statementId, ownStatement, signature, firstFrame, -1L);
+    this(connectionId, statementId, ownStatement, signature, firstFrame, -1L);
+  }
+
+  protected JdbcResultSet(String connectionId, int statementId,
+      boolean ownStatement, Meta.Signature signature, Meta.Frame firstFrame,
+      long updateCount) {
+    super(connectionId, statementId, ownStatement, signature, firstFrame, updateCount);
   }
 
   /** Creates a result set. */
   public static JdbcResultSet create(String connectionId, int statementId,
       ResultSet resultSet) {
-    return create(connectionId, statementId, resultSet, -1);
+    // -1 still limits to 100 but -2 does not limit to any number
+    return create(connectionId, statementId, resultSet,
+        JdbcMeta.UNLIMITED_COUNT);
   }
 
-  /** Creates a result set with maxRowCount. */
+  /** Creates a result set with maxRowCount.
+   *
+   * <p>If {@code maxRowCount} is -2 ({@link JdbcMeta#UNLIMITED_COUNT}),
+   * returns an unlimited number of rows in a single frame; any other
+   * negative value (typically -1) returns an unlimited number of rows
+   * in frames of the default frame size. */
   public static JdbcResultSet create(String connectionId, int statementId,
       ResultSet resultSet, long maxRowCount) {
     try {
       Meta.Signature sig = JdbcMeta.signature(resultSet.getMetaData());
+      return create(connectionId, statementId, resultSet, maxRowCount, sig);
+    } catch (SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static JdbcResultSet create(String connectionId, int statementId,
+      ResultSet resultSet, long maxRowCount, Meta.Signature signature) {
+    try {
       final Calendar calendar = Calendar.getInstance(DateTimeUtils.GMT_ZONE);
-      final int fetchRowCount =
-        (maxRowCount == -1 || maxRowCount > 100) ? 100 : (int) maxRowCount;
+      final int fetchRowCount;
+      if (maxRowCount == JdbcMeta.UNLIMITED_COUNT) {
+        fetchRowCount = -1;
+      } else if (maxRowCount < 0L) {
+        fetchRowCount = AvaticaStatement.DEFAULT_FETCH_SIZE;
+      } else if (maxRowCount > AvaticaStatement.DEFAULT_FETCH_SIZE) {
+        fetchRowCount = AvaticaStatement.DEFAULT_FETCH_SIZE;
+      } else {
+        fetchRowCount = (int) maxRowCount;
+      }
       final Meta.Frame firstFrame = frame(resultSet, 0, fetchRowCount, calendar);
       if (firstFrame.done) {
         resultSet.close();
       }
-      return new JdbcResultSet(connectionId, statementId, true, sig,
+      return new JdbcResultSet(connectionId, statementId, true, signature,
           firstFrame);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
   }
 
+  /** Creates a empty result set with empty frame */
+  public static JdbcResultSet empty(String connectionId, int statementId,
+      Meta.Signature signature) {
+    return new JdbcResultSet(connectionId, statementId, true, signature,
+        Meta.Frame.EMPTY);
+  }
+
+  /** Creates a result set that only has an update count. */
+  public static JdbcResultSet count(String connectionId, int statementId,
+      int updateCount) {
+    return new JdbcResultSet(connectionId, statementId, true, null, null, updateCount);
+  }
+
   /** Creates a frame containing a given number or unlimited number of rows
    * from a result set. */
   static Meta.Frame frame(ResultSet resultSet, long offset,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
index 7e7b39f..b3f45fd 100644
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
+++ b/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
@@ -57,6 +57,7 @@ import java.util.concurrent.TimeUnit;
 
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -341,6 +342,27 @@ public class RemoteDriverTest {
     }
   }
 
+  @Test public void testInsertDrop() throws Exception {
+    final String create = "create table if not exists TEST_TABLE2 ("
+        + "id int not null, "
+        + "msg varchar(3) not null)";
+    final String insert = "insert into TEST_TABLE2 values(1, 'foo')";
+    Connection connection = ljs();
+    Statement statement = connection.createStatement();
+    statement.execute(create);
+
+    Statement stmt = connection.createStatement();
+    int count = stmt.executeUpdate(insert);
+    assertThat(count, is(1));
+    ResultSet resultSet = stmt.getResultSet();
+    assertThat(resultSet, nullValue());
+
+    PreparedStatement pstmt = connection.prepareStatement(insert);
+    boolean status = pstmt.execute();
+    assertThat(status, is(false));
+    int updateCount = pstmt.getUpdateCount();
+    assertThat(updateCount, is(1));
+  }
 
   private void checkStatementExecuteQuery(Connection connection,
       boolean prepare) throws SQLException {
@@ -393,7 +415,8 @@ public class RemoteDriverTest {
       // PreparedStatement needed an extra fetch, as the execute will
       // trigger the 1st fetch. Where statement execute will execute direct
       // with results back.
-      checkExecuteFetch(getLocalConnection(), sql, true, 2);
+      // 1 fetch, because execute did the first fetch
+      checkExecuteFetch(getLocalConnection(), sql, true, 1);
     } finally {
       ConnectionSpec.getDatabaseLock().unlock();
     }
@@ -439,6 +462,19 @@ public class RemoteDriverTest {
     }
   }
 
+  @Test public void testFetchSize() throws Exception {
+    Connection connection = ljs();
+
+    Statement statement = connection.createStatement();
+    statement.setFetchSize(101);
+    assertEquals(statement.getFetchSize(), 101);
+
+    PreparedStatement preparedStatement =
+        connection.prepareStatement("select * from (values (1, 'a')) as tbl1 (c1, c2)");
+    preparedStatement.setFetchSize(1);
+    assertEquals(preparedStatement.getFetchSize(), 1);
+  }
+
   @Ignore("CALCITE-719: Refactor PreparedStatement to support setMaxRows")
   @Test public void testStatementPrepareExecuteLocalMaxRow() throws Exception {
     ConnectionSpec.getDatabaseLock().lock();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
index c4545e3..2007288 100644
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
+++ b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
@@ -47,6 +47,8 @@ import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
@@ -57,6 +59,7 @@ import static org.junit.Assert.assertTrue;
 /** Tests covering {@link RemoteMeta}. */
 @RunWith(Parameterized.class)
 public class RemoteMetaTest {
+  private static final Random RANDOM = new Random();
   private static final ConnectionSpec CONNECTION_SPEC = ConnectionSpec.HSQLDB;
 
   // Keep a reference to the servers we start to clean them up after
@@ -259,6 +262,21 @@ public class RemoteMetaTest {
       ConnectionSpec.getDatabaseLock().unlock();
     }
   }
+
+  @Test public void testRemoteStatementInsert() throws Exception {
+    System.out.println(url);
+    AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
+    Statement statement = conn.createStatement();
+    int status = statement.executeUpdate(
+        "create table if not exists "
+        + "TEST_TABLE2 (id int not null, msg varchar(255) not null)");
+    assertEquals(status, 0);
+
+    statement = conn.createStatement();
+    status = statement.executeUpdate("insert into TEST_TABLE2 values ("
+        + "'" + RANDOM.nextInt(Integer.MAX_VALUE) + "', '" + UUID.randomUUID() + "')");
+    assertEquals(status, 1);
+  }
 }
 
 // End RemoteMetaTest.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
index 1a7443c..66c03dd 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.Meta.MetaResultSet;
 import org.apache.calcite.avatica.remote.TypedValue;
 
 import java.sql.Array;
@@ -52,6 +53,16 @@ import java.util.concurrent.Executor;
  */
 public abstract class AvaticaConnection implements Connection {
 
+  /** The name of the sole column returned by DML statements, containing
+   * the number of rows modified. */
+  public static final String ROWCOUNT_COLUMN_NAME = "ROWCOUNT";
+
+  /** The name of the sole column returned by an EXPLAIN statement.
+   *
+   * <p>Actually Avatica does not care what this column is called, but here is
+   * a useful place to define a suggested value. */
+  public static final String PLAN_COLUMN_NAME = "PLAN";
+
   protected int statementCount;
   private boolean closed;
   private int holdability;
@@ -408,6 +419,9 @@ public abstract class AvaticaConnection implements Connection {
   protected ResultSet executeQueryInternal(AvaticaStatement statement,
       Meta.Signature signature, Meta.Frame firstFrame) throws SQLException {
     // Close the previous open result set, if there is one.
+    Meta.Frame frame = firstFrame;
+    Meta.Signature signature2 = signature;
+
     synchronized (statement) {
       if (statement.openResultSet != null) {
         final AvaticaResultSet rs = statement.openResultSet;
@@ -420,14 +434,37 @@ public abstract class AvaticaConnection implements Connection {
         }
       }
 
+      try {
+        if (statement.isWrapperFor(AvaticaPreparedStatement.class)) {
+          final AvaticaPreparedStatement pstmt = (AvaticaPreparedStatement) statement;
+          final Meta.ExecuteResult executeResult =
+              meta.execute(pstmt.handle, pstmt.getParameterValues(),
+                  statement.getFetchSize());
+          final MetaResultSet metaResultSet = executeResult.resultSets.get(0);
+          frame = metaResultSet.firstFrame;
+          statement.updateCount = metaResultSet.updateCount;
+          signature2 = executeResult.resultSets.get(0).signature;
+        }
+      } catch (Exception e) {
+        e.printStackTrace();
+        throw helper.createException(e.getMessage(), e);
+      }
+
       final TimeZone timeZone = getTimeZone();
-      statement.openResultSet =
-          factory.newResultSet(statement, signature, timeZone, firstFrame);
+      if (frame == null && signature2 == null && statement.updateCount != -1) {
+        statement.openResultSet = null;
+      } else {
+        statement.openResultSet =
+            factory.newResultSet(statement, signature2, timeZone, frame);
+      }
     }
     // Release the monitor before executing, to give another thread the
     // opportunity to call cancel.
     try {
-      statement.openResultSet.execute();
+      if (statement.openResultSet != null) {
+        statement.openResultSet.execute();
+        isUpdateCapable(statement);
+      }
     } catch (Exception e) {
       throw helper.createException(
           "exception while executing query: " + e.getMessage(), e);
@@ -435,6 +472,36 @@ public abstract class AvaticaConnection implements Connection {
     return statement.openResultSet;
   }
 
+  /** Returns whether a a statement is capable of updates and if so,
+   * and the statement's {@code updateCount} is still -1, proceeds to
+   * get updateCount value from statement's resultSet.
+   *
+   * <p>Handles "ROWCOUNT" object as Number or List
+   *
+   * @param statement Statement
+   * @throws SQLException on error
+   */
+  private void isUpdateCapable(final AvaticaStatement statement)
+      throws SQLException {
+    Meta.Signature signature = statement.getSignature();
+    if (signature == null || signature.statementType == null) {
+      return;
+    }
+    if (signature.statementType.canUpdate() && statement.updateCount == -1) {
+      statement.openResultSet.next();
+      Object obj = statement.openResultSet.getObject(ROWCOUNT_COLUMN_NAME);
+      if (obj instanceof Number) {
+        statement.updateCount = ((Number) obj).intValue();
+      } else if (obj instanceof List) {
+        statement.updateCount =
+            ((Number) ((List<Object>) obj).get(0)).intValue();
+      } else {
+        throw helper.createException("Not a valid return result.");
+      }
+      statement.openResultSet = null;
+    }
+  }
+
   protected Meta.ExecuteResult prepareAndExecuteInternal(
       final AvaticaStatement statement, String sql, long maxRowCount)
       throws SQLException {
@@ -459,6 +526,8 @@ public abstract class AvaticaConnection implements Connection {
 
           public void assign(Meta.Signature signature, Meta.Frame firstFrame,
               long updateCount) throws SQLException {
+            statement.setSignature(signature);
+
             if (updateCount != -1) {
               statement.updateCount = updateCount;
             } else {
@@ -471,6 +540,7 @@ public abstract class AvaticaConnection implements Connection {
           public void execute() throws SQLException {
             if (statement.openResultSet != null) {
               statement.openResultSet.execute();
+              isUpdateCapable(statement);
             }
           }
         };

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
index af00378..9144ed3 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
@@ -48,7 +48,6 @@ import java.util.List;
 public abstract class AvaticaPreparedStatement
     extends AvaticaStatement
     implements PreparedStatement, ParameterMetaData {
-  private Meta.Signature signature;
   private final ResultSetMetaData resultSetMetaData;
   private Calendar calendar;
   protected final TypedValue[] slots;
@@ -71,8 +70,7 @@ public abstract class AvaticaPreparedStatement
       int resultSetConcurrency,
       int resultSetHoldability) throws SQLException {
     super(connection, h, resultSetType, resultSetConcurrency,
-        resultSetHoldability);
-    this.signature = signature;
+        resultSetHoldability, signature);
     this.slots = new TypedValue[signature.parameters.size()];
     this.resultSetMetaData =
         connection.factory.newResultSetMetaData(this, signature);
@@ -107,7 +105,8 @@ public abstract class AvaticaPreparedStatement
   // implement PreparedStatement
 
   public ResultSet executeQuery() throws SQLException {
-    return getConnection().executeQueryInternal(this, signature, null);
+    this.updateCount = -1;
+    return getConnection().executeQueryInternal(this, getSignature(), null);
   }
 
   public ParameterMetaData getParameterMetaData() throws SQLException {
@@ -119,7 +118,7 @@ public abstract class AvaticaPreparedStatement
   }
 
   public long executeLargeUpdate() throws SQLException {
-    getConnection().executeQueryInternal(this, signature, null);
+    getConnection().executeQueryInternal(this, getSignature(), null);
     return updateCount;
   }
 
@@ -199,7 +198,8 @@ public abstract class AvaticaPreparedStatement
   }
 
   public boolean execute() throws SQLException {
-    getConnection().executeQueryInternal(this, signature, null);
+    this.updateCount = -1;
+    getConnection().executeQueryInternal(this, getSignature(), null);
     // Result set is null for DML or DDL.
     // Result set is closed if user cancelled the query.
     return openResultSet != null && !openResultSet.isClosed();
@@ -280,7 +280,7 @@ public abstract class AvaticaPreparedStatement
 
   protected AvaticaParameter getParameter(int param) throws SQLException {
     try {
-      return signature.parameters.get(param - 1);
+      return getSignature().parameters.get(param - 1);
     } catch (IndexOutOfBoundsException e) {
       //noinspection ThrowableResultOfMethodCallIgnored
       throw connection.helper.toSQLException(
@@ -295,7 +295,7 @@ public abstract class AvaticaPreparedStatement
   }
 
   public int getParameterCount() {
-    return signature.parameters.size();
+    return getSignature().parameters.size();
   }
 
   public int isNullable(int param) throws SQLException {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
index bed506d..1b338c3 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
@@ -34,6 +34,9 @@ import java.util.Objects;
  */
 public abstract class AvaticaStatement
     implements Statement {
+  /** The default value for {@link Statement#getFetchSize()}. */
+  public static final int DEFAULT_FETCH_SIZE = 100;
+
   public final AvaticaConnection connection;
   /** Statement id; unique within connection. */
   public Meta.StatementHandle handle;
@@ -58,10 +61,24 @@ public abstract class AvaticaStatement
   final int resultSetType;
   final int resultSetConcurrency;
   final int resultSetHoldability;
-  private int fetchSize;
+  private int fetchSize = DEFAULT_FETCH_SIZE;
   private int fetchDirection;
   protected long maxRowCount = 0;
 
+  private Meta.Signature signature;
+
+  protected void setSignature(Meta.Signature signature) {
+    this.signature = signature;
+  }
+
+  protected Meta.Signature getSignature() {
+    return signature;
+  }
+
+  public Meta.StatementType getStatementType() {
+    return signature.statementType;
+  }
+
   /**
    * Creates an AvaticaStatement.
    *
@@ -74,10 +91,17 @@ public abstract class AvaticaStatement
   protected AvaticaStatement(AvaticaConnection connection,
       Meta.StatementHandle h, int resultSetType, int resultSetConcurrency,
       int resultSetHoldability) {
+    this(connection, h, resultSetType, resultSetConcurrency, resultSetHoldability, null);
+  }
+
+  protected AvaticaStatement(AvaticaConnection connection,
+      Meta.StatementHandle h, int resultSetType, int resultSetConcurrency,
+      int resultSetHoldability, Meta.Signature signature) {
     this.connection = Objects.requireNonNull(connection);
     this.resultSetType = resultSetType;
     this.resultSetConcurrency = resultSetConcurrency;
     this.resultSetHoldability = resultSetHoldability;
+    this.signature = signature;
     this.closed = false;
     if (h == null) {
       final Meta.ConnectionHandle ch = new Meta.ConnectionHandle(connection.id);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
index 12f6e2d..ac0eb74 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
@@ -222,15 +222,23 @@ public interface Meta {
    * <p>The default implementation always returns null.
    *
    * @param h Statement handle
-   * @param parameterValues A list of parameter values, if statement is to be
-   *                        executed; otherwise null
    * @param offset Zero-based offset of first row in the requested frame
    * @param fetchMaxRowCount Maximum number of rows to return; negative means
    * no limit
    * @return Frame, or null if there are no more
    */
-  Frame fetch(StatementHandle h, List<TypedValue> parameterValues, long offset,
-      int fetchMaxRowCount);
+  Frame fetch(StatementHandle h, long offset, int fetchMaxRowCount);
+
+  /** Executes a prepared statement.
+   *
+   * @param h Statement handle
+   * @param parameterValues A list of parameter values; may be empty, not null
+   * @param maxRowCount Maximum number of rows to return; negative means
+   * no limit
+   * @return Frame, or null if there are no more
+   */
+  ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues,
+      long maxRowCount);
 
   /** Called during the creation of a statement to allocate a new handle.
    *
@@ -238,11 +246,10 @@ public interface Meta {
    */
   StatementHandle createStatement(ConnectionHandle ch);
 
-  /** Close a statement.
-   */
+  /** Closes a statement. */
   void closeStatement(StatementHandle h);
 
-  /** Close a connection */
+  /** Closes a connection. */
   void closeConnection(ConnectionHandle ch);
 
   /** Sync client and server view of connection properties.
@@ -613,17 +620,21 @@ public interface Meta {
     public final transient Map<String, Object> internalParameters;
     public final CursorFactory cursorFactory;
 
+    public final Meta.StatementType statementType;
+
     /** Creates a Signature. */
     public Signature(List<ColumnMetaData> columns,
         String sql,
         List<AvaticaParameter> parameters,
         Map<String, Object> internalParameters,
-        CursorFactory cursorFactory) {
+        CursorFactory cursorFactory,
+        Meta.StatementType statementType) {
       this.columns = columns;
       this.sql = sql;
       this.parameters = parameters;
       this.internalParameters = internalParameters;
       this.cursorFactory = cursorFactory;
+      this.statementType = statementType;
     }
 
     /** Used by Jackson to create a Signature by de-serializing JSON. */
@@ -632,23 +643,26 @@ public interface Meta {
         @JsonProperty("columns") List<ColumnMetaData> columns,
         @JsonProperty("sql") String sql,
         @JsonProperty("parameters") List<AvaticaParameter> parameters,
-        @JsonProperty("cursorFactory") CursorFactory cursorFactory) {
+        @JsonProperty("cursorFactory") CursorFactory cursorFactory,
+        @JsonProperty("statementType") Meta.StatementType statementType) {
       return new Signature(columns, sql, parameters,
-          Collections.<String, Object>emptyMap(), cursorFactory);
+          Collections.<String, Object>emptyMap(), cursorFactory, statementType);
     }
 
     /** Returns a copy of this Signature, substituting given CursorFactory. */
     public Signature setCursorFactory(CursorFactory cursorFactory) {
       return new Signature(columns, sql, parameters, internalParameters,
-          cursorFactory);
+          cursorFactory, statementType);
     }
 
     /** Creates a copy of this Signature with null lists and maps converted to
      * empty. */
     public Signature sanitize() {
-      if (columns == null || parameters == null || internalParameters == null) {
+      if (columns == null || parameters == null || internalParameters == null
+          || statementType == null) {
         return new Signature(sanitize(columns), sql, sanitize(parameters),
-            sanitize(internalParameters), cursorFactory);
+            sanitize(internalParameters), cursorFactory,
+            Meta.StatementType.SELECT);
       }
       return this;
     }
@@ -710,8 +724,10 @@ public interface Meta {
             Common.Signature.CURSOR_FACTORY_FIELD_NUMBER)) {
         cursorFactory = CursorFactory.fromProto(protoSignature.getCursorFactory());
       }
+      final Meta.StatementType statementType =
+          Meta.StatementType.fromProto(protoSignature.getStatementType());
 
-      return Signature.create(metadata, sql, parameters, cursorFactory);
+      return Signature.create(metadata, sql, parameters, cursorFactory, statementType);
     }
 
     @Override public int hashCode() {
@@ -1178,6 +1194,31 @@ public interface Meta {
         throws SQLException;
     void execute() throws SQLException;
   }
+
+  /** Type of statement. */
+  enum StatementType {
+    SELECT, INSERT, UPDATE, DELETE, UPSERT, MERGE, OTHER_DML, IS_DML,
+    CREATE, DROP, ALTER, OTHER_DDL, CALL;
+
+    public boolean canUpdate() {
+      switch(this) {
+      case INSERT:
+        return true;
+      case IS_DML:
+        return true;
+      default:
+        return false;
+      }
+    }
+
+    public Common.StatementType toProto() {
+      return Common.StatementType.valueOf(name());
+    }
+
+    public static StatementType fromProto(Common.StatementType proto) {
+      return StatementType.valueOf(proto.name());
+    }
+  }
 }
 
 // End Meta.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
index a61fcaf..867933d 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
@@ -252,7 +252,7 @@ public abstract class MetaImpl implements Meta {
       final AvaticaStatement statement = connection.createStatement();
       final Signature signature =
           new Signature(columns, "", Collections.<AvaticaParameter>emptyList(),
-              internalParameters, cursorFactory);
+              internalParameters, cursorFactory, Meta.StatementType.SELECT);
       return MetaResultSet.create(connection.id, statement.getId(), true,
           signature, firstFrame);
     } catch (SQLException e) {
@@ -736,8 +736,7 @@ public abstract class MetaImpl implements Meta {
     return new FetchIterable(handle, firstFrame, parameterValues);
   }
 
-  public Frame fetch(StatementHandle h, List<TypedValue> parameterValues,
-      long offset, int fetchMaxRowCount) {
+  public Frame fetch(StatementHandle h, long offset, int fetchMaxRowCount) {
     return null;
   }
 
@@ -865,7 +864,7 @@ public abstract class MetaImpl implements Meta {
           rows = null;
           break;
         }
-        frame = fetch(handle, parameterValues, frame.offset, 100);
+        frame = fetch(handle, frame.offset, AvaticaStatement.DEFAULT_FETCH_SIZE);
         parameterValues = null; // don't execute next time
         if (frame == null) {
           rows = null;
@@ -877,6 +876,16 @@ public abstract class MetaImpl implements Meta {
       }
     }
   }
+
+  /** Returns whether a list of parameter values has any null elements. */
+  public static boolean checkParameterValueHasNull(List<TypedValue> parameterValues) {
+    for (TypedValue x : parameterValues) {
+      if (x == null) {
+        return true;
+      }
+    }
+    return false;
+  }
 }
 
 // End MetaImpl.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/cee8e844/avatica/src/main/java/org/apache/calcite/avatica/proto/Common.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/proto/Common.java b/avatica/src/main/java/org/apache/calcite/avatica/proto/Common.java
index 182acbb..c4ed1e7 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/proto/Common.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/proto/Common.java
@@ -25,6 +25,192 @@ package org.apache.calcite.avatica.proto;
       com.google.protobuf.ExtensionRegistry registry) {
   }
   /**
+   * Protobuf enum {@code StatementType}
+   *
+   * <pre>
+   * Has to be consistent with Meta.StatementType
+   * </pre>
+   */
+  public enum StatementType
+      implements com.google.protobuf.ProtocolMessageEnum {
+    /**
+     * <code>SELECT = 0;</code>
+     */
+    SELECT(0, 0),
+    /**
+     * <code>INSERT = 1;</code>
+     */
+    INSERT(1, 1),
+    /**
+     * <code>UPDATE = 2;</code>
+     */
+    UPDATE(2, 2),
+    /**
+     * <code>DELETE = 3;</code>
+     */
+    DELETE(3, 3),
+    /**
+     * <code>UPSERT = 4;</code>
+     */
+    UPSERT(4, 4),
+    /**
+     * <code>MERGE = 5;</code>
+     */
+    MERGE(5, 5),
+    /**
+     * <code>OTHER_DML = 6;</code>
+     */
+    OTHER_DML(6, 6),
+    /**
+     * <code>CREATE = 7;</code>
+     */
+    CREATE(7, 7),
+    /**
+     * <code>DROP = 8;</code>
+     */
+    DROP(8, 8),
+    /**
+     * <code>ALTER = 9;</code>
+     */
+    ALTER(9, 9),
+    /**
+     * <code>OTHER_DDL = 10;</code>
+     */
+    OTHER_DDL(10, 10),
+    /**
+     * <code>CALL = 11;</code>
+     */
+    CALL(11, 11),
+    UNRECOGNIZED(-1, -1),
+    ;
+
+    /**
+     * <code>SELECT = 0;</code>
+     */
+    public static final int SELECT_VALUE = 0;
+    /**
+     * <code>INSERT = 1;</code>
+     */
+    public static final int INSERT_VALUE = 1;
+    /**
+     * <code>UPDATE = 2;</code>
+     */
+    public static final int UPDATE_VALUE = 2;
+    /**
+     * <code>DELETE = 3;</code>
+     */
+    public static final int DELETE_VALUE = 3;
+    /**
+     * <code>UPSERT = 4;</code>
+     */
+    public static final int UPSERT_VALUE = 4;
+    /**
+     * <code>MERGE = 5;</code>
+     */
+    public static final int MERGE_VALUE = 5;
+    /**
+     * <code>OTHER_DML = 6;</code>
+     */
+    public static final int OTHER_DML_VALUE = 6;
+    /**
+     * <code>CREATE = 7;</code>
+     */
+    public static final int CREATE_VALUE = 7;
+    /**
+     * <code>DROP = 8;</code>
+     */
+    public static final int DROP_VALUE = 8;
+    /**
+     * <code>ALTER = 9;</code>
+     */
+    public static final int ALTER_VALUE = 9;
+    /**
+     * <code>OTHER_DDL = 10;</code>
+     */
+    public static final int OTHER_DDL_VALUE = 10;
+    /**
+     * <code>CALL = 11;</code>
+     */
+    public static final int CALL_VALUE = 11;
+
+
+    public final int getNumber() {
+      if (index == -1) {
+        throw new java.lang.IllegalArgumentException(
+            "Can't get the number of an unknown enum value.");
+      }
+      return value;
+    }
+
+    public static StatementType valueOf(int value) {
+      switch (value) {
+        case 0: return SELECT;
+        case 1: return INSERT;
+        case 2: return UPDATE;
+        case 3: return DELETE;
+        case 4: return UPSERT;
+        case 5: return MERGE;
+        case 6: return OTHER_DML;
+        case 7: return CREATE;
+        case 8: return DROP;
+        case 9: return ALTER;
+        case 10: return OTHER_DDL;
+        case 11: return CALL;
+        default: return null;
+      }
+    }
+
+    public static com.google.protobuf.Internal.EnumLiteMap<StatementType>
+        internalGetValueMap() {
+      return internalValueMap;
+    }
+    private static com.google.protobuf.Internal.EnumLiteMap<StatementType>
+        internalValueMap =
+          new com.google.protobuf.Internal.EnumLiteMap<StatementType>() {
+            public StatementType findValueByNumber(int number) {
+              return StatementType.valueOf(number);
+            }
+          };
+
+    public final com.google.protobuf.Descriptors.EnumValueDescriptor
+        getValueDescriptor() {
+      return getDescriptor().getValues().get(index);
+    }
+    public final com.google.protobuf.Descriptors.EnumDescriptor
+        getDescriptorForType() {
+      return getDescriptor();
+    }
+    public static final com.google.protobuf.Descriptors.EnumDescriptor
+        getDescriptor() {
+      return org.apache.calcite.avatica.proto.Common.getDescriptor().getEnumTypes().get(0);
+    }
+
+    private static final StatementType[] VALUES = values();
+
+    public static StatementType valueOf(
+        com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
+      if (desc.getType() != getDescriptor()) {
+        throw new java.lang.IllegalArgumentException(
+          "EnumValueDescriptor is not for this type.");
+      }
+      if (desc.getIndex() == -1) {
+        return UNRECOGNIZED;
+      }
+      return VALUES[desc.getIndex()];
+    }
+
+    private final int index;
+    private final int value;
+
+    private StatementType(int index, int value) {
+      this.index = index;
+      this.value = value;
+    }
+
+    // @@protoc_insertion_point(enum_scope:StatementType)
+  }
+
+  /**
    * Protobuf enum {@code Rep}
    */
   public enum Rep
@@ -313,7 +499,7 @@ package org.apache.calcite.avatica.proto;
     }
     public static final com.google.protobuf.Descriptors.EnumDescriptor
         getDescriptor() {
-      return org.apache.calcite.avatica.proto.Common.getDescriptor().getEnumTypes().get(0);
+      return org.apache.calcite.avatica.proto.Common.getDescriptor().getEnumTypes().get(1);
     }
 
     private static final Rep[] VALUES = values();
@@ -2078,6 +2264,15 @@ package org.apache.calcite.avatica.proto;
      * <code>optional .CursorFactory cursor_factory = 4;</code>
      */
     org.apache.calcite.avatica.proto.Common.CursorFactoryOrBuilder getCursorFactoryOrBuilder();
+
+    /**
+     * <code>optional .StatementType statementType = 5;</code>
+     */
+    int getStatementTypeValue();
+    /**
+     * <code>optional .StatementType statementType = 5;</code>
+     */
+    org.apache.calcite.avatica.proto.Common.StatementType getStatementType();
   }
   /**
    * Protobuf type {@code Signature}
@@ -2098,6 +2293,7 @@ package org.apache.calcite.avatica.proto;
       columns_ = java.util.Collections.emptyList();
       sql_ = "";
       parameters_ = java.util.Collections.emptyList();
+      statementType_ = 0;
     }
 
     @java.lang.Override
@@ -2160,6 +2356,12 @@ package org.apache.calcite.avatica.proto;
 
               break;
             }
+            case 40: {
+              int rawValue = input.readEnum();
+
+              statementType_ = rawValue;
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -2332,6 +2534,22 @@ package org.apache.calcite.avatica.proto;
       return getCursorFactory();
     }
 
+    public static final int STATEMENTTYPE_FIELD_NUMBER = 5;
+    private int statementType_;
+    /**
+     * <code>optional .StatementType statementType = 5;</code>
+     */
+    public int getStatementTypeValue() {
+      return statementType_;
+    }
+    /**
+     * <code>optional .StatementType statementType = 5;</code>
+     */
+    public org.apache.calcite.avatica.proto.Common.StatementType getStatementType() {
+      org.apache.calcite.avatica.proto.Common.StatementType result = org.apache.calcite.avatica.proto.Common.StatementType.valueOf(statementType_);
+      return result == null ? org.apache.calcite.avatica.proto.Common.StatementType.UNRECOGNIZED : result;
+    }
+
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
       byte isInitialized = memoizedIsInitialized;
@@ -2357,6 +2575,9 @@ package org.apache.calcite.avatica.proto;
       if (cursorFactory_ != null) {
         output.writeMessage(4, getCursorFactory());
       }
+      if (statementType_ != org.apache.calcite.avatica.proto.Common.StatementType.SELECT.getNumber()) {
+        output.writeEnum(5, statementType_);
+      }
     }
 
     private int memoizedSerializedSize = -1;
@@ -2381,6 +2602,10 @@ package org.apache.calcite.avatica.proto;
         size += com.google.protobuf.CodedOutputStream
           .computeMessageSize(4, getCursorFactory());
       }
+      if (statementType_ != org.apache.calcite.avatica.proto.Common.StatementType.SELECT.getNumber()) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeEnumSize(5, statementType_);
+      }
       memoizedSerializedSize = size;
       return size;
     }
@@ -2513,6 +2738,8 @@ package org.apache.calcite.avatica.proto;
           cursorFactory_ = null;
           cursorFactoryBuilder_ = null;
         }
+        statementType_ = 0;
+
         return this;
       }
 
@@ -2561,6 +2788,7 @@ package org.apache.calcite.avatica.proto;
         } else {
           result.cursorFactory_ = cursorFactoryBuilder_.build();
         }
+        result.statementType_ = statementType_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -2636,6 +2864,9 @@ package org.apache.calcite.avatica.proto;
         if (other.hasCursorFactory()) {
           mergeCursorFactory(other.getCursorFactory());
         }
+        if (other.statementType_ != 0) {
+          setStatementTypeValue(other.getStatementTypeValue());
+        }
         onChanged();
         return this;
       }
@@ -3329,6 +3560,50 @@ package org.apache.calcite.avatica.proto;
         }
         return cursorFactoryBuilder_;
       }
+
+      private int statementType_ = 0;
+      /**
+       * <code>optional .StatementType statementType = 5;</code>
+       */
+      public int getStatementTypeValue() {
+        return statementType_;
+      }
+      /**
+       * <code>optional .StatementType statementType = 5;</code>
+       */
+      public Builder setStatementTypeValue(int value) {
+        statementType_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional .StatementType statementType = 5;</code>
+       */
+      public org.apache.calcite.avatica.proto.Common.StatementType getStatementType() {
+        org.apache.calcite.avatica.proto.Common.StatementType result = org.apache.calcite.avatica.proto.Common.StatementType.valueOf(statementType_);
+        return result == null ? org.apache.calcite.avatica.proto.Common.StatementType.UNRECOGNIZED : result;
+      }
+      /**
+       * <code>optional .StatementType statementType = 5;</code>
+       */
+      public Builder setStatementType(org.apache.calcite.avatica.proto.Common.StatementType value) {
+        if (value == null) {
+          throw new NullPointerException();
+        }
+        
+        statementType_ = value.getNumber();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional .StatementType statementType = 5;</code>
+       */
+      public Builder clearStatementType() {
+        
+        statementType_ = 0;
+        onChanged();
+        return this;
+      }
       public final Builder setUnknownFields(
           final com.google.protobuf.UnknownFieldSet unknownFields) {
         return this;
@@ -12224,54 +12499,59 @@ package org.apache.calcite.avatica.proto;
       "_isolation\030\004 \001(\r\022\017\n\007catalog\030\005 \001(\t\022\016\n\006sch" +
       "ema\030\006 \001(\t\"S\n\017StatementHandle\022\025\n\rconnecti" +
       "on_id\030\001 \001(\t\022\n\n\002id\030\002 \001(\r\022\035\n\tsignature\030\003 \001" +
-      "(\0132\n.Signature\"\211\001\n\tSignature\022 \n\007columns\030" +
+      "(\0132\n.Signature\"\260\001\n\tSignature\022 \n\007columns\030" +
       "\001 \003(\0132\017.ColumnMetaData\022\013\n\003sql\030\002 \001(\t\022%\n\np" +
       "arameters\030\003 \003(\0132\021.AvaticaParameter\022&\n\016cu",
-      "rsor_factory\030\004 \001(\0132\016.CursorFactory\"\255\003\n\016C" +
-      "olumnMetaData\022\017\n\007ordinal\030\001 \001(\r\022\026\n\016auto_i" +
-      "ncrement\030\002 \001(\010\022\026\n\016case_sensitive\030\003 \001(\010\022\022" +
-      "\n\nsearchable\030\004 \001(\010\022\020\n\010currency\030\005 \001(\010\022\020\n\010" +
-      "nullable\030\006 \001(\r\022\016\n\006signed\030\007 \001(\010\022\024\n\014displa" +
-      "y_size\030\010 \001(\r\022\r\n\005label\030\t \001(\t\022\023\n\013column_na" +
-      "me\030\n \001(\t\022\023\n\013schema_name\030\013 \001(\t\022\021\n\tprecisi" +
-      "on\030\014 \001(\r\022\r\n\005scale\030\r \001(\r\022\022\n\ntable_name\030\016 " +
-      "\001(\t\022\024\n\014catalog_name\030\017 \001(\t\022\021\n\tread_only\030\020" +
-      " \001(\010\022\020\n\010writable\030\021 \001(\010\022\033\n\023definitely_wri",
-      "table\030\022 \001(\010\022\031\n\021column_class_name\030\023 \001(\t\022\032" +
-      "\n\004type\030\024 \001(\0132\014.AvaticaType\"}\n\013AvaticaTyp" +
-      "e\022\n\n\002id\030\001 \001(\r\022\014\n\004name\030\002 \001(\t\022\021\n\003rep\030\003 \001(\016" +
-      "2\004.Rep\022 \n\007columns\030\004 \003(\0132\017.ColumnMetaData" +
-      "\022\037\n\tcomponent\030\005 \001(\0132\014.AvaticaType\"\221\001\n\020Av" +
-      "aticaParameter\022\016\n\006signed\030\001 \001(\010\022\021\n\tprecis" +
-      "ion\030\002 \001(\r\022\r\n\005scale\030\003 \001(\r\022\026\n\016parameter_ty" +
-      "pe\030\004 \001(\r\022\021\n\ttype_name\030\005 \001(\t\022\022\n\nclass_nam" +
-      "e\030\006 \001(\t\022\014\n\004name\030\007 \001(\t\"\263\001\n\rCursorFactory\022" +
-      "#\n\005style\030\001 \001(\0162\024.CursorFactory.Style\022\022\n\n",
-      "class_name\030\002 \001(\t\022\023\n\013field_names\030\003 \003(\t\"T\n" +
-      "\005Style\022\n\n\006OBJECT\020\000\022\n\n\006RECORD\020\001\022\025\n\021RECORD" +
-      "_PROJECTION\020\002\022\t\n\005ARRAY\020\003\022\010\n\004LIST\020\004\022\007\n\003MA" +
-      "P\020\005\"9\n\005Frame\022\016\n\006offset\030\001 \001(\004\022\014\n\004done\030\002 \001" +
-      "(\010\022\022\n\004rows\030\003 \003(\0132\004.Row\"!\n\003Row\022\032\n\005value\030\001" +
-      " \003(\0132\013.TypedValue\"3\n\020DatabaseProperty\022\014\n" +
-      "\004name\030\001 \001(\t\022\021\n\tfunctions\030\002 \003(\t\"4\n\013WireMe" +
-      "ssage\022\014\n\004name\030\001 \001(\t\022\027\n\017wrapped_message\030\002" +
-      " \001(\014\"\232\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\024\n\014bytes_values\030" +
-      "\005 \001(\014\022\024\n\014double_value\030\006 \001(\001\022\014\n\004null\030\007 \001(" +
-      "\010*\275\003\n\003Rep\022\025\n\021PRIMITIVE_BOOLEAN\020\000\022\022\n\016PRIM" +
-      "ITIVE_BYTE\020\001\022\022\n\016PRIMITIVE_CHAR\020\002\022\023\n\017PRIM" +
-      "ITIVE_SHORT\020\003\022\021\n\rPRIMITIVE_INT\020\004\022\022\n\016PRIM" +
-      "ITIVE_LONG\020\005\022\023\n\017PRIMITIVE_FLOAT\020\006\022\024\n\020PRI" +
-      "MITIVE_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_IN" +
-      "TEGER\020\031\022\017\n\013BIG_DECIMAL\020\032\022\021\n\rJAVA_SQL_TIM",
-      "E\020\020\022\026\n\022JAVA_SQL_TIMESTAMP\020\021\022\021\n\rJAVA_SQL_" +
-      "DATE\020\022\022\022\n\016JAVA_UTIL_DATE\020\023\022\017\n\013BYTE_STRIN" +
-      "G\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\030B\"\n org.apache.calcite.avatica." +
-      "protob\006proto3"
+      "rsor_factory\030\004 \001(\0132\016.CursorFactory\022%\n\rst" +
+      "atementType\030\005 \001(\0162\016.StatementType\"\255\003\n\016Co" +
+      "lumnMetaData\022\017\n\007ordinal\030\001 \001(\r\022\026\n\016auto_in" +
+      "crement\030\002 \001(\010\022\026\n\016case_sensitive\030\003 \001(\010\022\022\n" +
+      "\nsearchable\030\004 \001(\010\022\020\n\010currency\030\005 \001(\010\022\020\n\010n" +
+      "ullable\030\006 \001(\r\022\016\n\006signed\030\007 \001(\010\022\024\n\014display" +
+      "_size\030\010 \001(\r\022\r\n\005label\030\t \001(\t\022\023\n\013column_nam" +
+      "e\030\n \001(\t\022\023\n\013schema_name\030\013 \001(\t\022\021\n\tprecisio" +
+      "n\030\014 \001(\r\022\r\n\005scale\030\r \001(\r\022\022\n\ntable_name\030\016 \001" +
+      "(\t\022\024\n\014catalog_name\030\017 \001(\t\022\021\n\tread_only\030\020 ",
+      "\001(\010\022\020\n\010writable\030\021 \001(\010\022\033\n\023definitely_writ" +
+      "able\030\022 \001(\010\022\031\n\021column_class_name\030\023 \001(\t\022\032\n" +
+      "\004type\030\024 \001(\0132\014.AvaticaType\"}\n\013AvaticaType" +
+      "\022\n\n\002id\030\001 \001(\r\022\014\n\004name\030\002 \001(\t\022\021\n\003rep\030\003 \001(\0162" +
+      "\004.Rep\022 \n\007columns\030\004 \003(\0132\017.ColumnMetaData\022" +
+      "\037\n\tcomponent\030\005 \001(\0132\014.AvaticaType\"\221\001\n\020Ava" +
+      "ticaParameter\022\016\n\006signed\030\001 \001(\010\022\021\n\tprecisi" +
+      "on\030\002 \001(\r\022\r\n\005scale\030\003 \001(\r\022\026\n\016parameter_typ" +
+      "e\030\004 \001(\r\022\021\n\ttype_name\030\005 \001(\t\022\022\n\nclass_name" +
+      "\030\006 \001(\t\022\014\n\004name\030\007 \001(\t\"\263\001\n\rCursorFactory\022#",
+      "\n\005style\030\001 \001(\0162\024.CursorFactory.Style\022\022\n\nc" +
+      "lass_name\030\002 \001(\t\022\023\n\013field_names\030\003 \003(\t\"T\n\005" +
+      "Style\022\n\n\006OBJECT\020\000\022\n\n\006RECORD\020\001\022\025\n\021RECORD_" +
+      "PROJECTION\020\002\022\t\n\005ARRAY\020\003\022\010\n\004LIST\020\004\022\007\n\003MAP" +
+      "\020\005\"9\n\005Frame\022\016\n\006offset\030\001 \001(\004\022\014\n\004done\030\002 \001(" +
+      "\010\022\022\n\004rows\030\003 \003(\0132\004.Row\"!\n\003Row\022\032\n\005value\030\001 " +
+      "\003(\0132\013.TypedValue\"3\n\020DatabaseProperty\022\014\n\004" +
+      "name\030\001 \001(\t\022\021\n\tfunctions\030\002 \003(\t\"4\n\013WireMes" +
+      "sage\022\014\n\004name\030\001 \001(\t\022\027\n\017wrapped_message\030\002 " +
+      "\001(\014\"\232\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\024\n\014bytes_values\030\005" +
+      " \001(\014\022\024\n\014double_value\030\006 \001(\001\022\014\n\004null\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\005MERGE\020\005\022\r\n\tOTHER_DML\020\006\022\n\n\006CREATE\020\007\022\010\n\004" +
+      "DROP\020\010\022\t\n\005ALTER\020\t\022\r\n\tOTHER_DDL\020\n\022\010\n\004CALL" +
+      "\020\013*\275\003\n\003Rep\022\025\n\021PRIMITIVE_BOOLEAN\020\000\022\022\n\016PRI" +
+      "MITIVE_BYTE\020\001\022\022\n\016PRIMITIVE_CHAR\020\002\022\023\n\017PRI" +
+      "MITIVE_SHORT\020\003\022\021\n\rPRIMITIVE_INT\020\004\022\022\n\016PRI",
+      "MITIVE_LONG\020\005\022\023\n\017PRIMITIVE_FLOAT\020\006\022\024\n\020PR" +
+      "IMITIVE_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_I" +
+      "NTEGER\020\031\022\017\n\013BIG_DECIMAL\020\032\022\021\n\rJAVA_SQL_TI" +
+      "ME\020\020\022\026\n\022JAVA_SQL_TIMESTAMP\020\021\022\021\n\rJAVA_SQL" +
+      "_DATE\020\022\022\022\n\016JAVA_UTIL_DATE\020\023\022\017\n\013BYTE_STRI" +
+      "NG\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\030B\"\n org.apache.calcite.avatica" +
+      ".protob\006proto3"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
         new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {
@@ -12302,7 +12582,7 @@ package org.apache.calcite.avatica.proto;
     internal_static_Signature_fieldAccessorTable = new
       com.google.protobuf.GeneratedMessage.FieldAccessorTable(
         internal_static_Signature_descriptor,
-        new java.lang.String[] { "Columns", "Sql", "Parameters", "CursorFactory", });
+        new java.lang.String[] { "Columns", "Sql", "Parameters", "CursorFactory", "StatementType", });
     internal_static_ColumnMetaData_descriptor =
       getDescriptor().getMessageTypes().get(3);
     internal_static_ColumnMetaData_fieldAccessorTable = new