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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTJhM2I5YjU5YTg1NTVhNDgyOTJjYzE5YjI4YjlkZjkzZmI3NjFmYw==
4
+ MTMwMTA4MTcwZWRiNzFiMzJkOTFmYzgwNmVlYjRhNzQwZTZlOWQ5OA==
5
5
  data.tar.gz: !binary |-
6
- MjAyMjAwN2VhYTNmN2Q1NjhkNGZmN2FhNGY5OTgwMjdhNDY3ZWFjMQ==
6
+ OWQ3NWE4ZmQxOTQ3ZTcxMTk1MmE5MDM4OWU5NWNhNWY5NDUwZDUyYg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OWY5MmVmNDhmMGM0OTgzNmE2NjE4OWM5NmQ4M2QxOTJiMGFiOGQ4MDlkMmJj
10
- NDBhNTVkZDgyMGJmYmU1Y2YwOTdiOTNmN2ZkYTcxOWM1MWNkNjJmZmJiMmI3
11
- ODhmOGIzNWFiYTEwYTQzMThiNGNkNThlNDc0OGQzYTNmMjA4NWU=
9
+ MzY2NGFkZWYxNWM2ZGQyZWMyNjRmZTkzMzU2OTYwZDBmNzk2YjhmMzc4ZjFj
10
+ NjdiYTM5MTBjNmY3ZTQxNmVkOWU4YzFjYzY5MjY2NDQzZDI3MzVmZmUxYWQ0
11
+ NGZmYzE5MGM3ODlkZmQ1M2M4NGY4M2I2MGE0MzQyYzVkNzcwOTU=
12
12
  data.tar.gz: !binary |-
13
- MzI5MWY5NDRkN2ViNGFhMzUzOTQwMWZmZTQ3ZmUwZjE3ZGY5NDJjM2UxMjYy
14
- ZTdiMjY0NDA1OWJiNDVlM2YyM2I0NzdiOWVlN2E3M2M2YjFmNDc4NjQ4ZmJh
15
- ZWNlYTE0ODE4ZmQ0ZGQyZjUxNTk5YTM2YWIxYTg2Y2YzMGY4Nzg=
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
@@ -178,6 +178,8 @@ Literals that support multiple values in a list for a condition
178
178
  : INTEGER
179
179
  | DECIMAL
180
180
  | CHARACTER
181
+ | LPAREN literals RPAREN
182
+ | UMINUS literals
181
183
  ;
182
184
  ```
183
185
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.13
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(1, 1, 1, 0, 0, 0, "+00:00").iso8601
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)
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.4.8
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
- 48, 10, 11, 14, 54, 31, 32, 33, 34, 35,
20
- 36, 37, 38, 10, 11, 14, 55, 31, 32, 33,
21
- 34, 35, 36, 37, 38, 14, -28, 31, 32, 33,
22
- 34, 35, 36, 37, 38, 14, 23, 41, 42, 22,
23
- 43, 44, 45, 14, 15, 41, 42, nil, 43, 44,
24
- 45, 8, nil, 9, nil, 10, 11, 14, 8, nil,
25
- 9, nil, 10, 11, 14, 9, nil, 10, 11, 14,
26
- 9, nil, 10, 11, 14, 9, 56, 10, 11, 14,
27
- 57, 14, nil, 31, 32, 33, 17, 16, nil, 47,
28
- 18, 19, 17, 16 ]
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
- 22, 22, 22, 22, 29, 22, 22, 22, 22, 22,
32
- 22, 22, 22, 57, 57, 57, 40, 57, 57, 57,
33
- 57, 57, 57, 57, 57, 18, 28, 18, 18, 18,
34
- 18, 18, 18, 18, 18, 19, 15, 19, 19, 13,
35
- 19, 19, 19, 55, 1, 55, 55, nil, 55, 55,
36
- 55, 9, nil, 9, nil, 9, 9, 9, 0, nil,
37
- 0, nil, 0, 0, 0, 8, nil, 8, 8, 8,
38
- 16, nil, 16, 16, 16, 17, 49, 17, 17, 17,
39
- 49, 54, nil, 54, 54, 54, 21, 21, nil, 21,
40
- 6, 6, 2, 2 ]
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
- 53, 44, 87, nil, nil, nil, 87, nil, 58, 46,
44
- nil, nil, nil, 32, nil, 36, 63, 68, 14, 24,
45
- nil, 81, -8, nil, nil, nil, nil, nil, 14, -8,
46
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
47
- 4, nil, nil, nil, nil, nil, nil, nil, nil, 68,
48
- nil, nil, nil, nil, 70, 32, nil, 4, 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, -46, -1, -3, -4, -5, -46, -8, -46, -46,
53
- -13, -14, -15, -46, -21, -46, -46, -46, -46, -46,
54
- -9, -46, -46, 62, -10, -11, -6, -16, -17, -18,
55
- -27, -32, -33, -34, -35, -36, -37, -38, -39, -7,
56
- -46, -40, -41, -42, -43, -44, -45, -12, -19, -46,
57
- -22, -24, -25, -26, -46, -46, -20, -46, -29, -30,
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, 46, 30, 40, 2, 53, 50, 20, 29, 27,
62
- 49, 39, 26, 21, 1, 24, 25, nil, nil, nil,
63
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
64
- nil, nil, nil, nil, nil, nil, 59, 46, 58, 60,
65
- 53, 61 ]
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, 16, 17, 2, 6, 15, 3, 12, 11,
69
- 14, 8, 7, 2, 1, 3, 3, nil, nil, nil,
70
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
71
- nil, nil, nil, nil, nil, nil, 10, 10, 16, 17,
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, 14, 4, -1, nil, nil, -17, -6, -8, nil,
76
- -18, -9, -10, nil, -12, -16, -16, -16 ]
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, 51, nil, 13, nil, nil, 52, nil ]
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 = 46
136
+ racc_reduce_n = 48
131
137
 
132
- racc_shift_n = 62
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
- # reduce 35 omitted
343
+ def _reduce_35(val, _values, result)
344
+ result = val[1]
345
+ result
346
+ end
338
347
 
339
- # reduce 36 omitted
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
@@ -159,6 +159,8 @@ rule
159
159
  : INTEGER
160
160
  | DECIMAL
161
161
  | CHARACTER
162
+ | LPAREN literals RPAREN { result = val[1] }
163
+ | UMINUS literals { result = tokenize_literal_negation(val[1]) }
162
164
  ;
163
165
 
164
166
  ##### Literal
@@ -60,7 +60,7 @@ module Sparkql::ParserCompatibility
60
60
  :type => :function,
61
61
  # This type is not parseable, so no regex
62
62
  :operators => Sparkql::Token::OPERATORS + [Sparkql::Token::RANGE_OPERATOR]
63
- }
63
+ },
64
64
  ]
65
65
 
66
66
  OPERATORS_SUPPORTING_MULTIPLES = ["Eq","Ne"]
@@ -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 "tolower(SomeField)" do
18
- f = FunctionResolver.new('tolower', [{:type => :field, :value => "City"}])
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 'tolower', value[:value]
24
- assert_equal "City", value[:args].first
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 '0001-01-01T00:00:00+00:00', value[:value]
229
+ assert_equal '1970-01-01T00:00:00+00:00', value[:value]
93
230
  end
94
231
 
95
232
  test "maxdatetime()" do
@@ -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.13
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-06-27 00:00:00.000000000 Z
11
+ date: 2018-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: georuby