Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib/database/sql: sql comment tag injection experimental feature #1226

Merged
merged 104 commits into from Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
ad88ff6
Add sqlcommenter comments to traced queries
alexandre-normand Mar 17, 2022
58df219
Move comments as query prefix
alexandre-normand Mar 29, 2022
3177df6
Use QueryTextCarrier for propagating tags to query text
alexandre-normand Mar 29, 2022
409dcc3
Fix Query Not Mutated
alexandre-normand Mar 29, 2022
4345bab
Refactor to SQLCommentCarrier
alexandre-normand Mar 30, 2022
3ddbbef
Fix SQLCommentCarrier passed as value instead of pointer
alexandre-normand Mar 30, 2022
ef7a6f6
Add Missing calls to CommentQuery
alexandre-normand Mar 30, 2022
79cd4b2
Move call to inject context into SQL Comments Further Down
alexandre-normand Mar 30, 2022
f2b78d0
Add godoc and move main function to top
alexandre-normand Mar 30, 2022
33a6288
Add hack to set service name and meta tags to sql comments
alexandre-normand Mar 30, 2022
4c45603
Set trace meta as tags
alexandre-normand Mar 30, 2022
44fec1f
Remove unused function
alexandre-normand Mar 30, 2022
df7baa0
DBM-1020 Remove meta info from sql comments and shorten sql comment t…
alexandre-normand Apr 14, 2022
6960385
Add SQL Commenting Injection Mode Feature, Env and Version
alexandre-normand Apr 26, 2022
cc22234
Only inject service name in sql comments when it's not empty
alexandre-normand Apr 27, 2022
8591b70
Fix linting error
alexandre-normand Apr 27, 2022
b728ee3
Fix broken test (bad copy-pasta)
alexandre-normand Apr 27, 2022
5ffca62
Fix linting errors
alexandre-normand Apr 28, 2022
9577c6d
Refactor to handle code overrides
alexandre-normand May 5, 2022
ef456e4
Create new parent version key for propagation of the parent service v…
alexandre-normand Apr 28, 2022
a754c9b
Add explicit guard around sql comment injection
alexandre-normand Apr 28, 2022
0699673
Rename sqlCommentInjectionMode
alexandre-normand Apr 28, 2022
ceab980
Add ExperimentalInjector interface
alexandre-normand May 4, 2022
ba1ee99
Fix broken tests
alexandre-normand May 4, 2022
51b2c2e
Add commenting mode tests
alexandre-normand May 4, 2022
45bd67d
Fix sqlcomment tests
alexandre-normand May 5, 2022
505b865
Keep tryStartTrace only on methods that can do injection
alexandre-normand May 5, 2022
089fa01
Force close rows for more deterministic test
alexandre-normand May 5, 2022
29b3703
Fix typo in defer calls
alexandre-normand May 5, 2022
1d48c37
Fix linting error
alexandre-normand May 5, 2022
d678962
Cleanup of self-review
alexandre-normand May 5, 2022
ae32223
Use tryTrace instead of tryStartTrace on Close
alexandre-normand May 10, 2022
8d86d3f
Implement ForeachKey on SQLCommentCarrier
alexandre-normand May 10, 2022
9a34a17
Use iota for comment injection modes
alexandre-normand May 10, 2022
3d3160e
Add missing dots
alexandre-normand May 10, 2022
3ad8cb9
Rename ExperimentalInjector to WithOptionsInjector
alexandre-normand May 10, 2022
e0a6d43
Revert back to using tryTrace only with sql commenting done separately
alexandre-normand May 10, 2022
40f417f
Update WithOptionsInjector godoc
alexandre-normand May 10, 2022
3ddd663
Update godoc and add leftover TODO task
alexandre-normand May 10, 2022
1fa47fe
Update some godoc with dots
alexandre-normand May 11, 2022
4a5cd0e
Fix tests?
alexandre-normand May 11, 2022
0d7f225
Fix broken sampling priority injection when nil span context
alexandre-normand May 11, 2022
eeb5bce
Fix issue with nil span context when writing parent env and version
alexandre-normand May 12, 2022
a7f1077
Formatting/styling updates
alexandre-normand May 13, 2022
6d1577a
Address some formatting/styling comments
alexandre-normand May 18, 2022
529e2e6
Add Missing godoc
alexandre-normand May 18, 2022
1b449c0
WIP: Refactor SQL Comment Injection to New Propagator and Carrier
alexandre-normand May 25, 2022
ea8872f
Fix sql tests
alexandre-normand May 25, 2022
19a749b
Add propagator configuration for sql comment injection mode
alexandre-normand May 25, 2022
e980168
Add godoc and better sql comment propagator tests
alexandre-normand May 26, 2022
1b08ad0
Formatting / Styling Fixes
alexandre-normand May 26, 2022
3d973d5
Reorganize imports
alexandre-normand May 26, 2022
d70dd5d
Remove obsolete code
alexandre-normand May 26, 2022
25131c0
More reorganization of imports
alexandre-normand May 26, 2022
b1c9579
Remove accidental duplicated line
alexandre-normand May 26, 2022
ecacc6d
Add missing copyright headers
alexandre-normand May 26, 2022
ab995ec
Make SQLCommentPropagator be a no-op on Extract
alexandre-normand May 26, 2022
32a0a31
Fix getPropagators not including SQLCommentPropagator
alexandre-normand May 26, 2022
0f4c463
Revert comma removal
alexandre-normand May 26, 2022
28c1b59
Update ddtrace/tracer/option.go
alexandre-normand May 26, 2022
423a065
Make spancontext.meta thread safe
alexandre-normand May 26, 2022
182b92a
Update comment around setting of ParentVersion
alexandre-normand May 26, 2022
165f6e5
Fix sql comment span id being the parent span id instead of the SQL s…
alexandre-normand May 27, 2022
98730b9
Fix linting error
alexandre-normand May 27, 2022
53f7bff
Update comments to reflect latest implementation
alexandre-normand May 31, 2022
940cca6
Update condition on sql comment injection mode
alexandre-normand May 31, 2022
83db4a1
Update ddtrace/tracer/sqlcomment.go
alexandre-normand Jun 2, 2022
c7aeb0e
Update ddtrace/tracer/sqlcomment.go
alexandre-normand Jun 2, 2022
0cbb92d
Address last PR feedback
alexandre-normand Jun 2, 2022
733dd7b
Remove custom propagator
alexandre-normand Jun 7, 2022
ab44809
WIP: Removed propagator in favor or handling in chainedPropagator
alexandre-normand Jun 7, 2022
7d71b81
Fix linting errors
alexandre-normand Jun 7, 2022
363ab4c
Fix gorm tests
alexandre-normand Jun 7, 2022
a07504f
Fix wrong import order
alexandre-normand Jun 7, 2022
4d7aba0
Gofmt
alexandre-normand Jun 7, 2022
7d3fbb8
Update log statement
alexandre-normand Jun 7, 2022
f8bc080
Misc cleanup
alexandre-normand Jun 7, 2022
83bf1ae
Simplify tests
alexandre-normand Jun 7, 2022
6726b3d
Update contrib/database/sql/conn.go
alexandre-normand Jun 8, 2022
b6b62e7
Update contrib/database/sql/conn.go
alexandre-normand Jun 8, 2022
7993393
Update contrib/database/sql/conn.go
alexandre-normand Jun 8, 2022
738c446
Change sql comment testing strategy
alexandre-normand Jun 9, 2022
e22347a
Inline sqlcommenter implementation
alexandre-normand Jun 9, 2022
3576b56
Fix mode handling bug
alexandre-normand Jun 9, 2022
39b6a53
Add godoc to mockdriver
alexandre-normand Jun 9, 2022
d3605f0
Fix tests
alexandre-normand Jun 9, 2022
452d890
Update contrib/database/sql/conn.go
alexandre-normand Jun 9, 2022
8415c84
Update contrib/database/sql/conn.go
alexandre-normand Jun 9, 2022
7e742dc
Update ddtrace/tracer/sqlcomment.go
alexandre-normand Jun 9, 2022
bd54cb3
Use span id instead of allowing options
alexandre-normand Jun 9, 2022
b351637
Address some but not all comments
alexandre-normand Jun 9, 2022
ffc7af4
Update contrib/database/sql/internal/mockdriver.go
alexandre-normand Jun 9, 2022
42f27e9
Update contrib/database/sql/internal/mockdriver.go
alexandre-normand Jun 9, 2022
2a0dbed
Update ddtrace/tracer/sqlcomment.go
alexandre-normand Jun 9, 2022
cb5a1b8
Rename sqlcomment test cases
alexandre-normand Jun 9, 2022
8a2d5d4
Gofmt mockdriver.go
alexandre-normand Jun 9, 2022
04b59ca
More Renaming
alexandre-normand Jun 9, 2022
3cfb8d0
Add Benchmark and Include an Optimized Implementation of CommentQuery
alexandre-normand Jun 9, 2022
037c0f7
Revert "Use span id instead of allowing options"
alexandre-normand Jun 10, 2022
685ee1f
Address some more comments
alexandre-normand Jun 10, 2022
d39e3fb
Tweak test to increase confidence that sql comment span ids are new
alexandre-normand Jun 10, 2022
6b53e19
Address a few more
alexandre-normand Jun 10, 2022
b92c0ef
Make SQLCommentInjection mode a string
alexandre-normand Jun 10, 2022
3e87cec
Reorder imports
alexandre-normand Jun 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Expand Up @@ -80,5 +80,9 @@ might be running versions different from the vendored one, creating hard to debu
To run integration tests locally, you should set the `INTEGRATION` environment variable. The dependencies of the integration tests are best run via Docker. To get an
idea about the versions and the set-up take a look at our [CI config](./.circleci/config.yml).

