Skip to content

Commit

Permalink
feat: Oracle instrumentation now supports latest version (#2721)
Browse files Browse the repository at this point in the history
  • Loading branch information
tippmar-nr authored Aug 29, 2024
1 parent 1acb6e0 commit 50cb663
Show file tree
Hide file tree
Showing 9 changed files with 660 additions and 396 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,30 @@ SPDX-License-Identifier: Apache-2.0
<exactMethodMatcher methodName="ExecuteXmlReader" />
</match>

<!-- Oracle vendor driver for ManagedDataAccess -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand">
<!-- Oracle vendor driver for ManagedDataAccess packages targeting net462 -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand" maxVersion="4.122.23">
<exactMethodMatcher methodName="ExecuteReader" parameters="System.Boolean,System.Boolean,System.Data.CommandBehavior" />
<exactMethodMatcher methodName="ExecuteNonQuery" />
<exactMethodMatcher methodName="ExecuteScalar" />
<exactMethodMatcher methodName="ExecuteXmlReader" />
</match>

<!-- Oracle vendor driver for ManagedDataAccess.Core packages targeting net6.0 and later (yes, they use the same assembly name for core and fw) -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand" minVersion="23.0.0">
<exactMethodMatcher methodName="ExecuteReaderInternal" parameters="System.Boolean,System.Boolean,System.Data.CommandBehavior,System.Boolean" />
<exactMethodMatcher methodName="ExecuteNonQuery" />
<exactMethodMatcher methodName="ExecuteScalar" />
<exactMethodMatcher methodName="ExecuteXmlReader" />
</match>

<!-- Oracle vendor driver for ManagedDataAccess packages targeting net472+ -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand" minVersion="4.122.23">
<exactMethodMatcher methodName="ExecuteReaderInternal" parameters="System.Boolean,System.Boolean,System.Data.CommandBehavior,System.Boolean" />
<exactMethodMatcher methodName="ExecuteNonQuery" />
<exactMethodMatcher methodName="ExecuteScalar" />
<exactMethodMatcher methodName="ExecuteXmlReader" />
</match>

<!-- Oracle Devart driver -->
<match assemblyName="Devart.Data.Oracle" className="Devart.Data.Oracle.OracleCommand">
<exactMethodMatcher methodName="ExecuteReader" parameters="System.Data.CommandBehavior" />
Expand Down Expand Up @@ -196,6 +212,32 @@ SPDX-License-Identifier: Apache-2.0
<match assemblyName="Npgsql" className="Npgsql.NpgsqlCommand" minVersion="4.1">
<exactMethodMatcher methodName="ExecuteReaderAsync"/>
</match>

<!-- Oracle vendor driver for ManagedDataAccess.Core packages targeting net6.0 and later (yes, they use the same assembly name for core and fw) -->
<!-- We have to instrument every overload because they all call a "helper" method that returns a ValueTask -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand" minVersion="23.0.0">
<exactMethodMatcher methodName="ExecuteReaderAsync" />
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Data.CommandBehavior" />
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Data.CommandBehavior,System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="ExecuteNonQueryAsync" parameters="System.Threading.CancellationToken" />
<exactMethodMatcher methodName="ExecuteScalarAsync" parameters="System.Threading.CancellationToken" />
<exactMethodMatcher methodName="ExecuteXmlReaderAsync" />
<exactMethodMatcher methodName="ExecuteXmlReaderAsync" parameters="System.Threading.CancellationToken"/>
</match>

<!-- Oracle vendor driver for ManagedDataAccess packages targeting net472+ -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleCommand" minVersion="4.122.23">
<exactMethodMatcher methodName="ExecuteReaderAsync" />
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Data.CommandBehavior" />
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="ExecuteReaderAsync" parameters="System.Data.CommandBehavior,System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="ExecuteNonQueryAsync" parameters="System.Threading.CancellationToken" />
<exactMethodMatcher methodName="ExecuteScalarAsync" parameters="System.Threading.CancellationToken" />
<exactMethodMatcher methodName="ExecuteXmlReaderAsync" />
<exactMethodMatcher methodName="ExecuteXmlReaderAsync" parameters="System.Threading.CancellationToken"/>
</match>

</tracerFactory>

<!-- DataReader methods. DISABLED by default due to possible performance impact. Set enabled to "true" (or omit completely) to enable this instrumentation. -->
Expand Down Expand Up @@ -329,6 +371,19 @@ SPDX-License-Identifier: Apache-2.0
<exactMethodMatcher methodName="Read" parameters="System.Boolean,System.Threading.CancellationToken" />
<exactMethodMatcher methodName="TryFastRead" />
</match>

<!-- Oracle vendor driver for ManagedDataAccess.Core packages targeting net6.0 and later -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleDataReader" minVersion="23.0.0">
<exactMethodMatcher methodName="ReadAsync" parameters="System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="NextResultAsync" parameters="System.Threading.CancellationToken"/>
</match>

<!-- Oracle vendor driver for ManagedDataAccess packages targeting net472+ -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleDataReader" minVersion="4.122.23">
<exactMethodMatcher methodName="ReadAsync" parameters="System.Threading.CancellationToken"/>
<exactMethodMatcher methodName="NextResultAsync" parameters="System.Threading.CancellationToken"/>
</match>

</tracerFactory>


Expand Down Expand Up @@ -359,9 +414,10 @@ SPDX-License-Identifier: Apache-2.0
<exactMethodMatcher methodName="Open"/>
</match>

<!-- Oracle vendor driver for ManagedDataAccess -->
<!-- Oracle vendor driver for ManagedDataAccess, all versions (though some don't have OpenAsync) -->
<match assemblyName="Oracle.ManagedDataAccess" className="Oracle.ManagedDataAccess.Client.OracleConnection">
<exactMethodMatcher methodName="Open"/>
<exactMethodMatcher methodName="OpenAsync" parameters="System.Threading.CancellationToken"/>
</match>

<!-- MySql (official) driver -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
<!-- NServiceBus v9+ only supports .NET8+, so constraint FW target to the latest 8.x version -->
<PackageReference Include="NServiceBus" Version="8.2.2" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="NServiceBus" Version="9.1.1" Condition="'$(TargetFramework)' == 'net8.0'" />


<!-- modern oracle only supports net472+ and net6.0+ -->
<PackageReference Include="Oracle.ManagedDataAccess" Version="23.5.1" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.5.1" Condition="'$(TargetFramework)' == 'net8.0'" />

<PackageReference Include="RabbitMQ.Client" Version="6.8.1" Condition="'$(TargetFramework)' == 'net481'" />
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" Condition="'$(TargetFramework)' == 'net8.0'" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@
<!-- MySqlConnector .NET/Core references -->
<PackageReference Include="MySqlConnector" Version="1.0.1" Condition="'$(TargetFramework)' == 'net6.0'" />

<!-- Oracle.ManagedDataAccess references -->
<PackageReference Include="Oracle.ManagedDataAccess" Version="12.1.2400" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="Oracle.ManagedDataAccess" Version="21.15.0" Condition="'$(TargetFramework)' == 'net471'" />
<!-- oracle 23.x+ only supports net472+ and net6.0+ -->
<PackageReference Include="Oracle.ManagedDataAccess" Version="23.4.0" Condition="'$(TargetFramework)' == 'net48'" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.4.0" Condition="'$(TargetFramework)' == 'net6.0'" />

<!-- StackExchange.Redis framework references -->
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.1.608" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="StackExchange.Redis" Version="2.0.601" Condition="'$(TargetFramework)' == 'net471'" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright 2020 New Relic, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

using System;
using OracleConfiguration = NewRelic.Agent.IntegrationTests.Shared.OracleConfiguration;
using Oracle.ManagedDataAccess.Client;
using System.Data;
using System.Runtime.CompilerServices;
using NewRelic.Agent.IntegrationTests.Shared.ReflectionHelpers;
using NewRelic.Api.Agent;
using System.Linq;
using System.Threading.Tasks;
using NewRelic.Agent.IntegrationTests.Shared;


namespace MultiFunctionApplicationHelpers.NetStandardLibraries.Oracle
{
[Library]
public class OracleExerciser : IDisposable
{
private const string CreateHotelTableOracleSql = "CREATE TABLE {0} (HOTEL_ID INT NOT NULL, BOOKING_DATE DATE NOT NULL, " +
"ROOMS_TAKEN INT DEFAULT 0, PRIMARY KEY (HOTEL_ID, BOOKING_DATE))";
private const string DropHotelTableOracleSql = "DROP TABLE {0}";
private const string InsertHotelOracleSql = "INSERT INTO {0} (HOTEL_ID, BOOKING_DATE) VALUES (1, SYSDATE)";
private const string DeleteHotelOracleSql = "DELETE FROM {0} WHERE HOTEL_ID = 1";
private const string CountHotelOracleSql = "SELECT COUNT(*) FROM {0}";
private const string SelectFromUserTablesOracleSql = "SELECT DEGREE FROM user_tables WHERE ROWNUM <= 1";


private string _tableName;
private string _storedProcedureName;

[LibraryMethod]
public void InitializeTable(string tableName)
{
_tableName = tableName;
CreateTable();
}

[LibraryMethod]
public void InitializeStoredProcedure(string storedProcName)
{
_storedProcedureName = storedProcName;
CreateProcedure();
}

[LibraryMethod]
[Transaction]
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public void ExerciseSync()
{
if (string.IsNullOrEmpty(_tableName))
throw new Exception("Initialize table before exercising.");

var connectionString = OracleConfiguration.OracleConnectionString;

using var connection = new OracleConnection(connectionString);
connection.Open();

using (var command = new OracleCommand(SelectFromUserTablesOracleSql, connection))
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var foo = reader.GetString(reader.GetOrdinal("DEGREE"));
}
}

var insertSql = string.Format(InsertHotelOracleSql, _tableName);
var countSql = string.Format(CountHotelOracleSql, _tableName);
var deleteSql = string.Format(DeleteHotelOracleSql, _tableName);

using (var command = new OracleCommand(insertSql, connection))
{
var insertCount = command.ExecuteNonQuery();
}

using (var command = new OracleCommand(countSql, connection))
{
var hotelCount = command.ExecuteScalar();
}

using (var command = new OracleCommand(deleteSql, connection))
{
var deleteCount = command.ExecuteNonQuery();
}
}

[LibraryMethod]
[Transaction]
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public async Task ExerciseAsync()
{
if (string.IsNullOrEmpty(_tableName))
throw new Exception("Initialize table before exercising.");

var connectionString = OracleConfiguration.OracleConnectionString;

using var connection = new OracleConnection(connectionString);
await connection.OpenAsync();

using (var command = new OracleCommand(SelectFromUserTablesOracleSql, connection))
using (var reader = await command.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
var foo = reader.GetString(reader.GetOrdinal("DEGREE"));
}
}

var insertSql = string.Format(InsertHotelOracleSql, _tableName);
var countSql = string.Format(CountHotelOracleSql, _tableName);
var deleteSql = string.Format(DeleteHotelOracleSql, _tableName);

using (var command = new OracleCommand(insertSql, connection))
{
var insertCount = await command.ExecuteNonQueryAsync();
}

using (var command = new OracleCommand(countSql, connection))
{
var hotelCount = await command.ExecuteScalarAsync();
}

using (var command = new OracleCommand(deleteSql, connection))
{
var deleteCount = await command.ExecuteNonQueryAsync();
}
}

[LibraryMethod]
[Transaction]
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public async Task ExerciseStoredProcedure()
{
if (string.IsNullOrEmpty(_storedProcedureName))
throw new Exception("Initialize stored procedure before exercising.");

using (var connection = new OracleConnection(OracleConfiguration.OracleConnectionString))
{
using var command = new OracleCommand(_storedProcedureName, connection);

await connection.OpenAsync();

command.CommandType = CommandType.StoredProcedure;

foreach (var p in DbParameterData.OracleParameters)
{
command.Parameters.Add(p.ParameterName, p.Value);
}

await command.ExecuteNonQueryAsync();
}
}

private void CreateTable()
{
var createTable = string.Format(CreateHotelTableOracleSql, _tableName);

var connectionString = OracleConfiguration.OracleConnectionString;

using var connection = new OracleConnection(connectionString);
connection.Open();

using var command = new OracleCommand(createTable, connection);
command.ExecuteNonQuery();
}

private void DropTable()
{
if (!string.IsNullOrEmpty(_tableName))
{
var dropTableSql = string.Format(DropHotelTableOracleSql, _tableName);

using var connection = new OracleConnection(OracleConfiguration.OracleConnectionString);
connection.Open();

using var command = new OracleCommand(dropTableSql, connection);
command.ExecuteNonQuery();
}
}

public void Dispose()
{
DropTable();
DropProcedure();
_tableName = null;
_storedProcedureName = null;
}

private readonly string createProcedureStatement = @"CREATE PROCEDURE {0} ({1}) IS BEGIN NULL; END {0};";
private readonly string dropProcedureStatement = @"DROP PROCEDURE {0}";

private void CreateProcedure()
{
var parameters = string.Join(", ", DbParameterData.OracleParameters.Select(x => $"{x.ParameterName} IN {x.DbTypeName}"));
var statement = string.Format(createProcedureStatement, _storedProcedureName, parameters);
using (var connection = new OracleConnection(OracleConfiguration.OracleConnectionString))
using (var command = new OracleCommand(statement, connection))
{
connection.Open();
command.ExecuteNonQuery();
}
}

private void DropProcedure()
{
if (!string.IsNullOrEmpty(_storedProcedureName))
{
var statement = string.Format(dropProcedureStatement, _storedProcedureName);
using (var connection = new OracleConnection(OracleConfiguration.OracleConnectionString))
using (var command = new OracleCommand(statement, connection))
{
connection.Open();
command.ExecuteNonQuery();
}
}
}
}
}
Loading

0 comments on commit 50cb663

Please sign in to comment.