@@ -408,10 +408,62 @@ def __getattribute__(self, name):
408408 )
409409
410410 def cursor (self , * args , ** kwargs ):
411- return get_traced_cursor_proxy (
412- self .__wrapped__ .cursor (* args , ** kwargs ), db_api_integration
411+ wrapped_cursor = self .__wrapped__ .cursor (* args , ** kwargs )
412+
413+ # If a mysql-connector cursor was created with prepared=True
414+ # then MySQL statements will be prepared and executed natively.
415+ # 1:1 sqlcomment and span correlation in instrumentation will
416+ # break, so sqlcomment is not supported for this use case.
417+ # This is here because a client app can use multiple cursors
418+ # and to not check cursor with every traced request.
419+ is_prepared = False
420+ if (
421+ db_api_integration .database_system == "mysql"
422+ and db_api_integration .connect_module .__name__
423+ == "mysql.connector"
424+ ):
425+ is_prepared = self .is_mysql_connector_cursor_prepared (
426+ wrapped_cursor
427+ )
428+
429+ if is_prepared and db_api_integration .enable_commenter :
430+ _logger .warning (
431+ "sqlcomment is not supported for query statements executed with native prepared statement support. Disabling."
432+ )
433+ db_api_integration .enable_commenter = False
434+
435+ return get_traced_connection_proxy (
436+ wrapped_cursor , db_api_integration
413437 )
414438
439+ def is_mysql_connector_cursor_prepared (self , cursor ):
440+ try :
441+ from mysql .connector .cursor_cext import ( # pylint: disable=import-outside-toplevel
442+ CMySQLCursorPrepared ,
443+ CMySQLCursorPreparedDict ,
444+ CMySQLCursorPreparedNamedTuple ,
445+ CMySQLCursorPreparedRaw ,
446+ )
447+
448+ if type (cursor ) in [
449+ CMySQLCursorPrepared ,
450+ CMySQLCursorPreparedDict ,
451+ CMySQLCursorPreparedNamedTuple ,
452+ CMySQLCursorPreparedRaw ,
453+ ]:
454+ _logger .warning (
455+ "Adding sqlcomment to prepared MySQL statements is not supported. Please check OpenTelemetry configuration. Skipping."
456+ )
457+ return True
458+
459+ except ImportError as exc :
460+ _logger .warning (
461+ "Could not verify mysql.connector cursor, skipping prepared statement check: %s" ,
462+ exc ,
463+ )
464+
465+ return False
466+
415467 def __enter__ (self ):
416468 self .__wrapped__ .__enter__ ()
417469 return self
@@ -460,34 +512,6 @@ def _populate_span(
460512 if self ._db_api_integration .capture_parameters and len (args ) > 1 :
461513 span .set_attribute ("db.statement.parameters" , str (args [1 ]))
462514
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-
491515 def get_operation_name (self , cursor , args ): # pylint: disable=no-self-use
492516 if args and isinstance (args [0 ], str ):
493517 # Strip leading comments so we get the operation name.
@@ -522,67 +546,50 @@ def traced_execution(
522546 ) as span :
523547 if span .is_recording ():
524548 if args and self ._commenter_enabled :
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
549+ try :
550+ args_list = list (args )
551+
552+ # lazy capture of mysql-connector client version using cursor
553+ if (
554+ self ._db_api_integration .database_system == "mysql"
555+ and self ._db_api_integration .connect_module .__name__
556+ == "mysql.connector"
557+ and not self ._db_api_integration .commenter_data [
558+ "mysql_client_version"
559+ ]
560+ ):
561+ self ._db_api_integration .commenter_data [
562+ "mysql_client_version"
563+ ] = cursor ._cnx ._cmysql .get_client_info ()
564+
565+ commenter_data = dict (
566+ self ._db_api_integration .commenter_data
538567 )
539-
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
568+ if self ._commenter_options .get (
569+ "opentelemetry_values" , True
570+ ):
571+ commenter_data .update (
572+ ** _get_opentelemetry_values ()
576573 )
577574
578- args_list [0 ] = statement
579- args = tuple (args_list )
575+ # Filter down to just the requested attributes.
576+ commenter_data = {
577+ k : v
578+ for k , v in commenter_data .items ()
579+ if self ._commenter_options .get (k , True )
580+ }
581+ statement = _add_sql_comment (
582+ args_list [0 ], ** commenter_data
583+ )
584+
585+ args_list [0 ] = statement
586+ args = tuple (args_list )
580587
581- except Exception as exc : # pylint: disable=broad-except
582- _logger .exception (
583- "Exception while generating sql comment: %s" ,
584- exc ,
585- )
588+ except Exception as exc : # pylint: disable=broad-except
589+ _logger .exception (
590+ "Exception while generating sql comment: %s" ,
591+ exc ,
592+ )
586593
587594 self ._populate_span (span , cursor , * args )
588595
0 commit comments