timescaledb-rails 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b682514201eb2e88a829297e9582cfe9513a127432de8acf7a060b769f38ca2
4
- data.tar.gz: c30e268a3b9be19d95537230126a1c12c8aa97746afb3227f0e544293527d399
3
+ metadata.gz: 12170e03389f6ac4f3bba7816dfb20dc75a47103032ec3e3799f3c78dca25a4b
4
+ data.tar.gz: f26796af01f755648e952f9e851f24d05d7adb3439e3653d793beef624ea8044
5
5
  SHA512:
6
- metadata.gz: 1011081beb9b4a23ff4ceafe7b47278ad7418ace4438e3a67097e8db933802afd15875ba0a28f4141154ba12170bff7e4db28c4d8c5040d716c49f53b6c8d328
7
- data.tar.gz: cd429d48862ce9dd82002fe35d3953814e893bd066a6e2143a9d5245036015d21b2b23174165389888225649e963a696a2328e0b5a228bf88efc268c39a5919e
6
+ metadata.gz: 22c4c72512895fadb2a7701222e6b890b60a15948dc9c6b5c01ded24afff9865d6ebc954673452b55b95ac68b18c933dc2bf7aec6ac135285ecfa950d346e8e3
7
+ data.tar.gz: f67c442f8d2eb837566f8cc3a450d809a769343799997f3979a222bd0413c2ce89fad70b9b8f3e96267ab61e8bcf3cc2c167c15a60db1f1144ea17daf9cfee36
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,127 @@ 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
69
78
  end
70
79
  end
71
80
  ```
72
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
92
+ end
93
+ end
94
+ ```
95
+
96
+ ### Models
97
+
98
+ If one of your models need TimescaleDB support, just include `Timescaledb::Rails::Model`
99
+ ```ruby
100
+ class Event < ActiveRecord::Base
101
+ include Timescaledb::Rails::Model
102
+ end
103
+ ```
104
+
105
+ If the hypertable does not belong to the default schema, don't forget to override `table_name`
106
+
107
+ ```ruby
108
+ class Event < ActiveRecord::Base
109
+ include Timescaledb::Rails::Model
110
+
111
+ self.table_name = 'v1.events'
112
+ end
113
+ ```
114
+
115
+ If you need to query data for a specific time period, `Timescaledb::Rails::Model` incluldes useful scopes
116
+
117
+ ```ruby
118
+ # If you want to get all records from last year
119
+ Event.last_year #=> [#<Event name...>, ...]
120
+
121
+ # Or if you want to get records from this year
122
+ Event.this_year #=> [#<Event name...>, ...]
123
+
124
+ # Or even getting records from today
125
+ Event.today #=> [#<Event name...>, ...]
126
+ ```
127
+
128
+ Here the list of all available scopes
129
+
130
+ * last_year
131
+ * last_month
132
+ * last_week
133
+ * this_year
134
+ * this_month
135
+ * this_week
136
+ * yesterday
137
+ * today
138
+
139
+ If you need information about your hypertable, use the following helper methods to get useful information
140
+
141
+ ```ruby
142
+ # Hypertable metadata
143
+ Event.hypertable #=> #<Timescaledb::Rails::Hypertable ...>
144
+
145
+ # Hypertable chunks metadata
146
+ Event.hypertable_chunks #=> [#<Timescaledb::Rails::Chunk ...>, ...]
147
+
148
+ # Hypertable jobs, it includes jobs like compression, retention or reorder policies, etc.
149
+ Event.hypertable_jobs #=> [#<Timescaledb::Rails::Job ...>, ...]
150
+
151
+ # Hypertable dimensions, like time or space dimensions
152
+ Event.hypertable_dimensions #=> [#<Timescaledb::Rails::Dimension ...>, ...]
153
+
154
+ # Hypertable compression settings
155
+ Event.hypertable_compression_settings #=> [#<Timescaledb::Rails::CompressionSetting ...>, ...]
156
+ ```
157
+
158
+ If you need to compress or decompress a specific chunk
159
+
160
+ ```ruby
161
+ chunk = Event.hypertable_chunks.first
162
+
163
+ chunk.compress! unless chunk.is_compressed?
164
+
165
+ chunk.decompress! if chunk.is_compressed?
166
+ ```
167
+
168
+ ## Contributing
169
+
170
+ Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.
171
+
73
172
  ## Supported Ruby/Rails versions
