sparkql 1.1.13 → 1.1.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +6 -0
- data/GRAMMAR.md +2 -0
- data/VERSION +1 -1
- data/lib/sparkql/function_resolver.rb +87 -2
- data/lib/sparkql/lexer.rb +2 -0
- data/lib/sparkql/parser.rb +68 -52
- data/lib/sparkql/parser.y +2 -0
- data/lib/sparkql/parser_compatibility.rb +1 -1
- data/lib/sparkql/parser_tools.rb +19 -0
- data/test/unit/function_resolver_test.rb +142 -5
- data/test/unit/parser_test.rb +109 -22
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTMwMTA4MTcwZWRiNzFiMzJkOTFmYzgwNmVlYjRhNzQwZTZlOWQ5OA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OWQ3NWE4ZmQxOTQ3ZTcxMTk1MmE5MDM4OWU5NWNhNWY5NDUwZDUyYg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MzY2NGFkZWYxNWM2ZGQyZWMyNjRmZTkzMzU2OTYwZDBmNzk2YjhmMzc4ZjFj
|
10
|
+
NjdiYTM5MTBjNmY3ZTQxNmVkOWU4YzFjYzY5MjY2NDQzZDI3MzVmZmUxYWQ0
|
11
|
+
NGZmYzE5MGM3ODlkZmQ1M2M4NGY4M2I2MGE0MzQyYzVkNzcwOTU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NDVmYjg0NjJkYTI1OGJkNjI1MDYxMzc4OGNkNzkyZmFkNjM1ZmU1NmE1OGJl
|
14
|
+
ODcyOTRiYzcyYjcxNWJhMTk2NWM2ODc2YjQyZTg5YjgwMjBkNzk2NzczODAx
|
15
|
+
OTc4ZmVmZDBiY2I5ZDc0YWE3YmQyMDAwNTFlZTJkZTg4Yjg2Mjc=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
v1.1.14, 2018-07-12
|
2
|
+
-------------------
|
3
|
+
* [IMPROVEMENT] Allow Negation for integer and decimal literals
|
4
|
+
* [IMPROVEMENT] New Functions: round(), substring(), trim()
|
5
|
+
* [BUGFIX] `MIN_DATE_TIME` was increased to 1970
|
6
|
+
|
1
7
|
v1.1.13, 2018-06-27
|
2
8
|
-------------------
|
3
9
|
* [IMPROVEMENT] New Functions: length(), mindatetime(), maxdatetime()
|
data/GRAMMAR.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.1.
|
1
|
+
1.1.14
|
@@ -14,7 +14,7 @@ class Sparkql::FunctionResolver
|
|
14
14
|
STRFTIME_DATE_FORMAT = '%Y-%m-%d'
|
15
15
|
STRFTIME_TIME_FORMAT = '%H:%M:%S.%N'
|
16
16
|
VALID_REGEX_FLAGS = ["", "i"]
|
17
|
-
MIN_DATE_TIME = Time.new(
|
17
|
+
MIN_DATE_TIME = Time.new(1970, 1, 1, 0, 0, 0, "+00:00").iso8601
|
18
18
|
MAX_DATE_TIME = Time.new(9999, 12, 31, 23, 59, 59, "+00:00").iso8601
|
19
19
|
SUPPORTED_FUNCTIONS = {
|
20
20
|
:polygon => {
|
@@ -37,6 +37,19 @@ class Sparkql::FunctionResolver
|
|
37
37
|
}],
|
38
38
|
:return_type => :character
|
39
39
|
},
|
40
|
+
:substring => {
|
41
|
+
:args => [[:field, :character], :integer],
|
42
|
+
:opt_args => [{
|
43
|
+
:type => :integer
|
44
|
+
}],
|
45
|
+
:resolve_for_type => true,
|
46
|
+
:return_type => :character
|
47
|
+
},
|
48
|
+
:trim => {
|
49
|
+
:args => [[:field, :character]],
|
50
|
+
:resolve_for_type => true,
|
51
|
+
:return_type => :character
|
52
|
+
},
|
40
53
|
:tolower => {
|
41
54
|
:args => [[:field, :character]],
|
42
55
|
:resolve_for_type => true,
|
@@ -56,6 +69,11 @@ class Sparkql::FunctionResolver
|
|
56
69
|
:args => [[:field, :character], :character],
|
57
70
|
:return_type => :integer
|
58
71
|
},
|
72
|
+
:round => {
|
73
|
+
:args => [[:field, :decimal]],
|
74
|
+
:resolve_for_type => true,
|
75
|
+
:return_type => :integer
|
76
|
+
},
|
59
77
|
:startswith => {
|
60
78
|
:args => [:character],
|
61
79
|
:return_type => :startswith
|
@@ -264,6 +282,57 @@ class Sparkql::FunctionResolver
|
|
264
282
|
}
|
265
283
|
end
|
266
284
|
|
285
|
+
def trim_field(arg)
|
286
|
+
{
|
287
|
+
:type => :function,
|
288
|
+
:value => "trim",
|
289
|
+
:args => [arg]
|
290
|
+
}
|
291
|
+
end
|
292
|
+
|
293
|
+
def trim_character(arg)
|
294
|
+
{
|
295
|
+
:type => :character,
|
296
|
+
:value => arg.strip
|
297
|
+
}
|
298
|
+
end
|
299
|
+
|
300
|
+
def substring_field(field, first_index, number_chars)
|
301
|
+
return if substring_index_error?(number_chars)
|
302
|
+
{
|
303
|
+
:type => :function,
|
304
|
+
:value => "substring",
|
305
|
+
:args => [field, first_index, number_chars]
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
def substring_character(character, first_index, number_chars)
|
310
|
+
return if substring_index_error?(number_chars)
|
311
|
+
|
312
|
+
second_index = if number_chars.nil?
|
313
|
+
-1
|
314
|
+
else
|
315
|
+
number_chars + first_index - 1
|
316
|
+
end
|
317
|
+
|
318
|
+
new_string = character[first_index..second_index].to_s
|
319
|
+
|
320
|
+
{
|
321
|
+
:type => :character,
|
322
|
+
:value => new_string
|
323
|
+
}
|
324
|
+
end
|
325
|
+
|
326
|
+
def substring_index_error?(second_index)
|
327
|
+
if second_index.to_i < 0
|
328
|
+
@errors << Sparkql::ParserError.new(:token => second_index,
|
329
|
+
:message => "Function call 'substring' may not have a negative integer for its second parameter",
|
330
|
+
:status => :fatal)
|
331
|
+
true
|
332
|
+
end
|
333
|
+
false
|
334
|
+
end
|
335
|
+
|
267
336
|
def tolower_character(string)
|
268
337
|
{
|
269
338
|
:type => :character,
|
@@ -271,7 +340,8 @@ class Sparkql::FunctionResolver
|
|
271
340
|
}
|
272
341
|
end
|
273
342
|
|
274
|
-
def tolower_field(arg)
|
343
|
+
def tolower_field(arg)
|
344
|
+
{
|
275
345
|
:type => :function,
|
276
346
|
:value => "tolower",
|
277
347
|
:args => [arg]
|
@@ -398,6 +468,21 @@ class Sparkql::FunctionResolver
|
|
398
468
|
}
|
399
469
|
end
|
400
470
|
|
471
|
+
def round_decimal(arg)
|
472
|
+
{
|
473
|
+
:type => :integer,
|
474
|
+
:value => arg.round.to_s
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
def round_field(arg)
|
479
|
+
{
|
480
|
+
:type => :function,
|
481
|
+
:value => "round",
|
482
|
+
:args => [arg]
|
483
|
+
}
|
484
|
+
end
|
485
|
+
|
401
486
|
def indexof(arg1, arg2)
|
402
487
|
{
|
403
488
|
:type => :function,
|
data/lib/sparkql/lexer.rb
CHANGED
@@ -47,6 +47,8 @@ class Sparkql::Lexer < StringScanner
|
|
47
47
|
literal :DECIMAL, @current_token_value
|
48
48
|
when @current_token_value = scan(INTEGER)
|
49
49
|
literal :INTEGER, @current_token_value
|
50
|
+
when @current_token_value = scan(/\-/)
|
51
|
+
[:UMINUS, @current_token_value]
|
50
52
|
when @current_token_value = scan(CHARACTER)
|
51
53
|
literal :CHARACTER, @current_token_value
|
52
54
|
when @current_token_value = scan(BOOLEAN)
|
data/lib/sparkql/parser.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# DO NOT MODIFY!!!!
|
3
|
-
# This file is automatically generated by Racc 1.4.
|
3
|
+
# This file is automatically generated by Racc 1.4.14
|
4
4
|
# from Racc grammer file "".
|
5
5
|
#
|
6
6
|
|
@@ -16,68 +16,72 @@ module Sparkql
|
|
16
16
|
##### State transition tables begin ###
|
17
17
|
|
18
18
|
racc_action_table = [
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
19
|
+
35, 14, 15, 43, 44, 34, 45, 46, 47, 14,
|
20
|
+
22, 31, 32, 33, 36, 37, 38, 39, 40, 35,
|
21
|
+
17, 16, 23, 49, 34, 50, 10, 11, 14, -28,
|
22
|
+
31, 32, 33, 36, 37, 38, 39, 40, 35, 56,
|
23
|
+
60, 17, 16, 34, 61, 10, 11, 14, 59, 31,
|
24
|
+
32, 33, 36, 37, 38, 39, 40, 35, 18, 19,
|
25
|
+
35, 64, 34, nil, nil, 34, nil, nil, 31, 32,
|
26
|
+
33, 31, 32, 33, 35, 14, nil, 43, 44, 34,
|
27
|
+
45, 46, 47, 14, nil, 31, 32, 33, 8, nil,
|
28
|
+
9, nil, 10, 11, 14, 8, nil, 9, nil, 10,
|
29
|
+
11, 14, 9, nil, 10, 11, 14, 9, nil, 10,
|
30
|
+
11, 14, 9, nil, 10, 11, 14 ]
|
29
31
|
|
30
32
|
racc_action_check = [
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
18, 19, 1, 19, 19, 18, 19, 19, 19, 18,
|
34
|
+
13, 18, 18, 18, 18, 18, 18, 18, 18, 22,
|
35
|
+
21, 21, 15, 21, 22, 22, 22, 22, 22, 28,
|
36
|
+
22, 22, 22, 22, 22, 22, 22, 22, 61, 29,
|
37
|
+
51, 2, 2, 61, 51, 61, 61, 61, 42, 61,
|
38
|
+
61, 61, 61, 61, 61, 61, 61, 34, 6, 6,
|
39
|
+
35, 57, 34, nil, nil, 35, nil, nil, 34, 34,
|
40
|
+
34, 35, 35, 35, 56, 59, nil, 59, 59, 56,
|
41
|
+
59, 59, 59, 56, nil, 56, 56, 56, 0, nil,
|
42
|
+
0, nil, 0, 0, 0, 9, nil, 9, nil, 9,
|
43
|
+
9, 9, 8, nil, 8, 8, 8, 16, nil, 16,
|
44
|
+
16, 16, 17, nil, 17, 17, 17 ]
|
41
45
|
|
42
46
|
racc_action_pointer = [
|
43
|
-
|
44
|
-
nil, nil, nil,
|
45
|
-
nil,
|
46
|
-
nil, nil, nil, nil,
|
47
|
-
|
48
|
-
nil, nil, nil, nil,
|
49
|
-
nil, nil ]
|
47
|
+
83, 2, 36, nil, nil, nil, 55, nil, 95, 90,
|
48
|
+
nil, nil, nil, 3, nil, 22, 100, 105, -2, -10,
|
49
|
+
nil, 15, 17, nil, nil, nil, nil, nil, 17, 27,
|
50
|
+
nil, nil, nil, nil, 55, 58, nil, nil, nil, nil,
|
51
|
+
nil, nil, 36, nil, nil, nil, nil, nil, nil, nil,
|
52
|
+
nil, 32, nil, nil, nil, nil, 72, 53, nil, 64,
|
53
|
+
nil, 36, nil, nil, nil, nil, nil ]
|
50
54
|
|
51
55
|
racc_action_default = [
|
52
|
-
-2, -
|
53
|
-
-13, -14, -15, -
|
54
|
-
-9, -
|
55
|
-
-27, -32, -33, -34, -
|
56
|
-
-
|
57
|
-
-
|
58
|
-
-31, -23 ]
|
56
|
+
-2, -48, -1, -3, -4, -5, -48, -8, -48, -48,
|
57
|
+
-13, -14, -15, -48, -21, -48, -48, -48, -48, -48,
|
58
|
+
-9, -48, -48, 67, -10, -11, -6, -16, -17, -18,
|
59
|
+
-27, -32, -33, -34, -48, -48, -37, -38, -39, -40,
|
60
|
+
-41, -7, -48, -42, -43, -44, -45, -46, -47, -12,
|
61
|
+
-19, -48, -22, -24, -25, -26, -48, -48, -36, -48,
|
62
|
+
-20, -48, -29, -30, -35, -31, -23 ]
|
59
63
|
|
60
64
|
racc_goto_table = [
|
61
|
-
28,
|
62
|
-
|
63
|
-
nil, nil, nil,
|
64
|
-
nil, nil, nil, nil, nil, nil,
|
65
|
-
|
65
|
+
28, 48, 42, 2, 55, 52, 20, 30, 1, 26,
|
66
|
+
41, 27, 21, 29, 24, 25, 51, nil, nil, nil,
|
67
|
+
nil, nil, nil, 57, 58, nil, nil, nil, nil, nil,
|
68
|
+
nil, nil, nil, nil, nil, nil, nil, nil, 63, nil,
|
69
|
+
nil, 48, 65, 55, 66, 62 ]
|
66
70
|
|
67
71
|
racc_goto_check = [
|
68
|
-
10, 10,
|
69
|
-
|
70
|
-
nil, nil, nil,
|
71
|
-
nil, nil, nil, nil, nil, nil,
|
72
|
-
6, 15 ]
|
72
|
+
10, 10, 17, 2, 6, 15, 3, 16, 1, 7,
|
73
|
+
8, 11, 2, 12, 3, 3, 14, nil, nil, nil,
|
74
|
+
nil, nil, nil, 16, 16, nil, nil, nil, nil, nil,
|
75
|
+
nil, nil, nil, nil, nil, nil, nil, nil, 10, nil,
|
76
|
+
nil, 10, 17, 6, 15, 16 ]
|
73
77
|
|
74
78
|
racc_goto_pointer = [
|
75
|
-
nil,
|
76
|
-
-18, -
|
79
|
+
nil, 8, 3, -2, nil, nil, -18, -9, -9, nil,
|
80
|
+
-18, -7, -5, nil, -6, -17, -11, -17 ]
|
77
81
|
|
78
82
|
racc_goto_default = [
|
79
83
|
nil, nil, nil, 3, 4, 5, 6, nil, nil, 7,
|
80
|
-
12,
|
84
|
+
12, 53, nil, 13, nil, nil, 54, nil ]
|
81
85
|
|
82
86
|
racc_reduce_table = [
|
83
87
|
0, 0, :racc_error,
|
@@ -115,6 +119,8 @@ racc_reduce_table = [
|
|
115
119
|
1, 37, :_reduce_none,
|
116
120
|
1, 37, :_reduce_none,
|
117
121
|
1, 37, :_reduce_none,
|
122
|
+
3, 37, :_reduce_35,
|
123
|
+
2, 37, :_reduce_36,
|
118
124
|
1, 32, :_reduce_none,
|
119
125
|
1, 32, :_reduce_none,
|
120
126
|
1, 32, :_reduce_none,
|
@@ -127,9 +133,9 @@ racc_reduce_table = [
|
|
127
133
|
1, 38, :_reduce_none,
|
128
134
|
1, 38, :_reduce_none ]
|
129
135
|
|
130
|
-
racc_reduce_n =
|
136
|
+
racc_reduce_n = 48
|
131
137
|
|
132
|
-
racc_shift_n =
|
138
|
+
racc_shift_n = 67
|
133
139
|
|
134
140
|
racc_token_table = {
|
135
141
|
false => 0,
|
@@ -334,9 +340,15 @@ end
|
|
334
340
|
|
335
341
|
# reduce 34 omitted
|
336
342
|
|
337
|
-
|
343
|
+
def _reduce_35(val, _values, result)
|
344
|
+
result = val[1]
|
345
|
+
result
|
346
|
+
end
|
338
347
|
|
339
|
-
|
348
|
+
def _reduce_36(val, _values, result)
|
349
|
+
result = tokenize_literal_negation(val[1])
|
350
|
+
result
|
351
|
+
end
|
340
352
|
|
341
353
|
# reduce 37 omitted
|
342
354
|
|
@@ -356,6 +368,10 @@ end
|
|
356
368
|
|
357
369
|
# reduce 45 omitted
|
358
370
|
|
371
|
+
# reduce 46 omitted
|
372
|
+
|
373
|
+
# reduce 47 omitted
|
374
|
+
|
359
375
|
def _reduce_none(val, _values, result)
|
360
376
|
val[0]
|
361
377
|
end
|
data/lib/sparkql/parser.y
CHANGED
data/lib/sparkql/parser_tools.rb
CHANGED
@@ -91,6 +91,25 @@ module Sparkql::ParserTools
|
|
91
91
|
list
|
92
92
|
end
|
93
93
|
|
94
|
+
def tokenize_literal_negation(number_token)
|
95
|
+
old_val = case number_token[:type]
|
96
|
+
when :integer
|
97
|
+
number_token[:value].to_i
|
98
|
+
when :decimal
|
99
|
+
number_token[:value].to_f
|
100
|
+
else
|
101
|
+
tokenizer_error(:token => @lexer.current_token_value,
|
102
|
+
:expression => number_token,
|
103
|
+
:message => "Negation is only allowed for integer and floats",
|
104
|
+
:status => :fatal,
|
105
|
+
:syntax => true)
|
106
|
+
return number_token
|
107
|
+
end
|
108
|
+
number_token[:value] = (-1 * old_val).to_s
|
109
|
+
|
110
|
+
number_token
|
111
|
+
end
|
112
|
+
|
94
113
|
def tokenize_multiple(lit1, lit2)
|
95
114
|
final_type = lit1[:type]
|
96
115
|
if lit1[:type] != lit2[:type]
|
@@ -14,14 +14,151 @@ class FunctionResolverTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal(["35.12 -68.33", 1.0], value[:function_parameters])
|
15
15
|
end
|
16
16
|
|
17
|
-
test "
|
18
|
-
f = FunctionResolver.new('
|
17
|
+
test "round(float)" do
|
18
|
+
f = FunctionResolver.new('round', [{:type => :decimal, :value => 0.5}])
|
19
19
|
f.validate
|
20
20
|
assert !f.errors?, "Errors #{f.errors.inspect}"
|
21
21
|
value = f.call
|
22
|
+
assert_equal :integer, value[:type]
|
23
|
+
assert_equal '1', value[:value]
|
24
|
+
end
|
25
|
+
|
26
|
+
test "round(Field)" do
|
27
|
+
f = FunctionResolver.new('round', [{:type => :field, :value => 'ListPrice'}])
|
28
|
+
f.validate
|
29
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
30
|
+
value = f.call
|
31
|
+
|
22
32
|
assert_equal :function, value[:type]
|
23
|
-
assert_equal '
|
24
|
-
assert_equal "
|
33
|
+
assert_equal 'round', value[:value]
|
34
|
+
assert_equal "ListPrice", value[:args].first
|
35
|
+
end
|
36
|
+
|
37
|
+
test "substring character one index" do
|
38
|
+
f = FunctionResolver.new('substring', [
|
39
|
+
{:type => :character, :value => 'ListPrice'},
|
40
|
+
{:type => :integer, :value => 1}
|
41
|
+
])
|
42
|
+
|
43
|
+
f.validate
|
44
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
45
|
+
value = f.call
|
46
|
+
|
47
|
+
assert_equal :character, value[:type]
|
48
|
+
assert_equal 'istPrice', value[:value]
|
49
|
+
end
|
50
|
+
|
51
|
+
test "substring character two indexes" do
|
52
|
+
f = FunctionResolver.new('substring', [
|
53
|
+
{:type => :character, :value => 'alfb'},
|
54
|
+
{:type => :integer, :value => 1},
|
55
|
+
{:type => :integer, :value => 2}
|
56
|
+
])
|
57
|
+
|
58
|
+
f.validate
|
59
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
60
|
+
value = f.call
|
61
|
+
|
62
|
+
assert_equal :character, value[:type]
|
63
|
+
assert_equal "lf", value[:value]
|
64
|
+
end
|
65
|
+
|
66
|
+
test "substring character large first index" do
|
67
|
+
f = FunctionResolver.new('substring', [
|
68
|
+
{:type => :character, :value => 'ListPrice'},
|
69
|
+
{:type => :integer, :value => 10}
|
70
|
+
])
|
71
|
+
|
72
|
+
f.validate
|
73
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
74
|
+
value = f.call
|
75
|
+
|
76
|
+
assert_equal :character, value[:type]
|
77
|
+
assert_equal '', value[:value]
|
78
|
+
end
|
79
|
+
|
80
|
+
test "substring field one index" do
|
81
|
+
f = FunctionResolver.new('substring', [
|
82
|
+
{:type => :field, :value => 'ListPrice'},
|
83
|
+
{:type => :integer, :value => 1}
|
84
|
+
])
|
85
|
+
|
86
|
+
f.validate
|
87
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
88
|
+
value = f.call
|
89
|
+
|
90
|
+
assert_equal :function, value[:type]
|
91
|
+
assert_equal 'substring', value[:value]
|
92
|
+
assert_equal "ListPrice", value[:args].first
|
93
|
+
assert_nil value[:args].last
|
94
|
+
end
|
95
|
+
|
96
|
+
test "substring field two indexes" do
|
97
|
+
f = FunctionResolver.new('substring', [
|
98
|
+
{:type => :field, :value => 'ListPrice'},
|
99
|
+
{:type => :integer, :value => 1},
|
100
|
+
{:type => :integer, :value => 2}
|
101
|
+
])
|
102
|
+
|
103
|
+
f.validate
|
104
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
105
|
+
value = f.call
|
106
|
+
|
107
|
+
assert_equal :function, value[:type]
|
108
|
+
assert_equal 'substring', value[:value]
|
109
|
+
assert_equal "ListPrice", value[:args].first
|
110
|
+
assert_equal 2, value[:args].last
|
111
|
+
end
|
112
|
+
|
113
|
+
test "substring with negative M is a parse error" do
|
114
|
+
f = FunctionResolver.new('substring', [
|
115
|
+
{:type => :field, :value => 'ListPrice'},
|
116
|
+
{:type => :integer, :value => 1},
|
117
|
+
{:type => :integer, :value => -5}
|
118
|
+
])
|
119
|
+
|
120
|
+
f.validate
|
121
|
+
f.call
|
122
|
+
assert f.errors?
|
123
|
+
end
|
124
|
+
|
125
|
+
test "character substring with negative M is a parse error" do
|
126
|
+
f = FunctionResolver.new('substring', [
|
127
|
+
{:type => :character, :value => 'ListPrice'},
|
128
|
+
{:type => :integer, :value => 1},
|
129
|
+
{:type => :integer, :value => -5}
|
130
|
+
])
|
131
|
+
|
132
|
+
f.validate
|
133
|
+
f.call
|
134
|
+
assert f.errors?
|
135
|
+
end
|
136
|
+
|
137
|
+
test 'trim with field' do
|
138
|
+
f = FunctionResolver.new('trim', [
|
139
|
+
{:type => :field, :value => 'Name'}
|
140
|
+
])
|
141
|
+
|
142
|
+
f.validate
|
143
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
144
|
+
value = f.call
|
145
|
+
|
146
|
+
assert_equal :function, value[:type]
|
147
|
+
assert_equal 'trim', value[:value]
|
148
|
+
assert_equal "Name", value[:args].first
|
149
|
+
end
|
150
|
+
|
151
|
+
test 'trim with character' do
|
152
|
+
f = FunctionResolver.new('trim', [
|
153
|
+
{:type => :character, :value => ' val '}
|
154
|
+
])
|
155
|
+
|
156
|
+
f.validate
|
157
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
158
|
+
value = f.call
|
159
|
+
|
160
|
+
assert_equal :character, value[:type]
|
161
|
+
assert_equal 'val', value[:value]
|
25
162
|
end
|
26
163
|
|
27
164
|
test "tolower('string')" do
|
@@ -89,7 +226,7 @@ class FunctionResolverTest < Test::Unit::TestCase
|
|
89
226
|
value = f.call
|
90
227
|
assert_equal :datetime, value[:type]
|
91
228
|
|
92
|
-
assert_equal '
|
229
|
+
assert_equal '1970-01-01T00:00:00+00:00', value[:value]
|
93
230
|
end
|
94
231
|
|
95
232
|
test "maxdatetime()" do
|
data/test/unit/parser_test.rb
CHANGED
@@ -56,13 +56,6 @@ class ParserTest < Test::Unit::TestCase
|
|
56
56
|
assert_equal '10,11,12', expression[:condition]
|
57
57
|
end
|
58
58
|
|
59
|
-
def parse(q,v)
|
60
|
-
expressions = @parser.parse(q)
|
61
|
-
assert !@parser.errors?, "Unexpected error parsing #{q} #{@parser.errors.inspect}"
|
62
|
-
assert_equal v, expressions.first[:value], "Expression #{expressions.inspect}"
|
63
|
-
assert !expressions.first[:custom_field], "Unexepected custom field #{expressions.inspect}"
|
64
|
-
end
|
65
|
-
|
66
59
|
def test_invalid_syntax
|
67
60
|
@parser = Parser.new
|
68
61
|
expression = @parser.parse('Test Eq DERP')
|
@@ -103,20 +96,6 @@ class ParserTest < Test::Unit::TestCase
|
|
103
96
|
)
|
104
97
|
end
|
105
98
|
|
106
|
-
# verify each expression in the query is at the right nesting level and group
|
107
|
-
def assert_nesting(sparkql, levels=[], block_groups=nil)
|
108
|
-
block_groups = levels.clone if block_groups.nil?
|
109
|
-
parser = Parser.new
|
110
|
-
expressions = parser.parse(sparkql)
|
111
|
-
assert !parser.errors?, "Unexpected error parsing #{sparkql}: #{parser.errors.inspect}"
|
112
|
-
count = 0
|
113
|
-
expressions.each do |ex|
|
114
|
-
assert_equal levels[count], ex[:level], "Nesting level wrong for #{ex.inspect}"
|
115
|
-
assert_equal(block_groups[count], ex[:block_group], "Nesting block group wrong for #{ex.inspect}")
|
116
|
-
count +=1
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
99
|
def test_bad_queries
|
121
100
|
filter = "City IsLikeA 'Town'"
|
122
101
|
@parser = Parser.new
|
@@ -629,11 +608,119 @@ class ParserTest < Test::Unit::TestCase
|
|
629
608
|
assert_nil @parser.coercible_types(:integer, :date)
|
630
609
|
end
|
631
610
|
|
611
|
+
def test_literal_group
|
612
|
+
filter = "ListPrice Gt (5)"
|
613
|
+
@parser = Parser.new
|
614
|
+
@parser.parse(filter)
|
615
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
616
|
+
end
|
617
|
+
|
618
|
+
def test_integer_negation
|
619
|
+
filter = "ListPrice Gt -(5)"
|
620
|
+
@parser = Parser.new
|
621
|
+
expression = @parser.parse(filter).first
|
622
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
623
|
+
|
624
|
+
assert_equal :integer, expression[:type]
|
625
|
+
assert_equal '-5', expression[:value]
|
626
|
+
end
|
627
|
+
|
628
|
+
def test_decimal_negation
|
629
|
+
filter = "ListPrice Gt -(5.1)"
|
630
|
+
@parser = Parser.new
|
631
|
+
expressions = @parser.parse(filter)
|
632
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
633
|
+
|
634
|
+
expression = expressions.first
|
635
|
+
assert_equal :decimal, expression[:type]
|
636
|
+
assert_equal '-5.1', expression[:value]
|
637
|
+
end
|
638
|
+
|
639
|
+
def test_nested_negation
|
640
|
+
filter = "ListPrice Gt -(-5)"
|
641
|
+
@parser = Parser.new
|
642
|
+
expression = @parser.parse(filter).first
|
643
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
644
|
+
|
645
|
+
assert_equal :integer, expression[:type]
|
646
|
+
assert_equal '5', expression[:value]
|
647
|
+
|
648
|
+
filter = "ListPrice Gt --5"
|
649
|
+
@parser = Parser.new
|
650
|
+
expression = @parser.parse(filter).first
|
651
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
652
|
+
|
653
|
+
assert_equal :integer, expression[:type]
|
654
|
+
assert_equal '5', expression[:value]
|
655
|
+
end
|
656
|
+
|
657
|
+
def test_string_negation_does_not_parse
|
658
|
+
parser_errors("Field Eq -'Stringval'")
|
659
|
+
end
|
660
|
+
|
661
|
+
def test_substring
|
662
|
+
filter = "Name Eq substring('Andy', 1)"
|
663
|
+
@parser = Parser.new
|
664
|
+
expression = @parser.parse(filter).first
|
665
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
666
|
+
assert_equal 'ndy', expression[:value]
|
667
|
+
end
|
668
|
+
|
669
|
+
def test_round_with_literal
|
670
|
+
filter = "ListPrice Eq round(0.5)"
|
671
|
+
@parser = Parser.new
|
672
|
+
expression = @parser.parse(filter).first
|
673
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
674
|
+
|
675
|
+
assert_equal :integer, expression[:type]
|
676
|
+
assert_equal "1", expression[:value]
|
677
|
+
|
678
|
+
filter = "ListPrice Eq round(-0.5)"
|
679
|
+
@parser = Parser.new
|
680
|
+
expression = @parser.parse(filter).first
|
681
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
682
|
+
|
683
|
+
assert_equal :integer, expression[:type]
|
684
|
+
assert_equal "-1", expression[:value]
|
685
|
+
end
|
686
|
+
|
687
|
+
def test_round_with_field
|
688
|
+
filter = "ListPrice Eq round(FieldName)"
|
689
|
+
@parser = Parser.new
|
690
|
+
expression = @parser.parse(filter).first
|
691
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
692
|
+
|
693
|
+
assert_equal 'round', expression[:function_name]
|
694
|
+
assert_equal 'round(FieldName)', expression[:condition]
|
695
|
+
assert_equal(["FieldName"], expression[:function_parameters])
|
696
|
+
end
|
697
|
+
|
698
|
+
private
|
632
699
|
|
633
700
|
def parser_errors(filter)
|
634
701
|
@parser = Parser.new
|
635
702
|
expression = @parser.parse(filter)
|
636
703
|
assert @parser.errors?, "Should find errors for '#{filter}': #{expression}"
|
637
704
|
end
|
638
|
-
|
705
|
+
|
706
|
+
def parse(q,v)
|
707
|
+
expressions = @parser.parse(q)
|
708
|
+
assert !@parser.errors?, "Unexpected error parsing #{q} #{@parser.errors.inspect}"
|
709
|
+
assert_equal v, expressions.first[:value], "Expression #{expressions.inspect}"
|
710
|
+
assert !expressions.first[:custom_field], "Unexepected custom field #{expressions.inspect}"
|
711
|
+
end
|
712
|
+
|
713
|
+
# verify each expression in the query is at the right nesting level and group
|
714
|
+
def assert_nesting(sparkql, levels=[], block_groups=nil)
|
715
|
+
block_groups = levels.clone if block_groups.nil?
|
716
|
+
parser = Parser.new
|
717
|
+
expressions = parser.parse(sparkql)
|
718
|
+
assert !parser.errors?, "Unexpected error parsing #{sparkql}: #{parser.errors.inspect}"
|
719
|
+
count = 0
|
720
|
+
expressions.each do |ex|
|
721
|
+
assert_equal levels[count], ex[:level], "Nesting level wrong for #{ex.inspect}"
|
722
|
+
assert_equal(block_groups[count], ex[:block_group], "Nesting block group wrong for #{ex.inspect}")
|
723
|
+
count +=1
|
724
|
+
end
|
725
|
+
end
|
639
726
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sparkql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wade McEwen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: georuby
|