Projects
Mega:23.09
log4j12
_service:tar_scm:CVE-2022-23305.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:CVE-2022-23305.patch of Package log4j12
From: Markus Koschany <apo@debian.org> Date: Mon, 31 Jan 2022 10:55:04 +0100 Subject: CVE-2022-23305 Origin: https://github.com/qos-ch/reload4j/pull/26/commits/33d1697bb13b8cf869c450a64f8550b1593f8035 --- pom.xml | 6 + .../java/org/apache/log4j/jdbc/JDBCAppender.java | 97 ++++++++-- .../org/apache/log4j/jdbc/JdbcPatternParser.java | 101 ++++++++++ src/test/input/jdbc_h2_bufferSize1.properties | 25 +++ src/test/input/jdbc_h2_bufferSize2.properties | 25 +++ .../org/apache/log4j/jdbc/JdbcAppenderTest.java | 208 +++++++++++++++++++++ .../apache/log4j/jdbc/JdbcPatternParserTest.java | 50 +++++ 7 files changed, 495 insertions(+), 17 deletions(-) create mode 100644 src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java create mode 100644 src/test/input/jdbc_h2_bufferSize1.properties create mode 100644 src/test/input/jdbc_h2_bufferSize2.properties create mode 100644 src/test/java/org/apache/log4j/jdbc/JdbcAppenderTest.java create mode 100644 src/test/java/org/apache/log4j/jdbc/JdbcPatternParserTest.java diff --git a/pom.xml b/pom.xml index 93881cd..3ec2ccc 100644 --- a/pom.xml +++ b/pom.xml @@ -577,6 +577,12 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line. <version>1.0</version> <optional>true</optional> </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>2.1.210</version> + <scope>test</scope> + </dependency> </dependencies> <distributionManagement> <repository> diff --git a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java index ad35f65..2f979ae 100644 --- a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java +++ b/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java @@ -18,6 +18,7 @@ package org.apache.log4j.jdbc; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; @@ -78,6 +79,12 @@ import org.apache.log4j.spi.LoggingEvent; public class JDBCAppender extends org.apache.log4j.AppenderSkeleton implements org.apache.log4j.Appender { + private final Boolean secureSqlReplacement = + Boolean.parseBoolean(System.getProperty("org.apache.log4j.jdbc.JDBCAppender.secure_jdbc_replacement", "true")); + + private static final IllegalArgumentException ILLEGAL_PATTERN_FOR_SECURE_EXEC = + new IllegalArgumentException("Only org.apache.log4j.PatternLayout is supported for now due to CVE-2022-23305"); + /** * URL of the DB for default connection handling */ @@ -113,6 +120,8 @@ public class JDBCAppender extends org.apache.log4j.AppenderSkeleton */ protected String sqlStatement = ""; + private JdbcPatternParser preparedStatementParser = new JdbcPatternParser(); + /** * size of LoggingEvent buffer before writting to the database. * Default is 1. @@ -245,11 +254,11 @@ public class JDBCAppender extends org.apache.log4j.AppenderSkeleton */ protected Connection getConnection() throws SQLException { if (!DriverManager.getDrivers().hasMoreElements()) - setDriver("sun.jdbc.odbc.JdbcOdbcDriver"); + setDriver("sun.jdbc.odbc.JdbcOdbcDriver"); if (connection == null) { connection = DriverManager.getConnection(databaseURL, databaseUser, - databasePassword); + databasePassword); } return connection; @@ -280,29 +289,83 @@ public class JDBCAppender extends org.apache.log4j.AppenderSkeleton * If a statement fails the LoggingEvent stays in the buffer! */ public void flushBuffer() { + if (buffer.isEmpty()) { + return; + } //Do the actual logging removes.ensureCapacity(buffer.size()); - for (Iterator i = buffer.iterator(); i.hasNext();) { - LoggingEvent logEvent = (LoggingEvent)i.next(); - try { - String sql = getLogStatement(logEvent); - execute(sql); - } - catch (SQLException e) { - errorHandler.error("Failed to excute sql", e, - ErrorCode.FLUSH_FAILURE); - } finally { - removes.add(logEvent); - } + if (secureSqlReplacement) { + flushBufferSecure(); + } else { + flushBufferInsecure(); } - // remove from the buffer any events that were reported buffer.removeAll(removes); - + // clear the buffer of reported events removes.clear(); } + private void flushBufferInsecure() { + for (Iterator i = buffer.iterator(); i.hasNext(); ) { + LoggingEvent logEvent = (LoggingEvent) i.next(); + try { + String sql = getLogStatement(logEvent); + execute(sql); + } catch (SQLException e) { + errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE); + } finally { + removes.add(logEvent); + } + } + } + + private void flushBufferSecure() { + // Prepare events that we will store to the DB + removes.addAll(buffer); + + if (layout.getClass() != PatternLayout.class) { + errorHandler.error("Failed to convert pattern " + layout + " to SQL", ILLEGAL_PATTERN_FOR_SECURE_EXEC, ErrorCode.MISSING_LAYOUT); + return; + } + PatternLayout patternLayout = (PatternLayout) layout; + preparedStatementParser.setPattern(patternLayout.getConversionPattern()); + Connection con = null; + boolean useBatch = removes.size() > 1; + try { + con = getConnection(); + PreparedStatement ps = con.prepareStatement(preparedStatementParser.getParameterizedSql()); + try { + for (Iterator i = removes.iterator(); i.hasNext(); ) { + LoggingEvent logEvent = (LoggingEvent) i.next(); + try { + preparedStatementParser.setParameters(ps, logEvent); + if (useBatch) { + ps.addBatch(); + } + } catch (SQLException e) { + errorHandler.error("Failed to append parameters", e, ErrorCode.FLUSH_FAILURE); + } + } + if (useBatch) { + ps.executeBatch(); + } else { + ps.execute(); + } + } finally { + try { + ps.close(); + } catch (SQLException ignored) { + } + } + } catch (SQLException e) { + errorHandler.error("Failed to append messages sql", e, ErrorCode.FLUSH_FAILURE); + } finally { + if (con != null) { + closeConnection(con); + } + } + } /** closes the appender before disposal */ public void finalize() { @@ -391,7 +454,7 @@ public class JDBCAppender extends org.apache.log4j.AppenderSkeleton Class.forName(driverClass); } catch (Exception e) { errorHandler.error("Failed to load driver", e, - ErrorCode.GENERIC_FAILURE); + ErrorCode.GENERIC_FAILURE); } } } diff --git a/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java b/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java new file mode 100644 index 0000000..691ed56 --- /dev/null +++ b/src/main/java/org/apache/log4j/jdbc/JdbcPatternParser.java @@ -0,0 +1,101 @@ +/* + * 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.log4j.jdbc; + +import org.apache.log4j.helpers.PatternConverter; +import org.apache.log4j.helpers.PatternParser; +import org.apache.log4j.spi.LoggingEvent; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class JdbcPatternParser { + private final static Pattern STRING_LITERAL_PATTERN = Pattern.compile("'((?>[^']|'')+)'"); + + private String lastPattern; + private String parameterizedSql; + private final List<String> argPatterns = new ArrayList<String>(); + private final List<PatternConverter> args = new ArrayList<PatternConverter>(); + private StringBuffer buffer = new StringBuffer(); + + public String getParameterizedSql() { + return parameterizedSql; + } + + @Override + public String toString() { + return "JdbcPatternParser{sql=" + parameterizedSql + ",args=" + argPatterns + "}"; + } + + /** + * Converts '....' literals into bind variables in JDBC. + */ + void setPattern(String pattern) { + if (pattern == null) { + throw new IllegalArgumentException("Null pattern"); + } + if (pattern.equals(lastPattern)) { + return; + } + Matcher m = STRING_LITERAL_PATTERN.matcher(pattern); + StringBuffer sb = new StringBuffer(); + args.clear(); + argPatterns.clear(); + while (m.find()) { + String literal = m.group(1); + if (literal.indexOf('%') == -1) { + // Just literal, append it as is + // It can't contain user-provided parts like %m, etc. + m.appendReplacement(sb, "'$1'"); + continue; + } + + // Replace with bind + m.appendReplacement(sb, "?"); + // We will use prepared statements, so we don't need to escape quotes. + // And we assume the users had 'That''s a string with quotes' in their configs. + literal = literal.replaceAll("''", "'"); + argPatterns.add(literal); + args.add(new PatternParser(literal).parse()); + } + m.appendTail(sb); + parameterizedSql = sb.toString(); + lastPattern = pattern; + } + + public void setParameters(PreparedStatement ps, LoggingEvent logEvent) throws SQLException { + for (int i = 0; i < args.size(); i++) { + buffer.setLength(0); + PatternConverter c = args.get(i); + while (c != null) { + c.format(buffer, logEvent); + c = c.next; + } + ps.setString(i + 1, buffer.toString()); + } + // This clears "toString cache" + buffer.setLength(0); + if (buffer.capacity() > 100000) { + // Avoid leaking too much memory if we discover long parameter + buffer = new StringBuffer(); + } + } +} diff --git a/src/test/input/jdbc_h2_bufferSize1.properties b/src/test/input/jdbc_h2_bufferSize1.properties new file mode 100644 index 0000000..77e6083 --- /dev/null +++ b/src/test/input/jdbc_h2_bufferSize1.properties @@ -0,0 +1,25 @@ +# 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. + +log4j.threshold=ALL +log4j.rootLogger=ALL,A +log4j.appender.A=org.apache.log4j.jdbc.JDBCAppender +log4j.appender.A.URL=jdbc:h2:mem:test_db +log4j.appender.A.driver=org.h2.Driver +log4j.appender.A.bufferSize=1 +log4j.appender.A.user= +log4j.appender.A.password= +log4j.appender.A.layout=org.apache.log4j.PatternLayout +log4j.appender.A.sql=insert into logs(level,location,message,message2) values('%p','%c','%m', ' %c %p %m') diff --git a/src/test/input/jdbc_h2_bufferSize2.properties b/src/test/input/jdbc_h2_bufferSize2.properties new file mode 100644 index 0000000..fba02a3 --- /dev/null +++ b/src/test/input/jdbc_h2_bufferSize2.properties @@ -0,0 +1,25 @@ +# 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. + +log4j.threshold=ALL +log4j.rootLogger=ALL,A +log4j.appender.A=org.apache.log4j.jdbc.JDBCAppender +log4j.appender.A.URL=jdbc:h2:mem:test_db +log4j.appender.A.driver=org.h2.Driver +log4j.appender.A.bufferSize=2 +log4j.appender.A.user= +log4j.appender.A.password= +log4j.appender.A.layout=org.apache.log4j.PatternLayout +log4j.appender.A.sql=insert into logs(level,location,message,message2) values('%p','%c','%m', ' %c %p %m') diff --git a/src/test/java/org/apache/log4j/jdbc/JdbcAppenderTest.java b/src/test/java/org/apache/log4j/jdbc/JdbcAppenderTest.java new file mode 100644 index 0000000..f851063 --- /dev/null +++ b/src/test/java/org/apache/log4j/jdbc/JdbcAppenderTest.java @@ -0,0 +1,208 @@ +/* + * 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.log4j.jdbc; + +import org.apache.log4j.Appender; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; +import org.apache.log4j.TestContants; +import org.apache.log4j.VectorErrorHandler; +import org.apache.log4j.xml.XLevel; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class JdbcAppenderTest { + // Closing the last connection to "in-memory h2" removes the database + // So we keep the connection during the test + // The logger opens its own connection + Connection con; + + @Before + public void setup() throws SQLException { + con = DriverManager.getConnection("jdbc:h2:mem:test_db"); + Statement st = con.createStatement(); + try { + st.execute("create table logs(level varchar(100),location varchar(100),message varchar(100),message2 varchar(100))"); + } finally { + st.close(); + } + } + + @After + public void cleanup() throws SQLException { + LogManager.shutdown(); + con.close(); + } + + @Test + public void verifyJdbcInsecure() throws SQLException { + String secureJdbcReplacement = "org.apache.log4j.jdbc.JDBCAppender.secure_jdbc_replacement"; + System.setProperty(secureJdbcReplacement, "false"); + try { + PropertyConfigurator.configure(TestContants.TEST_INPUT_PREFIX + "jdbc_h2_bufferSize1.properties"); + Appender jdbcAppender = LogManager.getRootLogger().getAppender("A"); + // Keep errors + VectorErrorHandler errorHandler = new VectorErrorHandler(); + jdbcAppender.setErrorHandler(errorHandler); + + Logger logger = Logger.getLogger(JdbcAppenderTest.class); + + String oldThreadName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName("main"); + logger.debug("message with '' quote"); + Assert.assertEquals( + "batchSize=1, so messages should be added immediately", + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n", + joinSorted(getMessages())); + + // It should fail + logger.fatal("message with ' quote"); + + Assert.assertEquals( + "Inserting a message with ' should cause failure in insecure mode, so only one message should be in the DB", + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n", + joinSorted(getMessages())); + + StringBuilder exceptions = new StringBuilder(); + StringBuilder errorCodes = new StringBuilder(); + for (int i = 0; i < errorHandler.size(); i++) { + Exception ex = errorHandler.getException(i); + exceptions.append(ex.toString()); + errorCodes.append( + ex instanceof SQLException + ? ("SQLException.getErrorCode() = " + ((SQLException) ex).getErrorCode()) + : ("SQL Exception expected, got " + ex.getClass())); + exceptions.append('\n'); + errorCodes.append('\n'); + } + Assert.assertEquals( + "Logging a message with ' should trigger SQLException 'Syntax error in SQL statement...' when using insecure JDBCAppender mode, actual exceptions are\n" + exceptions, + "SQLException.getErrorCode() = 42001\n", + errorCodes.toString() + ); + } finally { + Thread.currentThread().setName(oldThreadName); + } + } finally { + System.getProperties().remove(secureJdbcReplacement); + } + } + + @Test + public void verifyJdbcBufferSize1() throws SQLException { + PropertyConfigurator.configure(TestContants.TEST_INPUT_PREFIX + "jdbc_h2_bufferSize1.properties"); + + Logger logger = Logger.getLogger(JdbcAppenderTest.class); + + String oldThreadName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName("main"); + logger.debug("message with ' quote"); + Assert.assertEquals( + "batchSize=1, so messages should be added immediately", + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n", + joinSorted(getMessages())); + + logger.fatal("message with \" quote"); + + Assert.assertEquals( + "batchSize=1, so two messages should be in DB after two logging calls", + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n" + + "FATAL; org.apache.log4j.jdbc.JdbcAppenderTest; message with \" quote; org.apache.log4j.jdbc.JdbcAppenderTest FATAL message with \" quote\n", + joinSorted(getMessages())); + } finally { + Thread.currentThread().setName(oldThreadName); + } + } + + @Test + public void verifyJdbcBufferSize2() throws SQLException { + PropertyConfigurator.configure(TestContants.TEST_INPUT_PREFIX + "jdbc_h2_bufferSize2.properties"); + + Logger logger = Logger.getLogger(JdbcAppenderTest.class); + + String oldThreadName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName("main"); + logger.log(XLevel.TRACE, "xtrace message"); + logger.debug("message with ' quote"); + logger.info("message with \" quote"); + logger.warn("?"); + // bufferSize=2, so this message won't yet be stored to the db + logger.error("m4"); + + Assert.assertEquals( + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n" + + "INFO; org.apache.log4j.jdbc.JdbcAppenderTest; message with \" quote; org.apache.log4j.jdbc.JdbcAppenderTest INFO message with \" quote\n" + + "TRACE; org.apache.log4j.jdbc.JdbcAppenderTest; xtrace message; org.apache.log4j.jdbc.JdbcAppenderTest TRACE xtrace message\n" + + "WARN; org.apache.log4j.jdbc.JdbcAppenderTest; ?; org.apache.log4j.jdbc.JdbcAppenderTest WARN ?\n", + joinSorted(getMessages())); + + logger.fatal("m5"); + + Assert.assertEquals( + "Logging m5 message should trigger buffer flush for both m4 and m5", + "DEBUG; org.apache.log4j.jdbc.JdbcAppenderTest; message with ' quote; org.apache.log4j.jdbc.JdbcAppenderTest DEBUG message with ' quote\n" + + "ERROR; org.apache.log4j.jdbc.JdbcAppenderTest; m4; org.apache.log4j.jdbc.JdbcAppenderTest ERROR m4\n" + + "FATAL; org.apache.log4j.jdbc.JdbcAppenderTest; m5; org.apache.log4j.jdbc.JdbcAppenderTest FATAL m5\n" + + "INFO; org.apache.log4j.jdbc.JdbcAppenderTest; message with \" quote; org.apache.log4j.jdbc.JdbcAppenderTest INFO message with \" quote\n" + + "TRACE; org.apache.log4j.jdbc.JdbcAppenderTest; xtrace message; org.apache.log4j.jdbc.JdbcAppenderTest TRACE xtrace message\n" + + "WARN; org.apache.log4j.jdbc.JdbcAppenderTest; ?; org.apache.log4j.jdbc.JdbcAppenderTest WARN ?\n", + joinSorted(getMessages())); + } finally { + Thread.currentThread().setName(oldThreadName); + } + } + + private static String joinSorted(List<String> list) { + Collections.sort(list); + StringBuilder sb = new StringBuilder(); + for (String s : list) { + sb.append(s).append('\n'); + } + return sb.toString(); + } + + private List<String> getMessages() throws SQLException { + List<String> res = new ArrayList<String>(); + PreparedStatement ps = con.prepareStatement("select level,location,message,message2 from logs"); + ResultSet rs = ps.executeQuery(); + try { + while (rs.next()) { + res.add(rs.getString(1) + "; " + rs.getString(2) + + "; " + rs.getString(3) + "; " + rs.getString(4)); + } + } finally { + rs.close(); + } + return res; + } +} diff --git a/src/test/java/org/apache/log4j/jdbc/JdbcPatternParserTest.java b/src/test/java/org/apache/log4j/jdbc/JdbcPatternParserTest.java new file mode 100644 index 0000000..aafbd80 --- /dev/null +++ b/src/test/java/org/apache/log4j/jdbc/JdbcPatternParserTest.java @@ -0,0 +1,50 @@ +/* + * 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.log4j.jdbc; + +import org.junit.Assert; +import org.junit.Test; + +public class JdbcPatternParserTest { + JdbcPatternParser parser = new JdbcPatternParser(); + + @Test + public void testParameterizedSql() { + assertParameterizedSql( + "JdbcPatternParser{sql=INSERT INTO A1 (TITLE3) VALUES ( ? ),args=[ %d - %c %-5p %c %x - %m%n ]}", + "INSERT INTO A1 (TITLE3) VALUES ( ' %d - %c %-5p %c %x - %m%n ' )" + ); + assertParameterizedSql( + "JdbcPatternParser{sql=INSERT INTO A1 (TITLE3) VALUES ( ?, ?, ?, ?, ?, ? ),args=[%d, %c, %-5p, '%c, %x, - %m%n ]}", + "INSERT INTO A1 (TITLE3) VALUES ( '%d', '%c', '%-5p', ' ''%c', '%x', ' - %m%n ' )" + ); + + assertParameterizedSql( + "JdbcPatternParser{sql=INSERT INTO A1 (TITLE3) VALUES ( ' just string literal', 'another literal with quotes '' asdf', ?),args=[message: %m]}", + "INSERT INTO A1 (TITLE3) VALUES ( ' just string literal', 'another literal with quotes '' asdf', 'message: %m')" + ); + } + + private void assertParameterizedSql(String expected, String input) { + parser.setPattern(input); + Assert.assertEquals( + "parser.setPattern(...).toString() for " + input, + expected, + parser.toString() + ); + } +}
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2