Index: django/db/backends/util.py
===================================================================
--- django/db/backends/util.py	(revision 14617)
+++ django/db/backends/util.py	(working copy)
@@ -52,6 +52,16 @@
     def __iter__(self):
         return iter(self.cursor)
 
+class CursorUseNotifyWrapper(object):
+    def __init__(self, cursor, on_use):
+        self.cursor = cursor
+        self.on_use = on_use
+    def __getattr__(self, attr):
+        self.on_use()
+        return getattr(self.cursor, attr)
+    def __iter__(self):
+        return iter(self.cursor)
+
 ###############################################
 # Converters from database (string) to Python #
 ###############################################
Index: django/db/backends/__init__.py
===================================================================
--- django/db/backends/__init__.py	(revision 14617)
+++ django/db/backends/__init__.py	(working copy)
@@ -22,6 +22,7 @@
         self.alias = alias
         self.vendor = 'unknown'
         self.use_debug_cursor = None
+        self.on_used = None
 
     def __eq__(self, other):
         return self.settings_dict == other.settings_dict
@@ -75,6 +76,9 @@
     def cursor(self):
         from django.conf import settings
         cursor = self._cursor()
+        # Used by transaction mechanism to be notified that db was used
+        if self.on_used:
+            cursor = util.CursorUseNotifyWrapper(cursor, self.on_used)
         if (self.use_debug_cursor or
             (self.use_debug_cursor is None and settings.DEBUG)):
             return self.make_debug_cursor(cursor)
Index: django/db/transaction.py
===================================================================
--- django/db/transaction.py	(revision 14617)
+++ django/db/transaction.py	(working copy)
@@ -67,6 +67,9 @@
     if thread_ident not in dirty or using not in dirty[thread_ident]:
         dirty.setdefault(thread_ident, {})
         dirty[thread_ident][using] = False
+    def set_dirty_if_managed():
+        if is_managed(using): set_dirty(using)
+    connection.on_used = set_dirty_if_managed
     connection._enter_transaction_management(managed)
 
 def leave_transaction_management(using=None):
Index: tests/regressiontests/transaction_regress/__init__.py
===================================================================
Index: tests/regressiontests/transaction_regress/tests.py
===================================================================
--- tests/regressiontests/transaction_regress/tests.py	(revision 0)
+++ tests/regressiontests/transaction_regress/tests.py	(revision 0)
@@ -0,0 +1,98 @@
+from django.test import TransactionTestCase
+from django.db import connection, transaction
+from django.db.transaction import \
+    commit_on_success, commit_manually, \
+    TransactionManagementError
+from models import Mod
+
+class Test9964(TransactionTestCase):
+
+    def test_raw_committed_on_success(self):
+        
+        @commit_on_success
+        def raw_sql():
+            cursor = connection.cursor()
+            cursor.execute("INSERT into transaction_regress_mod (id,fld) values (17,18)")
+            
+        raw_sql()
+        transaction.rollback()
+        try:
+            obj = Mod.objects.get(pk=17)
+            self.assertEqual(obj.fld, 18)
+        except Mod.DoesNotExist:
+            self.fail("transaction with raw sql not committed")
+
+    def test_commit_manually_enforced(self):
+        @commit_manually
+        def non_comitter():
+            _ = Mod.objects.count()
+            
+        self.assertRaises(TransactionManagementError, non_comitter)
+
+    def test_commit_manually_commit_ok(self):
+        @commit_manually
+        def committer():
+            _ = Mod.objects.count()
+            transaction.commit()
+        
+        try:
+            committer()
+        except TransactionManagementError:
+            self.fail("Commit did not clear the transaction state")
+
+    def test_commit_manually_rollback_ok(self):
+        @commit_manually
+        def roller_back():
+            _ = Mod.objects.count()
+            transaction.rollback()
+        
+        try:
+            roller_back()
+        except TransactionManagementError:
+            self.fail("Rollback did not clear the transaction state")
+
+    def test_commit_manually_enforced_after_commit(self):
+        @commit_manually
+        def fake_committer():
+            _ = Mod.objects.count()
+            transaction.commit()
+            _ = Mod.objects.count()            
+            
+        self.assertRaises(TransactionManagementError, fake_committer)
+
+    def test_reuse_cursor_reference(self):
+        @commit_on_success
+        def reuse_cursor_ref():
+            cursor = connection.cursor()
+            cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)")
+            transaction.rollback()
+            cursor.execute("INSERT into transaction_regress_mod (id,fld) values (1,2)")
+            
+        reuse_cursor_ref()
+        transaction.rollback()
+        try:
+            obj = Mod.objects.get(pk=1)
+            self.assertEquals(obj.fld, 2)
+        except Mod.DoesNotExist:
+            self.fail("After ending a transaction, cursor use no longer sets dirty")
+
+    def test_6669(self):
+
+        from django.contrib.auth.models import User
+
+        @transaction.commit_on_success
+        def create_system_user():
+            user = User.objects.create_user(username='system', password='iamr00t', email='root@SITENAME.com')
+            Mod.objects.create(fld=user.id)
+
+        try:
+            create_system_user()
+        except:
+            pass
+
+        try:
+            create_system_user()
+        except:
+            pass
+
+        print User.objects.all()
Index: tests/regressiontests/transaction_regress/models.py
===================================================================
--- tests/regressiontests/transaction_regress/models.py	(revision 0)
+++ tests/regressiontests/transaction_regress/models.py	(revision 0)
@@ -0,0 +1,8 @@
+from django.db import models
+
+class Mod(models.Model):
+    fld = models.IntegerField()
+from django.db import models
+
+class Mod(models.Model):
+    fld = models.IntegerField()
Index: tests/regressiontests/delete_regress/tests.py
===================================================================
--- tests/regressiontests/delete_regress/tests.py	(revision 14617)
+++ tests/regressiontests/delete_regress/tests.py	(working copy)
@@ -61,6 +61,7 @@
         Book.objects.filter(pagecount__lt=250).delete()
         transaction.commit()
         self.assertEqual(1, Book.objects.count())
