diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClientException.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClientException.java index f9c1e53c2ab409..7fcea7aa61aa2f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClientException.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClientException.java @@ -19,10 +19,33 @@ public class JdbcClientException extends RuntimeException { public JdbcClientException(String format, Throwable cause, Object... msg) { - super(String.format(format, msg), cause); + super(formatMessage(format, msg), cause); } public JdbcClientException(String format, Object... msg) { - super(String.format(format, msg)); + super(formatMessage(format, msg)); + } + + private static String formatMessage(String format, Object... msg) { + if (msg == null || msg.length == 0) { + return format; + } else { + return String.format(format, escapePercentInArgs(msg)); + } + } + + private static Object[] escapePercentInArgs(Object... args) { + if (args == null) { + return null; + } + Object[] escapedArgs = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof String) { + escapedArgs[i] = ((String) args[i]).replace("%", "%%"); + } else { + escapedArgs[i] = args[i]; + } + } + return escapedArgs; } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcClientExceptionTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcClientExceptionTest.java new file mode 100644 index 00000000000000..1bbf54e9438512 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcClientExceptionTest.java @@ -0,0 +1,132 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.datasource.jdbc; + +import org.apache.doris.datasource.jdbc.client.JdbcClientException; + +import org.junit.Assert; +import org.junit.Test; + +public class JdbcClientExceptionTest { + + @Test + public void testExceptionWithoutArgs() { + String message = "An error occurred."; + JdbcClientException exception = new JdbcClientException(message); + + Assert.assertEquals(message, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithFormattingArgs() { + String format = "Error code: %d, message: %s"; + int errorCode = 404; + String errorMsg = "Not Found"; + JdbcClientException exception = new JdbcClientException(format, errorCode, errorMsg); + + String expectedMessage = String.format(format, errorCode, errorMsg); + Assert.assertEquals(expectedMessage, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithPercentInFormatString() { + String format = "Usage is at 80%%, threshold is %d%%"; + int threshold = 75; + JdbcClientException exception = new JdbcClientException(format, threshold); + + String expectedMessage = String.format(format, threshold); + Assert.assertEquals(expectedMessage, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithPercentInArgs() { + String format = "Invalid input: %s"; + String input = "50% discount"; + JdbcClientException exception = new JdbcClientException(format, input); + + String expectedMessage = String.format(format, input.replace("%", "%%")); + Assert.assertEquals(expectedMessage, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithCause() { + String message = "Database connection failed."; + Exception cause = new Exception("Timeout occurred"); + JdbcClientException exception = new JdbcClientException(message, cause); + + Assert.assertEquals(message, exception.getMessage()); + Assert.assertEquals(cause, exception.getCause()); + } + + @Test + public void testExceptionWithFormattingArgsAndCause() { + String format = "Failed to execute query: %s"; + String query = "SELECT * FROM users"; + Exception cause = new Exception("Syntax error"); + JdbcClientException exception = new JdbcClientException(format, cause, query); + + String expectedMessage = String.format(format, query); + Assert.assertEquals(expectedMessage, exception.getMessage()); + Assert.assertEquals(cause, exception.getCause()); + } + + @Test + public void testExceptionWithPercentInArgsAndCause() { + String format = "File path: %s"; + String filePath = "C:\\Program Files\\App%20Data"; + Exception cause = new Exception("File not found"); + JdbcClientException exception = new JdbcClientException(format, cause, filePath); + + String expectedMessage = String.format(format, filePath.replace("%", "%%")); + Assert.assertEquals(expectedMessage, exception.getMessage()); + Assert.assertEquals(cause, exception.getCause()); + } + + @Test + public void testExceptionWithNoFormattingNeeded() { + String message = "Simple error message."; + JdbcClientException exception = new JdbcClientException(message, (Object[]) null); + + Assert.assertEquals(message, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithNullArgs() { + String format = "Error occurred: %s"; + JdbcClientException exception = new JdbcClientException(format, (Object[]) null); + + // Since args are null, message should remain unformatted + Assert.assertEquals(format, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } + + @Test + public void testExceptionWithEmptyArgs() { + String format = "Error occurred: %s"; + JdbcClientException exception = new JdbcClientException(format, new Object[]{}); + + // Since args are empty, message should remain unformatted + Assert.assertEquals(format, exception.getMessage()); + Assert.assertNull(exception.getCause()); + } +}