Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,13 @@ public static String onEnter(
DECORATE.DBM_ALWAYS_APPEND_SQL_COMMENT || "sqlserver".equals(dbInfo.getType());
sql =
SQLCommenter.inject(
sql, dbService, dbInfo.getType(), dbInfo.getHost(), dbInfo.getDb(), null, append);
sql,
dbService,
dbInfo.getType(),
dbInfo.getHost(),
DECORATE.getDbInstance(dbInfo),
null,
append);
return inputSql;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,20 @@ public static DBInfo parseDBInfo(
}

public String getDbService(final DBInfo dbInfo) {
String dbService = null;
if (null != dbInfo) {
dbService = dbService(dbInfo.getType(), dbInstance(dbInfo));
if (null == dbInfo) {
return null;
}
return dbService;
if (dbInfo.getInstance() != null) {
return dbInfo.getInstance();
}
return dbService(dbInfo.getType(), null);
}

public String getDbInstance(final DBInfo dbInfo) {
if (null == dbInfo) {
return null;
}
return dbInstance(dbInfo);
}

public static DBInfo parseDBInfoFromConnection(final Connection connection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig;
import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DBM_TRACE_INJECTED;
import static datadog.trace.instrumentation.jdbc.JDBCDecorator.DATABASE_QUERY;
import static datadog.trace.instrumentation.jdbc.JDBCDecorator.DECORATE;
Expand Down Expand Up @@ -145,13 +146,17 @@ public static AgentScope onEnter(
appendComment = true;
}

String dbService = DECORATE.getDbService(dbInfo);
if (dbService != null) {
dbService = traceConfig(span).getServiceMapping().getOrDefault(dbService, dbService);
}
sql =
SQLCommenter.inject(
sql,
span.getServiceName(),
dbService,
dbInfo.getType(),
dbInfo.getHost(),
dbInfo.getDb(),
DECORATE.getDbInstance(dbInfo),
injectTraceInComment ? traceParent : null,
appendComment);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import datadog.trace.agent.test.InstrumentationSpecification
import datadog.trace.api.config.TraceInstrumentationConfig
import test.TestConnection
import test.TestDatabaseMetaData
import test.TestPreparedStatement
import test.TestStatement

/**
* Tests that Oracle DBM SQL comment injection produces the correct dddbs and dddb tags.
*
* Bug 1: dddbs was populated with the generic type string "oracle" instead of the SID/service name.
* Bug 2: dddb was never injected because the Oracle URL parser sets instance, not db.
*/
abstract class OracleInjectionTestBase extends InstrumentationSpecification {
@Override
void configurePreAgent() {
super.configurePreAgent()

injectSysConfig(TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE, "full")
injectSysConfig("service.name", "my_service_name")
}

static query = "SELECT 1"
// Oracle URL with SID "BENEDB"
static oracleUrl = "jdbc:oracle:thin:@localhost:1521:BENEDB"
// Expected: dddbs should be the SID, not the generic "oracle" type.
// Note: the URL parser lowercases the full URL before extraction, so the SID is lowercase.
static serviceInjection = "ddps='my_service_name',dddbs='benedb',ddh='localhost',dddb='benedb'"

TestConnection createOracleConnection() {
def connection = new TestConnection(false)
def metadata = new TestDatabaseMetaData()
metadata.setURL(oracleUrl)
connection.setMetaData(metadata)
return connection
}
}

class OracleInjectionForkedTest extends OracleInjectionTestBase {

def "Oracle prepared statement injects instance name in dddbs and dddb"() {
setup:
def connection = createOracleConnection()

when:
def statement = connection.prepareStatement(query) as TestPreparedStatement
statement.execute()

then:
// dddbs must be the Oracle SID "BENEDB", not the generic type "oracle"
// dddb must also be present with the SID value
assert statement.sql == "/*${serviceInjection}*/ ${query}"
}

def "Oracle single statement injects instance name in dddbs and dddb"() {
setup:
def connection = createOracleConnection()

when:
def statement = connection.createStatement() as TestStatement
statement.executeQuery(query)

then:
// Oracle uses v$session.action for trace context, so no traceparent in comment
// dddbs must be the Oracle SID "BENEDB", not the generic type "oracle"
// dddb must also be present with the SID value
assert statement.sql == "/*${serviceInjection}*/ ${query}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,42 @@ class RemoteDBMTraceInjectedForkedTest extends RemoteJDBCInstrumentationTest {
final databaseNaming = new DatabaseNamingV1()
return databaseNaming.normalizedName(dbType)
}

def "Oracle DBM comment contains instance name in dddbs and dddb, not generic type string"() {
setup:
// Use a query text unlikely to already be in v$sql cursor cache
def markerQuery = "SELECT 1729 /* oracle-dbm-fix-verify */ FROM dual"
def conn = connectTo(ORACLE, peerConnectionProps(ORACLE))

when:
def stmt = conn.createStatement()
runUnderTrace("parent") {
stmt.execute(markerQuery)
}
TEST_WRITER.waitForTraces(1)

then:
// Connect as system to read v$sql — system shares the same password as the test user
// in the gvenzl/oracle-free image (both are set via ORACLE_PASSWORD).
def adminUrl = "jdbc:oracle:thin:@//${oracle.getHost()}:${oracle.getMappedPort(1521)}/freepdb1"
def adminConn = java.sql.DriverManager.getConnection(adminUrl, "system", oracle.getPassword())
def rs = adminConn.createStatement().executeQuery(
"SELECT sql_fulltext FROM v\$sql " +
"WHERE sql_fulltext LIKE '%1729%' AND sql_fulltext LIKE '%dddbs%' " +
"AND ROWNUM = 1"
)
assert rs.next() : "Instrumented Oracle query not found in v\$sql — DBM comment may be missing"
def sqlText = rs.getString(1)
// dddbs and dddb should both carry the PDB/service name, not the generic "oracle" type string
assert sqlText.contains("dddbs='freepdb1'") : "Expected dddbs='freepdb1' in SQL comment, got: ${sqlText}"
assert sqlText.contains("dddb='freepdb1'") : "Expected dddb='freepdb1' in SQL comment, got: ${sqlText}"
assert !sqlText.contains("dddbs='oracle'") : "dddbs must not be the generic type string 'oracle': ${sqlText}"

cleanup:
adminConn?.close()
stmt?.close()
conn?.close()
}
}

class RemoteDBMTraceInjectedForkedTestTracePreparedStatements extends RemoteJDBCInstrumentationTest {
Expand Down
Loading