7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# File 'lib/activerecord_time_intervals/relation_methods.rb', line 7
def in_intervals_of(step, range:, column:)
raise ArgumentError, "PostgreSQL only" unless klass.connection.adapter_name =~ /postg/i
raise ArgumentError, "column required" if column.nil?
secs = Integer(step.respond_to?(:to_i) ? step.to_i : step)
col = column.to_s
raise ArgumentError, "step must be positive" if secs <= 0
startq = klass.connection.quote(range.begin)
endq = klass.connection.quote(range.end)
interval_literal = klass.connection.quote("#{secs} seconds")
interval_expr = "#{interval_literal}::interval"
stop_expr = range.exclude_end? ? "(#{endq}::timestamptz - #{interval_expr})" : "#{endq}::timestamptz"
table_quoted = klass.connection.quote_table_name(klass.table_name)
col_quoted = klass.connection.quote_column_name(col)
primary_key = klass.primary_key
base_relation = self.except(:order, :limit, :offset).unscope(:select)
lateral_relation = base_relation
.where("#{table_quoted}.#{col_quoted} = series.ts")
.order(nil)
.limit(1)
series_sql = " SELECT series.ts AS interval_ts, matched.*\n FROM generate_series(\#{startq}::timestamptz, \#{stop_expr}, \#{interval_expr}) AS series(ts)\n LEFT JOIN LATERAL (\n \#{lateral_relation.to_sql}\n ) AS matched ON TRUE\n ORDER BY interval_ts\n SQL\n\n klass.find_by_sql(series_sql).map do |record|\n if primary_key && record.read_attribute(primary_key).nil?\n nil\n else\n record\n end\n end\nend\n"
|