From e0d46d51472a39ce0c0a698522a91432ed7f0096 Mon Sep 17 00:00:00 2001 From: Naji Astier Date: Fri, 7 Nov 2025 14:28:45 +0100 Subject: [PATCH 1/3] fix JDBC's SQLCommenter not taking into account semicolons After adding the new always append comment feature in #9798, a customer found a bug that was present in our SQL Commenter since a long time. Basically, JDBC always split queries to send to the DB on the semicolon. If a comment is after a semicolon, JDBC will still split the queries, and return multiple ResultSet. This provoked exceptions like this one: ``` org.postgresql.util.PSQLException: Multiple ResultSets were returned by the query ``` SDBM-2100 --- .../trace/instrumentation/jdbc/SQLCommenter.java | 12 +++++++++++- .../jdbc/src/test/groovy/SQLCommenterTest.groovy | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java index d9a409d2c29..1452014f665 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java @@ -103,9 +103,14 @@ public static String inject( StringBuilder sb = new StringBuilder(sql.length() + INJECTED_COMMENT_ESTIMATED_SIZE); if (appendComment) { - sb.append(sql); + if (sql.trim().endsWith(";")) { + sb.append(sql, 0, sql.lastIndexOf(";")); + } else { + sb.append(sql); + } sb.append(SPACE); } + sb.append(OPEN_COMMENT); int initSize = sb.length(); append(sb, PARENT_SERVICE, config.getServiceName(), initSize); @@ -128,6 +133,11 @@ public static String inject( sb.append(SPACE); sb.append(sql); } + + if (appendComment && sql.trim().endsWith(";")) { + sb.append(';'); + } + return sb.toString(); } diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy index 11c6c05d51a..d746ad5f4b4 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy @@ -47,6 +47,10 @@ class SQLCommenterTest extends InstrumentationSpecification { where: query | ddService | ddEnv | dbService | dbType | host | dbName | ddVersion | append | traceParent | expected "SELECT * FROM foo" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" + "SELECT * FROM foo;" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" + "SELECT * FROM foo; " | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" + "SELECT * FROM foo; SELECT * FROM bar" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo; SELECT * FROM bar /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" + "SELECT * FROM foo; SELECT * FROM bar; " | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo; SELECT * FROM bar /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" "SELECT * FROM foo" | "SqlCommenter" | "Test" | "my-service" | "postgres" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" "{call dogshelterProc(?, ?)}" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "{call dogshelterProc(?, ?)} /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" "{call dogshelterProc(?, ?)}" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | false | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "{call dogshelterProc(?, ?)} /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" From d17335dfcffde3b71bf6427bb02318df7568e8db Mon Sep 17 00:00:00 2001 From: Naji Astier Date: Wed, 12 Nov 2025 09:31:53 +0100 Subject: [PATCH 2/3] replace query trimming with manual iteration over query in SQL Commenter --- .../instrumentation/jdbc/SQLCommenter.java | 17 ++++++++++++++++- .../src/test/groovy/SQLCommenterTest.groovy | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java index 1452014f665..7973b56bbc8 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java @@ -103,7 +103,7 @@ public static String inject( StringBuilder sb = new StringBuilder(sql.length() + INJECTED_COMMENT_ESTIMATED_SIZE); if (appendComment) { - if (sql.trim().endsWith(";")) { + if (queryEndsWithSemicolon(sql)) { sb.append(sql, 0, sql.lastIndexOf(";")); } else { sb.append(sql); @@ -208,4 +208,19 @@ private static void append(StringBuilder sb, String key, String value, int initS } sb.append(key).append(EQUALS).append(QUOTE).append(encodedValue).append(QUOTE); } + + private static boolean queryEndsWithSemicolon(String query) { + for (int i = query.length() - 1; i >= 0; i--) { + char c = query.charAt(i); + if (c == ';') { + return true; + } else if (c <= ' ') { + continue; + } + + break; + } + + return false; + } } diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy index d746ad5f4b4..9d843ad8bf5 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/SQLCommenterTest.groovy @@ -48,7 +48,7 @@ class SQLCommenterTest extends InstrumentationSpecification { query | ddService | ddEnv | dbService | dbType | host | dbName | ddVersion | append | traceParent | expected "SELECT * FROM foo" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" "SELECT * FROM foo;" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" - "SELECT * FROM foo; " | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" + "SELECT * FROM foo; \t\n\r" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" "SELECT * FROM foo; SELECT * FROM bar" | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo; SELECT * FROM bar /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" "SELECT * FROM foo; SELECT * FROM bar; " | "SqlCommenter" | "Test" | "my-service" | "mysql" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo; SELECT * FROM bar /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/;" "SELECT * FROM foo" | "SqlCommenter" | "Test" | "my-service" | "postgres" | "h" | "n" | "TestVersion" | true | "00-00000000000000007fffffffffffffff-000000024cb016ea-00" | "SELECT * FROM foo /*ddps='SqlCommenter',dddbs='my-service',ddh='h',dddb='n',dde='Test',ddpv='TestVersion',traceparent='00-00000000000000007fffffffffffffff-000000024cb016ea-00'*/" From f16df987c1952c7332c0b3b46d9f82d21fb369b0 Mon Sep 17 00:00:00 2001 From: Naji Astier Date: Wed, 12 Nov 2025 15:47:22 +0100 Subject: [PATCH 3/3] improve performance of the detection of semicolon in the SQL commenter --- .../instrumentation/jdbc/SQLCommenter.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java index 7973b56bbc8..257fcf26d49 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/SQLCommenter.java @@ -102,9 +102,10 @@ public static String inject( Config config = Config.get(); StringBuilder sb = new StringBuilder(sql.length() + INJECTED_COMMENT_ESTIMATED_SIZE); + int closingSemicolonIndex = indexOfClosingSemicolon(sql); if (appendComment) { - if (queryEndsWithSemicolon(sql)) { - sb.append(sql, 0, sql.lastIndexOf(";")); + if (closingSemicolonIndex > -1) { + sb.append(sql, 0, closingSemicolonIndex); } else { sb.append(sql); } @@ -134,7 +135,7 @@ public static String inject( sb.append(sql); } - if (appendComment && sql.trim().endsWith(";")) { + if (appendComment && closingSemicolonIndex > -1) { sb.append(';'); } @@ -209,18 +210,22 @@ private static void append(StringBuilder sb, String key, String value, int initS sb.append(key).append(EQUALS).append(QUOTE).append(encodedValue).append(QUOTE); } - private static boolean queryEndsWithSemicolon(String query) { + /** + * @param query SQL query + * @return index of the semicolon that ends the query, or -1 if none + */ + private static int indexOfClosingSemicolon(String query) { for (int i = query.length() - 1; i >= 0; i--) { char c = query.charAt(i); if (c == ';') { - return true; - } else if (c <= ' ') { + return i; + } else if (Character.isWhitespace(c)) { continue; } break; } - return false; + return -1; } }