Add result set wrapping for jdbc library instrumentation (#13646)

This commit is contained in:
Lauri Tulmin 2025-04-10 02:28:17 +03:00 committed by GitHub
parent a6dacb3c85
commit 33024dcc64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1441 additions and 34 deletions

View File

@ -12,6 +12,7 @@ import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.sta
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
@ -45,7 +46,7 @@ public class PreparedStatementInstrumentation implements TypeInstrumentation {
public void transform(TypeTransformer transformer) { public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod( transformer.applyAdviceToMethod(
nameStartsWith("execute") nameStartsWith("execute")
.and(not(named("executeBatch"))) .and(not(namedOneOf("executeBatch", "executeLargeBatch")))
.and(takesArguments(0)) .and(takesArguments(0))
.and(isPublic()), .and(isPublic()),
PreparedStatementInstrumentation.class.getName() + "$PreparedStatementAdvice"); PreparedStatementInstrumentation.class.getName() + "$PreparedStatementAdvice");

View File

@ -12,6 +12,7 @@ import static io.opentelemetry.javaagent.instrumentation.jdbc.JdbcSingletons.sta
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
@ -52,7 +53,7 @@ public class StatementInstrumentation implements TypeInstrumentation {
named("clearBatch").and(isPublic()), named("clearBatch").and(isPublic()),
StatementInstrumentation.class.getName() + "$ClearBatchAdvice"); StatementInstrumentation.class.getName() + "$ClearBatchAdvice");
transformer.applyAdviceToMethod( transformer.applyAdviceToMethod(
named("executeBatch").and(takesNoArguments()).and(isPublic()), namedOneOf("executeBatch", "executeLargeBatch").and(takesNoArguments()).and(isPublic()),
StatementInstrumentation.class.getName() + "$ExecuteBatchAdvice"); StatementInstrumentation.class.getName() + "$ExecuteBatchAdvice");
} }

View File

