Index: trac/db_default.py
===================================================================
--- trac/db_default.py	(revision 3787)
+++ trac/db_default.py	(working copy)
@@ -17,7 +17,7 @@
 from trac.db import Table, Column, Index
 
 # Database version identifier. Used for automatic upgrades.
-db_version = 19
+db_version = 20
 
 def __mkreports(reports):
     """Utility function used to create report data in same syntax as the
@@ -88,14 +88,15 @@
         Column('author'),
         Column('message'),
         Index(['time'])],
-    Table('node_change', key=('rev', 'path', 'change_type'))[
+    Table('node_change')[
         Column('rev'),
         Column('path'),
         Column('node_type', size=1),
         Column('change_type', size=1),
         Column('base_path'),
         Column('base_rev'),
-        Index(['rev'])],
+        Index(['rev']),
+        Index(['rev', 'path', 'change_type'])],
 
     # Ticket system
     Table('ticket', key='id')[
Index: trac/upgrades/db20.py
===================================================================
--- trac/upgrades/db20.py	(revision 0)
+++ trac/upgrades/db20.py	(revision 0)
@@ -0,0 +1,29 @@
+from trac.db import Table, Column, Index, DatabaseManager
+
+def do_upgrade(env, ver, cursor):
+    """Replace the primary key constraint on the `node_change` table by an
+    index, as the key is not unique in some circumstances (#3778).
+    """
+    cursor.execute("CREATE TEMPORARY TABLE node_change_old AS "
+                   "SELECT * FROM node_change")
+    cursor.execute("DROP TABLE node_change")
+
+    table = Table('node_change')[
+        Column('rev'),
+        Column('path'),
+        Column('node_type', size=1),
+        Column('change_type', size=1),
+        Column('base_path'),
+        Column('base_rev'),
+        Index(['rev']),
+        Index(['rev', 'path', 'change_type'])]
+
+    db_connector, _ = DatabaseManager(env)._get_connector()
+    for stmt in db_connector.to_sql(table):
+        cursor.execute(stmt)
+
+    cursor.execute("INSERT INTO node_change "
+                   "(rev,path,node_type,change_type,base_path,base_rev) "
+                   "SELECT rev,path,node_type,change_type,base_path,base_rev "
+                   "FROM node_change_old")
+    cursor.execute("DROP TABLE node_change_old")

Property changes on: trac\upgrades\db20.py
___________________________________________________________________
Name: svn:eol-style
   + native


