sparkql 1.1.13 → 1.1.14
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 +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
|