Skip to content

Commit eafd743

Browse files
committed
[postgis,schema] TimescaleSchemaEditor: apply changes done in "postgres schema"
1 parent f9748f4 commit eafd743

File tree

1 file changed

+101
-14
lines changed

1 file changed

+101
-14
lines changed

timescale/db/backends/postgis/schema.py

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,61 @@
44

55

66
class TimescaleSchemaEditor(PostGISSchemaEditor):
7+
sql_is_hypertable = 'SELECT * FROM timescaledb_information.hypertables WHERE hypertable_name = {table}'
8+
9+
sql_assert_is_hypertable = (
10+
'DO $do$ BEGIN '
11+
'IF EXISTS ( '
12+
+ sql_is_hypertable +
13+
') '
14+
'THEN NULL; '
15+
'ELSE RAISE EXCEPTION {error_message}; '
16+
'END IF;'
17+
'END; $do$'
18+
)
19+
sql_assert_is_not_hypertable = (
20+
'DO $do$ BEGIN '
21+
'IF EXISTS ( '
22+
+ sql_is_hypertable +
23+
') '
24+
'THEN RAISE EXCEPTION {error_message}; '
25+
'ELSE NULL; '
26+
'END IF;'
27+
'END; $do$'
28+
)
29+
30+
sql_drop_primary_key = 'ALTER TABLE {table} DROP CONSTRAINT {pkey}'
31+
732
sql_add_hypertable = (
833
"SELECT create_hypertable("
934
"{table}, {partition_column}, "
10-
"chunk_time_interval => interval {interval})"
35+
"chunk_time_interval => interval {interval}, "
36+
"migrate_data => {migrate})"
1137
)
1238

13-
sql_drop_primary_key = (
14-
'ALTER TABLE {table} '
15-
'DROP CONSTRAINT {pkey}'
16-
)
39+
sql_set_chunk_time_interval = 'SELECT set_chunk_time_interval({table}, interval {interval})'
1740

18-
def drop_primary_key(self, model):
41+
def _assert_is_hypertable(self, model):
42+
"""
43+
Assert if the table is a hyper table
44+
"""
45+
table = self.quote_value(model._meta.db_table)
46+
error_message = self.quote_value("assert failed - " + table + " should be a hyper table")
47+
48+
sql = self.sql_assert_is_hypertable.format(table=table, error_message=error_message)
49+
self.execute(sql)
50+
51+
def _assert_is_not_hypertable(self, model):
52+
"""
53+
Assert if the table is not a hyper table
54+
"""
55+
table = self.quote_value(model._meta.db_table)
56+
error_message = self.quote_value("assert failed - " + table + " should not be a hyper table")
57+
58+
sql = self.sql_assert_is_not_hypertable.format(table=table, error_message=error_message)
59+
self.execute(sql)
60+
61+
def _drop_primary_key(self, model):
1962
"""
2063
Hypertables can't partition if the primary key is not
2164
the partition column.
@@ -29,26 +72,70 @@ def drop_primary_key(self, model):
2972

3073
self.execute(sql)
3174

32-
def create_hypertable(self, model, field):
75+
def _create_hypertable(self, model, field, should_migrate=False):
3376
"""
3477
Create the hypertable with the partition column being the field.
3578
"""
79+
# assert that the table is not already a hypertable
80+
self._assert_is_not_hypertable(model)
81+
82+
# drop primary key of the table
83+
self._drop_primary_key(model)
84+
3685
partition_column = self.quote_value(field.column)
3786
interval = self.quote_value(field.interval)
3887
table = self.quote_value(model._meta.db_table)
88+
migrate = "true" if should_migrate else "false"
89+
90+
if should_migrate and getattr(settings, "TIMESCALE_MIGRATE_HYPERTABLE_WITH_FRESH_TABLE", False):
91+
# TODO migrate with fresh table [https://github.com/schlunsen/django-timescaledb/issues/16]
92+
raise NotImplementedError()
93+
else:
94+
sql = self.sql_add_hypertable.format(
95+
table=table, partition_column=partition_column, interval=interval, migrate=migrate
96+
)
97+
self.execute(sql)
98+
99+
def _set_chunk_time_interval(self, model, field):
100+
"""
101+
Change time interval for hypertable
102+
"""
103+
# assert if already a hypertable
104+
self._assert_is_hypertable(model)
39105

40-
sql = self.sql_add_hypertable.format(
41-
table=table, partition_column=partition_column, interval=interval
42-
)
106+
table = self.quote_value(model._meta.db_table)
107+
interval = self.quote_value(field.interval)
43108

109+
sql = self.sql_set_chunk_time_interval.format(table=table, interval=interval)
44110
self.execute(sql)
45111

46112
def create_model(self, model):
47113
super().create_model(model)
48114

115+
# scan if any field is of instance `TimescaleDateTimeField`
49116
for field in model._meta.local_fields:
50-
if not isinstance(field, TimescaleDateTimeField):
51-
continue
117+
if isinstance(field, TimescaleDateTimeField):
118+
# create hypertable, with the field as partition column
119+
self._create_hypertable(model, field)
120+
break
121+
122+
def add_field(self, model, field):
123+
super().add_field(model, field)
124+
125+
# check if this field is type `TimescaleDateTimeField`
126+
if isinstance(field, TimescaleDateTimeField):
127+
# migrate existing table to hypertable
128+
self._create_hypertable(model, field, True)
129+
130+
def alter_field(self, model, old_field, new_field, strict=False):
131+
super().alter_field(model, old_field, new_field, strict)
52132

53-
self.drop_primary_key(model)
54-
self.create_hypertable(model, field)
133+
# check if old_field is not type `TimescaleDateTimeField` and new_field is
134+
if not isinstance(old_field, TimescaleDateTimeField) and isinstance(new_field, TimescaleDateTimeField):
135+
# migrate existing table to hypertable
136+
self._create_hypertable(model, new_field, True)
137+
# check if old_field and new_field is type `TimescaleDateTimeField` and `interval` is changed
138+
elif isinstance(old_field, TimescaleDateTimeField) and isinstance(new_field, TimescaleDateTimeField) \
139+
and old_field.interval != new_field.interval:
140+
# change chunk-size
141+
self._set_chunk_time_interval(model, new_field)

0 commit comments

Comments
 (0)