+        transaction.commit()
 
 class DeleteCascadeTests(TestCase):
     def test_generic_relation_cascade(self):
Index: tests/regressiontests/fixtures_regress/tests.py
===================================================================
--- tests/regressiontests/fixtures_regress/tests.py	(revision 14617)
+++ tests/regressiontests/fixtures_regress/tests.py	(working copy)
@@ -610,6 +610,7 @@
         self.assertEqual(Thingy.objects.count(), 1)
         transaction.rollback()
         self.assertEqual(Thingy.objects.count(), 0)
+        transaction.commit()
 
     def test_ticket_11101(self):
         """Test that fixtures can be rolled back (ticket #11101)."""
Index: docs/topics/db/sql.txt
===================================================================
--- docs/topics/db/sql.txt	(revision 14617)
+++ docs/topics/db/sql.txt	(working copy)
@@ -231,38 +231,19 @@
 
 Transactions and raw SQL
 ------------------------
-If you are using transaction decorators (such as ``commit_on_success``) to
-wrap your views and provide transaction control, you don't have to make a
-manual call to ``transaction.commit_unless_managed()`` -- you can manually
-commit if you want to, but you aren't required to, since the decorator will
-commit for you. However, if you don't manually commit your changes, you will
-need to manually mark the transaction as dirty, using
-``transaction.set_dirty()``::
 
-    @commit_on_success
-    def my_custom_sql_view(request, value):
-        from django.db import connection, transaction
-        cursor = connection.cursor()
+.. versionchanged:: 1.3
 
-        # Data modifying operation
-        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [value])
+If you write functions that modify the database via raw SQL, you need to
+consider the transaction management mode they run under. If this may be
+Django's default mode (by default or using the ``auto_commit``
+decorator), you need to make manual calls to
+``transaction.commit_unless_managed()`` to make sure the changes are
+committed. If you are using transaction decorators (such as
+``commit_on_success``) to wrap your views and provide transaction
+control, you don't have to make such calls.
 
-        # Since we modified data, mark the transaction as dirty
-        transaction.set_dirty()
 
-        # Data retrieval operation. This doesn't dirty the transaction,
-        # so no call to set_dirty() is required.
-        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [value])
-        row = cursor.fetchone()
-
-        return render_to_response('template.html', {'row': row})
-
-The call to ``set_dirty()`` is made automatically when you use the Django ORM
-to make data modifying database calls. However, when you use raw SQL, Django
-has no way of knowing if your SQL modifies data or not. The manual call to
-``set_dirty()`` ensures that Django knows that there are modifications that
-must be committed.
-
 Connections and cursors
 -----------------------
 
Index: docs/releases/1.3.txt
===================================================================
--- docs/releases/1.3.txt	(revision 14617)
+++ docs/releases/1.3.txt	(working copy)
@@ -264,6 +264,36 @@
       official designation "Aceh (ACE)".
 
 
+Transaction management
+~~~~~~~~~~~~~~~~~~~~~~
+
+When using managed transactions -- that is, anything but the default
+autocommit mode -- it is important when a transaction is marked as
+"dirty". Dirty transactions are committed by the ``commit_on_success``
+decorator or the ``TransactionMiddleware``, and ``commit_manually``
+forces them to be closed explicitly; clean transactions "get a pass",
+which means they are usually rolled back at the end of a request
+when the connection is closed.
+
+Until Django 1.3, transactions were only marked dirty when Django
+was aware of a modifying operation performed in them; that is, either
+some model was saved, some bulk update or delete was performed, or
+the user explicitly called ``transaction.set_dirty()``. In Django 1.3,
+a transaction is marked dirty when any database operation is performed;
+this means you no longer need to set a transaction dirty explicitly
+when you execute raw SQL or use a data-modifying ``select``. On the
+other hand, you do need to explicitly close read-only transactions
+under ``commit_manually``. For example, take a look at this code::
+
+      @transaction.commit_manually
+      def my_view(request, name):
+      	  obj = get_object_or_404(MyObject, name__iexact=name)
+	  return render_to_response('template', {'object':obj})
+
+Until Django 1.3, this works fine. With Django 1.3, this raises a
+``TransactionManagementError``; you need to end the transaction
+explicitly, one way or another.
+
 .. _deprecated-features-1.3:
 
 Features deprecated in 1.3
