6
6
#include "access/xact.h"
7
7
#include "utils/guc.h"
8
8
#include "utils/builtins.h"
9
+ #include "tcop/tcopprot.h"
9
10
10
11
extern PGDLLEXPORT void _PG_init (void );
11
12
@@ -16,6 +17,11 @@ static char *convertUUID(char *uuid);
16
17
static void pgfdw_xact_callback (XactEvent event , void * arg );
17
18
static void exitHook (int code , Datum arg );
18
19
20
+ /* Trace context extraction functions */
21
+ static char * extractTraceContextFromSession (void );
22
+ static char * extractTraceContextFromQueryComments (void );
23
+ static char * extractTraceContext (void );
24
+
19
25
void * serializePlanState (FdwPlanState * state );
20
26
FdwExecState * initializeExecState (void * internalstate );
21
27
// Required by postgres, doing basic checks to ensure compatibility,
@@ -103,29 +109,153 @@ static char *extractTraceContextFromSession(void)
103
109
const char * traceparent = GetConfigOption ("steampipe.traceparent" , true, false);
104
110
const char * tracestate = GetConfigOption ("steampipe.tracestate" , true, false);
105
111
char * result = NULL ;
106
-
112
+
107
113
// Format the result string for Go layer consumption
108
114
if (traceparent != NULL ) {
109
115
if (tracestate != NULL ) {
110
116
result = psprintf ("traceparent=%s;tracestate=%s" , traceparent , tracestate );
111
117
} else {
112
118
result = psprintf ("traceparent=%s" , traceparent );
113
119
}
114
-
120
+
115
121
elog (DEBUG1 , "extracted trace context: %s" , result );
116
122
} else {
117
123
elog (DEBUG2 , "no trace context found in session variables" );
118
124
}
119
-
125
+
120
126
return result ;
121
127
}
122
128
123
129
/*
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
125
133
*/
126
- char * getTraceContextFromSession (void )
134
+ static char * extractTraceContextFromQueryComments (void )
127
135
{
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
+ value ++ ;
188
+ char * end_quote = strrchr (value , * value == '\'' ? '\'' : '"' );
189
+ if (end_quote ) * end_quote = '\0' ;
190
+ }
191
+ if (tracestate ) pfree (tracestate );
192
+ tracestate = pstrdup (value );
193
+ elog (DEBUG2 , "extracted tracestate from SQLcommenter: %s" , tracestate );
194
+ }
195
+
196
+ token = strtok (NULL , "," );
197
+ }
198
+
199
+ pfree (comment_content );
200
+
201
+ // Look for next comment
202
+ comment_start = strstr (comment_end + 2 , "/*" );
203
+ }
204
+
205
+ // Format the result string for Go layer consumption
206
+ if (traceparent != NULL ) {
207
+ if (tracestate != NULL ) {
208
+ result = psprintf ("traceparent=%s;tracestate=%s" , traceparent , tracestate );
209
+ } else {
210
+ result = psprintf ("traceparent=%s" , traceparent );
211
+ }
212
+
213
+ elog (DEBUG1 , "extracted trace context from SQLcommenter: %s" , result );
214
+ } else {
215
+ elog (DEBUG2 , "no trace context found in SQL comments" );
216
+ }
217
+
218
+ // Clean up
219
+ if (traceparent ) pfree (traceparent );
220
+ if (tracestate ) pfree (tracestate );
221
+
222
+ return result ;
223
+ }
224
+
225
+ /*
226
+ * Extract trace context with fallback strategy:
227
+ * 1. Try PostgreSQL session variables first
228
+ * 2. Fall back to SQLcommenter in query comments
229
+ * 3. Return NULL if neither found
230
+ */
231
+ static char * extractTraceContext (void )
232
+ {
233
+ char * result = NULL ;
234
+
235
+ // First try session variables (primary method)
236
+ result = extractTraceContextFromSession ();
237
+ if (result != NULL ) {
238
+ elog (DEBUG1 , "using trace context from session variables" );
239
+ return result ;
240
+ }
241
+
242
+ // Fall back to SQLcommenter (secondary method)
243
+ result = extractTraceContextFromQueryComments ();
244
+ if (result != NULL ) {
245
+ elog (DEBUG1 , "using trace context from SQLcommenter" );
246
+ return result ;
247
+ }
248
+
249
+ elog (DEBUG2 , "no trace context found in session variables or SQLcommenter" );
250
+ return NULL ;
251
+ }
252
+
253
+ /*
254
+ * Public wrapper for extractTraceContext - callable from Go
255
+ */
256
+ char * getTraceContext (void )
257
+ {
258
+ return extractTraceContext ();
129
259
}
130
260
131
261
static void fdwGetForeignRelSize (PlannerInfo * root , RelOptInfo * baserel , Oid foreigntableid )
@@ -147,9 +277,9 @@ static void fdwGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid for
147
277
// Save plan state information
148
278
baserel -> fdw_private = planstate ;
149
279
planstate -> foreigntableid = foreigntableid ;
150
-
151
- // Extract trace context from session variables
152
- char * traceContext = extractTraceContextFromSession ();
280
+
281
+ // Extract trace context with fallback strategy ( session variables -> SQLcommenter)
282
+ char * traceContext = extractTraceContext ();
153
283
if (traceContext != NULL ) {
154
284
planstate -> trace_context_string = pstrdup (traceContext );
155
285
pfree (traceContext );
0 commit comments