Skip to content

Commit f6a12b4

Browse files
swar8080laurit
andauthored
Normalize SQL IN(?, ?, ...) statements to "in(?)" to reduce cardinality of db.statement attribute (#10564)
Co-authored-by: Lauri Tulmin <[email protected]>
1 parent 71a3e22 commit f6a12b4

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.opentelemetry.instrumentation.api.incubator.semconv.db;
77

8+
import java.util.regex.Pattern;
9+
810
%%
911

1012
%final
@@ -52,6 +54,10 @@ WHITESPACE = [ \t\r\n]+
5254
// max length of the sanitized statement - SQLs longer than this will be trimmed
5355
static final int LIMIT = 32 * 1024;
5456

57+
// Match on strings like "IN(?, ?, ...)"
58+
private static final Pattern IN_STATEMENT_PATTERN = Pattern.compile("(\\sIN\\s*)\\(\\s*\\?\\s*(?:,\\s*\\?\\s*)*+\\)", Pattern.CASE_INSENSITIVE);
59+
private static final String IN_STATEMENT_NORMALIZED = "$1(?)";
60+
5561
private final StringBuilder builder = new StringBuilder();
5662

5763
private void appendCurrentFragment() {
@@ -278,7 +284,11 @@ WHITESPACE = [ \t\r\n]+
278284
builder.delete(LIMIT, builder.length());
279285
}
280286
String fullStatement = builder.toString();
281-
return operation.getResult(fullStatement);
287+
288+
// Normalize all 'in (?, ?, ...)' statements to in (?) to reduce cardinality
289+
String normalizedStatement = IN_STATEMENT_PATTERN.matcher(fullStatement).replaceAll(IN_STATEMENT_NORMALIZED);
290+
291+
return operation.getResult(normalizedStatement);
282292
}
283293

284294
%}

instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java

+27-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ void randomBytesDontCauseExceptionsOrTimeouts() {
113113
}
114114
}
115115

116+
@Test
117+
public void longInStatementDoesntCauseStackOverflow() {
118+
StringBuilder s = new StringBuilder("select col from table where col in (");
119+
for (int i = 0; i < 10000; i++) {
120+
s.append("?,");
121+
}
122+
s.append("?)");
123+
124+
String sanitized = SqlStatementSanitizer.create(true).sanitize(s.toString()).getFullStatement();
125+
126+
assertThat(sanitized).isEqualTo("select col from table where col in (?)");
127+
}
128+
116129
static class SqlArgs implements ArgumentsProvider {
117130

118131
@Override
@@ -271,7 +284,11 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
271284
Arguments.of("select col from table1 as t1, table2 as t2", expect("SELECT", null)),
272285
Arguments.of(
273286
"select col from table where col in (1, 2, 3)",
274-
expect("select col from table where col in (?, ?, ?)", "SELECT", "table")),
287+
expect("select col from table where col in (?)", "SELECT", "table")),
288+
Arguments.of(
289+
"select 'a' IN(x, 'b') from table where col in (1) and z IN( '3', '4' )",
290+
expect(
291+
"select ? IN(x, ?) from table where col in (?) and z IN(?)", "SELECT", "table")),
275292
Arguments.of("select col from table order by col, col2", expect("SELECT", "table")),
276293
Arguments.of("select ąś∂ń© from źćļńĶ order by col, col2", expect("SELECT", "źćļńĶ")),
277294
Arguments.of("select 12345678", expect("select ?", "SELECT", null)),
@@ -298,6 +315,9 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
298315
"delete from `my table` where something something", expect("DELETE", "my table")),
299316
Arguments.of(
300317
"delete from \"my table\" where something something", expect("DELETE", "my table")),
318+
Arguments.of(
319+
"delete from foo where x IN (1,2,3)",
320+
expect("delete from foo where x IN (?)", "DELETE", "foo")),
301321
Arguments.of("delete from 12345678", expect("delete from ?", "DELETE", null)),
302322
Arguments.of("delete (((", expect("delete (((", "DELETE", null)),
303323

@@ -307,6 +327,12 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
307327
Arguments.of(
308328
"update `my table` set answer=42",
309329
expect("update `my table` set answer=?", "UPDATE", "my table")),
330+
Arguments.of(
331+
"update `my table` set answer=42 where x IN('a', 'b') AND y In ('a', 'b')",
332+
expect(
333+
"update `my table` set answer=? where x IN(?) AND y In (?)",
334+
"UPDATE",
335+
"my table")),
310336
Arguments.of(
311337
"update \"my table\" set answer=42",
312338
expect("update \"my table\" set answer=?", "UPDATE", "my table")),

0 commit comments

Comments
 (0)