Files
wehub-resource-sync e04ed9c211
CF: Deploy Dev Docs / deploy (push) Has been cancelled
Sync Labels / build (push) Has been cancelled
tests / unit tests (macos-latest) (push) Has been cancelled
tests / unit tests (windows-latest) (push) Has been cancelled
tests / unit tests (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:32:45 +08:00

128 lines
3.7 KiB
Go

// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sqlcommenter
import (
"context"
"fmt"
"net/url"
"sort"
"strings"
"github.com/googleapis/mcp-toolbox/internal/util"
"go.opentelemetry.io/otel/trace"
)
// PrependComment prepends a SQLCommenter-format comment to the given SQL statement.
// It gathers attributes from the context (trace, server, client, tool metadata)
// and the provided dbSystemName, then prepends them as key='value' pairs sorted
// alphabetically.
//
// sourceOverride is the per-source `sqlCommenter` setting from tools.yaml. When
// non-nil it takes priority over the global sql-commenter flag; when nil the
// global flag (from context) is used.
func PrependComment(ctx context.Context, statement string, dbSystemName string, sourceOverride *bool) string {
// Per-source config wins when set; otherwise fall back to the global flag.
enabled := util.SQLCommenterEnabledFromContext(ctx)
if sourceOverride != nil {
enabled = *sourceOverride
}
// Only prepend SQL comments when sql-commenter is enabled
if !enabled {
return statement
}
pairs := collectAttributes(ctx, dbSystemName)
if len(pairs) == 0 {
return statement
}
// Sort keys alphabetically
keys := make([]string, 0, len(pairs))
for k := range pairs {
keys = append(keys, k)
}
sort.Strings(keys)
// Build comment in SQLCommenter format: key='url_encoded_value'
parts := make([]string, 0, len(keys))
for _, k := range keys {
encodedKey := url.QueryEscape(k)
encodedVal := url.QueryEscape(pairs[k])
parts = append(parts, fmt.Sprintf("%s='%s'", encodedKey, encodedVal))
}
comment := strings.Join(parts, ",")
return "/*" + comment + "*/ " + statement
}
// collectAttributes gathers all available SQLCommenter attributes from context.
func collectAttributes(ctx context.Context, dbSystemName string) map[string]string {
attrs := make(map[string]string)
// traceparent from OTel span context
spanCtx := trace.SpanFromContext(ctx).SpanContext()
if spanCtx.IsValid() {
traceparent := fmt.Sprintf("00-%s-%s-%s",
spanCtx.TraceID().String(),
spanCtx.SpanID().String(),
spanCtx.TraceFlags().String(),
)
attrs["traceparent"] = traceparent
}
// server from UserAgent context
if ua, err := util.UserAgentFromContext(ctx); err == nil && ua != "" {
attrs["server"] = ua
}
// db.system.name from parameter
if dbSystemName != "" {
attrs["db.system.name"] = dbSystemName
}
// tool.name from GenAIMetricAttrs
if genAI := util.GenAIMetricAttrsFromContext(ctx); genAI != nil {
if genAI.ToolName != "" {
attrs["tool.name"] = genAI.ToolName
}
}
// Client attributes from TelemetryAttributes
if ta := util.TelemetryAttributesFromContext(ctx); ta != nil {
// Combined client = name/version
if ta.ClientName != "" && ta.ClientVersion != "" {
attrs["client"] = ta.ClientName + "/" + ta.ClientVersion
} else if ta.ClientName != "" {
attrs["client"] = ta.ClientName
} else if ta.ClientVersion != "" {
attrs["client"] = ta.ClientVersion
}
if ta.ClientModel != "" {
attrs["client.model"] = ta.ClientModel
}
if ta.ClientUserID != "" {
attrs["client.user.id"] = ta.ClientUserID
}
if ta.ClientAgentID != "" {
attrs["client.agent.id"] = ta.ClientAgentID
}
}
return attrs
}