timescaledb-rails 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b682514201eb2e88a829297e9582cfe9513a127432de8acf7a060b769f38ca2
4
- data.tar.gz: c30e268a3b9be19d95537230126a1c12c8aa97746afb3227f0e544293527d399
3
+ metadata.gz: 7fc0e42b6db3e42267d014923180abf3c64a643937942b8b3f17ad9b2a3bf223
4
+ data.tar.gz: 96c3105df56f184894cdd7e3ca8658b083a87e8a050711c17e52eeae6c836c3e
5
5
  SHA512:
6
- metadata.gz: 1011081beb9b4a23ff4ceafe7b47278ad7418ace4438e3a67097e8db933802afd15875ba0a28f4141154ba12170bff7e4db28c4d8c5040d716c49f53b6c8d328
7
- data.tar.gz: cd429d48862ce9dd82002fe35d3953814e893bd066a6e2143a9d5245036015d21b2b23174165389888225649e963a696a2328e0b5a228bf88efc268c39a5919e
6
+ metadata.gz: 9988f806f0512c73456e531dd42dfe3f3c5765d9ee426bcf6f4b45e3098568fcac3518884abde8fb473a42cba66d73aa4558631b2806256ea4726496a212bdb9
7
+ data.tar.gz: e85fa03def618ed5d114275a6f2b929871ca0c0ea52e95ada85cca332baa2f1252f8a6bc3fc29be544aaa8dc8225dbcda52df83903bedd530a63b3683be30e8b
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
- # TimescaleDB extension for Rails [![Actions Status](https://github.com/crunchloop/timescaledb-rails/workflows/CI/badge.svg?branch=main)](https://github.com/crunchloop/timescaledb-rails/actions?query=workflow%3ACI)
2
-
3
- `timescaledb-rails` extends ActiveRecord PostgreSQL adapter and provides features from [`TimescaleDB`](https://www.timescale.com). It provides support for hypertables and other features added by TimescaleDB PostgreSQL extension.
1
+ # TimescaleDB extension for Rails [![Gem Version](https://badge.fury.io/rb/timescaledb-rails.svg)](https://badge.fury.io/rb/timescaledb-rails) [![Actions Status](https://github.com/crunchloop/timescaledb-rails/workflows/CI/badge.svg?branch=main)](https://github.com/crunchloop/timescaledb-rails/actions?query=workflow%3ACI)
4
2
 
3
+ `timescaledb-rails` extends ActiveRecord PostgreSQL adapter and provides features from [TimescaleDB](https://www.timescale.com). It provides support for hypertables and other features added by TimescaleDB PostgreSQL extension.
5
4
 
6
5
  ## Installation
7
6
 
@@ -17,16 +16,18 @@ Or include it in your project's `Gemfile` with Bundler:
17
16
  gem 'timescaledb-rails', '~> 0.1'
18
17
  ```
19
18
 
20
- ## Examples
19
+ ## Usage
20
+
21
+ ### Migrations
21
22
 
22
- Create a hypertable from a PostgreSQL table by doing:
23
+ Create a hypertable from a PostgreSQL table
23
24
 
24
25
  ```ruby
25
26
  class CreateEvent < ActiveRecord::Migration[7.0]
26
27
  def change
27
28
  create_table :events, id: false do |t|
28
29
  t.string :name, null: false
29
- t.time :occured_at, null: false
30
+ t.time :occurred_at, null: false
30
31
 
31
32
  t.timestamps
32
33
  end
@@ -36,7 +37,7 @@ class CreateEvent < ActiveRecord::Migration[7.0]
36
37
  end
37
38
  ```
38
39
 
39
- Create a hypertable without a PostgreSQL table by doing:
40
+ Create a hypertable without a PostgreSQL table
40
41
 
41
42
  ```ruby
42
43
  class CreatePayloadHypertable < ActiveRecord::Migration[7.0]
@@ -50,29 +51,228 @@ class CreatePayloadHypertable < ActiveRecord::Migration[7.0]
50
51
  end
51
52
  ```
52
53
 
53
- Enable hypertable compression by doing:
54
+ Add hypertable compression
54
55
 
55
56
  ```ruby
56
57
  class AddEventCompression < ActiveRecord::Migration[7.0]
57
- def change
58
- add_hypertable_compression :events, 20.days, segment_by: :name, order_by: 'occured_at DESC'
58
+ def up
59
+ add_hypertable_compression :events, 20.days, segment_by: :name, order_by: 'occurred_at DESC'
60
+ end
61
+
62
+ def down
63
+ remove_hypertable_compression :events
59
64
  end
60
65
  end
61
66
  ```
62
67
 
63
- Disable hypertable compression by doing:
68
+ Add hypertable retention policy
64
69
 
65
70
  ```ruby
66
- class RemoveEventCompression < ActiveRecord::Migration[7.0]
67
- def change
68
- remove_hypertable_compression :events
71
+ class AddEventRetentionPolicy < ActiveRecord::Migration[7.0]
72
+ def up
73
+ add_hypertable_retention_policy :events, 1.year
74
+ end
75
+
76
+ def down
77
+ remove_hypertable_retention_policy :events
78
+ end
79
+ end
80
+ ```
81
+
82
+ Add hypertable reorder policy
83
+
84
+ ```ruby
85
+ class AddEventReorderPolicy < ActiveRecord::Migration[7.0]
86
+ def up
87
+ add_hypertable_reorder_policy :events, :index_events_on_created_at_and_name
88
+ end
89
+
90
+ def down
91
+ remove_hypertable_reorder_policy :events
69
92
  end
70
93
  end
71
94
  ```
72
95
 
96
+ Create continuous aggregate
97
+
98
+ ```ruby
99
+ class CreateTemperatureEventAggregate < ActiveRecord::Migration[7.0]
100
+ def up
101
+ create_continuous_aggregate(
102
+ :temperature_events,
103
+ Event.time_bucket(1.day).avg(:value).temperature.to_sql
104
+ )
105
+
106
+ add_continuous_aggregate_policy(:temperature_events, 1.month, 1.day, 1.hour)
107
+ end
108
+
109
+ def down
110
+ drop_continuous_aggregate(:temperature_events)
111
+
112
+ remove_continuous_aggregate_policy(:temperature_events)
113
+ end
114
+ end
115
+ ```
116
+
117
+ > **Reversible Migrations:**
118
+ >
119
+ > Above examples implement `up`/`down` methods to better document all the different APIs. Feel free to use `change` method, timescaledb-rails defines all the reverse calls for each API method so Active Record can automatically figure out how to reverse your migration.
120
+
121
+ ### Models
122
+
123
+ If one of your models need TimescaleDB support, just include `Timescaledb::Rails::Model`
124
+
125
+ ```ruby
126
+ class Payload < ActiveRecord::Base
127
+ include Timescaledb::Rails::Model
128
+
129
+ self.primary_key = 'id'
130
+ end
131
+ ```
132
+
133
+ When hypertable belongs to a non default schema, don't forget to override `table_name`
134
+
135
+ ```ruby
136
+ class Event < ActiveRecord::Base
137
+ include Timescaledb::Rails::Model
138
+
139
+ self.table_name = 'tdb.events'
140
+ end
141
+ ```
142
+
143
+ Using `.find` is not recommended, to achieve more performat results, use these other find methods
144
+
145
+ ```ruby
146
+ # When you know the exact time value
147
+ Payload.find_at_time(111, Time.new(2022, 01, 01, 10, 15, 30))
148
+
149
+ # If you know that the record occurred after a given time
150
+ Payload.find_after(222, 11.days.ago)
151
+
152
+ # Lastly, if you want to scope the search by a time range
153
+ Payload.find_between(333, 1.week.ago, 1.day.ago)
154
+ ```
155
+
156
+ If you need to query data for a specific time period, `Timescaledb::Rails::Model` includes useful scopes
157
+
158
+ ```ruby
159
+ # If you want to get all records from last year
160
+ Event.last_year #=> [#<Event name...>, ...]
161
+
162
+ # Or if you want to get records from this year
163
+ Event.this_year #=> [#<Event name...>, ...]
164
+
165
+ # Or even getting records from today
166
+ Event.today #=> [#<Event name...>, ...]
167
+ ```
168
+
169
+ Here the list of all available scopes
170
+
171
+ * last_year
172
+ * last_month
173
+ * last_week
174
+ * this_year
175
+ * this_month
176
+ * this_week
177
+ * yesterday
178
+ * today
179
+
180
+ If you still need to query data by other time periods, take a look at these other scopes
181
+
182
+ ```ruby
183
+ # If you want to get all records that occurred in the last 30 minutes
184
+ Event.after(30.minutes.ago) #=> [#<Event name...>, ...]
185
+
186
+ # If you want to get records that occurred in the last 4 days, excluding today
187
+ Event.between(4.days.ago, 1.day.ago) #=> [#<Event name...>, ...]
188
+
189
+ # If you want to get records that occurred at a specific time
190
+ Event.at_time(Time.new(2023, 01, 04, 10, 20, 30)) #=> [#<Event name...>, ...]
191
+ ```
192
+
193
+ If you need information about your hypertable, use the following helper methods to get useful information
194
+
195
+ ```ruby
196
+ # Hypertable metadata
197
+ Event.hypertable #=> #<Timescaledb::Rails::Hypertable ...>
198
+
199
+ # Hypertable chunks metadata
200
+ Event.hypertable_chunks #=> [#<Timescaledb::Rails::Chunk ...>, ...]
201
+
202
+ # Hypertable jobs, it includes jobs like compression, retention or reorder policies, etc.
203
+ Event.hypertable_jobs #=> [#<Timescaledb::Rails::Job ...>, ...]
204
+
205
+ # Hypertable dimensions, like time or space dimensions
206
+ Event.hypertable_dimensions #=> [#<Timescaledb::Rails::Dimension ...>, ...]
207
+
208
+ # Hypertable compression settings
209
+ Event.hypertable_compression_settings #=> [#<Timescaledb::Rails::CompressionSetting ...>, ...]
210
+ ```
211
+
212
+ If you need to compress or decompress a specific chunk
213
+
214
+ ```ruby
215
+ chunk = Event.hypertable_chunks.first
216
+
217
+ chunk.compress! unless chunk.is_compressed?
218
+
219
+ chunk.decompress! if chunk.is_compressed?
220
+ ```
221
+
222
+ If you need to reorder a specific chunk
223
+
224
+ ```ruby
225
+ chunk = Event.hypertable_chunks.first
226
+
227
+ # If an index is not specified, it will use the one from the reorder policy
228
+ # In case there is no reorder policy index it will raise an ArgumentError
229
+ chunk.reorder!
230
+
231
+ # If an index is specified it will use that index
232
+ chunk.reorder!(index)
233
+ ```
234
+
235
+ If you need to manually refresh a continuous aggregate
236
+
237
+ ```ruby
238
+ aggregate = Event.hypertable.continuous_aggregates.first
239
+
240
+ aggregate.refresh!(5.days.ago, 1.day.ago)
241
+ ```
242
+
243
+ ### Hyperfunctions
244
+
245
+ #### Time bucket
246
+
247
+ You can call the time bucket function with an interval (note that leaving the target column blank will use the default time column of the hypertable)
248
+
249
+ ```ruby
250
+ Event.time_bucket(1.day)
251
+
252
+ Event.time_bucket('1 day')
253
+
254
+ Event.time_bucket(1.day, :created_at)
255
+
256
+ Event.time_bucket(1.day, 'occurred_at')
257
+ ```
258
+
259
+ You may add aggregation like so:
260
+
261
+ ```ruby
262
+ Event.time_bucket(1.day).avg(:column)
263
+ Event.time_bucket(1.day).sum(:column)
264
+ Event.time_bucket(1.day).min(:column)
265
+ Event.time_bucket(1.day).max(:column)
266
+ Event.time_bucket(1.day).count
267
+ ```
268
+
269
+ ## Contributing
270
+
271
+ Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.
272
+
73
273
  ## Supported Ruby/Rails versions
74
274
 
75
- Supported Ruby/Rails versions are listed in [`.github/workflows/ci.yaml`](https://github.com/crunchloop/timescaledb-rails/blob/main/.github/workflows/ci.yaml)
275
+ Supported Ruby/Rails versions are listed in [`.github/workflows/ci.yaml`](./.github/workflows/ci.yaml)
76
276
 
77
277
  ## License
78
278
 
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Timescaledb
4
+ module Rails
5
+ module ActiveRecord
6
+ # :nodoc:
7
+ module Base
8
+ extend ActiveSupport::Concern
9
+
10
+ # :nodoc:
11
+ module ClassMethods
12
+ # Returns if the current active record model is a hypertable.
13
+ def hypertable?
14
+ connection.hypertable_exists?(table_name)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -17,6 +17,38 @@ module Timescaledb
17
17
  record(:remove_hypertable_compression, args, &block)
18
18
  end
19
19
 
20
+ def add_hypertable_reorder_policy(*args, &block)
21
+ record(:add_hypertable_reorder_policy, args, &block)
22
+ end
23
+
24
+ def remove_hypertable_reorder_policy(*args, &block)
25
+ record(:remove_hypertable_reorder_policy, args, &block)
26
+ end
27
+
28
+ def add_hypertable_retention_policy(*args, &block)
29
+ record(:add_hypertable_retention_policy, args, &block)
30
+ end
31
+
32
+ def remove_hypertable_retention_policy(*args, &block)
33
+ record(:remove_hypertable_retention_policy, args, &block)
34
+ end
35
+
36
+ def create_continuous_aggregate(*args, &block)
37
+ record(:create_continuous_aggregate, args, &block)
38
+ end
39
+
40
+ def drop_continuous_aggregate(*args, &block)
41
+ record(:drop_continuous_aggregate, args, &block)
42
+ end
43
+
44
+ def add_continuous_aggregate_policy(*args, &block)
45
+ record(:add_continuous_aggregate_policy, args, &block)
46
+ end
47
+
48
+ def remove_continuous_aggregate_policy(*args, &block)
49
+ record(:remove_continuous_aggregate_policy, args, &block)
50
+ end
51
+
20
52
  def invert_create_hypertable(args, &block)
21
53
  if block.nil?
22
54
  raise ::ActiveRecord::IrreversibleMigration, 'create_hypertable is only reversible if given a block (can be empty).' # rubocop:disable Layout/LineLength
@@ -36,6 +68,54 @@ module Timescaledb
36
68
 
37
69
  [:add_hypertable_compression, args, block]
38
70
  end
71
+
72
+ def invert_add_hypertable_retention_policy(args, &block)
73
+ [:remove_hypertable_retention_policy, args, block]
74
+ end
75
+
76
+ def invert_remove_hypertable_retention_policy(args, &block)
77
+ if args.size < 2
78
+ raise ::ActiveRecord::IrreversibleMigration, 'remove_hypertable_retention_policy is only reversible if given table name and drop after period.' # rubocop:disable Layout/LineLength
79
+ end
80
+
81
+ [:add_hypertable_retention_policy, args, block]
82
+ end
83
+
84
+ def invert_add_hypertable_reorder_policy(args, &block)
85
+ [:remove_hypertable_reorder_policy, args, block]
86
+ end
87
+
88
+ def invert_remove_hypertable_reorder_policy(args, &block)
89
+ if args.size < 2
90
+ raise ::ActiveRecord::IrreversibleMigration, 'remove_hypertable_reorder_policy is only reversible if given table name and index name.' # rubocop:disable Layout/LineLength
91
+ end
92
+
93
+ [:add_hypertable_reorder_policy, args, block]
94
+ end
95
+
96
+ def invert_create_continuous_aggregate(args, &block)
97
+ [:drop_continuous_aggregate, args, block]
98
+ end
99
+
100
+ def invert_drop_continuous_aggregate(args, &block)
101
+ if args.size < 2
102
+ raise ::ActiveRecord::IrreversibleMigration, 'drop_continuous_aggregate is only reversible if given view name and view query.' # rubocop:disable Layout/LineLength
103
+ end
104
+
105
+ [:create_continuous_aggregate, args, block]
106
+ end
107
+
108
+ def invert_add_continuous_aggregate_policy(args, &block)
109
+ [:remove_continuous_aggregate_policy, args, block]
110
+ end
111
+
112
+ def invert_remove_continuous_aggregate_policy(args, &block)
113
+ if args.size < 4
114
+ raise ::ActiveRecord::IrreversibleMigration, 'remove_continuous_aggregate_policy is only reversible if given view name, start offset, end offset and schedule interval.' # rubocop:disable Layout/LineLength
115
+ end
116
+
117
+ [:add_continuous_aggregate_policy, args, block]
118
+ end
39
119
  end
40
120
  end
41
121
  end
@@ -7,36 +7,53 @@ module Timescaledb
7
7
  module Rails
8
8
  module ActiveRecord
9
9
  # :nodoc:
10
+ # rubocop:disable Layout/LineLength
10
11
  module PostgreSQLDatabaseTasks
11
12
  # @override
12
- def structure_dump(filename, extra_flags) # rubocop:disable Metrics/MethodLength
13
+ def structure_dump(filename, extra_flags)
13
14
  extra_flags = Array(extra_flags)
14
- extra_flags << timescale_structure_dump_default_flags if timescale_enabled?
15
+ extra_flags |= timescale_structure_dump_default_flags if timescale_enabled?
15
16
 
16
17
  super(filename, extra_flags)
17
18
 
18
19
  return unless timescale_enabled?
19
20
 
21
+ hypertables(filename)
22
+ continuous_aggregates(filename)
23
+ end
24
+
25
+ def hypertables(filename)
20
26
  File.open(filename, 'a') do |file|
21
27
  Timescaledb::Rails::Hypertable.all.each do |hypertable|
22
28
  drop_ts_insert_trigger_statment(hypertable, file)
23
29
  create_hypertable_statement(hypertable, file)
24
30
  add_hypertable_compression_statement(hypertable, file)
31
+ add_hypertable_reorder_policy_statement(hypertable, file)
32
+ add_hypertable_retention_policy_statement(hypertable, file)
33
+ end
34
+ end
35
+ end
36
+
37
+ def continuous_aggregates(filename)
38
+ File.open(filename, 'a') do |file|
39
+ Timescaledb::Rails::ContinuousAggregate.all.each do |continuous_aggregate|
40
+ create_continuous_aggregate_statement(continuous_aggregate, file)
41
+ add_continuous_aggregate_policy_statement(continuous_aggregate, file)
25
42
  end
26
43
  end
27
44
  end
28
45
 
29
46
  def drop_ts_insert_trigger_statment(hypertable, file)
30
47
  file << "---\n"
31
- file << "--- Drop ts_insert_blocker previously created by pg_dump to avoid pg errors, create_hypertable will re-create it again.\n" # rubocop:disable Layout/LineLength
48
+ file << "--- Drop ts_insert_blocker previously created by pg_dump to avoid pg errors, create_hypertable will re-create it again.\n"
32
49
  file << "---\n\n"
33
- file << "DROP TRIGGER IF EXISTS ts_insert_blocker ON #{hypertable.hypertable_name};\n"
50
+ file << "DROP TRIGGER IF EXISTS ts_insert_blocker ON #{hypertable.hypertable_schema}.#{hypertable.hypertable_name};\n"
34
51
  end
35
52
 
36
53
  def create_hypertable_statement(hypertable, file)
37
54
  options = hypertable_options(hypertable)
38
55
 
39
- file << "SELECT create_hypertable('#{hypertable.hypertable_name}', '#{hypertable.time_column_name}', #{options});\n\n" # rubocop:disable Layout/LineLength
56
+ file << "SELECT create_hypertable('#{hypertable.hypertable_schema}.#{hypertable.hypertable_name}', '#{hypertable.time_column_name}', #{options});\n\n"
40
57
  end
41
58
 
42
59
  def add_hypertable_compression_statement(hypertable, file)
@@ -44,8 +61,35 @@ module Timescaledb
44
61
 
45
62
  options = hypertable_compression_options(hypertable)
46
63
 
47
- file << "ALTER TABLE #{hypertable.hypertable_name} SET (#{options});\n\n"
48
- file << "SELECT add_compression_policy('#{hypertable.hypertable_name}', INTERVAL '#{hypertable.compression_policy_interval}');\n\n" # rubocop:disable Layout/LineLength
64
+ file << "ALTER TABLE #{hypertable.hypertable_schema}.#{hypertable.hypertable_name} SET (#{options});\n\n"
65
+ file << "SELECT add_compression_policy('#{hypertable.hypertable_schema}.#{hypertable.hypertable_name}', INTERVAL '#{hypertable.compression_policy_interval}');\n\n"
66
+ end
67
+
68
+ def add_hypertable_reorder_policy_statement(hypertable, file)
69
+ return unless hypertable.reorder?
70
+
71
+ file << "SELECT add_reorder_policy('#{hypertable.hypertable_schema}.#{hypertable.hypertable_name}', '#{hypertable.reorder_policy_index_name}');\n\n"
72
+ end
73
+
74
+ def add_hypertable_retention_policy_statement(hypertable, file)
75
+ return unless hypertable.retention?
76
+
77
+ file << "SELECT add_retention_policy('#{hypertable.hypertable_schema}.#{hypertable.hypertable_name}', INTERVAL '#{hypertable.retention_policy_interval}');\n\n"
78
+ end
79
+
80
+ def create_continuous_aggregate_statement(continuous_aggregate, file)
81
+ file << "CREATE MATERIALIZED VIEW #{continuous_aggregate.view_schema}.#{continuous_aggregate.view_name} WITH (timescaledb.continuous) AS\n"
82
+ file << "#{continuous_aggregate.view_definition.strip.indent(2)}\n\n"
83
+ end
84
+
85
+ def add_continuous_aggregate_policy_statement(continuous_aggregate, file)
86
+ return unless continuous_aggregate.refresh?
87
+
88
+ start_offset = continuous_aggregate.refresh_start_offset
89
+ end_offset = continuous_aggregate.refresh_end_offset
90
+ schedule_interval = continuous_aggregate.refresh_schedule_interval
91
+
92
+ file << "SELECT add_continuous_aggregate_policy('#{continuous_aggregate.view_schema}.#{continuous_aggregate.view_name}', start_offset => INTERVAL '#{start_offset}', end_offset => INTERVAL '#{end_offset}', schedule_interval => INTERVAL '#{schedule_interval}');\n\n"
49
93
  end
50
94
 
51
95
  def hypertable_options(hypertable)
@@ -56,27 +100,41 @@ module Timescaledb
56
100
  end
57
101
 
58
102
  def hypertable_compression_options(hypertable)
59
- segmentby_setting = hypertable.compression_settings.segmentby_setting.first
60
- orderby_setting = hypertable.compression_settings.orderby_setting.first
61
-
62
103
  sql_statements = ['timescaledb.compress']
63
- sql_statements << "timescaledb.compress_segmentby = '#{segmentby_setting.attname}'" if segmentby_setting
64
104
 
65
- if orderby_setting
66
- orderby = Timescaledb::Rails::OrderbyCompression.new(orderby_setting.attname,
67
- orderby_setting.orderby_asc).to_s
105
+ if (segments = compression_segment_settings(hypertable)).present?
106
+ sql_statements << "timescaledb.compress_segmentby = '#{segments.join(', ')}'"
107
+ end
68
108
 
69
- sql_statements << "timescaledb.compress_orderby = '#{orderby}'"
109
+ if (orders = compression_order_settings(hypertable)).present?
110
+ sql_statements << "timescaledb.compress_orderby = '#{orders.join(', ')}'"
70
111
  end
71
112
 
72
113
  sql_statements.join(', ')
73
114
  end
74
115
 
75
- # Returns `pg_dump` flag to exclude `_timescaledb_internal` schema tables.
116
+ def compression_order_settings(hypertable)
117
+ hypertable.compression_order_settings.map do |os|
118
+ Timescaledb::Rails::OrderbyCompression.new(os.attname, os.orderby_asc).to_s
119
+ end
120
+ end
121
+
122
+ def compression_segment_settings(hypertable)
123
+ hypertable.compression_segment_settings.map(&:attname)
124
+ end
125
+
126
+ # Returns `pg_dump` flags to exclude `_timescaledb_internal` schema tables and
127
+ # exclude the corresponding continuous aggregate views.
76
128
  #
77
- # @return [String]
129
+ # @return [Array<String>]
78
130
  def timescale_structure_dump_default_flags
79
- '--exclude-schema=_timescaledb_internal'
131
+ flags = ['--exclude-schema=_timescaledb_internal']
132
+
133
+ Timescaledb::Rails::ContinuousAggregate.pluck(:view_schema, :view_name).each do |view_schema, view_name|
134
+ flags << "--exclude-table=#{view_schema}.#{view_name}"
135
+ end
136
+
137
+ flags
80
138
  end
81
139
 
82
140
  # @return [Boolean]
@@ -84,6 +142,7 @@ module Timescaledb
84
142
  Timescaledb::Rails::Hypertable.table_exists?
85
143
  end
86
144
  end
145
+ # rubocop:enable Layout/LineLength
87
146
  end
88
147
  end
89
148
  end
@@ -7,7 +7,46 @@ module Timescaledb
7
7
  module Rails
8
8
  module ActiveRecord
9
9
  # :nodoc:
10
- module SchemaDumper
10
+ module SchemaDumper # rubocop:disable Metrics/ModuleLength
11
+ # @override
12
+ def tables(stream)
13
+ super
14
+
15
+ continuous_aggregates(stream)
16
+ stream
17
+ end
18
+
19
+ def continuous_aggregates(stream)
20
+ return unless timescale_enabled?
21
+
22
+ Timescaledb::Rails::ContinuousAggregate.all.each do |continuous_aggregate|
23
+ continuous_aggregate(continuous_aggregate, stream)
24
+ continuous_aggregate_policy(continuous_aggregate, stream)
25
+ end
26
+ end
27
+
28
+ def continuous_aggregate(continuous_aggregate, stream)
29
+ stream.puts " create_continuous_aggregate #{continuous_aggregate.view_name.inspect}, <<-SQL"
30
+ stream.puts " #{continuous_aggregate.view_definition.strip.indent(2)}"
31
+ stream.puts ' SQL'
32
+ stream.puts
33
+ end
34
+
35
+ def continuous_aggregate_policy(continuous_aggregate, stream)
36
+ return unless continuous_aggregate.refresh?
37
+
38
+ options = [
39
+ continuous_aggregate.view_name.inspect,
40
+ continuous_aggregate.refresh_start_offset.inspect,
41
+ continuous_aggregate.refresh_end_offset.inspect,
42
+ continuous_aggregate.refresh_schedule_interval.inspect
43
+ ]
44
+
45
+ stream.puts " add_continuous_aggregate_policy #{options.join(', ')}"
46
+ stream.puts
47
+ end
48
+
49
+ # @override
11
50
  def table(table, stream)
12
51
  super(table, stream)
13
52
 
@@ -16,6 +55,8 @@ module Timescaledb
16
55
 
17
56
  hypertable(hypertable, stream)
18
57
  hypertable_compression(hypertable, stream)
58
+ hypertable_reorder(hypertable, stream)
59
+ hypertable_retention(hypertable, stream)
19
60
  end
20
61
 
21
62
  private
@@ -38,6 +79,24 @@ module Timescaledb
38
79
  stream.puts
39
80
  end
40
81
 
82
+ def hypertable_reorder(hypertable, stream)
83
+ return unless hypertable.reorder?
84
+
85
+ options = [hypertable.hypertable_name.inspect, hypertable.reorder_policy_index_name.inspect]
86
+
87
+ stream.puts " add_hypertable_reorder_policy #{options.join(', ')}"
88
+ stream.puts
89
+ end
90
+
91
+ def hypertable_retention(hypertable, stream)
92
+ return unless hypertable.retention?
93
+
94
+ options = [hypertable.hypertable_name.inspect, hypertable.retention_policy_interval.inspect]
95
+
96
+ stream.puts " add_hypertable_retention_policy #{options.join(', ')}"
97
+ stream.puts
98
+ end
99
+
41
100
  def hypertable_options(hypertable)
42
101
  options = {
43
102
  chunk_time_interval: hypertable.chunk_time_interval
@@ -50,21 +109,27 @@ module Timescaledb
50
109
  end
51
110
 
52
111
  def hypertable_compression_options(hypertable)
53
- segmentby_setting = hypertable.compression_settings.segmentby_setting.first
54
- orderby_setting = hypertable.compression_settings.orderby_setting.first
55
-
56
112
  [].tap do |result|
57
- result << "segment_by: #{segmentby_setting.attname.inspect}" if segmentby_setting
58
-
59
- if orderby_setting
60
- orderby = Timescaledb::Rails::OrderbyCompression.new(orderby_setting.attname,
61
- orderby_setting.orderby_asc).to_s
113
+ if (segments = compression_segment_settings(hypertable)).present?
114
+ result << "segment_by: #{segments.join(', ').inspect}"
115
+ end
62
116
 
63
- result << "order_by: #{orderby.inspect}"
117
+ if (orders = compression_order_settings(hypertable)).present?
118
+ result << "order_by: #{orders.join(', ').inspect}"
64
119
  end
65
120
  end
66
121
  end
67
122
 
123
+ def compression_order_settings(hypertable)
124
+ hypertable.compression_order_settings.map do |os|
125
+ Timescaledb::Rails::OrderbyCompression.new(os.attname, os.orderby_asc).to_s
126
+ end
127
+ end
128
+
129
+ def compression_segment_settings(hypertable)
130
+ hypertable.compression_segment_settings.map(&:attname)
131
+ end
132
+
68
133
  def format_hypertable_option_value(value)
69
134
  case value
70
135
  when String then value.inspect