66#include "access/xact.h"
77#include "utils/guc.h"
88#include "utils/builtins.h"
9+ #include "tcop/tcopprot.h"
910
1011extern PGDLLEXPORT void _PG_init (void );
1112
@@ -16,6 +17,11 @@ static char *convertUUID(char *uuid);
1617static void pgfdw_xact_callback (XactEvent event , void * arg );
1718static void exitHook (int code , Datum arg );
1819
20+ /* Trace context extraction functions */
21+ static char * extractTraceContextFromSession (void );
22+ static char * extractTraceContextFromQueryComments (void );
23+ static char * extractTraceContext (void );
24+
1925void * serializePlanState (FdwPlanState * state );
2026FdwExecState * initializeExecState (void * internalstate );
2127// Required by postgres, doing basic checks to ensure compatibility,
@@ -103,29 +109,154 @@ static char *extractTraceContextFromSession(void)
103109 const char * traceparent = GetConfigOption ("steampipe.traceparent" , true, false);
104110 const char * tracestate = GetConfigOption ("steampipe.tracestate" , true, false);
105111 char * result = NULL ;
106-
112+
107113 // Format the result string for Go layer consumption
108114 if (traceparent != NULL ) {
109115 if (tracestate != NULL ) {
110116 result = psprintf ("traceparent=%s;tracestate=%s" , traceparent , tracestate );
111117 } else {
112118 result = psprintf ("traceparent=%s" , traceparent );
113119 }
114-
120+
115121 elog (DEBUG1 , "extracted trace context: %s" , result );
116122 } else {
117123 elog (DEBUG2 , "no trace context found in session variables" );
118124 }
119-
125+
120126 return result ;
121127}
122128
123129/*
124- * Public wrapper for extractTraceContextFromSession - callable from Go
130+ * Extract OpenTelemetry trace context from SQL query comments (SQLcommenter format)
131+ * Parses comments like: /*traceparent='00-...',tracestate='rojo=...'*\/
132+ * Returns a formatted string containing traceparent and tracestate, or NULL if not found
125133 */
126- char * getTraceContextFromSession (void )
134+ static char * extractTraceContextFromQueryComments (void )
127135{
128- return extractTraceContextFromSession ();
136+ const char * query_string = debug_query_string ;
137+ char * result = NULL ;
138+ char * traceparent = NULL ;
139+ char * tracestate = NULL ;
140+
141+ if (query_string == NULL ) {
142+ elog (DEBUG2 , "no query string available for SQLcommenter parsing" );
143+ return NULL ;
144+ }
145+
146+ elog (DEBUG2 , "parsing SQLcommenter from query: %.100s..." , query_string );
147+
148+ // Look for SQL comments in the format /*...*/
149+ const char * comment_start = strstr (query_string , "/*" );
150+ while (comment_start != NULL ) {
151+ const char * comment_end = strstr (comment_start , "*/" );
152+ if (comment_end == NULL ) {
153+ break ; // Malformed comment, skip
154+ }
155+
156+ // Extract the comment content
157+ size_t comment_len = comment_end - comment_start - 2 ; // Exclude /* and */
158+ char * comment_content = palloc (comment_len + 1 );
159+ strncpy (comment_content , comment_start + 2 , comment_len );
160+ comment_content [comment_len ] = '\0' ;
161+
162+ elog (DEBUG2 , "found SQL comment: %s" , comment_content );
163+
164+ // Parse key-value pairs in the comment
165+ char * token = strtok (comment_content , "," );
166+ while (token != NULL ) {
167+ // Trim whitespace
168+ while (* token == ' ' || * token == '\t' ) token ++ ;
169+
170+ // Look for traceparent or tracestate
171+ if (strncmp (token , "traceparent=" , 12 ) == 0 ) {
172+ char * value = token + 12 ;
173+ // Remove quotes if present
174+ if (* value == '\'' || * value == '"' ) {
175+ char quote_char = * value ;
176+ value ++ ;
177+ char * end_quote = strrchr (value , quote_char );
178+ if (end_quote ) * end_quote = '\0' ;
179+ }
180+ if (traceparent ) pfree (traceparent );
181+ traceparent = pstrdup (value );
182+ elog (DEBUG2 , "extracted traceparent from SQLcommenter: %s" , traceparent );
183+ } else if (strncmp (token , "tracestate=" , 11 ) == 0 ) {
184+ char * value = token + 11 ;
185+ // Remove quotes if present
186+ if (* value == '\'' || * value == '"' ) {
187+ char quote_char = * value ;
188+ value ++ ;
189+ char * end_quote = strrchr (value , quote_char );
190+ if (end_quote ) * end_quote = '\0' ;
191+ }
192+ if (tracestate ) pfree (tracestate );
193+ tracestate = pstrdup (value );
194+ elog (DEBUG2 , "extracted tracestate from SQLcommenter: %s" , tracestate );
195+ }
196+
197+ token = strtok (NULL , "," );
198+ }
199+
200+ pfree (comment_content );
201+
202+ // Look for next comment
203+ comment_start = strstr (comment_end + 2 , "/*" );
204+ }
205+
206+ // Format the result string for Go layer consumption
207+ if (traceparent != NULL ) {
208+ if (tracestate != NULL ) {
209+ result = psprintf ("traceparent=%s;tracestate=%s" , traceparent , tracestate );
210+ } else {
211+ result = psprintf ("traceparent=%s" , traceparent );
212+ }
213+
214+ elog (DEBUG1 , "extracted trace context from SQLcommenter: %s" , result );
215+ } else {
216+ elog (DEBUG2 , "no trace context found in SQL comments" );
217+ }
218+
219+ // Clean up
220+ if (traceparent ) pfree (traceparent );
221+ if (tracestate ) pfree (tracestate );
222+
223+ return result ;
224+ }
225+
226+ /*
227+ * Extract trace context with fallback strategy:
228+ * 1. Try PostgreSQL session variables first
229+ * 2. Fall back to SQLcommenter in query comments
230+ * 3. Return NULL if neither found
231+ */
232+ static char * extractTraceContext (void )
233+ {
234+ char * result = NULL ;
235+
236+ // First try session variables (primary method)
237+ result = extractTraceContextFromSession ();
238+ if (result != NULL ) {
239+ elog (DEBUG1 , "using trace context from session variables" );
240+ return result ;
241+ }
242+
243+ // Fall back to SQLcommenter (secondary method)
244+ result = extractTraceContextFromQueryComments ();
245+ if (result != NULL ) {
246+ elog (DEBUG1 , "using trace context from SQLcommenter" );
247+ return result ;
248+ }
249+
250+ elog (DEBUG2 , "no trace context found in session variables or SQLcommenter" );
251+ return NULL ;
252+ }
253+
254+ /*
255+ * Public wrapper for extractTraceContext - callable from Go
256+ */
257+ char * getTraceContext (void )
258+ {
259+ return extractTraceContext ();
129260}
130261
131262static void fdwGetForeignRelSize (PlannerInfo * root , RelOptInfo * baserel , Oid foreigntableid )
@@ -147,9 +278,9 @@ static void fdwGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid for
147278 // Save plan state information
148279 baserel -> fdw_private = planstate ;
149280 planstate -> foreigntableid = foreigntableid ;
150-
151- // Extract trace context from session variables
152- char * traceContext = extractTraceContextFromSession ();
281+
282+ // Extract trace context with fallback strategy ( session variables -> SQLcommenter)
283+ char * traceContext = extractTraceContext ();
153284 if (traceContext != NULL ) {
154285 planstate -> trace_context_string = pstrdup (traceContext );
155286 pfree (traceContext );
0 commit comments