Skip to content

Commit 3193e8e

Browse files
committed
Support (current|system|valid)_time for selects and inserts
1 parent 051f2c7 commit 3193e8e

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

lib/sequel/adapters/shared/xtdb.rb

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,96 @@ def primary_key(_table)
99
# eg used for RETURNING on insert (prevents crash)
1010
:_id
1111
end
12+
13+
# Get a dataset with `current`, `valid` and `system` set.
14+
#
15+
# For selects this creates the SETTING pre-amble, e.g. 'SETTING DEFAULT VALID_TIME ...':
16+
# ```
17+
# DB.as_of(current: 2.weeks.ago).select(Sequel.lit('current_timestamp')).single_value
18+
# ```
19+
#
20+
# A block can be provided as a convenience to stay in SQL-land (selects only):
21+
# ```
22+
# DB.as_of(current: 2.hours.ago) do
23+
# DB["select current_timestamp"]
24+
# end.sql
25+
# =>
26+
# SETTING
27+
# CURRENT_TIME TO TIMESTAMP '2024-12-17T12:59:48+01:00'
28+
# select current_timestamp
29+
# ```
30+
#
31+
# When doing inserts, the `_valid_from` will be added (if not provided):
32+
# ```
33+
# DB[:products].as_of(valid: 2.weeks.ago).insert(_id: 1, name: 'Spam')
34+
# ```
35+
def as_of(...)
36+
ds = @default_dataset.as_of(...)
37+
return ds unless block_given?
38+
39+
yield.clone(append_sql: ds.select_setting_sql(""))
40+
end
1241
end
1342

1443
module DatasetMethods
44+
Dataset.def_sql_method(self, :select,
45+
[["if opts[:values]",
46+
%w[values compounds order limit]],
47+
["else",
48+
%w[setting select distinct columns from join where group having compounds order limit lock]]])
49+
50+
def as_of(valid: nil, system: nil, current: nil)
51+
{valid: valid, system: system, current: current}.reject { |_k, v| v.nil? }.then do |opts|
52+
clone(opts)
53+
end
54+
end
55+
56+
def server_version
57+
0
58+
end
59+
60+
def insert_values_sql(sql)
61+
if (from_ix = opts[:columns].index(:_valid_from))
62+
opts[:values][from_ix] = Sequel.lit("TIMESTAMP ?", opts[:values][from_ix])
63+
end
64+
65+
if (to_ix = opts[:columns].index(:_valid_to))
66+
opts[:values][to_ix] = Sequel.lit("TIMESTAMP ?", opts[:values][to_ix])
67+
end
68+
69+
super
70+
end
71+
72+
def insert_columns_sql(sql)
73+
if opts[:valid] && !opts[:columns].index(:_valid_from)
74+
opts[:columns] << :_valid_from
75+
opts[:values] << opts[:valid]
76+
end
77+
super
78+
end
79+
80+
def select_setting_sql(sql)
81+
setting = opts.slice(:current, :valid, :system)
82+
return sql if setting.empty?
83+
84+
cast_value = ->(v) do
85+
case v
86+
when DateTime, Time
87+
literal_append "TIMESTAMP ", v.iso8601
88+
when Date
89+
literal_append "DATE ", v.iso8601
90+
end
91+
end
92+
sql << "SETTING "
93+
sql << setting.map do |k, v|
94+
if k == :current
95+
literal_append "CURRENT_TIME TO TIMESTAMP ", v.iso8601
96+
else
97+
"DEFAULT #{k.upcase}_TIME AS OF #{cast_value[v]}"
98+
end
99+
end.join(", ")
100+
sql << " "
101+
end
15102
end
16103
end
17104
end

0 commit comments

Comments
 (0)