Example #1
0
 def __init__(self, *args, **kwargs):
     super(RangePartition, self).__init__(*args, **kwargs)
     self.partition_range = kwargs['partition_range']
     self.partition_subtype = kwargs['partition_subtype']
     self.datetime = DateTimeUtil(self.column_value, self.partition_range, model=self.model)
Example #2
0
 def __init__(self, *args, **kwargs):
     super(RangePartition, self).__init__(*args, **kwargs)
     self.partition_range = kwargs['partition_range']
     self.partition_subtype = kwargs['partition_subtype']
     self.datetime = DateTimeUtil(self.column_value, self.partition_range, model=self.model)
Example #3
0
class RangePartition(Partition):
    """Range partition type implementation"""
    def __init__(self, *args, **kwargs):
        super(RangePartition, self).__init__(*args, **kwargs)
        self.partition_range = kwargs['partition_range']
        self.partition_subtype = kwargs['partition_subtype']
        self.datetime = DateTimeUtil(self.column_value, self.partition_range, model=self.model)

    def prepare(self):
        """Converts original table to partitioned one"""
        super(RangePartition, self).prepare()
        self.datetime.now = None

        self.cursor.execute("""
            -- We need to create zero partition to speed up things due to the partitioning
            -- implementation in the early versions of MySQL database (see bug #49754)
            ALTER TABLE {parent_table} PARTITION BY RANGE ({function}({partition_column}))(
                PARTITION {partition_pattern} VALUES LESS THAN (0)
            );
        """.format(
            parent_table=self.table,
            partition_column=self.partition_column,
            partition_pattern=self._get_name(),
            function=self._get_partition_function(),
        ))

        transaction.commit_unless_managed()

    def create(self):
        """Creates new partition"""
        self.cursor.execute("""
            ALTER TABLE {parent_table} ADD PARTITION (
                PARTITION {child_table} VALUES LESS THAN ({function}('{period_end}') + {addition})
            );
        """.format(
            child_table=self._get_name(),
            parent_table=self.table,
            function=self._get_partition_function(),
            period_end=self.datetime.get_period()[1],
            addition='86400' if self._get_column_type() == 'timestamp' else '1',
        ))

        transaction.commit_unless_managed()

    def _get_name(self):
        """Dynamically defines new partition name depending on the partition subtype"""
        try:
            return getattr(self, '_get_{0}_name'.format(self.partition_subtype))()
        except AttributeError:
            import re
            raise PartitionRangeSubtypeError(
                model=self.model,
                current_value=self.partition_subtype,
                allowed_values=[re.match('_get_(\w+)_name', c).group(1) for c in dir(
                    self) if re.match('_get_\w+_name', c) is not None]
            )

    def _get_date_name(self):
        """Defines name for a new partition for date partition subtype"""
        return '{0}_{1}'.format(self.table, self.datetime.get_name())

    def _get_partition_function(self):
        """Returns correct partition function depending on the MySQL column type"""
        functions = {
            'date': 'TO_DAYS',
            'datetime': 'TO_DAYS',
            'timestamp': 'UNIX_TIMESTAMP',
        }

        column_type = self._get_column_type()

        try:
            return functions[column_type]
        except KeyError:
            raise PartitionFunctionError(current_value=column_type, allowed_values=functions.keys())

    def _get_column_type(self):
        """
        We can't rely on self.column_type in MySQL, because Django uses only date
        and datetime types internally, but MySQL has an additional timestamp type
        and we need to know that, otherwise we can apply incorrect partition function
        """
        self.cursor.execute("""
            SELECT data_type
            FROM information_schema.columns
            WHERE table_name = '{parent_table}' AND column_name = '{partition_column}';
        """.format(
            parent_table=self.table,
            partition_column=self.partition_column,
        ))

        return self.cursor.fetchone()[0]
Example #4
0
class RangePartition(Partition):
    """Range partition type implementation"""
    def __init__(self, *args, **kwargs):
        super(RangePartition, self).__init__(*args, **kwargs)
        self.partition_range = kwargs['partition_range']
        self.partition_subtype = kwargs['partition_subtype']
        self.datetime = DateTimeUtil(self.column_value, self.partition_range, model=self.model)

    def prepare(self):
        """Converts original table to partitioned one"""
        super(RangePartition, self).prepare()

        self.cursor.execute("""
            -- We need to create zero partition to speed up things due to the partitioning
            -- implementation in the early versions of MySQL database (see bug #49754)
            ALTER TABLE {parent_table} PARTITION BY RANGE ({function}({partition_column}))(
                PARTITION {partition_pattern} VALUES LESS THAN (0)
            );
        """.format(
            parent_table=self.table,
            partition_column=self.partition_column,
            partition_pattern=self._get_name(),
            function=self._get_partition_function(),
        ))

        transaction.commit_unless_managed()

    def create(self):
        """Creates new partition"""
        self.cursor.execute("""
            ALTER TABLE {parent_table} ADD PARTITION (
                PARTITION {child_table} VALUES LESS THAN ({function}('{period_end}') + {addition})
            );
        """.format(
            child_table=self._get_name(),
            parent_table=self.table,
            function=self._get_partition_function(),
            period_end=self.datetime.get_period()[1],
            addition='86400' if self._get_column_type() == 'timestamp' else '1',
        ))

        transaction.commit_unless_managed()

    def _get_name(self):
        """Dynamically defines new partition name depending on the partition subtype"""
        try:
            return getattr(self, '_get_{}_name'.format(self.partition_subtype))()
        except AttributeError:
            import re
            raise PartitionRangeSubtypeError(
                model=self.model,
                current_value=self.partition_subtype,
                allowed_values=[re.match('_get_(\w+)_name', c).group(1) for c in dir(
                    self) if re.match('_get_\w+_name', c) is not None]
            )

    def _get_date_name(self):
        """Defines name for a new partition for date partition subtype"""
        return '{}_{}'.format(self.table, self.datetime.get_name())

    def _get_partition_function(self):
        """Returns correct partition function depending on the MySQL column type"""
        functions = {
            'date': 'TO_DAYS',
            'datetime': 'TO_DAYS',
            'timestamp': 'UNIX_TIMESTAMP',
        }

        column_type = self._get_column_type()

        try:
            return functions[column_type]
        except KeyError:
            raise PartitionFunctionError(current_value=column_type, allowed_values=functions.keys())

    def _get_column_type(self):
        """
        We can't rely on self.column_type in MySQL, because Django uses only date
        and datetime types internally, but MySQL has an additional timestamp type
        and we need to know that, otherwise we can apply incorrect partition function
        """
        self.cursor.execute("""
            SELECT data_type
            FROM information_schema.columns
            WHERE table_name = '{parent_table}' AND column_name = '{partition_column}';
        """.format(
            parent_table=self.table,
            partition_column=self.partition_column,
        ))

        return self.cursor.fetchone()[0]