content: /*
   * Copyright (C) 2016-2023 ActionTech.
   * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
   */
  
  package com.actiontech.dble.services.rwsplit.handle;
  
  import com.actiontech.dble.backend.datasource.PhysicalDbGroup;
  import com.actiontech.dble.backend.datasource.PhysicalDbInstance;
  import com.actiontech.dble.backend.mysql.ByteUtil;
  import com.actiontech.dble.backend.mysql.nio.handler.PreparedResponseHandler;
  import com.actiontech.dble.backend.mysql.nio.handler.ResponseHandler;
  import com.actiontech.dble.config.ErrorCode;
  import com.actiontech.dble.net.connection.AbstractConnection;
  import com.actiontech.dble.net.connection.BackendConnection;
  import com.actiontech.dble.net.mysql.FieldPacket;
  import com.actiontech.dble.net.mysql.PreparedClosePacket;
  import com.actiontech.dble.net.mysql.RowDataPacket;
  import com.actiontech.dble.net.service.AbstractService;
  import com.actiontech.dble.net.service.WriteFlags;
  import com.actiontech.dble.services.mysqlsharding.MySQLResponseService;
  import com.actiontech.dble.services.rwsplit.RWSplitService;
  
  import java.io.IOException;
  import java.nio.ByteBuffer;
  import java.util.List;
  
  public class PSHandler implements ResponseHandler, PreparedResponseHandler {
  
      private final RWSplitService rwSplitService;
      private final AbstractConnection frontedConnection;
      private final PreparedStatementHolder holder;
      private boolean write2Client = false;
      private long originStatementId;
  
      public PSHandler(RWSplitService service, PreparedStatementHolder holder) {
          this.rwSplitService = service;
          this.frontedConnection = service.getConnection();
          this.holder = holder;
      }
  
      public void execute(PhysicalDbGroup rwGroup) throws IOException {
          PhysicalDbInstance instance = rwGroup.select(true, true, false);
          instance.getConnection(rwSplitService.getSchema(), this, null, true);
      }
  
      @Override
      public void connectionAcquired(BackendConnection conn) {
          MySQLResponseService mysqlService = conn.getBackendService();
          mysqlService.setResponseHandler(this);
          mysqlService.execute(rwSplitService, holder.getPrepareOrigin());
      }
  
      @Override
      public void preparedOkResponse(byte[] ok, List<byte[]> fields, List<byte[]> params, MySQLResponseService service) {
          originStatementId = ByteUtil.readUB4(ok, 5);
          ByteUtil.writeUB4(holder.getExecuteOrigin(), originStatementId, 5);
          int length = ByteUtil.readUB3(holder.getExecuteOrigin(), 0);
          int paramsCount = holder.getParamsCount();
          int nullBitMapSize = (paramsCount + 7) / 8;
          byte[] originExecuteByte = holder.getExecuteOrigin();
          if (holder.isNeedAddFieldType()) {
              length += paramsCount * 2;
              ByteBuffer buffer = service.allocate(originExecuteByte.length + paramsCount * 2);
              buffer.put(originExecuteByte, 0, 14 + nullBitMapSize);
              ByteUtil.writeUB3(buffer, length, 0);
              //flag type
              buffer.put(14 + nullBitMapSize, (byte) 1);
              //field type
              buffer.position(15 + nullBitMapSize);
              byte[] fileType = holder.getFieldType();
              buffer.put(fileType, 0, fileType.length);
              buffer.position(15 + nullBitMapSize + paramsCount * 2);
              buffer.put(originExecuteByte, 15 + nullBitMapSize, originExecuteByte.length - (15 + nullBitMapSize));
              service.setResponseHandler(this);
              service.execute(buffer);
          } else {
              service.setResponseHandler(this);
              service.execute(holder.getExecuteOrigin());
          }
      }
  
      @Override
      public void connectionError(Throwable e, Object attachment) {
          rwSplitService.writeErrMessage(ErrorCode.ER_DB_INSTANCE_ABORTING_CONNECTION, "can't connect to dbGroup[" + rwSplitService.getUserConfig().getDbGroup());
      }
  
      @Override
      public void errorResponse(byte[] err, AbstractService service) {
          synchronized (this) {
              if (!write2Client) {
                  err[3] = (byte) rwSplitService.nextPacketId();
                  frontedConnection.getService().write(err, WriteFlags.QUERY_END);
                  write2Client = true;
                  PreparedClosePacket close = new PreparedClosePacket(originStatementId);
                  close.bufferWrite(service.getConnection());
                  ((MySQLResponseService) service).release();
              }
          }
      }
  
      @Override
      public void okResponse(byte[] ok, AbstractService service) {
          MySQLResponseService mysqlService = (MySQLResponseService) service;
          boolean executeResponse = mysqlService.syncAndExecute();
          if (executeResponse) {
              synchronized (this) {
                  if (!write2Client) {
                      frontedConnection.getService().write(ok, WriteFlags.QUERY_END);
                      write2Client = true;
                      PreparedClosePacket close = new PreparedClosePacket(originStatementId);
                      close.bufferWrite(service.getConnection());
                      ((MySQLResponseService) service).release();
                  }
              }
          }
      }
  
      @Override
      public void connectionClose(AbstractService service, String reason) {
          rwSplitService.getConnection().close("backend connection is close in ps");
      }
  
      @Override
      public void preparedExecuteResponse(byte[] header, List<byte[]> fields, byte[] eof, MySQLResponseService service) {
      }
  
      @Override
      public void fieldEofResponse(byte[] header, List<byte[]> fields, List<FieldPacket> fieldPackets, byte[] eof, boolean isLeft, AbstractService service) {
  
      }
  
      @Override
      public boolean rowResponse(byte[] rowNull, RowDataPacket rowPacket, boolean isLeft, AbstractService service) {
          return false;
      }
  
      @Override
      public void rowEofResponse(byte[] eof, boolean isLeft, AbstractService service) {
  
      }
  
  }
  