74
173
 
75
- Supported Ruby/Rails versions are listed in [`.github/workflows/ci.yaml`](https://github.com/crunchloop/timescaledb-rails/blob/main/.github/workflows/ci.yaml)
174
+ Supported Ruby/Rails versions are listed in [`.github/workflows/ci.yaml`](./.github/workflows/ci.yaml)
76
175
 
77
176
  ## License
78
177
 
@@ -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,22 @@ 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
+
20
36
  def invert_create_hypertable(args, &block)
21
37
  if block.nil?
22
38
  raise ::ActiveRecord::IrreversibleMigration, 'create_hypertable is only reversible if given a block (can be empty).' # rubocop:disable Layout/LineLength
@@ -36,6 +52,30 @@ module Timescaledb
36
52
 
37
53
  [:add_hypertable_compression, args, block]
38
54
  end
55
+
56
+ def invert_add_hypertable_retention_policy(args, &block)
57
+ [:remove_hypertable_retention_policy, args, block]
58
+ end
59
+
60
+ def invert_remove_hypertable_retention_policy(args, &block)
61
+ if args.size < 2
62
+ raise ::ActiveRecord::IrreversibleMigration, 'remove_hypertable_retention_policy is only reversible if given table name and drop after period.' # rubocop:disable Layout/LineLength
63
+ end
64
+
65
+ [:add_hypertable_retention_policy, args, block]
66
+ end
67
+
68
+ def invert_add_hypertable_reorder_policy(args, &block)
69
+ [:remove_hypertable_reorder_policy, args, block]
70
+ end
71
+
72
+ def invert_remove_hypertable_reorder_policy(args, &block)
73
+ if args.size < 2
74
+ raise ::ActiveRecord::IrreversibleMigration, 'remove_hypertable_reorder_policy is only reversible if given table name and index name.' # rubocop:disable Layout/LineLength
75
+ end
76
+
77
+ [:add_hypertable_reorder_policy, args, block]
78
+ end
39
79
  end
40
80
  end
41
81
  end
@@ -22,6 +22,8 @@ module Timescaledb
22
22
  drop_ts_insert_trigger_statment(hypertable, file)
23
23
  create_hypertable_statement(hypertable, file)
24
24
  add_hypertable_compression_statement(hypertable, file)
25
+ add_hypertable_reorder_policy_statement(hypertable, file)
26
+ add_hypertable_retention_policy_statement(hypertable, file)
25
27
  end
26
28
  end
27
29
  end
@@ -48,6 +50,18 @@ module Timescaledb
48
50
  file << "SELECT add_compression_policy('#{hypertable.hypertable_name}', INTERVAL '#{hypertable.compression_policy_interval}');\n\n" # rubocop:disable Layout/LineLength
49
51
  end
50
52
 
53
+ def add_hypertable_reorder_policy_statement(hypertable, file)
54
+ return unless hypertable.reorder?
55
+
56
+ file << "SELECT add_reorder_policy('#{hypertable.hypertable_name}', '#{hypertable.reorder_policy_index_name}');\n\n" # rubocop:disable Layout/LineLength
57
+ end
58
+
59
+ def add_hypertable_retention_policy_statement(hypertable, file)
60
+ return unless hypertable.retention?
61
+
62
+ file << "SELECT add_retention_policy('#{hypertable.hypertable_name}', INTERVAL '#{hypertable.retention_policy_interval}');\n\n" # rubocop:disable Layout/LineLength
63
+ end
64
+
51
65
  def hypertable_options(hypertable)
52
66
  sql_statements = ["if_not_exists => 'TRUE'"]
53
67
  sql_statements << "chunk_time_interval => INTERVAL '#{hypertable.chunk_time_interval}'"
@@ -56,22 +70,29 @@ module Timescaledb
56
70
  end
57
71
 
58
72
  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
73
  sql_statements = ['timescaledb.compress']
63
- sql_statements << "timescaledb.compress_segmentby = '#{segmentby_setting.attname}'" if segmentby_setting
64
74
 
65
- if orderby_setting
66
- orderby = Timescaledb::Rails::OrderbyCompression.new(orderby_setting.attname,
67
- orderby_setting.orderby_asc).to_s
75
+ if (segments = compression_segment_settings(hypertable)).present?
76
+ sql_statements << "timescaledb.compress_segmentby = '#{segments.join(', ')}'"
77
+ end
68
78
 
69
- sql_statements << "timescaledb.compress_orderby = '#{orderby}'"
79
+ if (orders = compression_order_settings(hypertable)).present?
80
+ sql_statements << "timescaledb.compress_orderby = '#{orders.join(', ')}'"
70
81
  end
71
82
 
72
83
  sql_statements.join(', ')
73
84
  end
74
85
 
86
+ def compression_order_settings(hypertable)
87
+ hypertable.compression_order_settings.map do |os|
88
+ Timescaledb::Rails::OrderbyCompression.new(os.attname, os.orderby_asc).to_s
89
+ end
90
+ end
91
+
92
+ def compression_segment_settings(hypertable)
93
+ hypertable.compression_segment_settings.map(&:attname)
94
+ end
95
+
75
96
  # Returns `pg_dump` flag to exclude `_timescaledb_internal` schema tables.
76
97
  #
77
98
  # @return [String]
@@ -16,6 +16,8 @@ module Timescaledb
16
16
 
17
17
  hypertable(hypertable, stream)
18
18
  hypertable_compression(hypertable, stream)
19
+ hypertable_reorder(hypertable, stream)
20
+ hypertable_retention(hypertable, stream)
19
21
  end
20
22
 
21
23
  private
@@ -38,6 +40,24 @@ module Timescaledb
38
40
  stream.puts
39
41
  end
40
42
 
43
+ def hypertable_reorder(hypertable, stream)
44
+ return unless hypertable.reorder?
45
+
46
+ options = [hypertable.hypertable_name.inspect, hypertable.reorder_policy_index_name.inspect]
47
+
48
+ stream.puts " add_hypertable_reorder_policy #{options.join(', ')}"
49
+ stream.puts
50
+ end
51
+
52
+ def hypertable_retention(hypertable, stream)
53
+ return unless hypertable.retention?
54
+
55
+ options = [hypertable.hypertable_name.inspect, hypertable.retention_policy_interval.inspect]
56
+
57
+ stream.puts " add_hypertable_retention_policy #{options.join(', ')}"
58
+ stream.puts
59
+ end
60
+
41
61
  def hypertable_options(hypertable)
42
62
  options = {
43
63
  chunk_time_interval: hypertable.chunk_time_interval
@@ -50,21 +70,27 @@ module Timescaledb
50
70
  end
51
71
 
52
72
  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
73
  [].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
74
+ if (segments = compression_segment_settings(hypertable)).present?
75
+ result << "segment_by: #{segments.join(', ').inspect}"
76
+ end
62
77
 
63
- result << "order_by: #{orderby.inspect}"
78
+ if (orders = compression_order_settings(hypertable)).present?
79
+ result << "order_by: #{orders.join(', ').inspect}"
64
80
  end
65
81
  end
66
82
  end
67
83
 
84
+ def compression_order_settings(hypertable)
85
+ hypertable.compression_order_settings.map do |os|
86
+ Timescaledb::Rails::OrderbyCompression.new(os.attname, os.orderby_asc).to_s
87
+ end
88
+ end
89
+
90
+ def compression_segment_settings(hypertable)
91
+ hypertable.compression_segment_settings.map(&:attname)
92
+ end
93
+
68
94
  def format_hypertable_option_value(value)
69
95
  case value
70
96
  when String then value.inspect
@@ -1,12 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record/connection_adapters/postgresql_adapter'
4
-
5
3
  module Timescaledb
6
4
  module Rails
7
5
  module ActiveRecord
8
6
  # :nodoc:
9
7
  module SchemaStatements
8
+ # Returns an array of hypertable names defined in the database.
9
+ def hypertables
10
+ query_values('SELECT hypertable_name FROM timescaledb_information.hypertables')
11
+ end
12
+
13
+ # Checks to see if the hypertable exists on the database.
14
+ #
15
+ # hypertable_exists?(:developers)
16
+ #
17
+ def hypertable_exists?(hypertable)
18
+ query_value(
19
+ <<-SQL.squish
20
+ SELECT COUNT(*) FROM timescaledb_information.hypertables WHERE hypertable_name = #{quote(hypertable)}
21
+ SQL
22
+ ).to_i.positive?
23
+ end
24
+
10
25
  # Converts given standard PG table into a hypertable.
11
26
  #
12
27
  # create_hypertable('readings', 'created_at', chunk_time_interval: '7 days')
@@ -38,7 +53,7 @@ module Timescaledb
38
53
 
39
54
  execute "ALTER TABLE #{table_name} SET (#{options.join(', ')})"
40
55
 
41
- execute "SELECT add_compression_policy('#{table_name}', INTERVAL '#{compress_after}')"
56
+ execute "SELECT add_compression_policy('#{table_name}', INTERVAL '#{compress_after.inspect}')"
42
57
  end
43
58
 
44
59
  # Disables compression from given table.
@@ -46,7 +61,39 @@ module Timescaledb
46
61
  # remove_hypertable_compression('events')
47
62
  #
48
63
  def remove_hypertable_compression(table_name, compress_after = nil, segment_by: nil, order_by: nil) # rubocop:disable Lint/UnusedMethodArgument
49
- execute "SELECT remove_compression_policy('#{table_name}');"
64
+ execute "SELECT remove_compression_policy('#{table_name.inspect}');"
65
+ end
66
+
67
+ # Add a data retention policy to given hypertable.
68
+ #
69
+ # add_hypertable_retention_policy('events', 7.days)
70
+ #
71
+ def add_hypertable_retention_policy(table_name, drop_after)
72
+ execute "SELECT add_retention_policy('#{table_name}', INTERVAL '#{drop_after.inspect}')"
73
+ end
74
+
75
+ # Removes data retention policy from given hypertable.
76
+ #
77
+ # remove_hypertable_retention_policy('events')
78
+ #
79
+ def remove_hypertable_retention_policy(table_name, _drop_after = nil)
80
+ execute "SELECT remove_retention_policy('#{table_name}')"
81
+ end
82
+
83
+ # Adds a policy to reorder chunks on a given hypertable index in the background.
84
+ #
85
+ # add_hypertable_reorder_policy('events', 'index_events_on_created_at_and_name')
86
+ #
87
+ def add_hypertable_reorder_policy(table_name, index_name)
88
+ execute "SELECT add_reorder_policy('#{table_name}', '#{index_name}')"
89
+ end
90
+
91
+ # Removes a policy to reorder a particular hypertable.
92
+ #
93
+ # remove_hypertable_reorder_policy('events')
94
+ #
95
+ def remove_hypertable_reorder_policy(table_name, _index_name = nil)
96
+ execute "SELECT remove_reorder_policy('#{table_name}')"
50
97
  end
51
98
 
52
99
  # @return [String]
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Timescaledb
4
+ module Rails
5
+ module Model
6
+ # :nodoc:
7
+ module Scopes
8
+ extend ActiveSupport::Concern
9
+
10
+ # rubocop:disable Metrics/BlockLength
11
+ included do
12
+ scope :last_year, lambda {
13
+ date = Date.current - 1.year
14
+
15
+ between_time_column(date.beginning_of_year, date.end_of_year)
16
+ }
17
+
18
+ scope :last_month, lambda {
19
+ date = Date.current - 1.month
20
+
21
+ between_time_column(date.beginning_of_month, date.end_of_month)
22
+ }
23
+
24
+ scope :last_week, lambda {
25
+ date = Date.current - 1.week
26
+
27
+ between_time_column(date.beginning_of_week, date.end_of_week)
28
+ }
29
+
30
+ scope :yesterday, lambda {
31
+ where("DATE(#{hypertable_time_column_name}) = ?", Date.current - 1.day)
32
+ }
33
+
34
+ scope :this_year, lambda {
35
+ between_time_column(Date.current.beginning_of_year, Date.current.end_of_year)
36
+ }
37
+
38
+ scope :this_month, lambda {
39
+ between_time_column(Date.current.beginning_of_month, Date.current.end_of_month)
40
+ }
41
+
42
+ scope :this_week, lambda {
43
+ between_time_column(Date.current.beginning_of_week, Date.current.end_of_week)
44
+ }
45
+
46
+ scope :today, lambda {
47
+ where("DATE(#{hypertable_time_column_name}) = ?", Date.current)
48
+ }
49
+
50
+ # @!visibility private
51
+ scope :between_time_column, lambda { |from, to|
52
+ where("DATE(#{hypertable_time_column_name}) BETWEEN ? AND ?", from, to)
53
+ }
54
+ end
55
+ # rubocop:enable Metrics/BlockLength
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'timescaledb/rails/model/scopes'
4
+
5
+ module Timescaledb
6
+ module Rails
7
+ # :nodoc:
8
+ module Model
9
+ PUBLIC_SCHEMA_NAME = 'public'
10
+
11
+ extend ActiveSupport::Concern
12
+
13
+ include Scopes
14
+
15
+ # :nodoc:
16
+ module ClassMethods
17
+ delegate :time_column_name, to: :hypertable, prefix: true
18
+
19
+ # Returns only the name of the hypertable, table_name could include
20
+ # the schema path, we need to remove it.
21
+ #
22
+ # @return [String]
23
+ def hypertable_name
24
+ table_name.split('.').last
25
+ end
26
+
27
+ # Returns the schema where hypertable is stored.
28
+ #
29
+ # @return [String]
30
+ def hypertable_schema
31
+ if table_name.split('.').size > 1
32
+ table_name.split('.')[0..-2].join('.')
33
+ else
34
+ PUBLIC_SCHEMA_NAME
35
+ end
36
+ end
37
+
38
+ # @return [Timescaledb::Rails::Hypertable]
39
+ def hypertable
40
+ Timescaledb::Rails::Hypertable.find_by(hypertable_where_options)
41
+ end
42
+
43
+ # @return [ActiveRecord::Relation<Timescaledb::Rails::Chunk>]
44
+ def hypertable_chunks
45
+ Timescaledb::Rails::Chunk.where(hypertable_where_options)
46
+ end
47
+
48
+ # @return [ActiveRecord::Relation<Timescaledb::Rails::Job>]
49
+ def hypertable_jobs
50
+ Timescaledb::Rails::Job.where(hypertable_where_options)
51
+ end
52
+
53
+ # @return [ActiveRecord::Relation<Timescaledb::Rails::Dimension>]
54
+ def hypertable_dimensions
55
+ Timescaledb::Rails::Dimension.where(hypertable_where_options)
56
+ end
57
+
58
+ # @return [ActiveRecord::Relation<Timescaledb::Rails::CompressionSetting>]
59
+ def hypertable_compression_settings
60
+ Timescaledb::Rails::CompressionSetting.where(hypertable_where_options)
61
+ end
62
+
63
+ private
64
+
65
+ # Returns hypertable name and schema.
66
+ #
67
+ # @return [Hash]
68
+ def hypertable_where_options
69
+ { hypertable_name: hypertable_name, hypertable_schema: hypertable_schema }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Timescaledb
4
+ module Rails
5
+ # :nodoc:
6
+ class Chunk < ::ActiveRecord::Base
7
+ self.table_name = 'timescaledb_information.chunks'
8
+ self.primary_key = 'hypertable_name'
9
+
10
+ scope :compressed, -> { where(is_compressed: true) }
11
+ scope :decompressed, -> { where(is_compressed: false) }
12
+
13
+ def chunk_full_name
14
+ "#{chunk_schema}.#{chunk_name}"
15
+ end
16
+
17
+ def compress!
18
+ ::ActiveRecord::Base.connection.execute(
19
+ "SELECT compress_chunk('#{chunk_full_name}')"
20
+ )
21
+ end
22
+
23
+ def decompress!
24
+ ::ActiveRecord::Base.connection.execute(
25
+ "SELECT decompress_chunk('#{chunk_full_name}')"
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -11,6 +11,7 @@ module Timescaledb
11
11
  class_name: 'Timescaledb::Rails::CompressionSetting'
12
12
  has_many :dimensions, foreign_key: 'hypertable_name', class_name: 'Timescaledb::Rails::Dimension'
13
13
  has_many :jobs, foreign_key: 'hypertable_name', class_name: 'Timescaledb::Rails::Job'
14
+ has_many :chunks, foreign_key: 'hypertable_name', class_name: 'Timescaledb::Rails::Chunk'
14
15
 
15
16
  # @return [String]
16
17
  def time_column_name
@@ -24,11 +25,29 @@ module Timescaledb
24
25
  interval.is_a?(String) ? interval : interval.inspect
25
26
  end
26
27
 
28
+ # @return [ActiveRecord::Relation<CompressionSetting>]
29
+ def compression_segment_settings
30
+ compression_settings.segmentby_setting
31
+ end
32
+
33
+ # @return [ActiveRecord::Relation<CompressionSetting>]
34
+ def compression_order_settings
35
+ compression_settings.orderby_setting.where.not(attname: time_column_name)
36
+ end
37
+
27
38
  # @return [String]
28
39
  def compression_policy_interval
29
- ActiveSupport::Duration.parse(compression_job.config['compress_after']).inspect
30
- rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
31
- compression_job.config['compress_after']
40
+ parse_duration(compression_job.config['compress_after'])
41
+ end
42
+
43
+ # @return [String]
44
+ def reorder_policy_index_name
45
+ reorder_job.config['index_name']
46
+ end
47
+
48
+ # @return [String]
49
+ def retention_policy_interval
50
+ parse_duration(retention_job.config['drop_after'])
32
51
  end
33
52
 
34
53
  # @return [Boolean]
@@ -36,8 +55,28 @@ module Timescaledb
36
55
  compression_job.present?
37
56
  end
38
57
 
58
+ # @return [Boolean]
59
+ def reorder?
60
+ reorder_job.present?
61
+ end
62
+
63
+ # @return [Boolean]
64
+ def retention?
65
+ retention_job.present?
66
+ end
67
+
39
68
  private
40
69
 
70
+ # @return [Job]
71
+ def reorder_job
72
+ @reorder_job ||= jobs.policy_reorder.first
73
+ end
74
+
75
+ # @return [Job]
76
+ def retention_job
77
+ @retention_job ||= jobs.policy_retention.first
78
+ end
79
+
41
80
  # @return [Job]
42
81
  def compression_job
43
82
  @compression_job ||= jobs.policy_compression.first
@@ -47,6 +86,13 @@ module Timescaledb
47
86
  def time_dimension
48
87
  @time_dimension ||= dimensions.time.first
49
88
  end
89
+
90
+ # @return [String]
91
+ def parse_duration(duration)
92
+ ActiveSupport::Duration.parse(duration).inspect
93
+ rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
94
+ duration
95
+ end
50
96
  end
51
97
  end
52
98
  end
@@ -8,8 +8,12 @@ module Timescaledb
8
8
  self.primary_key = 'hypertable_name'
9
9
 
10
10
  POLICY_COMPRESSION = 'policy_compression'
11
+ POLICY_REORDER = 'policy_reorder'
12
+ POLICY_RETENTION = 'policy_retention'
11
13
 
12
14
  scope :policy_compression, -> { where(proc_name: POLICY_COMPRESSION) }
15
+ scope :policy_reorder, -> { where(proc_name: POLICY_REORDER) }
16
+ scope :policy_retention, -> { where(proc_name: POLICY_RETENTION) }
13
17
  end
14
18
  end
15
19
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './models/chunk'
3
4
  require_relative './models/compression_setting'
4
5
  require_relative './models/dimension'
5
- require_relative './models/job'
6
6
  require_relative './models/hypertable'
7
+ require_relative './models/job'
@@ -2,11 +2,6 @@
2
2
 
3
3
  require 'rails'
4
4
 
5
- require_relative 'extensions/active_record/command_recorder'
6
- require_relative 'extensions/active_record/postgresql_database_tasks'
7
- require_relative 'extensions/active_record/schema_dumper'
8
- require_relative 'extensions/active_record/schema_statements'
9
-
10
5
  module Timescaledb
11
6
  module Rails
12
7
  # :nodoc:
@@ -19,21 +14,7 @@ module Timescaledb
19
14
 
20
15
  initializer 'timescaledb-rails.add_timescale_support_to_active_record' do
21
16
  ActiveSupport.on_load(:active_record) do
22
- ::ActiveRecord::Migration::CommandRecorder.prepend(
23
- Timescaledb::Rails::ActiveRecord::CommandRecorder
24
- )
25
-
26
- ::ActiveRecord::Tasks::PostgreSQLDatabaseTasks.prepend(
27
- Timescaledb::Rails::ActiveRecord::PostgreSQLDatabaseTasks
28
- )
29
-
30
- ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(
31
- Timescaledb::Rails::ActiveRecord::SchemaStatements
32
- )
33
-
34
- ::ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(
35
- Timescaledb::Rails::ActiveRecord::SchemaDumper
36
- )
17
+ Timescaledb::Rails.load
37
18
  end
38
19
  end
39
20
  end
@@ -3,6 +3,6 @@
3
3
  module Timescaledb
4
4
  # :nodoc:
5
5
  module Rails
6
- VERSION = '0.1.3'
6
+ VERSION = '0.1.4'
7
7
  end
8
8
  end
@@ -1,7 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './rails/model'
4
+
5
+ require_relative './rails/extensions/active_record/base'
6
+ require_relative './rails/extensions/active_record/command_recorder'
7
+ require_relative './rails/extensions/active_record/postgresql_database_tasks'
8
+ require_relative './rails/extensions/active_record/schema_dumper'
9
+ require_relative './rails/extensions/active_record/schema_statements'
10
+
3
11
  module Timescaledb
4
12
  # :nodoc:
5
13
  module Rails
14
+ # Adds TimescaleDB support to ActiveRecord.
15
+ def self.load
16
+ ::ActiveRecord::Migration::CommandRecorder.prepend(ActiveRecord::CommandRecorder)
17
+ ::ActiveRecord::Tasks::PostgreSQLDatabaseTasks.prepend(ActiveRecord::PostgreSQLDatabaseTasks)
18
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::SchemaStatements)
19
+ ::ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(ActiveRecord::SchemaDumper)
20
+ ::ActiveRecord::Base.include(ActiveRecord::Base) # rubocop:disable Rails/ActiveSupportOnLoad
21
+ end
6
22
  end
7
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timescaledb-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iván Etchart
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-11-29 00:00:00.000000000 Z
12
+ date: 2022-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '6.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: debug
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: pg
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -119,11 +133,15 @@ files:
119
133
  - README.md
120
134
  - lib/timescaledb-rails.rb
121
135
  - lib/timescaledb/rails.rb
136
+ - lib/timescaledb/rails/extensions/active_record/base.rb
122
137
  - lib/timescaledb/rails/extensions/active_record/command_recorder.rb
123
138
  - lib/timescaledb/rails/extensions/active_record/postgresql_database_tasks.rb
124
139
  - lib/timescaledb/rails/extensions/active_record/schema_dumper.rb
125
140
  - lib/timescaledb/rails/extensions/active_record/schema_statements.rb
141
+ - lib/timescaledb/rails/model.rb
142
+ - lib/timescaledb/rails/model/scopes.rb
126
143
  - lib/timescaledb/rails/models.rb
144
+ - lib/timescaledb/rails/models/chunk.rb
127
145
  - lib/timescaledb/rails/models/compression_setting.rb
128
146
  - lib/timescaledb/rails/models/dimension.rb
129
147
  - lib/timescaledb/rails/models/hypertable.rb