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
master
ndebeiss 9 years ago committed by Sam Brannen
parent de6bbe7797
commit 629bcb6599
  1. 13
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java
  2. 19
      spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsIntegrationTests.java
  3. 21
      spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java
  4. 7
      spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/users-data-with-single-quotes-nested-in-double-quotes.sql

@ -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) {

@ -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");
}

@ -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 <a href="https://jira.spring.io/browse/SPR-13218">SPR-13218</a>
*/
@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<String> statements = new ArrayList<String>();
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 <a href="https://jira.spring.io/browse/SPR-11560">SPR-11560</a>
*/

@ -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');
Loading…
Cancel
Save