JackieTien97 commented on code in PR #17027:
URL: https://github.com/apache/iotdb/pull/17027#discussion_r2758226722


##########
iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java:
##########
@@ -47,80 +48,70 @@ public boolean isNull() {
     }
   }
 
-  private PreparedParameterSerializer() {}
+  private PreparedParameterSerde() {}
 
   /** Serialize parameters to binary format. */
   public static ByteBuffer serialize(Object[] values, int[] jdbcTypes, int 
count) {
-    try {
-      ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      DataOutputStream dos = new DataOutputStream(baos);
-
-      dos.writeInt(count);
+    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

Review Comment:
   Using `PublicBAOS` instead of `ByteArrayOutputStream`, and then you can use 
`ByteBuffer.wrap(PublicBAOS.getBuf(), 0, PublicBAOS.size())` to avoid copying 
in `.toByteArray()`.



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java:
##########
@@ -1637,31 +1616,48 @@ public TSStatus 
deallocatePreparedStatement(TSDeallocatePreparedReq req) {
     }
   }
 
-  private Literal convertToLiteral(DeserializedParam param) {
+  private Pair<Literal, String> convertToLiteralWithString(DeserializedParam 
param) {
     if (param.isNull()) {
-      return new NullLiteral();
+      return new Pair<>(new NullLiteral(), "NULL");
     }
 
     switch (param.type) {
       case BOOLEAN:
-        return new BooleanLiteral((Boolean) param.value ? "true" : "false");
+        String boolStr = (boolean) param.value ? "true" : "false";
+        return new Pair<>(new BooleanLiteral(boolStr), boolStr);
       case INT32:
       case INT64:
-        return new LongLiteral(String.valueOf(param.value));
+        String numStr = String.valueOf(param.value);
+        return new Pair<>(new LongLiteral(numStr), numStr);
       case FLOAT:
-        return new DoubleLiteral((Float) param.value);
+        String floatStr = String.valueOf(param.value);
+        return new Pair<>(new DoubleLiteral((Float) param.value), floatStr);
       case DOUBLE:
-        return new DoubleLiteral((Double) param.value);
+        String doubleStr = String.valueOf(param.value);
+        return new Pair<>(new DoubleLiteral((Double) param.value), doubleStr);
       case TEXT:
       case STRING:
-        return new StringLiteral((String) param.value);
+        String strVal = (String) param.value;
+        // Escape single quotes for SQL
+        String escapedStr = "'" + strVal.replace("'", "''") + "'";
+        return new Pair<>(new StringLiteral(strVal), escapedStr);
       case BLOB:
-        return new BinaryLiteral((byte[]) param.value);
+        byte[] bytes = (byte[]) param.value;
+        String hexStr = "X'" + bytesToHex(bytes) + "'";
+        return new Pair<>(new BinaryLiteral(bytes), hexStr);
       default:
         throw new IllegalArgumentException("Unknown parameter type: " + 
param.type);
     }
   }
 
+  private static String bytesToHex(byte[] bytes) {
+    StringBuilder sb = new StringBuilder(bytes.length * 2);
+    for (byte b : bytes) {
+      sb.append(String.format("%02X", b));
+    }
+    return sb.toString();
+  }

Review Comment:
   you can define it in Serde class, then you can call it here and 
`IoTDBTablePreparedStatement`.



##########
iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/stmt/PreparedParameterSerde.java:
##########
@@ -47,80 +48,70 @@ public boolean isNull() {
     }
   }
 
-  private PreparedParameterSerializer() {}
+  private PreparedParameterSerde() {}
 
   /** Serialize parameters to binary format. */
   public static ByteBuffer serialize(Object[] values, int[] jdbcTypes, int 
count) {
-    try {
-      ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      DataOutputStream dos = new DataOutputStream(baos);
-
-      dos.writeInt(count);
+    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+      ReadWriteIOUtils.write(count, outputStream);
       for (int i = 0; i < count; i++) {
-        serializeParameter(dos, values[i], jdbcTypes[i]);
+        serializeParameter(outputStream, values[i], jdbcTypes[i]);
       }
-
-      dos.flush();
-      return ByteBuffer.wrap(baos.toByteArray());
+      return ByteBuffer.wrap(outputStream.toByteArray());
     } catch (IOException e) {
-      throw new RuntimeException("Failed to serialize parameters", e);
+      // Should not happen with ByteArrayOutputStream
+      throw new IllegalStateException("Failed to serialize parameters", e);
     }
   }
 
-  private static void serializeParameter(DataOutputStream dos, Object value, 
int jdbcType)
+  private static void serializeParameter(OutputStream outputStream, Object 
value, int jdbcType)
       throws IOException {
     if (value == null || jdbcType == Types.NULL) {
-      dos.writeByte(TSDataType.UNKNOWN.serialize());
+      ReadWriteIOUtils.write(TSDataType.UNKNOWN, outputStream);
       return;
     }
 
     switch (jdbcType) {
       case Types.BOOLEAN:
-        dos.writeByte(TSDataType.BOOLEAN.serialize());
-        dos.writeByte((Boolean) value ? 1 : 0);
+        ReadWriteIOUtils.write(TSDataType.BOOLEAN, outputStream);
+        ReadWriteIOUtils.write((Boolean) value, outputStream);

Review Comment:
   ReadWriteIOUtils.write((boolean) value, outputStream);



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java:
##########
@@ -1542,80 +1554,47 @@ public TSPrepareResp prepareStatement(TSPrepareReq req) 
{
 
   @Override
   public TSExecuteStatementResp executePreparedStatement(TSExecutePreparedReq 
req) {
-    boolean finished = false;
-    long queryId = Long.MIN_VALUE;
     IClientSession clientSession = 
SESSION_MANAGER.getCurrSessionAndUpdateIdleTime();
 
-    if (!SESSION_MANAGER.checkLogin(clientSession)) {
-      return RpcUtils.getTSExecuteStatementResp(getNotLoggedInStatus());
-    }
-
-    long startTime = System.nanoTime();
-    Throwable t = null;
     try {
       String statementName = req.getStatementName();
 
+      // Deserialize parameters and build Execute statement
       List<DeserializedParam> rawParams =
-          
PreparedParameterSerializer.deserialize(ByteBuffer.wrap(req.getParameters()));
+          
PreparedParameterSerde.deserialize(ByteBuffer.wrap(req.getParameters()));
       List<Literal> parameters = new ArrayList<>(rawParams.size());
+      List<String> paramStrings = new ArrayList<>(rawParams.size());
       for (DeserializedParam param : rawParams) {
-        parameters.add(convertToLiteral(param));
+        Pair<Literal, String> literalAndString = 
convertToLiteralWithString(param);
+        parameters.add(literalAndString.left);
+        paramStrings.add(literalAndString.right);
       }
-
       Execute executeStatement = new Execute(new Identifier(statementName), 
parameters);
 
-      queryId = SESSION_MANAGER.requestQueryId(clientSession, 
req.getStatementId());
+      String fullStatement =
+          paramStrings.isEmpty()
+              ? "EXECUTE " + statementName
+              : "EXECUTE " + statementName + " USING " + String.join(", ", 
paramStrings);
 
-      long timeout = req.isSetTimeout() ? req.getTimeout() : 
config.getQueryTimeoutThreshold();
-      ExecutionResult result =
-          COORDINATOR.executeForTableModel(
-              executeStatement,
-              relationSqlParser,
-              clientSession,
-              queryId,
-              SESSION_MANAGER.getSessionInfo(clientSession),
-              "EXECUTE " + statementName,
-              metadata,
-              timeout,
-              true);
-
-      if (result.status.code != TSStatusCode.SUCCESS_STATUS.getStatusCode()
-          && result.status.code != 
TSStatusCode.REDIRECTION_RECOMMEND.getStatusCode()) {
-        finished = true;
-        return RpcUtils.getTSExecuteStatementResp(result.status);
+      // Build a compatible TSExecuteStatementReq
+      TSExecuteStatementReq executeReq = new TSExecuteStatementReq();
+      executeReq.setSessionId(clientSession.getId());
+      executeReq.setStatement(fullStatement);
+      executeReq.setStatementId(req.getStatementId());
+      if (req.isSetFetchSize()) {
+        executeReq.setFetchSize(req.getFetchSize());
       }
-
-      IQueryExecution queryExecution = COORDINATOR.getQueryExecution(queryId);
-
-      try (SetThreadName threadName = new 
SetThreadName(result.queryId.getId())) {
-        TSExecuteStatementResp resp;
-        if (queryExecution != null && queryExecution.isQuery()) {
-          resp = createResponse(queryExecution.getDatasetHeader(), queryId);
-          resp.setStatus(result.status);
-          int fetchSize =
-              req.isSetFetchSize() ? req.getFetchSize() : 
config.getThriftMaxFrameSize();
-          finished = setResultForPrepared.apply(resp, queryExecution, 
fetchSize);
-          resp.setMoreData(!finished);
-        } else {
-          finished = true;
-          resp = RpcUtils.getTSExecuteStatementResp(result.status);
-        }
-        return resp;
+      if (req.isSetTimeout()) {
+        executeReq.setTimeout(req.getTimeout());
       }
+
+      return executeStatementInternal(executeReq, setResultForPrepared, () -> 
executeStatement);

Review Comment:
   not right, you shouldn't directly reuse current `executeStatementInternal`, 
you need to do some changes to it.
   
   For example, you should always do the login check at the very fisrt of each 
rpc method, otherwise it may have DoS risks.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to