99 */
1010package net .sf .jsqlparser .schema ;
1111
12+ import java .time .Duration ;
13+ import net .sf .jsqlparser .parser .CCJSqlParserUtil ;
14+ import net .sf .jsqlparser .statement .select .PlainSelect ;
1215import org .assertj .core .api .Assertions ;
1316import org .junit .jupiter .api .Test ;
1417
@@ -21,4 +24,31 @@ void replaceBackticksWithDoubleQuotes() {
2124 Assertions .assertThat ("\" starbake\" .\" customers\" " ).isEqualToIgnoringCase (
2225 MultiPartName .replaceBackticksWithDoubleQuotes ("`starbake`.`customers`" ));
2326 }
27+
28+ @ Test
29+ void unquoteMatchesQuotePattern () {
30+ String [] names = {"" , "a" , "\" a\" " , "[a]" , "`a`" , "\" a\" .\" b\" " , "\" \" \" \" " , "\" ]" ,
31+ "[\" a\" ]" , "a\" " , "\" a" , "]a[" , "``a``" , "\" a\" \" b\" " , "[[a]]" };
32+ for (String name : names ) {
33+ assertEquals (MultiPartName .LEADING_TRAILING_QUOTES_PATTERN .matcher (name ).replaceAll ("" ),
34+ MultiPartName .unquote (name ), name );
35+ assertEquals (MultiPartName .LEADING_TRAILING_QUOTES_PATTERN .matcher (name ).find (),
36+ MultiPartName .isQuoted (name ), name );
37+ }
38+ assertNull (MultiPartName .unquote (null ));
39+ assertFalse (MultiPartName .isQuoted (null ));
40+ }
41+
42+ @ Test
43+ void unquoteIsLinearOnRunsOfEscapedQuotes () {
44+ // a quoted identifier with a dot and many doubled quotes used to backtrack quadratically
45+ String quotes = "\" \" " .repeat (40000 );
46+ String sql = "SELECT * FROM \" a" + quotes + ".b\" " ;
47+ PlainSelect select = (PlainSelect ) assertTimeoutPreemptively (Duration .ofSeconds (2 ),
48+ () -> CCJSqlParserUtil .parse (sql ));
49+ assertNotNull (select .getFromItem ());
50+ assertEquals ("a" + quotes + ".b" ,
51+ assertTimeoutPreemptively (Duration .ofSeconds (2 ),
52+ () -> MultiPartName .unquote ("\" a" + quotes + ".b\" " )));
53+ }
2454}
0 commit comments