pre-migration.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # -*- coding: utf-8 -*-
  2. """
  3. Pre-migration script to prepare database schema for template integration
  4. This script runs BEFORE the module update to:
  5. 1. Make template_id nullable
  6. 2. Add workflow_template_id column
  7. 3. Prepare for constraint changes
  8. """
  9. import logging
  10. _logger = logging.getLogger(__name__)
  11. def migrate(cr, version):
  12. """Prepare database schema for template integration"""
  13. try:
  14. _logger.info("=" * 80)
  15. _logger.info("Starting pre-migration: Preparing database schema")
  16. _logger.info("=" * 80)
  17. # 1. Check if workflow_template_id column exists
  18. cr.execute("""
  19. SELECT column_name
  20. FROM information_schema.columns
  21. WHERE table_name = 'helpdesk_template_field'
  22. AND column_name = 'workflow_template_id'
  23. """)
  24. workflow_col_exists = cr.fetchone()
  25. if not workflow_col_exists:
  26. _logger.info("Adding workflow_template_id column to helpdesk_template_field...")
  27. cr.execute("""
  28. ALTER TABLE helpdesk_template_field
  29. ADD COLUMN workflow_template_id INTEGER
  30. """)
  31. _logger.info("✅ Column workflow_template_id added")
  32. else:
  33. _logger.info("Column workflow_template_id already exists")
  34. # 2. Add foreign key constraint if it doesn't exist
  35. cr.execute("""
  36. SELECT constraint_name
  37. FROM information_schema.table_constraints
  38. WHERE table_name = 'helpdesk_template_field'
  39. AND constraint_name = 'helpdesk_template_field_workflow_template_id_fkey'
  40. """)
  41. fk_exists = cr.fetchone()
  42. if not fk_exists:
  43. _logger.info("Adding foreign key constraint for workflow_template_id...")
  44. cr.execute("""
  45. ALTER TABLE helpdesk_template_field
  46. ADD CONSTRAINT helpdesk_template_field_workflow_template_id_fkey
  47. FOREIGN KEY (workflow_template_id)
  48. REFERENCES helpdesk_workflow_template(id)
  49. ON DELETE CASCADE
  50. """)
  51. _logger.info("✅ Foreign key constraint added")
  52. else:
  53. _logger.info("Foreign key constraint already exists")
  54. # 3. Make template_id nullable (if it's not already)
  55. cr.execute("""
  56. SELECT is_nullable
  57. FROM information_schema.columns
  58. WHERE table_name = 'helpdesk_template_field'
  59. AND column_name = 'template_id'
  60. """)
  61. template_nullable = cr.fetchone()
  62. if template_nullable and template_nullable[0] == 'NO':
  63. _logger.info("Making template_id nullable...")
  64. # First, drop the NOT NULL constraint by altering the column
  65. cr.execute("""
  66. ALTER TABLE helpdesk_template_field
  67. ALTER COLUMN template_id DROP NOT NULL
  68. """)
  69. _logger.info("✅ template_id is now nullable")
  70. else:
  71. _logger.info("template_id is already nullable")
  72. # 4. Add index on workflow_template_id for performance
  73. cr.execute("""
  74. SELECT indexname
  75. FROM pg_indexes
  76. WHERE tablename = 'helpdesk_template_field'
  77. AND indexname = 'helpdesk_template_field_workflow_template_id_index'
  78. """)
  79. index_exists = cr.fetchone()
  80. if not index_exists:
  81. _logger.info("Adding index on workflow_template_id...")
  82. cr.execute("""
  83. CREATE INDEX helpdesk_template_field_workflow_template_id_index
  84. ON helpdesk_template_field(workflow_template_id)
  85. """)
  86. _logger.info("✅ Index added")
  87. else:
  88. _logger.info("Index already exists")
  89. # 5. Drop old UNIQUE constraints if they exist (they don't work well with NULLs)
  90. cr.execute("""
  91. SELECT constraint_name
  92. FROM information_schema.table_constraints
  93. WHERE table_name = 'helpdesk_template_field'
  94. AND constraint_name IN ('helpdesk_template_field_unique_template_field',
  95. 'helpdesk_template_field_unique_workflow_template_field')
  96. """)
  97. old_constraints = cr.fetchall()
  98. for constraint in old_constraints:
  99. constraint_name = constraint[0]
  100. _logger.info(f"Dropping old constraint: {constraint_name}...")
  101. try:
  102. cr.execute(f"""
  103. ALTER TABLE helpdesk_template_field
  104. DROP CONSTRAINT IF EXISTS {constraint_name}
  105. """)
  106. _logger.info(f"✅ Dropped constraint: {constraint_name}")
  107. except Exception as e:
  108. _logger.warning(f"Could not drop constraint {constraint_name}: {e}")
  109. # 6. Add partial unique indexes (work correctly with NULLs)
  110. # Index for template_id + field_id (only when template_id IS NOT NULL)
  111. cr.execute("""
  112. SELECT indexname
  113. FROM pg_indexes
  114. WHERE tablename = 'helpdesk_template_field'
  115. AND indexname = 'helpdesk_template_field_unique_template_field_idx'
  116. """)
  117. template_idx_exists = cr.fetchone()
  118. if not template_idx_exists:
  119. _logger.info("Adding partial unique index for template_id + field_id...")
  120. cr.execute("""
  121. CREATE UNIQUE INDEX helpdesk_template_field_unique_template_field_idx
  122. ON helpdesk_template_field(template_id, field_id)
  123. WHERE template_id IS NOT NULL
  124. """)
  125. _logger.info("✅ Partial unique index for template_id added")
  126. else:
  127. _logger.info("Partial unique index for template_id already exists")
  128. # Index for workflow_template_id + field_id (only when workflow_template_id IS NOT NULL)
  129. cr.execute("""
  130. SELECT indexname
  131. FROM pg_indexes
  132. WHERE tablename = 'helpdesk_template_field'
  133. AND indexname = 'helpdesk_template_field_unique_workflow_template_field_idx'
  134. """)
  135. workflow_idx_exists = cr.fetchone()
  136. if not workflow_idx_exists:
  137. _logger.info("Adding partial unique index for workflow_template_id + field_id...")
  138. cr.execute("""
  139. CREATE UNIQUE INDEX helpdesk_template_field_unique_workflow_template_field_idx
  140. ON helpdesk_template_field(workflow_template_id, field_id)
  141. WHERE workflow_template_id IS NOT NULL
  142. """)
  143. _logger.info("✅ Partial unique index for workflow_template_id added")
  144. else:
  145. _logger.info("Partial unique index for workflow_template_id already exists")
  146. # 7. Add CHECK constraint to ensure only one of template_id or workflow_template_id is set
  147. cr.execute("""
  148. SELECT constraint_name
  149. FROM information_schema.table_constraints
  150. WHERE table_name = 'helpdesk_template_field'
  151. AND constraint_name = 'helpdesk_template_field_check_template_or_workflow'
  152. """)
  153. check_exists = cr.fetchone()
  154. if not check_exists:
  155. _logger.info("Adding CHECK constraint for template_or_workflow...")
  156. cr.execute("""
  157. ALTER TABLE helpdesk_template_field
  158. ADD CONSTRAINT helpdesk_template_field_check_template_or_workflow
  159. CHECK ((template_id IS NOT NULL AND workflow_template_id IS NULL)
  160. OR (template_id IS NULL AND workflow_template_id IS NOT NULL))
  161. """)
  162. _logger.info("✅ CHECK constraint added")
  163. else:
  164. _logger.info("CHECK constraint already exists")
  165. cr.commit()
  166. _logger.info("=" * 80)
  167. _logger.info("✅ Pre-migration completed successfully")
  168. _logger.info("=" * 80)
  169. except Exception as e:
  170. _logger.error(f"❌ Error in pre-migration: {e}", exc_info=True)
  171. try:
  172. cr.rollback()
  173. except Exception:
  174. pass # Ignore rollback errors
  175. # Raise to prevent module update if pre-migration fails
  176. # This ensures database is in consistent state
  177. raise