@@ -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
17104end
0 commit comments