@ -25,6 +25,7 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYST
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -61,6 +62,7 @@ import java.util.stream.Stream;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.derby.jdbc.EmbeddedDataSource; import org.apache.derby.jdbc.EmbeddedDataSource;
import org.apache.derby.jdbc.EmbeddedDriver; import org.apache.derby.jdbc.EmbeddedDriver;
import org.assertj.core.api.ThrowingConsumer;
import org.h2.jdbcx.JdbcDataSource; import org.h2.jdbcx.JdbcDataSource;
import org.hsqldb.jdbc.JDBCDriver; import org.hsqldb.jdbc.JDBCDriver;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
@ -803,10 +805,81 @@ class JdbcInstrumentationTest {
String url, String url,
String table) String table)
throws SQLException { throws SQLException {
testPreparedStatementUpdateImpl(
system,
connection,
username,
query,
spanName,
url,
table,
statement -> assertThat(statement.executeUpdate()).isEqualTo(0));
}
static Stream<Arguments> preparedStatementLargeUpdateStream() throws SQLException {
return Stream.of(
Arguments.of(
"h2",
new org.h2.Driver().connect(jdbcUrls.get("h2"), null),
null,
"CREATE TABLE PS_LARGE_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))",
"CREATE TABLE jdbcunittest.PS_LARGE_H2",
"h2:mem:",
"PS_LARGE_H2"),
Arguments.of(
"h2",
cpDatasources.get("tomcat").get("h2").getConnection(),
null,
"CREATE TABLE PS_LARGE_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))",
"CREATE TABLE jdbcunittest.PS_LARGE_H2_TOMCAT",
"h2:mem:",
"PS_LARGE_H2_TOMCAT"),
Arguments.of(
"h2",
cpDatasources.get("hikari").get("h2").getConnection(),
null,
"CREATE TABLE PS_LARGE_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))",
"CREATE TABLE jdbcunittest.PS_LARGE_H2_HIKARI",
"h2:mem:",
"PS_LARGE_H2_HIKARI"));
}
@ParameterizedTest
@MethodSource("preparedStatementLargeUpdateStream")
void testPreparedStatementLargeUpdate(
String system,
Connection connection,
String username,
String query,
String spanName,
String url,
String table)
throws SQLException {
testPreparedStatementUpdateImpl(
system,
connection,
username,
query,
spanName,
url,
table,
statement -> assertThat(statement.executeLargeUpdate()).isEqualTo(0));
}
void testPreparedStatementUpdateImpl(
String system,
Connection connection,
String username,
String query,
String spanName,
String url,
String table,
ThrowingConsumer<PreparedStatement> action)
throws SQLException {
String sql = connection.nativeSQL(query); String sql = connection.nativeSQL(query);
PreparedStatement statement = connection.prepareStatement(sql); PreparedStatement statement = connection.prepareStatement(sql);
cleanup.deferCleanup(statement); cleanup.deferCleanup(statement);
testing.runWithSpan("parent", () -> assertThat(statement.executeUpdate()).isEqualTo(0)); testing.runWithSpan("parent", () -> action.accept(statement));
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
@ -1333,7 +1406,39 @@ class JdbcInstrumentationTest {
@MethodSource("batchStream") @MethodSource("batchStream")
void testBatch(String system, Connection connection, String username, String url) void testBatch(String system, Connection connection, String username, String url)
throws SQLException { throws SQLException {
String tableName = "simple_batch_test"; testBatchImpl(
system,
connection,
username,
url,
"simple_batch_test",
statement -> assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1}));
}
@ParameterizedTest
@MethodSource("batchStream")
void testLargeBatch(String system, Connection connection, String username, String url)
throws SQLException {
// derby and hsqldb used in this test don't support executeLargeBatch
assumeTrue("h2".equals(system));
testBatchImpl(
system,
connection,
username,
url,
"simple_batch_test_large",
statement -> assertThat(statement.executeLargeBatch()).isEqualTo(new long[] {1, 1}));
}
private static void testBatchImpl(
String system,
Connection connection,
String username,
String url,
String tableName,
ThrowingConsumer<Statement> action)
throws SQLException {
Statement createTable = connection.createStatement(); Statement createTable = connection.createStatement();
createTable.execute("CREATE TABLE " + tableName + " (id INTEGER not NULL, PRIMARY KEY ( id ))"); createTable.execute("CREATE TABLE " + tableName + " (id INTEGER not NULL, PRIMARY KEY ( id ))");
cleanup.deferCleanup(createTable); cleanup.deferCleanup(createTable);
@ -1347,8 +1452,7 @@ class JdbcInstrumentationTest {
statement.clearBatch(); statement.clearBatch();
statement.addBatch("INSERT INTO " + tableName + " VALUES(1)"); statement.addBatch("INSERT INTO " + tableName + " VALUES(1)");
statement.addBatch("INSERT INTO " + tableName + " VALUES(2)"); statement.addBatch("INSERT INTO " + tableName + " VALUES(2)");
testing.runWithSpan( testing.runWithSpan("parent", () -> action.accept(statement));
"parent", () -> assertThat(statement.executeBatch()).isEqualTo(new int[] {1, 1}));
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->

View File

@ -16,6 +16,15 @@ dependencies {
} }
tasks { tasks {
// We cannot use "--release" javac option here because that will forbid using apis that were added
// in later versions. In JDBC wrappers we wish to implement delegation for methods that are not
// present in jdk8.
compileJava {
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
options.release.set(null as Int?)
}
shadowJar { shadowJar {
dependencies { dependencies {
// including only current module excludes its transitive dependencies // including only current module excludes its transitive dependencies

View File

@ -244,7 +244,7 @@ public final class OpenTelemetryDriver implements Driver {
Instrumenter<DbRequest, Void> statementInstrumenter = Instrumenter<DbRequest, Void> statementInstrumenter =
JdbcInstrumenterFactory.createStatementInstrumenter(openTelemetry); JdbcInstrumenterFactory.createStatementInstrumenter(openTelemetry);
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter); return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
} }
@Override @Override

View File

@ -93,14 +93,14 @@ public class OpenTelemetryDataSource implements DataSource, AutoCloseable {
public Connection getConnection() throws SQLException { public Connection getConnection() throws SQLException {
Connection connection = wrapCall(delegate::getConnection); Connection connection = wrapCall(delegate::getConnection);
DbInfo dbInfo = getDbInfo(connection); DbInfo dbInfo = getDbInfo(connection);
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter); return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
} }
@Override @Override
public Connection getConnection(String username, String password) throws SQLException { public Connection getConnection(String username, String password) throws SQLException {
Connection connection = wrapCall(() -> delegate.getConnection(username, password)); Connection connection = wrapCall(() -> delegate.getConnection(username, password));
DbInfo dbInfo = getDbInfo(connection); DbInfo dbInfo = getDbInfo(connection);
return new OpenTelemetryConnection(connection, dbInfo, statementInstrumenter); return OpenTelemetryConnection.create(connection, dbInfo, statementInstrumenter);
} }
@Override @Override

View File

@ -35,17 +35,15 @@ import java.sql.NClob;
import java.sql.Ref; import java.sql.Ref;
import java.sql.RowId; import java.sql.RowId;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.SQLType;
import java.sql.SQLXML; import java.sql.SQLXML;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Calendar; import java.util.Calendar;
import java.util.Map; import java.util.Map;
/** @SuppressWarnings("OverloadMethodsDeclarationOrder")
* This class is internal and is hence not for public use. Its APIs are unstable and can change at class OpenTelemetryCallableStatement<S extends CallableStatement>
* any time.
*/
public class OpenTelemetryCallableStatement<S extends CallableStatement>
extends OpenTelemetryPreparedStatement<S> implements CallableStatement { extends OpenTelemetryPreparedStatement<S> implements CallableStatement {
public OpenTelemetryCallableStatement( public OpenTelemetryCallableStatement(
@ -489,6 +487,7 @@ public class OpenTelemetryCallableStatement<S extends CallableStatement>
delegate.setBinaryStream(parameterName, x); delegate.setBinaryStream(parameterName, x);
} }
@SuppressWarnings("UngroupedOverloads")
@Override @Override
public void setObject(String parameterName, Object x, int targetSqlType, int scale) public void setObject(String parameterName, Object x, int targetSqlType, int scale)
throws SQLException { throws SQLException {
@ -710,4 +709,51 @@ public class OpenTelemetryCallableStatement<S extends CallableStatement>
public Reader getCharacterStream(String parameterName) throws SQLException { public Reader getCharacterStream(String parameterName) throws SQLException {
return delegate.getCharacterStream(parameterName); return delegate.getCharacterStream(parameterName);
} }
// JDBC 4.2
@Override
public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength)
throws SQLException {
delegate.setObject(parameterName, x, targetSqlType, scaleOrLength);
}
@Override
public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException {
delegate.setObject(parameterName, x, targetSqlType);
}
@Override
public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException {
delegate.registerOutParameter(parameterIndex, sqlType);
}
@Override
public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale)
throws SQLException {
delegate.registerOutParameter(parameterIndex, sqlType, scale);
}
@Override
public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName)
throws SQLException {
delegate.registerOutParameter(parameterIndex, sqlType, typeName);
}
@Override
public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException {
delegate.registerOutParameter(parameterName, sqlType);
}
@Override
public void registerOutParameter(String parameterName, SQLType sqlType, int scale)
throws SQLException {
delegate.registerOutParameter(parameterName, sqlType, scale);
}
@Override
public void registerOutParameter(String parameterName, SQLType sqlType, String typeName)
throws SQLException {
delegate.registerOutParameter(parameterName, sqlType, typeName);
}
} }

View File

@ -35,6 +35,7 @@ import java.sql.SQLException;
import java.sql.SQLWarning; import java.sql.SQLWarning;
import java.sql.SQLXML; import java.sql.SQLXML;
import java.sql.Savepoint; import java.sql.Savepoint;
import java.sql.ShardingKey;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Struct; import java.sql.Struct;
import java.util.Map; import java.util.Map;
@ -47,17 +48,36 @@ import java.util.concurrent.Executor;
*/ */
public class OpenTelemetryConnection implements Connection { public class OpenTelemetryConnection implements Connection {
private final Connection delegate; private static final boolean hasJdbc43 = hasJdbc43();
protected final Connection delegate;
private final DbInfo dbInfo; private final DbInfo dbInfo;
protected final Instrumenter<DbRequest, Void> statementInstrumenter; protected final Instrumenter<DbRequest, Void> statementInstrumenter;
public OpenTelemetryConnection( protected OpenTelemetryConnection(
Connection delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> statementInstrumenter) { Connection delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> statementInstrumenter) {
this.delegate = delegate; this.delegate = delegate;
this.dbInfo = dbInfo; this.dbInfo = dbInfo;
this.statementInstrumenter = statementInstrumenter; this.statementInstrumenter = statementInstrumenter;
} }
// visible for testing
static boolean hasJdbc43() {
try {
Class.forName("java.sql.ShardingKey");
return true;
} catch (ClassNotFoundException exception) {
return false;
}
}
public static Connection create(
Connection delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> statementInstrumenter) {
if (hasJdbc43) {
return new OpenTelemetryConnectionJdbc43(delegate, dbInfo, statementInstrumenter);
}
return new OpenTelemetryConnection(delegate, dbInfo, statementInstrumenter);
}
@Override @Override
public Statement createStatement() throws SQLException { public Statement createStatement() throws SQLException {
Statement statement = delegate.createStatement(); Statement statement = delegate.createStatement();
@ -369,4 +389,50 @@ public class OpenTelemetryConnection implements Connection {
public DbInfo getDbInfo() { public DbInfo getDbInfo() {
return dbInfo; return dbInfo;
} }
// JDBC 4.3
static class OpenTelemetryConnectionJdbc43 extends OpenTelemetryConnection {
OpenTelemetryConnectionJdbc43(
Connection delegate, DbInfo dbInfo, Instrumenter<DbRequest, Void> statementInstrumenter) {
super(delegate, dbInfo, statementInstrumenter);
}
@SuppressWarnings("Since15")
@Override
public void beginRequest() throws SQLException {
delegate.beginRequest();
}
@SuppressWarnings("Since15")
@Override
public void endRequest() throws SQLException {
delegate.endRequest();
}
@SuppressWarnings("Since15")
@Override
public boolean setShardingKeyIfValid(
ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLException {
return delegate.setShardingKeyIfValid(shardingKey, superShardingKey, timeout);
}
@SuppressWarnings("Since15")
@Override
public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException {
return delegate.setShardingKeyIfValid(shardingKey, timeout);
}
@SuppressWarnings("Since15")
@Override
public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey)
throws SQLException {
delegate.setShardingKey(shardingKey, superShardingKey);
}
@SuppressWarnings("Since15")
@Override
public void setShardingKey(ShardingKey shardingKey) throws SQLException {
delegate.setShardingKey(shardingKey);
}
}
} }

View File

@ -38,17 +38,15 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.RowId; import java.sql.RowId;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.SQLType;
import java.sql.SQLXML; import java.sql.SQLXML;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Calendar; import java.util.Calendar;
/** @SuppressWarnings("OverloadMethodsDeclarationOrder")
* This class is internal and is hence not for public use. Its APIs are unstable and can change at class OpenTelemetryPreparedStatement<S extends PreparedStatement> extends OpenTelemetryStatement<S>
* any time. implements PreparedStatement {
*/
public class OpenTelemetryPreparedStatement<S extends PreparedStatement>
extends OpenTelemetryStatement<S> implements PreparedStatement {
public OpenTelemetryPreparedStatement( public OpenTelemetryPreparedStatement(
S delegate, S delegate,
@ -61,7 +59,7 @@ public class OpenTelemetryPreparedStatement<S extends PreparedStatement>
@Override @Override
public ResultSet executeQuery() throws SQLException { public ResultSet executeQuery() throws SQLException {
return wrapCall(query, delegate::executeQuery); return new OpenTelemetryResultSet(wrapCall(query, delegate::executeQuery), this);
} }
@Override @Override
@ -374,4 +372,22 @@ public class OpenTelemetryPreparedStatement<S extends PreparedStatement>
DbRequest request = DbRequest.create(dbInfo, query, batchSize); DbRequest request = DbRequest.create(dbInfo, query, batchSize);
return wrapCall(request, callable); return wrapCall(request, callable);
} }
// JDBC 4.2
@Override
public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength)
throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
}
@Override
public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType);
}
@Override
public long executeLargeUpdate() throws SQLException {
return wrapCall(query, delegate::executeLargeUpdate);
}
} }

View File

@ -32,11 +32,7 @@ import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** class OpenTelemetryStatement<S extends Statement> implements Statement {
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public class OpenTelemetryStatement<S extends Statement> implements Statement {
protected final S delegate; protected final S delegate;
protected final OpenTelemetryConnection connection; protected final OpenTelemetryConnection connection;
@ -180,7 +176,7 @@ public class OpenTelemetryStatement<S extends Statement> implements Statement {
@Override @Override
public ResultSet getResultSet() throws SQLException { public ResultSet getResultSet() throws SQLException {
return delegate.getResultSet(); return new OpenTelemetryResultSet(delegate.getResultSet(), this);
} }
@Override @Override
@ -250,7 +246,7 @@ public class OpenTelemetryStatement<S extends Statement> implements Statement {
@Override @Override
public ResultSet getGeneratedKeys() throws SQLException { public ResultSet getGeneratedKeys() throws SQLException {
return delegate.getGeneratedKeys(); return new OpenTelemetryResultSet(delegate.getGeneratedKeys(), this);
} }
@Override @Override
@ -293,6 +289,74 @@ public class OpenTelemetryStatement<S extends Statement> implements Statement {
return delegate.isWrapperFor(iface); return delegate.isWrapperFor(iface);
} }
// JDBC 4.2
@Override
public long getLargeUpdateCount() throws SQLException {
return delegate.getLargeUpdateCount();
}
@Override
public void setLargeMaxRows(long max) throws SQLException {
delegate.setLargeMaxRows(max);
}
@Override
public long getLargeMaxRows() throws SQLException {
return delegate.getLargeMaxRows();
}
@Override
public long[] executeLargeBatch() throws SQLException {
return wrapBatchCall(delegate::executeLargeBatch);
}
@Override
public long executeLargeUpdate(String sql) throws SQLException {
return wrapCall(sql, () -> delegate.executeLargeUpdate(sql));
}
@Override
public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return wrapCall(sql, () -> delegate.executeLargeUpdate(sql, autoGeneratedKeys));
}
@Override
public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
return wrapCall(sql, () -> delegate.executeLargeUpdate(sql, columnIndexes));
}
@Override
public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
return wrapCall(sql, () -> delegate.executeLargeUpdate(sql, columnNames));
}
// JDBC 4.3
@SuppressWarnings("Since15")
@Override
public String enquoteLiteral(String val) throws SQLException {
return delegate.enquoteLiteral(val);
}
@SuppressWarnings("Since15")
@Override
public String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException {
return delegate.enquoteIdentifier(identifier, alwaysQuote);
}
@SuppressWarnings("Since15")
@Override
public boolean isSimpleIdentifier(String identifier) throws SQLException {
return delegate.isSimpleIdentifier(identifier);
}
@SuppressWarnings("Since15")
@Override
public String enquoteNCharLiteral(String val) throws SQLException {
return delegate.enquoteNCharLiteral(val);
}
protected <T, E extends Exception> T wrapCall(String sql, ThrowingSupplier<T, E> callable) protected <T, E extends Exception> T wrapCall(String sql, ThrowingSupplier<T, E> callable)
throws E { throws E {
DbRequest request = DbRequest.create(dbInfo, sql); DbRequest request = DbRequest.create(dbInfo, sql);

View File

@ -128,7 +128,7 @@ public class OpenTelemetryDriverTest {
Connection connection = OpenTelemetryDriver.INSTANCE.connect("jdbc:otel:test:", null); Connection connection = OpenTelemetryDriver.INSTANCE.connect("jdbc:otel:test:", null);
OpenTelemetryDriver.removeDriverCandidate(driver); OpenTelemetryDriver.removeDriverCandidate(driver);
assertThat(connection).isExactlyInstanceOf(OpenTelemetryConnection.class); assertThat(connection).isInstanceOf(OpenTelemetryConnection.class);
} }
@DisplayName("verify remove driver candidate") @DisplayName("verify remove driver candidate")
@ -220,7 +220,7 @@ public class OpenTelemetryDriverTest {
Connection connection2 = OpenTelemetryDriver.INSTANCE.connect("jdbc:otel:test:", null); Connection connection2 = OpenTelemetryDriver.INSTANCE.connect("jdbc:otel:test:", null);
assertThat(connection2).isNotNull(); assertThat(connection2).isNotNull();
assertThat(connection2).isExactlyInstanceOf(OpenTelemetryConnection.class); assertThat(connection2).isInstanceOf(OpenTelemetryConnection.class);
} }
@DisplayName("Verify get property info with test driver url") @DisplayName("Verify get property info with test driver url")

View File

@ -67,7 +67,7 @@ class OpenTelemetryDataSourceTest {
? null ? null
: "postgresql://127.0.0.1:5432")))); : "postgresql://127.0.0.1:5432"))));
assertThat(connection).isExactlyInstanceOf(OpenTelemetryConnection.class); assertThat(connection).isInstanceOf(OpenTelemetryConnection.class);
DbInfo dbInfo = ((OpenTelemetryConnection) connection).getDbInfo(); DbInfo dbInfo = ((OpenTelemetryConnection) connection).getDbInfo();
assertDbInfo(dbInfo); assertDbInfo(dbInfo);
} }
@ -83,7 +83,7 @@ class OpenTelemetryDataSourceTest {
assertThat(testing.waitForTraces(0)).isEmpty(); assertThat(testing.waitForTraces(0)).isEmpty();
assertThat(connection).isExactlyInstanceOf(OpenTelemetryConnection.class); assertThat(connection).isInstanceOf(OpenTelemetryConnection.class);
DbInfo dbInfo = ((OpenTelemetryConnection) connection).getDbInfo(); DbInfo dbInfo = ((OpenTelemetryConnection) connection).getDbInfo();
assertDbInfo(dbInfo); assertDbInfo(dbInfo);
} }

