This is an automated email from the ASF dual-hosted git repository.

curth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new b99a17949 feat(csharp): add statement-level trace parent support 
(#3896)
b99a17949 is described below

commit b99a17949debc3d310ccbd3380bbf93dc90a5873
Author: eric-wang-1990 <[email protected]>
AuthorDate: Fri Jan 16 09:45:36 2026 -0800

    feat(csharp): add statement-level trace parent support (#3896)
    
    ## Summary
    
    Enable setting trace parent at the statement level for connection
    pooling scenarios like Power BI, where multiple queries reuse the same
    connection but need different trace IDs for distributed tracing.
    
    ## Changes
    
    - **TracingStatement**: Add `_statementTraceParent` field and
    `SetTraceParent()` method
    - **TracingStatement**: `TraceParent` property now returns statement
    override or falls back to connection's trace parent
    - **HiveServer2Statement**: Add `SetOption` case for
    `AdbcOptions.Telemetry.TraceParent`
    - Add comprehensive test coverage for statement-level trace parent
    functionality
    
    ## Motivation
    
    This brings C# implementation to parity with Go drivers which already
    support statement-level trace parent via `SetOption`.
    
    ### Power BI Use Case
    
    Power BI uses connection pooling where multiple queries reuse the same
    connection. Each Power BI query has its own trace ID for distributed
    tracing correlation. Without statement-level trace parent support, all
    queries from a pooled connection would share the same trace context,
    making it impossible to correlate individual query traces.
    
    With this change, Power BI can:
    ```csharp
    var connection = pool.GetConnection();
    
    // Query 1 with Trace ID A
    var stmt1 = connection.CreateStatement();
    stmt1.SetOption("adbc.telemetry.trace_parent", powerBiQueryTraceIdA);
    stmt1.ExecuteQuery();
    
    // Query 2 with Trace ID B (same connection!)
    var stmt2 = connection.CreateStatement();
    stmt2.SetOption("adbc.telemetry.trace_parent", powerBiQueryTraceIdB);
    stmt2.ExecuteQuery();
    ```
    
    ## Test Plan
    
    Added `CanSetTraceParentOnStatement` test that verifies:
    1. Statement inherits connection's trace parent by default
    2. Statement can override with its own trace parent via `SetOption`
    3. Activities created by the statement use the statement's trace parent
    4. Setting trace parent to null falls back to connection's trace parent
    
    All existing tests pass.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude Sonnet 4.5 <[email protected]>
---
 .../Apache.Arrow.Adbc/Tracing/TracingStatement.cs  |  8 ++-
 .../Drivers/Apache/Hive2/HiveServer2Statement.cs   |  3 +
 .../Tracing/TracingTests.cs                        | 78 ++++++++++++++++++++++
 3 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/csharp/src/Apache.Arrow.Adbc/Tracing/TracingStatement.cs 
b/csharp/src/Apache.Arrow.Adbc/Tracing/TracingStatement.cs
index b2d83aa33..486b15ced 100644
--- a/csharp/src/Apache.Arrow.Adbc/Tracing/TracingStatement.cs
+++ b/csharp/src/Apache.Arrow.Adbc/Tracing/TracingStatement.cs
@@ -22,6 +22,7 @@ namespace Apache.Arrow.Adbc.Tracing
     public abstract class TracingStatement : AdbcStatement, ITracingStatement
     {
         private readonly ActivityTrace _trace;
+        private string? _statementTraceParent;
 
         public TracingStatement(TracingConnection connection)
         {
@@ -30,7 +31,12 @@ namespace Apache.Arrow.Adbc.Tracing
 
         ActivityTrace IActivityTracer.Trace => _trace;
 
-        string? IActivityTracer.TraceParent => _trace.TraceParent;
+        string? IActivityTracer.TraceParent => _statementTraceParent ?? 
_trace.TraceParent;
+
+        protected void SetTraceParent(string? traceParent)
+        {
+            _statementTraceParent = traceParent;
+        }
 
         public abstract string AssemblyVersion { get; }
 
diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs 
b/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
index 25e14f1cc..ea71ecf18 100644
--- a/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
+++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2Statement.cs
@@ -319,6 +319,9 @@ namespace Apache.Arrow.Adbc.Drivers.Apache.Hive2
                         this.EscapePatternWildcards = escapePatternWildcards;
                     }
                     break;
+                case AdbcOptions.Telemetry.TraceParent:
+                    SetTraceParent(string.IsNullOrWhiteSpace(value) ? null : 
value);
+                    break;
                 default:
                     throw AdbcException.NotImplemented($"Option '{key}' is not 
implemented.");
             }
diff --git a/csharp/test/Apache.Arrow.Adbc.Tests/Tracing/TracingTests.cs 
b/csharp/test/Apache.Arrow.Adbc.Tests/Tracing/TracingTests.cs
index b1134c998..021eaed53 100644
--- a/csharp/test/Apache.Arrow.Adbc.Tests/Tracing/TracingTests.cs
+++ b/csharp/test/Apache.Arrow.Adbc.Tests/Tracing/TracingTests.cs
@@ -255,6 +255,57 @@ namespace Apache.Arrow.Adbc.Tests.Tracing
             }
         }
 
+        [Fact]
+        internal void CanSetTraceParentOnStatement()
+        {
+            string activitySourceName = NewName();
+            Queue<Activity> exportedActivities = new();
+            using TracerProvider provider = Sdk.CreateTracerProviderBuilder()
+                .AddSource(activitySourceName)
+                .AddTestActivityQueueExporter(exportedActivities)
+                .Build();
+
+            // Create a connection with a connection-level trace parent
+            const string connectionTraceParent = 
"00-11111111111111111111111111111111-2222222222222222-01";
+            var connection = new MyTracingConnection(
+                new Dictionary<string, string> { { 
AdbcOptions.Telemetry.TraceParent, connectionTraceParent } },
+                activitySourceName);
+
+            // Create a statement and verify it uses the connection's trace 
parent
+            var statement = new MyTracingStatement(connection);
+            Assert.Equal(connectionTraceParent, 
((IActivityTracer)statement).TraceParent);
+
+            // Test 1: Execute with connection's trace parent
+            string eventName1 = NewName();
+            statement.MethodWithActivity(eventName1);
+            Assert.Single(exportedActivities);
+            var activity1 = exportedActivities.First();
+            Assert.Equal(connectionTraceParent, activity1.ParentId);
+
+            // Test 2: Set statement-specific trace parent
+            exportedActivities.Clear();
+            const string statementTraceParent = 
"00-33333333333333333333333333333333-4444444444444444-01";
+            statement.SetOption(AdbcOptions.Telemetry.TraceParent, 
statementTraceParent);
+            Assert.Equal(statementTraceParent, 
((IActivityTracer)statement).TraceParent);
+
+            string eventName2 = NewName();
+            statement.MethodWithActivity(eventName2);
+            Assert.Single(exportedActivities);
+            var activity2 = exportedActivities.First();
+            Assert.Equal(statementTraceParent, activity2.ParentId);
+
+            // Test 3: Set trace parent to null (should fall back to 
connection's trace parent)
+            exportedActivities.Clear();
+            statement.SetOption(AdbcOptions.Telemetry.TraceParent, null);
+            Assert.Equal(connectionTraceParent, 
((IActivityTracer)statement).TraceParent);
+
+            string eventName3 = NewName();
+            statement.MethodWithActivity(eventName3);
+            Assert.Single(exportedActivities);
+            var activity3 = exportedActivities.First();
+            Assert.Equal(connectionTraceParent, activity3.ParentId);
+        }
+
         internal static string NewName() => 
Guid.NewGuid().ToString().Replace("-", "").ToLower();
 
         protected virtual void Dispose(bool disposing)
@@ -451,6 +502,33 @@ namespace Apache.Arrow.Adbc.Tests.Tracing
             public override IArrowArrayStream GetTableTypes() => throw new 
NotImplementedException();
         }
 
+        private class MyTracingStatement(TracingConnection connection) : 
TracingStatement(connection)
+        {
+            public override string AssemblyVersion => "1.0.0";
+            public override string AssemblyName => "TestStatement";
+
+            public void MethodWithActivity(string activityName)
+            {
+                this.TraceActivity(activity =>
+                {
+                    activity?.AddTag("testTag", "testValue");
+                }, activityName);
+            }
+
+            public override void SetOption(string key, string? value)
+            {
+                if (key == AdbcOptions.Telemetry.TraceParent)
+                {
+                    SetTraceParent(string.IsNullOrWhiteSpace(value) ? null : 
value);
+                    return;
+                }
+                throw AdbcException.NotImplemented($"Option '{key}' is not 
implemented.");
+            }
+
+            public override QueryResult ExecuteQuery() => throw new 
NotImplementedException();
+            public override UpdateResult ExecuteUpdate() => throw new 
NotImplementedException();
+        }
+
         internal class ActivityQueueExporter(Queue<Activity> 
exportedActivities) : BaseExporter<Activity>
         {
             private Queue<Activity> ExportedActivities { get; } = 
exportedActivities;

Reply via email to