From 629bcb6599f02be63bcdf03da6e1d3df3481437f Mon Sep 17 00:00:00 2001 From: ndebeiss Date: Fri, 10 Jul 2015 14:45:18 +0200 Subject: [PATCH] Support single quotes nested in double quotes in SQL scripts Some databases such as Oracle permit double quoted column aliases that contain case-sensitive characters, single quotes, and other special characters; however, prior to this commit, SqlScripts interpreted a single quote nested within double quotes as the start of a string literal resulting in improper parsing. This commit addresses this issue by ensuring that double quoted strings such as column aliases are properly parsed even when containing single quotes. Issue: SPR-13218 --- .../jdbc/datasource/init/ScriptUtils.java | 13 ++++++++---- .../init/ScriptUtilsIntegrationTests.java | 19 ++++++++++++++--- .../datasource/init/ScriptUtilsUnitTests.java | 21 +++++++++++++++++-- ...-single-quotes-nested-in-double-quotes.sql | 7 +++++++ 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-single-quotes-nested-in-double-quotes.sql diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java index 21bbe07076..6aaaec0405 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java @@ -45,6 +45,7 @@ import org.springframework.util.StringUtils; * @author Chris Beams * @author Oliver Gierke * @author Chris Baldwin + * @author Nicolas Debeissat * @since 4.0.3 */ public abstract class ScriptUtils { @@ -173,7 +174,8 @@ public abstract class ScriptUtils { Assert.hasText(blockCommentEndDelimiter, "blockCommentEndDelimiter must not be null or empty"); StringBuilder sb = new StringBuilder(); - boolean inLiteral = false; + boolean inSingleQuote = false; + boolean inDoubleQuote = false; boolean inEscape = false; char[] content = script.toCharArray(); for (int i = 0; i < script.length(); i++) { @@ -189,10 +191,13 @@ public abstract class ScriptUtils { sb.append(c); continue; } - if (c == '\'') { - inLiteral = !inLiteral; + if (!inDoubleQuote && (c == '\'')) { + inSingleQuote = !inSingleQuote; + } + else if (!inSingleQuote && (c == '"')) { + inDoubleQuote = !inDoubleQuote; } - if (!inLiteral) { + if (!inSingleQuote && !inDoubleQuote) { if (script.startsWith(separator, i)) { // we've reached the end of the current statement if (sb.length() > 0) { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java index 7a34a9db8a..7bd998b133 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.jdbc.datasource.init; import java.sql.SQLException; +import org.junit.Before; import org.junit.Test; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @@ -28,8 +29,8 @@ import static org.springframework.jdbc.datasource.init.ScriptUtils.*; * Integration tests for {@link ScriptUtils}. * * @author Sam Brannen - * @see ScriptUtilsUnitTests * @since 4.0.3 + * @see ScriptUtilsUnitTests */ public class ScriptUtilsIntegrationTests extends AbstractDatabaseInitializationTests { @@ -37,11 +38,23 @@ public class ScriptUtilsIntegrationTests extends AbstractDatabaseInitializationT return EmbeddedDatabaseType.HSQL; } + @Before + public void setUpSchema() throws SQLException { + executeSqlScript(db.getConnection(), usersSchema()); + } + @Test public void executeSqlScriptContainingMuliLineComments() throws SQLException { - executeSqlScript(db.getConnection(), usersSchema()); executeSqlScript(db.getConnection(), resource("test-data-with-multi-line-comments.sql")); + assertUsersDatabaseCreated("Hoeller", "Brannen"); + } + /** + * @since 4.2 + */ + @Test + public void executeSqlScriptContainingSingleQuotesNestedInsideDoubleQuotes() throws SQLException { + executeSqlScript(db.getConnection(), resource("users-data-with-single-quotes-nested-in-double-quotes.sql")); assertUsersDatabaseCreated("Hoeller", "Brannen"); } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java index 224a22b75c..ab9a5a5eb9 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,9 @@ import static org.springframework.jdbc.datasource.init.ScriptUtils.*; * @author Sam Brannen * @author Phillip Webb * @author Chris Baldwin - * @see ScriptUtilsIntegrationTests + * @author Nicolas Debeissat * @since 4.0.3 + * @see ScriptUtilsIntegrationTests */ public class ScriptUtilsUnitTests { @@ -85,6 +86,22 @@ public class ScriptUtilsUnitTests { statements.get(0)); } + /** + * See SPR-13218 + */ + @Test + public void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() throws Exception { + String statement1 = "select '1' as \"Dogbert's owner's\" from dual"; + String statement2 = "select '2' as \"Dilbert's\" from dual"; + char delim = ';'; + String script = statement1 + delim + statement2 + delim; + List statements = new ArrayList(); + splitSqlScript(script, ';', statements); + assertEquals("wrong number of statements", 2, statements.size()); + assertEquals("statement 1 not split correctly", statement1, statements.get(0)); + assertEquals("statement 2 not split correctly", statement2, statements.get(1)); + } + /** * See SPR-11560 */ diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-single-quotes-nested-in-double-quotes.sql b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-single-quotes-nested-in-double-quotes.sql new file mode 100644 index 0000000000..1f5e3e0888 --- /dev/null +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-single-quotes-nested-in-double-quotes.sql @@ -0,0 +1,7 @@ +INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller'); + +-- The following is not actually used; we just want to ensure that it does not +-- result in a parsing exception due to the nested single quote. +SELECT last_name AS "Juergen's Last Name" FROM users WHERE last_name='Hoeller'; + +INSERT INTO users(first_name, last_name) values('Sam', 'Brannen'); \ No newline at end of file