/**
  * Called to flush the output after data has been sent to the server and just before receiving
  * data. This method also reads the status code from the server and throws any exception the
  * server sent.
  *
  * @param transfer the transfer object
  * @throws DbException if the server sent an exception
  * @throws IOException if there is a communication problem between client and server
  */
 public void done(Transfer transfer) throws IOException {
   transfer.flush();
   int status = transfer.readInt();
   if (status == STATUS_ERROR) {
     String sqlstate = transfer.readString();
     String message = transfer.readString();
     String sql = transfer.readString();
     int errorCode = transfer.readInt();
     String stackTrace = transfer.readString();
     JdbcSQLException s =
         new JdbcSQLException(message, sql, sqlstate, errorCode, null, stackTrace);
     if (errorCode == ErrorCode.CONNECTION_BROKEN_1) {
       // allow re-connect
       IOException e = new IOException(s.toString(), s);
       throw e;
     }
     throw DbException.convert(s);
   } else if (status == STATUS_CLOSED) {
     transferList = null;
   } else if (status == STATUS_OK_STATE_CHANGED) {
     sessionStateChanged = true;
   } else if (status == STATUS_OK) {
     // ok
   } else {
     throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "unexpected status " + status);
   }
 }
  @Override
  public void run() {
    try {
      transfer.init();
      trace("Connect");
      // TODO server: should support a list of allowed databases
      // and a list of allowed clients
      try {
        // 如果没有加-tcpAllowOthers参数,那么只接受本地连接
        if (!server.allow(transfer.getSocket())) {
          throw DbException.get(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED);
        }
        int minClientVersion = transfer.readInt();
        if (minClientVersion < Constants.TCP_PROTOCOL_VERSION_6) {
          throw DbException.get(
              ErrorCode.DRIVER_VERSION_ERROR_2,
              "" + clientVersion,
              "" + Constants.TCP_PROTOCOL_VERSION_6);
        } else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_12) {
          throw DbException.get(
              ErrorCode.DRIVER_VERSION_ERROR_2,
              "" + clientVersion,
              "" + Constants.TCP_PROTOCOL_VERSION_12);
        }
        int maxClientVersion = transfer.readInt();
        if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
          clientVersion = Constants.TCP_PROTOCOL_VERSION_12;
        } else {
          clientVersion = minClientVersion;
        }
        transfer.setVersion(clientVersion);
        String db = transfer.readString();
        String originalURL = transfer.readString();
        if (db == null && originalURL == null) {
          String targetSessionId = transfer.readString();
          int command = transfer.readInt();
          stop = true;
          if (command == SessionRemote.SESSION_CANCEL_STATEMENT) {
            // cancel a running statement
            int statementId = transfer.readInt();
            server.cancelStatement(targetSessionId, statementId);
          } else if (command == SessionRemote.SESSION_CHECK_KEY) {
            // check if this is the correct server
            db = server.checkKeyAndGetDatabaseName(targetSessionId);
            if (!targetSessionId.equals(db)) {
              transfer.writeInt(SessionRemote.STATUS_OK);
            } else {
              transfer.writeInt(SessionRemote.STATUS_ERROR);
            }
          }
        }
        // 启动TcpServer时加"-baseDir"或者像这样System.setProperty("h2.baseDir", "E:\\H2\\baseDir")
        String baseDir = server.getBaseDir();
        if (baseDir == null) {
          baseDir = SysProperties.getBaseDir();
        }
        // 例如启动TcpServer时,指定了"-key mydb mydatabase",
        // 如果db变量是mydb,那么实际上就是mydatabase,相当于做一次映射
        // 如果db变量不是mydb,那么抛错: org.h2.jdbc.JdbcSQLException: Wrong user name or password [28000-170]
        db = server.checkKeyAndGetDatabaseName(db);
        ConnectionInfo ci = new ConnectionInfo(db);

        ci.setOriginalURL(originalURL);
        ci.setUserName(transfer.readString());
        // password参数的值已经转换成userPasswordHash和filePasswordHash了,
        // 不能由userPasswordHash和filePasswordHash得到原始的password
        ci.setUserPasswordHash(transfer.readBytes());
        ci.setFilePasswordHash(transfer.readBytes()); // 只有指定"CIPHER"参数时filePasswordHash才是非null的
        int len = transfer.readInt();
        for (int i = 0; i < len; i++) {
          ci.setProperty(transfer.readString(), transfer.readString());
        }
        // override client's requested properties with server settings
        if (baseDir != null) {
          ci.setBaseDir(baseDir);
        }
        if (server.getIfExists()) {
          // 启动TcpServer时加"-ifExists",限制只有数据库存在时客户端才能连接,也就是不允许在客户端创建数据库
          ci.setProperty("IFEXISTS", "TRUE");
        }
        session = Engine.getInstance().createSession(ci);
        transfer.setSession(session);
        transfer.writeInt(SessionRemote.STATUS_OK);
        transfer.writeInt(clientVersion);
        transfer.flush();
        // 每建立一个新的Session对象时,把它保存到内存数据库management_db_9092的SESSIONS表
        server.addConnection(threadId, originalURL, ci.getUserName());
        trace("Connected");
      } catch (Throwable e) {
        sendError(e);
        stop = true;
      }
      while (!stop) {
        try {
          process();
        } catch (Throwable e) {
          sendError(e);
        }
      }
      trace("Disconnect");
    } catch (Throwable e) {
      server.traceError(e);
    } finally {
      close();
    }
  }
 // 总共18条命令,这里包含16条
 // 下面这两条在run方法中特殊处理
 // SESSION_CANCEL_STATEMENT
 // SESSION_CHECK_KEY
 // 分三种级别,从大到小: SESSION级、COMMAND级、RESULT级
 private void process() throws IOException {
   int operation = transfer.readInt();
   switch (operation) {
     case SessionRemote.SESSION_PREPARE_READ_PARAMS:
     case SessionRemote.SESSION_PREPARE:
       {
         int id = transfer.readInt();
         String sql = transfer.readString();
         int old = session.getModificationId();
         Command command = session.prepareLocal(sql);
         boolean readonly = command.isReadOnly();
         cache.addObject(id, command);
         boolean isQuery = command.isQuery();
         ArrayList<? extends ParameterInterface> params = command.getParameters();
         transfer
             .writeInt(getState(old))
             .writeBoolean(isQuery)
             .writeBoolean(readonly)
             .writeInt(params.size());
         if (operation == SessionRemote.SESSION_PREPARE_READ_PARAMS) {
           for (ParameterInterface p : params) {
             ParameterRemote.writeMetaData(transfer, p);
           }
         }
         transfer.flush();
         break;
       }
     case SessionRemote.SESSION_CLOSE:
       {
         stop = true;
         closeSession();
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         close();
         break;
       }
     case SessionRemote.COMMAND_COMMIT:
       {
         // 并不是通过org.h2.jdbc.JdbcConnection.commit()触发,此方法是通过发送COMMIT SQL触发
         // 触发SessionRemote.COMMAND_COMMIT的是在集群环境下,通过org.h2.engine.SessionRemote.autoCommitIfCluster()触发
         if (commit == null) {
           commit = session.prepareLocal("COMMIT");
         }
         int old = session.getModificationId();
         commit.executeUpdate();
         transfer.writeInt(getState(old)).flush();
         break;
       }
     case SessionRemote.COMMAND_GET_META_DATA:
       {
         int id = transfer.readInt();
         int objectId = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         ResultInterface result = command.getMetaData();
         cache.addObject(objectId, result);
         int columnCount = result.getVisibleColumnCount();
         transfer.writeInt(SessionRemote.STATUS_OK).writeInt(columnCount).writeInt(0);
         for (int i = 0; i < columnCount; i++) {
           ResultColumn.writeColumn(transfer, result, i);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_EXECUTE_QUERY:
       {
         int id = transfer.readInt();
         int objectId = transfer.readInt();
         int maxRows = transfer.readInt();
         int fetchSize = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         setParameters(command);
         int old = session.getModificationId();
         ResultInterface result;
         synchronized (session) {
           result = command.executeQuery(maxRows, false);
         }
         cache.addObject(objectId, result);
         int columnCount = result.getVisibleColumnCount();
         int state = getState(old);
         transfer.writeInt(state).writeInt(columnCount);
         int rowCount = result.getRowCount();
         transfer.writeInt(rowCount);
         for (int i = 0; i < columnCount; i++) {
           ResultColumn.writeColumn(transfer, result, i);
         }
         int fetch = Math.min(rowCount, fetchSize);
         for (int i = 0; i < fetch; i++) {
           sendRow(result);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_EXECUTE_UPDATE:
       {
         int id = transfer.readInt();
         Command command = (Command) cache.getObject(id, false);
         // if(command!=null)throw new Error();
         setParameters(command);
         int old = session.getModificationId();
         int updateCount;
         synchronized (session) {
           updateCount = command.executeUpdate();
         }
         int status;
         if (session.isClosed()) {
           status = SessionRemote.STATUS_CLOSED;
         } else {
           status = getState(old);
         }
         transfer.writeInt(status).writeInt(updateCount).writeBoolean(session.getAutoCommit());
         transfer.flush();
         break;
       }
     case SessionRemote.COMMAND_CLOSE:
       {
         int id = transfer.readInt();
         Command command = (Command) cache.getObject(id, true);
         if (command != null) {
           command.close();
           cache.freeObject(id);
         }
         break;
       }
     case SessionRemote.RESULT_FETCH_ROWS:
       {
         int id = transfer.readInt();
         int count = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, false);
         transfer.writeInt(SessionRemote.STATUS_OK);
         for (int i = 0; i < count; i++) {
           sendRow(result);
         }
         transfer.flush();
         break;
       }
     case SessionRemote.RESULT_RESET:
       {
         int id = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, false);
         result.reset();
         break;
       }
     case SessionRemote.RESULT_CLOSE:
       {
         int id = transfer.readInt();
         ResultInterface result = (ResultInterface) cache.getObject(id, true);
         if (result != null) {
           result.close();
           cache.freeObject(id);
         }
         break;
       }
     case SessionRemote.CHANGE_ID:
       {
         int oldId = transfer.readInt();
         int newId = transfer.readInt();
         Object obj = cache.getObject(oldId, false);
         cache.freeObject(oldId);
         cache.addObject(newId, obj);
         break;
       }
     case SessionRemote.SESSION_SET_ID:
       {
         sessionId = transfer.readString();
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         break;
       }
     case SessionRemote.SESSION_SET_AUTOCOMMIT:
       {
         boolean autoCommit = transfer.readBoolean();
         session.setAutoCommit(autoCommit);
         transfer.writeInt(SessionRemote.STATUS_OK).flush();
         break;
       }
     case SessionRemote.SESSION_HAS_PENDING_TRANSACTION:
       {
         transfer
             .writeInt(SessionRemote.STATUS_OK)
             .writeInt(session.hasPendingTransaction() ? 1 : 0)
             .flush();
         break;
       }
     case SessionRemote.LOB_READ:
       {
         long lobId = transfer.readLong();
         byte[] hmac;
         CachedInputStream in;
         boolean verifyMac;
         if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_11) {
           if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
             hmac = transfer.readBytes();
             verifyMac = true;
           } else {
             hmac = null;
             verifyMac = false;
           }
           in = lobs.get(lobId);
           if (in == null && verifyMac) {
             in = new CachedInputStream(null);
             lobs.put(lobId, in);
           }
         } else {
           verifyMac = false;
           hmac = null;
           in = lobs.get(lobId);
         }
         long offset = transfer.readLong();
         int length = transfer.readInt();
         if (verifyMac) {
           transfer.verifyLobMac(hmac, lobId);
         }
         if (in == null) {
           throw DbException.get(ErrorCode.OBJECT_CLOSED);
         }
         if (in.getPos() != offset) {
           LobStorageInterface lobStorage = session.getDataHandler().getLobStorage();
           InputStream lobIn = lobStorage.getInputStream(lobId, hmac, -1);
           in = new CachedInputStream(lobIn);
           lobs.put(lobId, in);
           lobIn.skip(offset);
         }
         // limit the buffer size
         length = Math.min(16 * Constants.IO_BUFFER_SIZE, length);
         byte[] buff = new byte[length];
         length = IOUtils.readFully(in, buff, 0, length);
         transfer.writeInt(SessionRemote.STATUS_OK);
         transfer.writeInt(length);
         transfer.writeBytes(buff, 0, length);
         transfer.flush();
         break;
       }
     default:
       trace("Unknown operation: " + operation);
       closeSession();
       close();
   }
 }