1
1
import textwrap
2
+ from typing import Optional , Mapping , Any
2
3
3
- from sqlalchemy import schema , event , DDL
4
+ from sqlalchemy import schema , event , DDL , Table , Dialect , ExecutableDDLElement
4
5
from sqlalchemy .dialects .postgresql .asyncpg import PGDialect_asyncpg
5
6
from sqlalchemy .dialects .postgresql .base import PGDDLCompiler
6
7
from sqlalchemy .dialects .postgresql .psycopg2 import PGDialect_psycopg2
8
+ from sqlalchemy .engine .interfaces import SchemaTranslateMapType
9
+ from sqlalchemy .ext import compiler
10
+ from sqlalchemy_utils .view import CreateView , compile_create_materialized_view
7
11
8
12
try :
9
13
import alembic
@@ -16,8 +20,44 @@ class TimescaledbImpl(postgresql.PostgresqlImpl):
16
20
__dialect__ = 'timescaledb'
17
21
18
22
23
+ def _get_interval (value ):
24
+ if isinstance (value , str ):
25
+ return f"INTERVAL '{ value } '"
26
+ elif isinstance (value , int ):
27
+ return str (value )
28
+ else :
29
+ return "NULL"
30
+
31
+
32
+ def _create_map (mapping : dict ):
33
+ return ", " .join ([f'{ key } => { value } ' for key , value in mapping .items ()])
34
+
35
+
36
+ @compiler .compiles (CreateView , 'timescaledb' )
37
+ def compile_create_view (create , compiler , ** kw ):
38
+ return compiler .visit_create_view (create , ** kw )
39
+
19
40
class TimescaledbDDLCompiler (PGDDLCompiler ):
20
- def post_create_table (self , table ):
41
+
42
+ def visit_create_view (self , create , ** kw ):
43
+ ret = compile_create_materialized_view (create , self , ** kw )
44
+ view = create .element
45
+ continuous = view .kwargs .get ('timescaledb_continuous' , {})
46
+ if continuous :
47
+ event .listen (
48
+ view ,
49
+ 'after_create' ,
50
+ self .ddl_add_continuous (
51
+ view .name , continuous
52
+ ).execute_if (
53
+ dialect = 'timescaledb'
54
+ )
55
+ )
56
+ return ret
57
+
58
+ def visit_create_table (self , create , ** kw ):
59
+ ret = super ().visit_create_table (create , ** kw )
60
+ table = create .element
21
61
hypertable = table .kwargs .get ('timescaledb_hypertable' , {})
22
62
compress = table .kwargs .get ('timescaledb_compress' , {})
23
63
@@ -52,28 +92,15 @@ def post_create_table(self, table):
52
92
)
53
93
)
54
94
55
-
56
- return super ().post_create_table (table )
95
+ return ret
57
96
58
97
@staticmethod
59
98
def ddl_hypertable (table_name , hypertable ):
60
99
time_column_name = hypertable ['time_column_name' ]
61
- chunk_time_interval = hypertable .get ('chunk_time_interval' , '7 days' )
62
-
63
- if isinstance (chunk_time_interval , str ):
64
- if chunk_time_interval .isdigit ():
65
- chunk_time_interval = int (chunk_time_interval )
66
- else :
67
- chunk_time_interval = f"INTERVAL '{ chunk_time_interval } '"
100
+ chunk_time_interval = _get_interval (hypertable .get ('chunk_time_interval' , '7 days' ))
68
101
69
- return DDL (textwrap .dedent (f"""
70
- SELECT create_hypertable(
71
- '{ table_name } ',
72
- '{ time_column_name } ',
73
- chunk_time_interval => { chunk_time_interval } ,
74
- if_not_exists => TRUE
75
- )
76
- """ ))
102
+ parameters = _create_map (dict (chunk_time_interval = chunk_time_interval , if_not_exists = "TRUE" ))
103
+ return DDL (textwrap .dedent (f"""SELECT create_hypertable('{ table_name } ','{ time_column_name } ',{ parameters } )""" ))
77
104
78
105
@staticmethod
79
106
def ddl_compress (table_name , compress ):
@@ -85,11 +112,20 @@ def ddl_compress(table_name, compress):
85
112
86
113
@staticmethod
87
114
def ddl_compression_policy (table_name , compress ):
88
- compression_policy_interval = compress .get ('compression_policy_interval ' , '7 days' )
115
+ schedule_interval = _get_interval ( compress .get ('compression_policy_schedule_interval ' , '7 days' ) )
89
116
90
- return DDL (textwrap .dedent (f"""
91
- SELECT add_compression_policy('{ table_name } ', INTERVAL '{ compression_policy_interval } ')
92
- """ ))
117
+ parameters = _create_map (dict (schedule_interval = schedule_interval ))
118
+ return DDL (textwrap .dedent (f"""SELECT add_compression_policy('{ table_name } ', { parameters } ')""" ))
119
+
120
+ @staticmethod
121
+ def ddl_add_continuous (table_name , continuous ):
122
+ start_offset = _get_interval (continuous .get ('continuous_aggregate_policy_start_offset' , None ))
123
+ end_offset = _get_interval (continuous .get ('continuous_aggregate_policy_end_offset' , None ))
124
+ schedule_interval = _get_interval (continuous .get ('continuous_aggregate_policy_schedule_interval' , None ))
125
+
126
+ parameters = _create_map (
127
+ dict (start_offset = start_offset , end_offset = end_offset , schedule_interval = schedule_interval ))
128
+ return DDL (textwrap .dedent (f"""SELECT add_continuous_aggregate_policy('{ table_name } ', { parameters } ')""" ))
93
129
94
130
95
131
class TimescaledbDialect :
@@ -99,7 +135,8 @@ class TimescaledbDialect:
99
135
(
100
136
schema .Table , {
101
137
"hypertable" : {},
102
- "compress" : {}
138
+ "compress" : {},
139
+ "continuous" : {},
103
140
}
104
141
)
105
142
]
0 commit comments