@@ -460,6 +460,34 @@ def _populate_span(
460460 if self ._db_api_integration .capture_parameters and len (args ) > 1 :
461461 span .set_attribute ("db.statement.parameters" , str (args [1 ]))
462462
463+ def _is_mysql_connector_cursor_prepared (self , cursor ): # pylint: disable=no-self-use
464+ try :
465+ from mysql .connector .cursor_cext import ( # pylint: disable=import-outside-toplevel
466+ CMySQLCursorPrepared ,
467+ CMySQLCursorPreparedDict ,
468+ CMySQLCursorPreparedNamedTuple ,
469+ CMySQLCursorPreparedRaw ,
470+ )
471+
472+ if type (cursor ) in [
473+ CMySQLCursorPrepared ,
474+ CMySQLCursorPreparedDict ,
475+ CMySQLCursorPreparedNamedTuple ,
476+ CMySQLCursorPreparedRaw ,
477+ ]:
478+ _logger .warning (
479+ "Adding sqlcomment to prepared MySQL statements is not supported. Please check OpenTelemetry configuration. Skipping."
480+ )
481+ return True
482+
483+ except ImportError as exc :
484+ _logger .warning (
485+ "Could not verify mysql.connector cursor, skipping sqlcomment: %s" ,
486+ exc ,
487+ )
488+
489+ return False
490+
463491 def get_operation_name (self , cursor , args ): # pylint: disable=no-self-use
464492 if args and isinstance (args [0 ], str ):
465493 # Strip leading comments so we get the operation name.
@@ -494,49 +522,67 @@ def traced_execution(
494522 ) as span :
495523 if span .is_recording ():
496524 if args and self ._commenter_enabled :
497- try :
498- args_list = list (args )
499-
500- # lazy capture of mysql-connector client version using cursor
501- if (
502- self ._db_api_integration .database_system == "mysql"
503- and self ._db_api_integration .connect_module .__name__
504- == "mysql.connector"
505- and not self ._db_api_integration .commenter_data [
506- "mysql_client_version"
507- ]
508- ):
509- self ._db_api_integration .commenter_data [
510- "mysql_client_version"
511- ] = cursor ._cnx ._cmysql .get_client_info ()
512-
513- commenter_data = dict (
514- self ._db_api_integration .commenter_data
525+ # If a mysql-connector cursor was created with prepared=True
526+ # then MySQL statements will be prepared and executed natively.
527+ # 1:1 sqlcomment and span correlation in instrumentation will
528+ # break, so sqlcomment is not supported for this use case.
529+ # This is here because a client app can use multiple cursors.
530+ is_prepared = False
531+ if (
532+ self ._db_api_integration .database_system == "mysql"
533+ and self ._db_api_integration .connect_module .__name__
534+ == "mysql.connector"
535+ ):
536+ is_prepared = self ._is_mysql_connector_cursor_prepared (
537+ cursor
515538 )
516- if self ._commenter_options .get (
517- "opentelemetry_values" , True
518- ):
519- commenter_data .update (
520- ** _get_opentelemetry_values ()
521- )
522539
523- # Filter down to just the requested attributes.
524- commenter_data = {
525- k : v
526- for k , v in commenter_data .items ()
527- if self ._commenter_options .get (k , True )
528- }
529- statement = _add_sql_comment (
530- args_list [0 ], ** commenter_data
531- )
540+ if not is_prepared :
541+ try :
542+ args_list = list (args )
543+
544+ # lazy capture of mysql-connector client version using cursor
545+ if (
546+ self ._db_api_integration .database_system
547+ == "mysql"
548+ and self ._db_api_integration .connect_module .__name__
549+ == "mysql.connector"
550+ and not self ._db_api_integration .commenter_data [
551+ "mysql_client_version"
552+ ]
553+ ):
554+ self ._db_api_integration .commenter_data [
555+ "mysql_client_version"
556+ ] = cursor ._cnx ._cmysql .get_client_info ()
557+
558+ commenter_data = dict (
559+ self ._db_api_integration .commenter_data
560+ )
561+ if self ._commenter_options .get (
562+ "opentelemetry_values" , True
563+ ):
564+ commenter_data .update (
565+ ** _get_opentelemetry_values ()
566+ )
567+
568+ # Filter down to just the requested attributes.
569+ commenter_data = {
570+ k : v
571+ for k , v in commenter_data .items ()
572+ if self ._commenter_options .get (k , True )
573+ }
574+ statement = _add_sql_comment (
575+ args_list [0 ], ** commenter_data
576+ )
532577
533- args_list [0 ] = statement
534- args = tuple (args_list )
578+ args_list [0 ] = statement
579+ args = tuple (args_list )
535580
536- except Exception as exc : # pylint: disable=broad-except
537- _logger .exception (
538- "Exception while generating sql comment: %s" , exc
539- )
581+ except Exception as exc : # pylint: disable=broad-except
582+ _logger .exception (
583+ "Exception while generating sql comment: %s" ,
584+ exc ,
585+ )
540586
541587 self ._populate_span (span , cursor , * args )
542588
0 commit comments