The best way to run the entire test suite is using the [CircleCI CLI](https://circleci.com/docs/2.0/local-jobs/). Simply run `circleci build`
in the repository root. Note that you might have to increase the resources dedicated to Docker to around 4GB.
The best way to run the entire test suite is using the [CircleCI CLI](https://circleci.com/docs/2.0/local-cli/). In order to run
alexandre-normand marked this conversation as resolved.
Show resolved Hide resolved
jobs locally, you'll first need to convert the Circle CI configuration to a format accepted by the `circleci` cli tool:
* `circleci config process .circleci/config.yml > process.yml` (from the repository root)

Once you have a converted `process.yml`, simply run `circleci local execute -c process.yml --job <job-name>`.
Note that you might have to increase the resources dedicated to Docker to around 4GB.
65 changes: 48 additions & 17 deletions contrib/database/sql/conn.go
Expand Up @@ -15,6 +15,7 @@ import (
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
)

var _ driver.Conn = (*tracedConn)(nil)
Expand Down Expand Up @@ -58,27 +59,34 @@ func (tc *tracedConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx dr

func (tc *tracedConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
start := time.Now()
mode := tc.cfg.commentInjectionMode
if mode == tracer.SQLInjectionModeFull {
// no context other than service in prepared statements
mode = tracer.SQLInjectionModeService
}
cquery, spanID := injectComments(ctx, query, mode)
if connPrepareCtx, ok := tc.Conn.(driver.ConnPrepareContext); ok {
stmt, err := connPrepareCtx.PrepareContext(ctx, query)
tc.tryTrace(ctx, queryTypePrepare, query, start, err)
stmt, err := connPrepareCtx.PrepareContext(ctx, cquery)
gbbr marked this conversation as resolved.
Show resolved Hide resolved
tc.tryTrace(ctx, queryTypePrepare, query, start, err, tracer.WithSpanID(spanID))
alexandre-normand marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
return &tracedStmt{stmt, tc.traceParams, ctx, query}, nil
return &tracedStmt{Stmt: stmt, traceParams: tc.traceParams, ctx: ctx, query: query}, nil
}
stmt, err = tc.Prepare(query)
tc.tryTrace(ctx, queryTypePrepare, query, start, err)
stmt, err = tc.Prepare(cquery)
tc.tryTrace(ctx, queryTypePrepare, query, start, err, tracer.WithSpanID(spanID))
if err != nil {
return nil, err
}
return &tracedStmt{stmt, tc.traceParams, ctx, query}, nil
return &tracedStmt{Stmt: stmt, traceParams: tc.traceParams, ctx: ctx, query: query}, nil
}

func (tc *tracedConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) {
start := time.Now()
if execContext, ok := tc.Conn.(driver.ExecerContext); ok {
r, err := execContext.ExecContext(ctx, query, args)
tc.tryTrace(ctx, queryTypeExec, query, start, err)
cquery, spanID := injectComments(ctx, query, tc.cfg.commentInjectionMode)
r, err := execContext.ExecContext(ctx, cquery, args)
tc.tryTrace(ctx, queryTypeExec, query, start, err, tracer.WithSpanID(spanID))
return r, err
}
if execer, ok := tc.Conn.(driver.Execer); ok {
Expand All @@ -91,8 +99,9 @@ func (tc *tracedConn) ExecContext(ctx context.Context, query string, args []driv
return nil, ctx.Err()
default:
}
r, err = execer.Exec(query, dargs)
tc.tryTrace(ctx, queryTypeExec, query, start, err)
cquery, spanID := injectComments(ctx, query, tc.cfg.commentInjectionMode)
r, err = execer.Exec(cquery, dargs)
tc.tryTrace(ctx, queryTypeExec, query, start, err, tracer.WithSpanID(spanID))
return r, err
}
return nil, driver.ErrSkip
Expand All @@ -111,8 +120,9 @@ func (tc *tracedConn) Ping(ctx context.Context) (err error) {
func (tc *tracedConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) {
start := time.Now()
if queryerContext, ok := tc.Conn.(driver.QueryerContext); ok {
rows, err := queryerContext.QueryContext(ctx, query, args)
tc.tryTrace(ctx, queryTypeQuery, query, start, err)
cquery, spanID := injectComments(ctx, query, tc.cfg.commentInjectionMode)
rows, err := queryerContext.QueryContext(ctx, cquery, args)
tc.tryTrace(ctx, queryTypeQuery, query, start, err, tracer.WithSpanID(spanID))
return rows, err
}
if queryer, ok := tc.Conn.(driver.Queryer); ok {
Expand All @@ -125,8 +135,9 @@ func (tc *tracedConn) QueryContext(ctx context.Context, query string, args []dri
return nil, ctx.Err()
default:
}
rows, err = queryer.Query(query, dargs)
tc.tryTrace(ctx, queryTypeQuery, query, start, err)
cquery, spanID := injectComments(ctx, query, tc.cfg.commentInjectionMode)
rows, err = queryer.Query(cquery, dargs)
tc.tryTrace(ctx, queryTypeQuery, query, start, err, tracer.WithSpanID(spanID))
return rows, err
}
return nil, driver.ErrSkip
Expand Down Expand Up @@ -167,8 +178,28 @@ func WithSpanTags(ctx context.Context, tags map[string]string) context.Context {
return context.WithValue(ctx, spanTagsKey, tags)
}

// injectComments returns the query with SQL comments injected according to the comment injection mode along
// with a span ID injected into SQL comments. The returned span ID should be used when the SQL span is created
// following the traced database call.
func injectComments(ctx context.Context, query string, mode tracer.SQLCommentInjectionMode) (cquery string, spanID uint64) {
// The sql span only gets created after the call to the database because we need to be able to skip spans
// when a driver returns driver.ErrSkip. In order to work with those constraints, a new span id is generated and
// used during SQL comment injection and returned for the sql span to be used later when/if the span
// gets created.
var spanCtx ddtrace.SpanContext
if span, ok := tracer.SpanFromContext(ctx); ok {
spanCtx = span.Context()
gbbr marked this conversation as resolved.
Show resolved Hide resolved
}
carrier := tracer.SQLCommentCarrier{Query: query, Mode: mode}
if err := carrier.Inject(spanCtx); err != nil {
// this should never happen
log.Warn("contrib/database/sql: failed to inject query comments: %v", err)
}
return carrier.Query, carrier.SpanID
}

// tryTrace will create a span using the given arguments, but will act as a no-op when err is driver.ErrSkip.
func (tp *traceParams) tryTrace(ctx context.Context, qtype queryType, query string, startTime time.Time, err error) {
func (tp *traceParams) tryTrace(ctx context.Context, qtype queryType, query string, startTime time.Time, err error, spanOpts ...ddtrace.StartSpanOption) {
if err == driver.ErrSkip {
gbbr marked this conversation as resolved.
Show resolved Hide resolved
// Not a user error: driver is telling sql package that an
// optional interface method is not implemented. There is
Expand All @@ -180,11 +211,11 @@ func (tp *traceParams) tryTrace(ctx context.Context, qtype queryType, query stri
return
}
name := fmt.Sprintf("%s.query", tp.driverName)
opts := []ddtrace.StartSpanOption{
opts := append(spanOpts,
tracer.ServiceName(tp.cfg.serviceName),
tracer.SpanType(ext.SpanTypeSQL),
tracer.StartTime(startTime),
}
)
if !math.IsNaN(tp.cfg.analyticsRate) {
opts = append(opts, tracer.Tag(ext.EventSampleRate, tp.cfg.analyticsRate))
}
Expand Down
169 changes: 169 additions & 0 deletions contrib/database/sql/injection_test.go
@@ -0,0 +1,169 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.

package sql

import (
"context"
"database/sql"
"regexp"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql/internal"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

func TestCommentInjection(t *testing.T) {
testCases := []struct {
name string
opts []RegisterOption
callDB func(ctx context.Context, db *sql.DB) error
prepared []string
executed []*regexp.Regexp
}{
{
name: "prepare",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.PrepareContext(ctx, "SELECT 1 from DUAL")
return err
},
prepared: []string{"SELECT 1 from DUAL"},
},
{
name: "prepare-disabled",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.PrepareContext(ctx, "SELECT 1 from DUAL")
return err
},
prepared: []string{"SELECT 1 from DUAL"},
},
{
name: "prepare-service",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeService)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.PrepareContext(ctx, "SELECT 1 from DUAL")
return err
},
prepared: []string{"/*dde='test-env',ddsn='test-service',ddsv='1.0.0'*/ SELECT 1 from DUAL"},
},
{
name: "prepare-full",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeFull)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.PrepareContext(ctx, "SELECT 1 from DUAL")
return err
},
prepared: []string{"/*dde='test-env',ddsn='test-service',ddsv='1.0.0'*/ SELECT 1 from DUAL"},
},
{
name: "query",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.QueryContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("SELECT 1 from DUAL")},
},
{
name: "query-disabled",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.QueryContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("SELECT 1 from DUAL")},
},
{
name: "query-service",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeService)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.QueryContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("/\\*dde='test-env',ddsn='test-service',ddsv='1.0.0'\\*/ SELECT 1 from DUAL")},
},
{
name: "query-full",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeFull)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.QueryContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("/\\*dde='test-env',ddsid='\\d+',ddsn='test-service',ddsp='1',ddsv='1.0.0',ddtid='1'\\*/ SELECT 1 from DUAL")},
},
{
name: "exec",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("SELECT 1 from DUAL")},
},
{
name: "exec-disabled",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionDisabled)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("SELECT 1 from DUAL")},
},
{
name: "exec-service",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeService)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("/\\*dde='test-env',ddsn='test-service',ddsv='1.0.0'\\*/ SELECT 1 from DUAL")},
},
{
name: "exec-full",
opts: []RegisterOption{WithSQLCommentInjection(tracer.SQLInjectionModeFull)},
callDB: func(ctx context.Context, db *sql.DB) error {
_, err := db.ExecContext(ctx, "SELECT 1 from DUAL")
return err
},
executed: []*regexp.Regexp{regexp.MustCompile("/\\*dde='test-env',ddsid='\\d+',ddsn='test-service',ddsp='1',ddsv='1.0.0',ddtid='1'\\*/ SELECT 1 from DUAL")},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tracer.Start(tracer.WithService("test-service"), tracer.WithEnv("test-env"), tracer.WithServiceVersion("1.0.0"))
defer tracer.Stop()

d := &internal.MockDriver{}
Register("test", d, tc.opts...)
defer unregister("test")

db, err := Open("test", "dn")
require.NoError(t, err)

s, ctx := tracer.StartSpanFromContext(context.Background(), "test.call", tracer.WithSpanID(1))
err = tc.callDB(ctx, db)
s.Finish()

require.NoError(t, err)
require.Len(t, d.Prepared, len(tc.prepared))
for i, e := range tc.prepared {
assert.Equal(t, e, d.Prepared[i])
}

require.Len(t, d.Executed, len(tc.executed))
for i, e := range tc.executed {
assert.Regexp(t, e, d.Executed[i])
// the injected span ID should not be the parent's span ID
assert.NotContains(t, d.Executed[i], "ddsid='1'")
}
})
}
}