plan.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from dataclasses import dataclass, field
  2. from typing import TYPE_CHECKING, List, Optional, cast
  3. from django.db import connections, transaction
  4. from .config import PostgresPartitioningConfig
  5. from .constants import AUTO_PARTITIONED_COMMENT
  6. from .partition import PostgresPartition
  7. if TYPE_CHECKING:
  8. from psqlextra.backend.schema import PostgresSchemaEditor
  9. @dataclass
  10. class PostgresModelPartitioningPlan:
  11. """Describes the partitions that are going to be created/deleted for a
  12. particular partitioning config.
  13. A "partitioning config" applies to one model.
  14. """
  15. config: PostgresPartitioningConfig
  16. creations: List[PostgresPartition] = field(default_factory=list)
  17. deletions: List[PostgresPartition] = field(default_factory=list)
  18. def apply(self, using: Optional[str]) -> None:
  19. """Applies this partitioning plan by creating and deleting the planned
  20. partitions.
  21. Applying the plan runs in a transaction.
  22. Arguments:
  23. using:
  24. Optional name of the database connection to use.
  25. """
  26. connection = connections[using or "default"]
  27. with transaction.atomic():
  28. with connection.schema_editor() as schema_editor:
  29. for partition in self.creations:
  30. partition.create(
  31. self.config.model,
  32. cast("PostgresSchemaEditor", schema_editor),
  33. comment=AUTO_PARTITIONED_COMMENT,
  34. )
  35. for partition in self.deletions:
  36. partition.delete(
  37. self.config.model,
  38. cast("PostgresSchemaEditor", schema_editor),
  39. )
  40. def print(self) -> None:
  41. """Prints this model plan to the terminal in a readable format."""
  42. print(f"{self.config.model.__name__}:")
  43. for partition in self.deletions:
  44. print(" - %s" % partition.name())
  45. for key, value in partition.deconstruct().items():
  46. print(f" {key}: {value}")
  47. for partition in self.creations:
  48. print(" + %s" % partition.name())
  49. for key, value in partition.deconstruct().items():
  50. print(f" {key}: {value}")
  51. @dataclass
  52. class PostgresPartitioningPlan:
  53. """Describes the partitions that are going to be created/deleted."""
  54. model_plans: List[PostgresModelPartitioningPlan]
  55. @property
  56. def creations(self) -> List[PostgresPartition]:
  57. """Gets a complete flat list of the partitions that are going to be
  58. created."""
  59. creations = []
  60. for model_plan in self.model_plans:
  61. creations.extend(model_plan.creations)
  62. return creations
  63. @property
  64. def deletions(self) -> List[PostgresPartition]:
  65. """Gets a complete flat list of the partitions that are going to be
  66. deleted."""
  67. deletions = []
  68. for model_plan in self.model_plans:
  69. deletions.extend(model_plan.deletions)
  70. return deletions
  71. def apply(self, using: Optional[str] = None) -> None:
  72. """Applies this plan by creating/deleting all planned partitions."""
  73. for model_plan in self.model_plans:
  74. model_plan.apply(using=using)
  75. def print(self) -> None:
  76. """Prints this plan to the terminal in a readable format."""
  77. for model_plan in self.model_plans:
  78. model_plan.print()
  79. print("")
  80. create_count = len(self.creations)
  81. delete_count = len(self.deletions)
  82. print(f"{delete_count} partitions will be deleted")
  83. print(f"{create_count} partitions will be created")
  84. __all__ = ["PostgresPartitioningPlan", "PostgresModelPartitioningPlan"]