timescaledb-rails 0.1.3 → 0.1.4

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: 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