@Override
 public void releasePrepareStatement(String sql, int statementId) throws QueryException {
   //        if (log.isDebugEnabled()) log.debug("Closing prepared statement "+statementId);
   lock.lock();
   try {
     if (urlParser.getOptions().cachePrepStmts && prepareStatementCache.containsKey(sql)) {
       PrepareResult pr = prepareStatementCache.get(sql);
       pr.removeUse();
       if (!pr.hasToBeClose()) {
         //                        log.debug("closing aborded, prepared statement used in another
         // statement");
         return;
       }
       prepareStatementCache.remove(sql);
     }
     final SendClosePrepareStatementPacket packet =
         new SendClosePrepareStatementPacket(statementId);
     try {
       packet.send(writer);
     } catch (IOException e) {
       throw new QueryException(
           "Could not send query: " + e.getMessage(),
           -1,
           ExceptionMapper.SqlStates.CONNECTION_EXCEPTION.getSqlState(),
           e);
     }
   } finally {
     lock.unlock();
   }
 }
  @Override
  public PrepareResult prepare(String sql) throws QueryException {
    try {
      if (urlParser.getOptions().cachePrepStmts && prepareStatementCache.containsKey(sql)) {
        PrepareResult pr = prepareStatementCache.get(sql);
        pr.addUse();
        return pr;
      }

      SendPrepareStatementPacket sendPrepareStatementPacket = new SendPrepareStatementPacket(sql);
      sendPrepareStatementPacket.send(writer);

      ByteBuffer byteBuffer = packetFetcher.getReusableBuffer();

      if (byteBuffer.get(0) == -1) {
        ErrorPacket ep = new ErrorPacket(byteBuffer);
        String message = ep.getMessage();
        throw new QueryException(
            "Error preparing query: " + message, ep.getErrorNumber(), ep.getSqlState());
      }

      byte bit = byteBuffer.get(0);
      if (bit == 0) {
        /* Prepared Statement OK */
        Reader reader = new Reader(byteBuffer);
        reader.readByte(); /* skip field count */
        final int statementId = reader.readInt();
        final int numColumns = reader.readShort();
        final int numParams = reader.readShort();
        reader.readByte(); // reserved
        this.hasWarnings = reader.readShort() > 0;
        ColumnInformation[] params = new ColumnInformation[numParams];
        if (numParams > 0) {
          for (int i = 0; i < numParams; i++) {
            params[i] = new ColumnInformation(packetFetcher.getRawPacket().getByteBuffer());
          }
          readEofPacket();
        }
        ColumnInformation[] columns = new ColumnInformation[numColumns];
        if (numColumns > 0) {
          for (int i = 0; i < numColumns; i++) {
            columns[i] = new ColumnInformation(packetFetcher.getRawPacket().getByteBuffer());
          }
          readEofPacket();
        }
        PrepareResult prepareResult = new PrepareResult(statementId, columns, params);
        if (urlParser.getOptions().cachePrepStmts
            && sql != null
            && sql.length() < urlParser.getOptions().prepStmtCacheSqlLimit) {
          prepareStatementCache.putIfNone(sql, prepareResult);
        }
        //                if (log.isDebugEnabled()) log.debug("prepare statementId : " +
        // prepareResult.statementId);
        return prepareResult;
      } else {
        throw new QueryException("Unexpected packet returned by server, first byte " + bit);
      }
    } catch (IOException e) {
      throw new QueryException(
          e.getMessage(), -1, ExceptionMapper.SqlStates.CONNECTION_EXCEPTION.getSqlState(), e);
    }
  }