Skip to content

Commit d94b923

Browse files
committed
avoid quadratic unescaping in getNotExcapedValue
Signed-off-by: Javid Khan <dxbjavid@gmail.com>
1 parent 26d3e02 commit d94b923

2 files changed

Lines changed: 31 additions & 7 deletions

File tree

‎src/main/java/net/sf/jsqlparser/expression/StringValue.java‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,14 @@ public String getNotExcapedValue() {
123123
if (quoteStr != null && quoteStr.startsWith("$")) {
124124
return value;
125125
}
126-
StringBuilder buffer = new StringBuilder(value);
127-
int index = 0;
128-
int deletesNum = 0;
129-
while ((index = value.indexOf("''", index)) != -1) {
130-
buffer.deleteCharAt(index - deletesNum);
131-
index += 2;
132-
deletesNum++;
126+
StringBuilder buffer = new StringBuilder(value.length());
127+
int from = 0;
128+
int index;
129+
while ((index = value.indexOf("''", from)) != -1) {
130+
buffer.append(value, from, index + 1);
131+
from = index + 2;
133132
}
133+
buffer.append(value, from, value.length());
134134
return buffer.toString();
135135
}
136136

‎src/test/java/net/sf/jsqlparser/expression/StringValueTest.java‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
package net.sf.jsqlparser.expression;
1111

1212
import static org.junit.jupiter.api.Assertions.assertEquals;
13+
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
1314

15+
import java.time.Duration;
1416
import net.sf.jsqlparser.JSQLParserException;
1517
import net.sf.jsqlparser.statement.select.PlainSelect;
1618
import net.sf.jsqlparser.test.TestUtils;
@@ -50,6 +52,28 @@ public void testGetNotExcapedValue() {
5052
assertEquals(expResult, result);
5153
}
5254

55+
@Test
56+
public void testGetNotExcapedValueLargeInputIsLinear() {
57+
// a string literal made of many escaped single quotes used to be unescaped with a
58+
// deleteCharAt() per pair, which is quadratic and lets a modest literal burn CPU
59+
int pairs = 1_000_000;
60+
StringBuilder sb = new StringBuilder(2 * pairs + 2);
61+
sb.append('\'');
62+
for (int i = 0; i < pairs; i++) {
63+
sb.append("''");
64+
}
65+
sb.append('\'');
66+
StringValue instance = new StringValue(sb.toString());
67+
68+
String result = assertTimeoutPreemptively(Duration.ofSeconds(2),
69+
instance::getNotExcapedValue);
70+
71+
assertEquals(pairs, result.length());
72+
for (int i = 0; i < result.length(); i++) {
73+
assertEquals('\'', result.charAt(i));
74+
}
75+
}
76+
5377
@Test
5478
public void testPrefixes() {
5579
checkStringValue("E'test'", "test", "E");

0 commit comments

Comments
 (0)