timescaledb 0.2.8 → 0.3.0
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 +4 -4
- data/lib/timescaledb/acts_as_hypertable.rb +0 -1
- data/lib/timescaledb/connection.rb +12 -1
- data/lib/timescaledb/connection_handling.rb +9 -4
- data/lib/timescaledb/continuous_aggregates.rb +24 -0
- data/lib/timescaledb/extension.rb +23 -0
- data/lib/timescaledb/migration_helpers.rb +9 -1
- data/lib/timescaledb/scenic/extension.rb +1 -2
- data/lib/timescaledb/schema_dumper.rb +23 -8
- data/lib/timescaledb/version.rb +1 -1
- data/lib/timescaledb.rb +9 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a20c2f12a3673694b39fd99aa005d743bbc027031d3abc7c25fda904f71ee33f
|
4
|
+
data.tar.gz: fd27816a15c114baa3d75a08bd083de5493efd7ce60ebe200d6543a3dcb266bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db6642dbdb2588359fc63fff080f34479725cef75d7972f081aa2a1beb680314cf1555ba7ec94956d16cceb3ac3b7724de131e010ba1d06361350f162ed3250c
|
7
|
+
data.tar.gz: df04ac079954e4b815d922d2a076929e6c4d5f5e9c11d46a54287138e8485fb171b276361a3bae43618c068709fa9f07c0ec34faa88c3afff18ac9d65d511766
|
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module Timescaledb
|
4
|
+
# Minimal connection setup for Timescaledb directly with the PG.
|
5
|
+
# The concept is use a singleton component that can query
|
6
|
+
# independently of the ActiveRecord::Base connections.
|
7
|
+
# This is useful for the extension and hypertable metadata.
|
8
|
+
# It can also #use_connection from active record if needed.
|
4
9
|
class Connection
|
5
10
|
include Singleton
|
6
11
|
|
@@ -34,10 +39,16 @@ module Timescaledb
|
|
34
39
|
!@config.nil?
|
35
40
|
end
|
36
41
|
|
42
|
+
# Override the connection with a raw PG connection.
|
43
|
+
# @param [PG::Connection] connection The raw PG connection.
|
44
|
+
def use_connection connection
|
45
|
+
@connection = connection
|
46
|
+
end
|
47
|
+
|
37
48
|
private
|
38
49
|
|
39
50
|
def connection
|
40
51
|
@connection ||= PG.connect(@config)
|
41
52
|
end
|
42
53
|
end
|
43
|
-
end
|
54
|
+
end
|
@@ -1,16 +1,21 @@
|
|
1
1
|
module Timescaledb
|
2
2
|
class ConnectionNotEstablishedError < StandardError; end
|
3
3
|
|
4
|
-
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# @param [String] config with the postgres connection string.
|
5
7
|
def establish_connection(config)
|
6
8
|
Connection.instance.config = config
|
7
9
|
end
|
8
|
-
|
10
|
+
|
11
|
+
# @param [PG::Connection] to use it directly from a raw connection
|
12
|
+
def use_connection conn
|
13
|
+
Connection.instance.use_connection conn
|
14
|
+
end
|
9
15
|
|
10
16
|
def connection
|
11
17
|
raise ConnectionNotEstablishedError.new unless Connection.instance.connected?
|
12
18
|
|
13
19
|
Connection.instance
|
14
20
|
end
|
15
|
-
|
16
|
-
end
|
21
|
+
end
|
@@ -14,6 +14,30 @@ module Timescaledb
|
|
14
14
|
total: count
|
15
15
|
}
|
16
16
|
end
|
17
|
+
|
18
|
+
scope :hierarchical, -> do
|
19
|
+
with_recursive = <<~SQL
|
20
|
+
WITH RECURSIVE caggs AS (
|
21
|
+
SELECT mat_hypertable_id, parent_mat_hypertable_id, user_view_name
|
22
|
+
FROM _timescaledb_catalog.continuous_agg
|
23
|
+
UNION ALL
|
24
|
+
SELECT continuous_agg.mat_hypertable_id, continuous_agg.parent_mat_hypertable_id, continuous_agg.user_view_name
|
25
|
+
FROM _timescaledb_catalog.continuous_agg
|
26
|
+
JOIN caggs ON caggs.parent_mat_hypertable_id = continuous_agg.mat_hypertable_id
|
27
|
+
)
|
28
|
+
SELECT * FROM caggs
|
29
|
+
ORDER BY mat_hypertable_id
|
30
|
+
SQL
|
31
|
+
views = unscoped
|
32
|
+
.select("distinct user_view_name")
|
33
|
+
.from("(#{with_recursive}) as caggs")
|
34
|
+
.pluck(:user_view_name)
|
35
|
+
.uniq
|
36
|
+
|
37
|
+
views.map do |view|
|
38
|
+
find_by(view_name: view)
|
39
|
+
end
|
40
|
+
end
|
17
41
|
end
|
18
42
|
ContinuousAggregates = ContinuousAggregate
|
19
43
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Timescaledb
|
2
|
+
|
3
|
+
# Provides metadata around the extension in the database
|
4
|
+
module Extension
|
5
|
+
module_function
|
6
|
+
# @return String version of the timescaledb extension
|
7
|
+
def version
|
8
|
+
@version ||= Timescaledb.connection.query_first(<<~SQL)&.version
|
9
|
+
SELECT extversion as version
|
10
|
+
FROM pg_extension
|
11
|
+
WHERE extname = 'timescaledb'
|
12
|
+
SQL
|
13
|
+
end
|
14
|
+
|
15
|
+
def installed?
|
16
|
+
version.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def update!
|
20
|
+
Timescaledb.connection.execute('ALTER EXTENSION timescaledb UPDATE')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -49,7 +49,7 @@ module Timescaledb
|
|
49
49
|
original_logger = ActiveRecord::Base.logger
|
50
50
|
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
51
51
|
|
52
|
-
options = ["chunk_time_interval =>
|
52
|
+
options = ["chunk_time_interval => #{chunk_time_interval_clause(chunk_time_interval)}"]
|
53
53
|
options += hypertable_options.map { |k, v| "#{k} => #{quote(v)}" }
|
54
54
|
|
55
55
|
arguments = [
|
@@ -165,6 +165,14 @@ module Timescaledb
|
|
165
165
|
value = options[option_key] ? 'true' : 'false'
|
166
166
|
",timescaledb.#{option_key}=#{value}"
|
167
167
|
end
|
168
|
+
|
169
|
+
def chunk_time_interval_clause(chunk_time_interval)
|
170
|
+
if chunk_time_interval.is_a?(Numeric)
|
171
|
+
chunk_time_interval
|
172
|
+
else
|
173
|
+
"INTERVAL '#{chunk_time_interval}'"
|
174
|
+
end
|
175
|
+
end
|
168
176
|
end
|
169
177
|
end
|
170
178
|
|
@@ -25,7 +25,6 @@ module Timescaledb
|
|
25
25
|
|
26
26
|
# @override Scenic::Adapters::Postgres#create_view
|
27
27
|
# to add the `with: ` keyword that can be used for such option.
|
28
|
-
#
|
29
28
|
def create_view(name, version: nil, with: nil, sql_definition: nil, materialized: false, no_data: false)
|
30
29
|
if version.present? && sql_definition.present?
|
31
30
|
raise(
|
@@ -69,4 +68,4 @@ end
|
|
69
68
|
|
70
69
|
|
71
70
|
Scenic::Adapters::Postgres.include(Timescaledb::Scenic::Extension)
|
72
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
71
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(Timescaledb::Scenic::MigrationHelpers)
|
@@ -7,14 +7,29 @@ module Timescaledb
|
|
7
7
|
# * retention policies
|
8
8
|
# * continuous aggregates
|
9
9
|
# * compression settings
|
10
|
+
# It also ignores Timescale related schemas when dumping the schema.
|
11
|
+
# It also ignores dumping options as extension is not installed or no hypertables are available.
|
10
12
|
module SchemaDumper
|
11
13
|
def tables(stream)
|
12
14
|
super # This will call #table for each table in the database
|
13
|
-
return unless Timescaledb::Hypertable.table_exists?
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
if exports_timescaledb_metadata?
|
17
|
+
timescale_hypertables(stream)
|
18
|
+
timescale_retention_policies(stream)
|
19
|
+
timescale_continuous_aggregates(stream) # Define these before any Scenic views that might use them
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Ignore dumps in case DB is not eligible for TimescaleDB metadata.
|
24
|
+
# @return [Boolean] true if the extension is installed and hypertables are available, otherwise false.
|
25
|
+
private def exports_timescaledb_metadata?
|
26
|
+
# Note it's safe to use the raw connection here because we're only reading from the database
|
27
|
+
# and not modifying it. We're also on the same connection pool as ActiveRecord::Base.
|
28
|
+
# The dump process also runs standalone, so we don't need to worry about the connection being
|
29
|
+
# used elsewhere.
|
30
|
+
Timescaledb.use_connection @connection.raw_connection
|
31
|
+
|
32
|
+
Timescaledb.extension.installed? && Timescaledb.hypertables.any?
|
18
33
|
end
|
19
34
|
|
20
35
|
# Ignores Timescale related schemas when dumping the schema
|
@@ -63,7 +78,7 @@ module Timescaledb
|
|
63
78
|
|
64
79
|
options = {
|
65
80
|
time_column: time.column_name,
|
66
|
-
chunk_time_interval: time.time_interval.inspect,
|
81
|
+
chunk_time_interval: time.time_interval ? time.time_interval.inspect : time.integer_interval,
|
67
82
|
**timescale_compression_settings_for(hypertable),
|
68
83
|
**timescale_space_partition_for(hypertable),
|
69
84
|
**timescale_index_options_for(hypertable)
|
@@ -139,19 +154,19 @@ module Timescaledb
|
|
139
154
|
def timescale_continuous_aggregates(stream)
|
140
155
|
return unless Timescaledb::ContinuousAggregates.table_exists?
|
141
156
|
|
142
|
-
Timescaledb::ContinuousAggregates.
|
157
|
+
Timescaledb::ContinuousAggregates.hierarchical.each do |aggregate|
|
143
158
|
refresh_policies_opts = if (refresh_policy = aggregate.jobs.refresh_continuous_aggregate.first)
|
144
159
|
interval = timescale_interval(refresh_policy.schedule_interval)
|
145
160
|
end_offset = timescale_interval(refresh_policy.config["end_offset"])
|
146
161
|
start_offset = timescale_interval(refresh_policy.config["start_offset"])
|
147
|
-
%(refresh_policies: { start_offset: "#{start_offset}", end_offset: "#{end_offset}", schedule_interval: "#{interval}"})
|
162
|
+
%(refresh_policies: { start_offset: "#{start_offset}", end_offset: "#{end_offset}", schedule_interval: "#{interval}"}, )
|
148
163
|
else
|
149
164
|
""
|
150
165
|
end
|
151
166
|
|
152
167
|
with_clause_opts = "materialized_only: #{aggregate[:materialized_only]}, finalized: #{aggregate[:finalized]}"
|
153
168
|
stream.puts <<~AGG.indent(2)
|
154
|
-
create_continuous_aggregate("#{aggregate.view_name}", <<-SQL, #{refresh_policies_opts}
|
169
|
+
create_continuous_aggregate("#{aggregate.view_name}", <<-SQL, #{refresh_policies_opts}#{with_clause_opts})
|
155
170
|
#{aggregate.view_definition.strip.gsub(/;$/, '')}
|
156
171
|
SQL
|
157
172
|
AGG
|
data/lib/timescaledb/version.rb
CHANGED
data/lib/timescaledb.rb
CHANGED
@@ -17,11 +17,20 @@ require_relative 'timescaledb/schema_dumper'
|
|
17
17
|
require_relative 'timescaledb/stats'
|
18
18
|
require_relative 'timescaledb/stats_report'
|
19
19
|
require_relative 'timescaledb/migration_helpers'
|
20
|
+
require_relative 'timescaledb/extension'
|
20
21
|
require_relative 'timescaledb/version'
|
21
22
|
|
22
23
|
module Timescaledb
|
23
24
|
module_function
|
24
25
|
|
26
|
+
def connection
|
27
|
+
Connection.instance
|
28
|
+
end
|
29
|
+
|
30
|
+
def extension
|
31
|
+
Extension
|
32
|
+
end
|
33
|
+
|
25
34
|
def chunks
|
26
35
|
Chunk.all
|
27
36
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timescaledb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jônatas Davi Paganini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- lib/timescaledb/database/schema_statements.rb
|
164
164
|
- lib/timescaledb/database/types.rb
|
165
165
|
- lib/timescaledb/dimensions.rb
|
166
|
+
- lib/timescaledb/extension.rb
|
166
167
|
- lib/timescaledb/hypertable.rb
|
167
168
|
- lib/timescaledb/job.rb
|
168
169
|
- lib/timescaledb/job_stats.rb
|