View File

@ -30,6 +30,7 @@ import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -93,6 +94,33 @@ class OpenTelemetryConnectionTest {
"parent", "parent",
() -> { () -> {
assertThat(statement.execute()).isTrue(); assertThat(statement.execute()).isTrue();
ResultSet resultSet = statement.getResultSet();
assertThat(resultSet).isInstanceOf(OpenTelemetryResultSet.class);
assertThat(resultSet.getStatement()).isEqualTo(statement);
});
jdbcTraceAssertion(dbInfo, query);
statement.close();
connection.close();
}
@Test
void testVerifyPrepareStatementQuery() throws SQLException {
Instrumenter<DbRequest, Void> instrumenter =
createStatementInstrumenter(testing.getOpenTelemetry());
DbInfo dbInfo = getDbInfo();
OpenTelemetryConnection connection =
new OpenTelemetryConnection(new TestConnection(), dbInfo, instrumenter);
String query = "SELECT * FROM users";
PreparedStatement statement = connection.prepareStatement(query);
testing.runWithSpan(
"parent",
() -> {
ResultSet resultSet = statement.executeQuery();
assertThat(resultSet).isInstanceOf(OpenTelemetryResultSet.class);
assertThat(resultSet.getStatement()).isEqualTo(statement);
}); });
jdbcTraceAssertion(dbInfo, query); jdbcTraceAssertion(dbInfo, query);

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection.OpenTelemetryConnectionJdbc43;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class WrapperTest {
@Test
void wrapperImplementsAllMethods() throws Exception {
validate(Statement.class, OpenTelemetryStatement.class);
validate(PreparedStatement.class, OpenTelemetryPreparedStatement.class);
validate(CallableStatement.class, OpenTelemetryCallableStatement.class);
validate(
Connection.class,
OpenTelemetryConnection.hasJdbc43()
? OpenTelemetryConnectionJdbc43.class
: OpenTelemetryConnection.class);
validate(ResultSet.class, OpenTelemetryResultSet.class);
}
void validate(Class<?> jdbcClass, Class<?> wrapperClass) throws Exception {
for (Method method : jdbcClass.getMethods()) {
Method result = wrapperClass.getMethod(method.getName(), method.getParameterTypes());
if (!result.getDeclaringClass().getName().startsWith("io.opentelemetry")) {
Assertions.fail(wrapperClass.getName() + " does not override " + method);
}
}
}
}