@@ -587,3 +587,147 @@ def test_db_atomic_rollback_executemany(sentry_init, client, capture_events):
587587 # Verify queries and rollback statements are siblings
588588 for insert_span in insert_spans :
589589 assert rollback_span ["parent_span_id" ] == insert_span ["parent_span_id" ]
590+
591+
592+ @pytest .mark .forked
593+ @pytest_mark_django_db_decorator (transaction = True )
594+ def test_db_atomic_execute_exception (sentry_init , client , capture_events ):
595+ sentry_init (
596+ integrations = [DjangoIntegration ()],
597+ send_default_pii = True ,
598+ traces_sample_rate = 1.0 ,
599+ )
600+
601+ if "postgres" not in connections :
602+ pytest .skip ("postgres tests disabled" )
603+
604+ # trigger Django to open a new connection by marking the existing one as None.
605+ connections ["postgres" ].connection = None
606+
607+ events = capture_events ()
608+
609+ client .get (reverse ("postgres_insert_orm_atomic_exception" ))
610+
611+ (event ,) = events
612+
613+ # Ensure operation is rolled back
614+ assert not User .objects .using ("postgres" ).exists ()
615+
616+ assert event ["contexts" ]["trace" ]["origin" ] == "auto.http.django"
617+
618+ rollback_spans = [
619+ span
620+ for span in event ["spans" ]
621+ if span ["data" ].get (SPANDATA .DB_OPERATION ) == DBOPERATION .ROLLBACK
622+ ]
623+ assert len (rollback_spans ) == 1
624+ rollback_span = rollback_spans [0 ]
625+ assert rollback_span ["origin" ] == "auto.db.django"
626+
627+ # Verify other database attributes
628+ assert rollback_span ["data" ].get (SPANDATA .DB_SYSTEM ) == "postgresql"
629+ conn_params = connections ["postgres" ].get_connection_params ()
630+ assert rollback_span ["data" ].get (SPANDATA .DB_NAME ) is not None
631+ assert rollback_span ["data" ].get (SPANDATA .DB_NAME ) == conn_params .get (
632+ "database"
633+ ) or conn_params .get ("dbname" )
634+ assert rollback_span ["data" ].get (SPANDATA .SERVER_ADDRESS ) == os .environ .get (
635+ "SENTRY_PYTHON_TEST_POSTGRES_HOST" , "localhost"
636+ )
637+ assert rollback_span ["data" ].get (SPANDATA .SERVER_PORT ) == os .environ .get (
638+ "SENTRY_PYTHON_TEST_POSTGRES_PORT" , "5432"
639+ )
640+
641+ insert_spans = [
642+ span for span in event ["spans" ] if span ["description" ].startswith ("INSERT INTO" )
643+ ]
644+ assert len (insert_spans ) == 1
645+ insert_span = insert_spans [0 ]
646+
647+ # Verify query and rollback statements are siblings
648+ assert rollback_span ["parent_span_id" ] == insert_span ["parent_span_id" ]
649+
650+
651+ @pytest .mark .forked
652+ @pytest_mark_django_db_decorator (transaction = True )
653+ def test_db_atomic_executemany_exception (sentry_init , client , capture_events ):
654+ sentry_init (
655+ integrations = [DjangoIntegration ()],
656+ send_default_pii = True ,
657+ traces_sample_rate = 1.0 ,
658+ )
659+
660+ events = capture_events ()
661+
662+ with start_transaction (name = "test_transaction" ):
663+ from django .db import connection , transaction
664+
665+ try :
666+ with transaction .atomic ():
667+ cursor = connection .cursor ()
668+
669+ query = """INSERT INTO auth_user (
670+ password,
671+ is_superuser,
672+ username,
673+ first_name,
674+ last_name,
675+ email,
676+ is_staff,
677+ is_active,
678+ date_joined
679+ )
680+ VALUES ('password', false, %s, %s, %s, %s, false, true, %s);"""
681+
682+ query_list = (
683+ (
684+ "user1" ,
685+ "John" ,
686+ "Doe" ,
687+ "user1@example.com" ,
688+ datetime (1970 , 1 , 1 ),
689+ ),
690+ (
691+ "user2" ,
692+ "Max" ,
693+ "Mustermann" ,
694+ "user2@example.com" ,
695+ datetime (1970 , 1 , 1 ),
696+ ),
697+ )
698+ cursor .executemany (query , query_list )
699+ 1 / 0
700+ except ZeroDivisionError :
701+ pass
702+
703+ (event ,) = events
704+
705+ # Ensure operation is rolled back
706+ assert not User .objects .exists ()
707+
708+ assert event ["contexts" ]["trace" ]["origin" ] == "manual"
709+
710+ rollback_spans = [
711+ span
712+ for span in event ["spans" ]
713+ if span ["data" ].get (SPANDATA .DB_OPERATION ) == DBOPERATION .ROLLBACK
714+ ]
715+ assert len (rollback_spans ) == 1
716+ rollback_span = rollback_spans [0 ]
717+ assert rollback_span ["origin" ] == "auto.db.django"
718+
719+ # Verify other database attributes
720+ assert rollback_span ["data" ].get (SPANDATA .DB_SYSTEM ) == "sqlite"
721+ conn_params = connection .get_connection_params ()
722+ assert rollback_span ["data" ].get (SPANDATA .DB_NAME ) is not None
723+ assert rollback_span ["data" ].get (SPANDATA .DB_NAME ) == conn_params .get (
724+ "database"
725+ ) or conn_params .get ("dbname" )
726+
727+ insert_spans = [
728+ span for span in event ["spans" ] if span ["description" ].startswith ("INSERT INTO" )
729+ ]
730+
731+ # Verify queries and rollback statements are siblings
732+ for insert_span in insert_spans :
733+ assert rollback_span ["parent_span_id" ] == insert_span ["parent_span_id" ]
0 commit comments