safrano 0.4.3 → 0.5.1
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/core_ext/Dir/iter.rb +18 -0
- data/lib/core_ext/Hash/transform.rb +21 -0
- data/lib/core_ext/Integer/edm.rb +13 -0
- data/lib/core_ext/REXML/Document/output.rb +16 -0
- data/lib/core_ext/String/convert.rb +25 -0
- data/lib/core_ext/String/edm.rb +13 -0
- data/lib/core_ext/dir.rb +3 -0
- data/lib/core_ext/hash.rb +3 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/rexml.rb +3 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/odata/attribute.rb +8 -4
- data/lib/odata/batch.rb +9 -7
- data/lib/odata/collection.rb +139 -642
- data/lib/odata/collection_filter.rb +18 -42
- data/lib/odata/collection_media.rb +111 -54
- data/lib/odata/collection_order.rb +5 -2
- data/lib/odata/common_logger.rb +2 -0
- data/lib/odata/complex_type.rb +196 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +78 -123
- data/lib/odata/error.rb +170 -37
- data/lib/odata/expand.rb +20 -17
- data/lib/odata/filter/base.rb +9 -1
- data/lib/odata/filter/error.rb +43 -27
- data/lib/odata/filter/parse.rb +39 -25
- data/lib/odata/filter/sequel.rb +112 -56
- data/lib/odata/filter/sequel_function_adapter.rb +50 -49
- data/lib/odata/filter/token.rb +21 -18
- data/lib/odata/filter/tree.rb +78 -44
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +641 -0
- data/lib/odata/navigation_attribute.rb +9 -24
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +17 -5
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +100 -24
- data/lib/odata/walker.rb +18 -10
- data/lib/safrano.rb +18 -38
- data/lib/safrano/contract.rb +141 -0
- data/lib/safrano/core.rb +24 -106
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +29 -24
- data/lib/safrano/rack_app.rb +62 -63
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -1
- data/lib/safrano/request.rb +96 -38
- data/lib/safrano/response.rb +4 -2
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +156 -110
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +6 -19
- metadata +30 -11
data/lib/odata/filter/parse.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './token
|
4
|
-
require_relative './tree
|
5
|
-
require_relative './error
|
3
|
+
require_relative './token'
|
4
|
+
require_relative './tree'
|
5
|
+
require_relative './error'
|
6
6
|
|
7
|
-
# top level
|
8
|
-
module
|
7
|
+
# top level Safrano namespace
|
8
|
+
module Safrano
|
9
9
|
# for handling $filter
|
10
10
|
module Filter
|
11
11
|
# Parser for $filter input
|
12
12
|
class Parser
|
13
13
|
include Token
|
14
14
|
attr_reader :cursor
|
15
|
+
attr_reader :error
|
16
|
+
|
15
17
|
def initialize(input)
|
16
18
|
@tree = RootTree.new
|
17
19
|
@cursor = @tree
|
@@ -20,10 +22,14 @@ module OData
|
|
20
22
|
@binop_stack = []
|
21
23
|
end
|
22
24
|
|
25
|
+
def server_error
|
26
|
+
(@error = ::Safrano::ServerError)
|
27
|
+
end
|
28
|
+
|
23
29
|
def grow_at_cursor(child)
|
24
|
-
|
30
|
+
return server_error if @cursor.nil?
|
25
31
|
|
26
|
-
@cursor.attach(child)
|
32
|
+
@cursor.attach(child).tap_error { |err| return (@error = err) }
|
27
33
|
@cursor = child
|
28
34
|
end
|
29
35
|
|
@@ -42,7 +48,7 @@ module OData
|
|
42
48
|
def insert_before_cursor(node)
|
43
49
|
left = detach_cursor
|
44
50
|
grow_at_cursor(node)
|
45
|
-
@cursor.attach(left)
|
51
|
+
@cursor.attach(left).tap_error { |err| return (@error = err) }
|
46
52
|
end
|
47
53
|
|
48
54
|
def invalid_separator_error(tok, typ)
|
@@ -58,12 +64,7 @@ module OData
|
|
58
64
|
end
|
59
65
|
|
60
66
|
def with_accepted(tok, typ)
|
61
|
-
|
62
|
-
if acc
|
63
|
-
yield
|
64
|
-
else
|
65
|
-
@error = err
|
66
|
-
end
|
67
|
+
(err = @cursor.accept?(tok, typ)) ? (@error = err) : yield
|
67
68
|
end
|
68
69
|
|
69
70
|
def build
|
@@ -71,15 +72,19 @@ module OData
|
|
71
72
|
case typ
|
72
73
|
when :FuncTree
|
73
74
|
with_accepted(tok, typ) { grow_at_cursor(FuncTree.new(tok)) }
|
75
|
+
|
74
76
|
when :Delimiter
|
75
77
|
case tok
|
76
78
|
when '('
|
77
79
|
with_accepted(tok, typ) do
|
78
80
|
grow_at_cursor(IdentityFuncTree.new) unless @cursor.is_a? FuncTree
|
79
|
-
|
80
|
-
|
81
|
-
|
81
|
+
unless @error
|
82
|
+
openarg = ArgTree.new('(')
|
83
|
+
@stack << openarg
|
84
|
+
grow_at_cursor(openarg)
|
85
|
+
end
|
82
86
|
end
|
87
|
+
|
83
88
|
when ')'
|
84
89
|
break invalid_closing_delimiter_error(tok, typ) unless (@cursor = @stack.pop)
|
85
90
|
|
@@ -123,6 +128,7 @@ module OData
|
|
123
128
|
end
|
124
129
|
insert_before_cursor(binoptr)
|
125
130
|
end
|
131
|
+
|
126
132
|
when :BinopArithm
|
127
133
|
with_accepted(tok, typ) do
|
128
134
|
binoptr = BinopArithm.new(tok)
|
@@ -145,6 +151,12 @@ module OData
|
|
145
151
|
grow_at_cursor(Literal.new(tok))
|
146
152
|
end
|
147
153
|
|
154
|
+
when :NullLiteral
|
155
|
+
with_accepted(tok, typ) do
|
156
|
+
@cursor.update_state(tok, typ)
|
157
|
+
grow_at_cursor(NullLiteral.new(tok))
|
158
|
+
end
|
159
|
+
|
148
160
|
when :Qualit
|
149
161
|
with_accepted(tok, typ) do
|
150
162
|
@cursor.update_state(tok, typ)
|
@@ -162,19 +174,21 @@ module OData
|
|
162
174
|
@cursor.update_state(tok, typ)
|
163
175
|
grow_at_cursor(FPNumber.new(tok))
|
164
176
|
end
|
177
|
+
|
165
178
|
when :unmatchedQuote
|
166
179
|
break unmatched_quote_error(tok, typ)
|
180
|
+
|
181
|
+
when :space
|
182
|
+
with_accepted(tok, typ) do
|
183
|
+
@cursor.update_state(tok, typ)
|
184
|
+
end
|
167
185
|
else
|
168
|
-
|
186
|
+
server_error
|
169
187
|
end
|
170
|
-
break if @error
|
171
|
-
end
|
172
|
-
begin
|
173
|
-
@tree.check_types unless @error
|
174
|
-
rescue ErrorInvalidArgumentType => e
|
175
|
-
@error = e
|
188
|
+
break(@error) if @error
|
176
189
|
end
|
177
|
-
@error
|
190
|
+
(@error = @tree.check_types) unless @error
|
191
|
+
@error ? @error : Contract.valid(@tree)
|
178
192
|
end
|
179
193
|
end
|
180
194
|
end
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './base
|
4
|
-
require_relative './sequel_function_adapter
|
3
|
+
require_relative './base'
|
4
|
+
require_relative './sequel_function_adapter'
|
5
5
|
|
6
|
-
module
|
6
|
+
module Safrano
|
7
7
|
module Filter
|
8
8
|
# Base class for Leaves, Trees, RootTrees etc
|
9
9
|
# class Node
|
@@ -16,8 +16,9 @@ module OData
|
|
16
16
|
# RootTrees have childrens but no parent
|
17
17
|
class RootTree
|
18
18
|
def apply_to_dataset(dtcx, jh)
|
19
|
-
|
20
|
-
|
19
|
+
@children.first.leuqes(jh).if_valid do |filtexpr|
|
20
|
+
jh.dataset(dtcx).where(filtexpr).select_all(jh.start_model.table_name)
|
21
|
+
end
|
21
22
|
end
|
22
23
|
|
23
24
|
def sequel_expr(jh)
|
@@ -36,62 +37,102 @@ module OData
|
|
36
37
|
def leuqes(jh)
|
37
38
|
case @value
|
38
39
|
when :startswith
|
39
|
-
|
40
|
-
|
40
|
+
Contract.collect_result!(args[0].leuqes(jh),
|
41
|
+
args[1].leuqes_starts_like(jh)) do |l0, l1|
|
42
|
+
Sequel.like(l0, l1)
|
43
|
+
end
|
44
|
+
|
41
45
|
when :endswith
|
42
|
-
|
43
|
-
|
46
|
+
Contract.collect_result!(args[0].leuqes(jh),
|
47
|
+
args[1].leuqes_ends_like(jh)) do |l0, l1|
|
48
|
+
Sequel.like(l0, l1)
|
49
|
+
end
|
50
|
+
|
44
51
|
when :substringof
|
45
52
|
|
46
53
|
# there are multiple possible argument types (but all should return edm.string)
|
47
54
|
if args[0].is_a?(QString)
|
48
55
|
# substringof('Rhum', name) -->
|
49
56
|
# name contains substr 'Rhum'
|
50
|
-
|
51
|
-
|
57
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
58
|
+
args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
|
59
|
+
Sequel.like(l1, l0)
|
60
|
+
end
|
61
|
+
|
52
62
|
# special non standard (ui5 client) case ?
|
53
63
|
elsif args[0].is_a?(Literal) && args[1].is_a?(Literal)
|
54
|
-
|
55
|
-
|
64
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
65
|
+
args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
|
66
|
+
Sequel.like(l1, l0)
|
67
|
+
end
|
68
|
+
|
56
69
|
elsif args[1].is_a?(QString)
|
57
70
|
substringof_sig2(jh) # adapter specific
|
58
71
|
else
|
59
72
|
# TODO... actually not supported?
|
60
|
-
raise
|
73
|
+
raise Safrano::Filter::Parser::ErrorFunctionArgumentType
|
61
74
|
end
|
62
75
|
when :concat
|
63
|
-
|
64
|
-
|
76
|
+
Contract.collect_result!(args[0].leuqes(jh),
|
77
|
+
args[1].leuqes(jh)) do |l0, l1|
|
78
|
+
Sequel.join([l0, l1])
|
79
|
+
end
|
80
|
+
|
65
81
|
when :length
|
66
|
-
|
82
|
+
args.first.leuqes(jh)
|
83
|
+
.map_result! { |l| Sequel.char_length(l) }
|
84
|
+
|
67
85
|
when :trim
|
68
|
-
|
86
|
+
args.first.leuqes(jh)
|
87
|
+
.map_result! { |l| Sequel.trim(l) }
|
88
|
+
|
69
89
|
when :toupper
|
70
|
-
|
90
|
+
args.first.leuqes(jh)
|
91
|
+
.map_result! { |l| Sequel.function(:upper, l) }
|
92
|
+
|
71
93
|
when :tolower
|
72
|
-
|
94
|
+
args.first.leuqes(jh)
|
95
|
+
.map_result! { |l| Sequel.function(:lower, l) }
|
96
|
+
|
73
97
|
# all datetime funcs are adapter specific (because sqlite does not have extract)
|
74
98
|
when :year
|
75
|
-
|
99
|
+
args.first.leuqes(jh)
|
100
|
+
.map_result! { |l| year(l) }
|
101
|
+
|
76
102
|
when :month
|
77
|
-
|
103
|
+
args.first.leuqes(jh)
|
104
|
+
.map_result! { |l| month(l) }
|
105
|
+
|
78
106
|
when :second
|
79
|
-
|
107
|
+
args.first.leuqes(jh)
|
108
|
+
.map_result! { |l| second(l) }
|
109
|
+
|
80
110
|
when :minute
|
81
|
-
|
111
|
+
args.first.leuqes(jh)
|
112
|
+
.map_result! { |l| minute(l) }
|
113
|
+
|
82
114
|
when :hour
|
83
|
-
|
115
|
+
args.first.leuqes(jh)
|
116
|
+
.map_result! { |l| hour(l) }
|
117
|
+
|
84
118
|
when :day
|
85
|
-
|
119
|
+
args.first.leuqes(jh)
|
120
|
+
.map_result! { |l| day(l) }
|
121
|
+
|
86
122
|
# math functions
|
87
123
|
when :round
|
88
|
-
|
124
|
+
args.first.leuqes(jh)
|
125
|
+
.map_result! { |l| Sequel.function(:round, l) }
|
126
|
+
|
89
127
|
when :floor
|
90
|
-
|
128
|
+
args.first.leuqes(jh)
|
129
|
+
.if_valid { |l| floor(l) }
|
130
|
+
|
91
131
|
when :ceiling
|
92
|
-
|
132
|
+
args.first.leuqes(jh)
|
133
|
+
.if_valid { |l| ceiling(l) }
|
93
134
|
else
|
94
|
-
|
135
|
+
Safrano::FilterParseError
|
95
136
|
end
|
96
137
|
end
|
97
138
|
end
|
@@ -110,9 +151,9 @@ module OData
|
|
110
151
|
def leuqes(jh)
|
111
152
|
case @value
|
112
153
|
when :not
|
113
|
-
|
154
|
+
@children.first.leuqes(jh).map_result! { |l| Sequel.~(l) }
|
114
155
|
else
|
115
|
-
|
156
|
+
Safrano::FilterParseError
|
116
157
|
end
|
117
158
|
end
|
118
159
|
end
|
@@ -138,12 +179,19 @@ module OData
|
|
138
179
|
when :and
|
139
180
|
:AND
|
140
181
|
else
|
141
|
-
|
182
|
+
return Safrano::FilterParseError
|
142
183
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
184
|
+
Contract.collect_result!(@children[0].leuqes(jh),
|
185
|
+
@children[1].leuqes(jh)) do |c0, c1|
|
186
|
+
if c1 == NullLiteral::LEUQES
|
187
|
+
if @value == :eq
|
188
|
+
leuqes_op = :IS
|
189
|
+
elsif @value == :ne
|
190
|
+
leuqes_op = :'IS NOT'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
Sequel::SQL::BooleanExpression.new(leuqes_op, c0, c1)
|
194
|
+
end
|
147
195
|
end
|
148
196
|
end
|
149
197
|
|
@@ -162,12 +210,12 @@ module OData
|
|
162
210
|
when :mod
|
163
211
|
:%
|
164
212
|
else
|
165
|
-
|
213
|
+
return Safrano::FilterParseError
|
166
214
|
end
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
215
|
+
Contract.collect_result!(@children[0].leuqes(jh),
|
216
|
+
@children[1].leuqes(jh)) do |c0, c1|
|
217
|
+
Sequel::SQL::NumericExpression.new(leuqes_op, c0, c1)
|
218
|
+
end
|
171
219
|
end
|
172
220
|
end
|
173
221
|
|
@@ -178,42 +226,42 @@ module OData
|
|
178
226
|
# Numbers (floating point, ints, dec)
|
179
227
|
class FPNumber
|
180
228
|
def leuqes(_jh)
|
181
|
-
Sequel.lit(@value)
|
229
|
+
success Sequel.lit(@value)
|
182
230
|
end
|
183
231
|
|
184
232
|
def leuqes_starts_like(_jh)
|
185
|
-
"#{@value}%"
|
233
|
+
success "#{@value}%"
|
186
234
|
end
|
187
235
|
|
188
236
|
def leuqes_ends_like(_jh)
|
189
|
-
"%#{@value}"
|
237
|
+
success "%#{@value}"
|
190
238
|
end
|
191
239
|
|
192
240
|
def leuqes_substringof_sig1(_jh)
|
193
|
-
"%#{@value}%"
|
241
|
+
success "%#{@value}%"
|
194
242
|
end
|
195
243
|
end
|
196
244
|
|
197
245
|
# Literals are unquoted words
|
198
246
|
class Literal
|
199
247
|
def leuqes(jh)
|
200
|
-
|
248
|
+
return Safrano::FilterParseErrorWrongColumnName unless jh.start_model.db_schema.key?(@value.to_sym)
|
201
249
|
|
202
|
-
Sequel[jh.start_model.table_name][@value.to_sym]
|
250
|
+
success Sequel[jh.start_model.table_name][@value.to_sym]
|
203
251
|
end
|
204
252
|
|
205
253
|
# non stantard extensions to support things like
|
206
254
|
# substringof(Rhum, name) ????
|
207
255
|
def leuqes_starts_like(_jh)
|
208
|
-
"#{@value}%"
|
256
|
+
success "#{@value}%"
|
209
257
|
end
|
210
258
|
|
211
259
|
def leuqes_ends_like(_jh)
|
212
|
-
"%#{@value}"
|
260
|
+
success "%#{@value}"
|
213
261
|
end
|
214
262
|
|
215
263
|
def leuqes_substringof_sig1(_jh)
|
216
|
-
"%#{@value}%"
|
264
|
+
success "%#{@value}%"
|
217
265
|
end
|
218
266
|
|
219
267
|
def as_string
|
@@ -221,31 +269,39 @@ module OData
|
|
221
269
|
end
|
222
270
|
end
|
223
271
|
|
272
|
+
# Null
|
273
|
+
class NullLiteral
|
274
|
+
def leuqes(jh)
|
275
|
+
# Sequel's representation of NULL
|
276
|
+
success LEUQES
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
224
280
|
# Qualit (qualified lits) are words separated by /
|
225
281
|
class Qualit
|
226
282
|
def leuqes(jh)
|
227
283
|
jh.add(@path)
|
228
284
|
talias = jh.start_model.get_alias_sym(@path)
|
229
|
-
Sequel[talias][@attrib.to_sym]
|
285
|
+
success Sequel[talias][@attrib.to_sym]
|
230
286
|
end
|
231
287
|
end
|
232
288
|
|
233
289
|
# Quoted Strings
|
234
290
|
class QString
|
235
291
|
def leuqes(_jh)
|
236
|
-
@value
|
292
|
+
success @value
|
237
293
|
end
|
238
294
|
|
239
295
|
def leuqes_starts_like(_jh)
|
240
|
-
"#{@value}%"
|
296
|
+
success "#{@value}%"
|
241
297
|
end
|
242
298
|
|
243
299
|
def leuqes_ends_like(_jh)
|
244
|
-
"%#{@value}"
|
300
|
+
success "%#{@value}"
|
245
301
|
end
|
246
302
|
|
247
303
|
def leuqes_substringof_sig1(_jh)
|
248
|
-
"%#{@value}%"
|
304
|
+
success "%#{@value}%"
|
249
305
|
end
|
250
306
|
end
|
251
307
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './tree
|
4
|
-
require_relative './sequel
|
3
|
+
require_relative './tree'
|
4
|
+
require_relative './sequel'
|
5
5
|
|
6
|
-
module
|
6
|
+
module Safrano
|
7
7
|
module Filter
|
8
8
|
# sqlite adapter specific function handler
|
9
9
|
module FuncTreeSqlite
|
@@ -11,10 +11,11 @@ module OData
|
|
11
11
|
# substringof(name, '__Route du Rhum__') -->
|
12
12
|
# '__Route du Rhum__' contains name as a substring
|
13
13
|
# sqlite uses instr()
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
15
|
+
args[0].leuqes(jh)) do |l1, l0|
|
16
|
+
substr_func = Sequel.function(:instr, l1, l0)
|
17
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
18
|
+
end
|
18
19
|
end
|
19
20
|
# %d day of month: 00
|
20
21
|
# %f fractional seconds: SS.SSS
|
@@ -31,77 +32,73 @@ module OData
|
|
31
32
|
# %% %
|
32
33
|
|
33
34
|
# sqlite does not have extract but recommends to use strftime
|
34
|
-
def year(
|
35
|
-
Sequel.function(:strftime, '%Y',
|
35
|
+
def year(lq)
|
36
|
+
Sequel.function(:strftime, '%Y', lq).cast(:integer)
|
36
37
|
end
|
37
38
|
|
38
|
-
def month(
|
39
|
-
Sequel.function(:strftime, '%m',
|
39
|
+
def month(lq)
|
40
|
+
Sequel.function(:strftime, '%m', lq).cast(:integer)
|
40
41
|
end
|
41
42
|
|
42
|
-
def second(
|
43
|
-
Sequel.function(:strftime, '%S',
|
43
|
+
def second(lq)
|
44
|
+
Sequel.function(:strftime, '%S', lq).cast(:integer)
|
44
45
|
end
|
45
46
|
|
46
|
-
def minute(
|
47
|
-
Sequel.function(:strftime, '%M',
|
47
|
+
def minute(lq)
|
48
|
+
Sequel.function(:strftime, '%M', lq).cast(:integer)
|
48
49
|
end
|
49
50
|
|
50
|
-
def hour(
|
51
|
-
Sequel.function(:strftime, '%H',
|
51
|
+
def hour(lq)
|
52
|
+
Sequel.function(:strftime, '%H', lq).cast(:integer)
|
52
53
|
end
|
53
54
|
|
54
|
-
def day(
|
55
|
-
Sequel.function(:strftime, '%d',
|
55
|
+
def day(lq)
|
56
|
+
Sequel.function(:strftime, '%d', lq).cast(:integer)
|
56
57
|
end
|
57
58
|
|
58
|
-
def floor(
|
59
|
-
|
59
|
+
def floor(_lq)
|
60
|
+
Safrano::FilterFunctionNotImplementedError.new("$filter function 'floor' is not implemented in sqlite adapter")
|
60
61
|
end
|
61
62
|
|
62
|
-
def ceiling(
|
63
|
-
|
63
|
+
def ceiling(_lq)
|
64
|
+
Safrano::FilterFunctionNotImplementedError.new("$filter function 'ceiling' is not implemented in sqlite adapter")
|
64
65
|
end
|
65
66
|
end
|
66
67
|
# re-useable module with math floor/ceil functions for those adapters having these SQL funcs
|
67
68
|
module MathFloorCeilFuncTree
|
68
|
-
def floor(
|
69
|
-
Sequel.function(:floor,
|
69
|
+
def floor(lq)
|
70
|
+
success Sequel.function(:floor, lq)
|
70
71
|
end
|
71
72
|
|
72
|
-
def ceiling(
|
73
|
-
Sequel.function(:ceil,
|
73
|
+
def ceiling(lq)
|
74
|
+
success Sequel.function(:ceil, lq)
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
77
78
|
# re-useable module with Datetime functions with extract()
|
78
79
|
module DateTimeFuncTreeExtract
|
79
|
-
def year(
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def year(jh)
|
84
|
-
args.first.leuqes(jh).extract(:year)
|
80
|
+
def year(lq)
|
81
|
+
lq.extract(:year)
|
85
82
|
end
|
86
83
|
|
87
|
-
def month(
|
88
|
-
|
84
|
+
def month(lq)
|
85
|
+
lq.extract(:month)
|
89
86
|
end
|
90
87
|
|
91
|
-
def second(
|
92
|
-
|
88
|
+
def second(lq)
|
89
|
+
lq.extract(:second)
|
93
90
|
end
|
94
91
|
|
95
|
-
def minute(
|
96
|
-
|
92
|
+
def minute(lq)
|
93
|
+
lq.extract(:minute)
|
97
94
|
end
|
98
95
|
|
99
|
-
def hour(
|
100
|
-
|
96
|
+
def hour(lq)
|
97
|
+
lq.extract(:hour)
|
101
98
|
end
|
102
99
|
|
103
|
-
def day(
|
104
|
-
|
100
|
+
def day(lq)
|
101
|
+
lq.extract(:day)
|
105
102
|
end
|
106
103
|
end
|
107
104
|
|
@@ -111,9 +108,11 @@ module OData
|
|
111
108
|
# substringof(name, '__Route du Rhum__') -->
|
112
109
|
# '__Route du Rhum__' contains name as a substring
|
113
110
|
# postgres does not know instr() but has strpos
|
114
|
-
|
115
|
-
|
116
|
-
|
111
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
112
|
+
args[0].leuqes(jh)) do |l1, l0|
|
113
|
+
substr_func = Sequel.function(:strpos, l1, l0)
|
114
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
115
|
+
end
|
117
116
|
end
|
118
117
|
|
119
118
|
# postgres uses extract()
|
@@ -132,9 +131,11 @@ module OData
|
|
132
131
|
# substringof(name, '__Route du Rhum__') -->
|
133
132
|
# '__Route du Rhum__' contains name as a substring
|
134
133
|
# instr() seems to be the most common substring func
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
135
|
+
args[0].leuqes(jh)) do |l1, l0|
|
136
|
+
substr_func = Sequel.function(:instr, l1, l0)
|
137
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
138
|
+
end
|
138
139
|
end
|
139
140
|
|
140
141
|
# XYZ uses extract() ?
|