sparkql 1.2.3 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +5 -0
- data/VERSION +1 -1
- data/lib/sparkql/parser_compatibility.rb +35 -16
- data/lib/sparkql/parser_tools.rb +29 -5
- data/test/unit/parser_compatability_test.rb +15 -0
- data/test/unit/parser_test.rb +48 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YmIyYTljOGIyZmFjZTEyZmY1MzU2ODZjYTg4ZWU1NDI2NWNkOWRkYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NDkwMjczNDY2N2JlOGRmMzA5MDQ1YmI4Njg3ZTM5MTJlMzBiODVhMQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTk2NTgwODMyNjExMjBiZThmODdjNzczZmM4M2MzMmY0MWNiNTkxODU5NThk
|
10
|
+
Y2UwYzY0ZTQ1NGRhNmFjYjI1ODIwYzBiMmMyMDM2NzFmNmNhNTI1OGNkYjNj
|
11
|
+
NzJhZTRjMDk4MzVmYjJlNDM4YTE2ZWFjYTVjNzlmMGZlNzM4NjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjhmNWU0NmZjNDVkMmUzMDJhMGM2ZTY0ZWVjMTk3ZGUwZGI3MzM2OTQwN2M4
|
14
|
+
MTUzYTk3N2E1NzI2YWFmMTA1YWIxZWY2MTliZTRiOGNjOTU5NjEwMGFlODkz
|
15
|
+
YjRmYmVmN2M4NTJkMGUyM2NlNjhmODA0ZGFkYWYzM2Q0OTNlMDU=
|
data/CHANGELOG.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.4
|
@@ -161,7 +161,7 @@ module Sparkql::ParserCompatibility
|
|
161
161
|
def datetime_escape(string)
|
162
162
|
DateTime.parse(string)
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
def time_escape(string)
|
166
166
|
DateTime.parse(string)
|
167
167
|
end
|
@@ -214,7 +214,8 @@ module Sparkql::ParserCompatibility
|
|
214
214
|
|
215
215
|
# Checks the type of an expression with what is expected.
|
216
216
|
def check_type!(expression, expected, supports_nulls = true)
|
217
|
-
if expected == expression[:type]
|
217
|
+
if (expected == expression[:type] && !expression.key?(:field_manipulations)) ||
|
218
|
+
(expression.key?(:field_manipulations) && check_function_type?(expression, expected)) ||
|
218
219
|
(supports_nulls && expression[:type] == :null)
|
219
220
|
return true
|
220
221
|
# If the field will be passed into a function,
|
@@ -230,7 +231,7 @@ module Sparkql::ParserCompatibility
|
|
230
231
|
expression[:type] = :datetime
|
231
232
|
expression[:cast] = :date
|
232
233
|
return true
|
233
|
-
elsif expected == :date && expression[:type] == :datetime
|
234
|
+
elsif expected == :date && expression[:type] == :datetime
|
234
235
|
expression[:type] = :date
|
235
236
|
expression[:cast] = :datetime
|
236
237
|
if multiple_values?(expression[:value])
|
@@ -253,26 +254,44 @@ module Sparkql::ParserCompatibility
|
|
253
254
|
:message => "expected #{expected} but found #{expression[:type]}",
|
254
255
|
:status => :fatal )
|
255
256
|
end
|
256
|
-
|
257
|
+
|
257
258
|
# If a function is being applied to a field, we check that the return type of
|
258
259
|
# the function matches what is expected, and that the function supports the
|
259
260
|
# field type as the first argument.
|
260
261
|
def check_function_type?(expression, expected)
|
261
|
-
|
262
|
-
# Lookup the function arguments
|
263
|
-
function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[deepest_function(expression[:field_manipulations])[:function_name].to_sym]
|
264
|
-
return false if function.nil?
|
265
|
-
|
266
|
-
Array(function[:args].first).include?(expected)
|
262
|
+
validate_manipulation_types(expression[:field_manipulations], expected)
|
267
263
|
end
|
268
264
|
|
265
|
+
def validate_manipulation_types(field_manipulations, expected)
|
266
|
+
if field_manipulations[:type] == :function
|
267
|
+
function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[field_manipulations[:function_name].to_sym]
|
268
|
+
return false if function.nil?
|
269
|
+
field_manipulations[:args].each_with_index do |arg, index|
|
270
|
+
if arg[:type] == :field
|
271
|
+
return false unless function[:args][index].include?(:field)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
elsif field_manipulations[:type] == :arithmetic
|
275
|
+
lhs = field_manipulations[:lhs]
|
276
|
+
return false unless validate_side(lhs, expected)
|
269
277
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
278
|
+
rhs = field_manipulations[:rhs]
|
279
|
+
return false unless rhs.nil? || validate_side(rhs, expected)
|
280
|
+
end
|
281
|
+
true
|
282
|
+
end
|
283
|
+
|
284
|
+
def validate_side(side, expected)
|
285
|
+
if side[:type] == :arithmetic
|
286
|
+
return validate_manipulation_types(side, expected)
|
287
|
+
elsif side[:type] == :field
|
288
|
+
return false unless [:decimal, :integer].include?(expected)
|
289
|
+
elsif side[:type] == :function
|
290
|
+
return false unless [:decimal, :integer].include?(side[:return_type])
|
291
|
+
elsif ![:decimal, :integer].include?(side[:type])
|
292
|
+
return false
|
275
293
|
end
|
294
|
+
true
|
276
295
|
end
|
277
296
|
|
278
297
|
# Builds the correct operator based on the type and the value.
|
@@ -305,7 +324,7 @@ module Sparkql::ParserCompatibility
|
|
305
324
|
def operator_supports_multiples?(operator)
|
306
325
|
OPERATORS_SUPPORTING_MULTIPLES.include?(operator)
|
307
326
|
end
|
308
|
-
|
327
|
+
|
309
328
|
def coerce_datetime datetime
|
310
329
|
if datestr = datetime.match(/^(\d{4}-\d{2}-\d{2})/)
|
311
330
|
datestr[0]
|
data/lib/sparkql/parser_tools.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# This is the guts of the parser internals and is mixed into the parser for organization.
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
2
4
|
module Sparkql::ParserTools
|
3
5
|
|
4
6
|
# Coercible types from highest precision to lowest
|
@@ -264,19 +266,22 @@ module Sparkql::ParserTools
|
|
264
266
|
def add_fold(n1, n2)
|
265
267
|
return if arithmetic_error?(n1) || arithmetic_error?(n2)
|
266
268
|
|
267
|
-
|
269
|
+
value = escape_arithmetic_value(n1) + escape_arithmetic_value(n2)
|
270
|
+
{ type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
|
268
271
|
end
|
269
272
|
|
270
273
|
def sub_fold(n1, n2)
|
271
274
|
return if arithmetic_error?(n1) || arithmetic_error?(n2)
|
272
275
|
|
273
|
-
|
276
|
+
value = escape_arithmetic_value(n1) - escape_arithmetic_value(n2)
|
277
|
+
{ type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
|
274
278
|
end
|
275
279
|
|
276
280
|
def mul_fold(n1, n2)
|
277
281
|
return if arithmetic_error?(n1) || arithmetic_error?(n2)
|
278
282
|
|
279
|
-
|
283
|
+
value = escape_arithmetic_value(n1) * escape_arithmetic_value(n2)
|
284
|
+
{ type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
|
280
285
|
end
|
281
286
|
|
282
287
|
def div_fold(n1, n2)
|
@@ -284,7 +289,8 @@ module Sparkql::ParserTools
|
|
284
289
|
arithmetic_error?(n2) ||
|
285
290
|
zero_error?(n2)
|
286
291
|
|
287
|
-
|
292
|
+
value = escape_arithmetic_value(n1) / escape_arithmetic_value(n2)
|
293
|
+
{ type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
|
288
294
|
end
|
289
295
|
|
290
296
|
def mod_fold(n1, n2)
|
@@ -292,7 +298,8 @@ module Sparkql::ParserTools
|
|
292
298
|
arithmetic_error?(n2) ||
|
293
299
|
zero_error?(n2)
|
294
300
|
|
295
|
-
|
301
|
+
value = escape_arithmetic_value(n1) % escape_arithmetic_value(n2)
|
302
|
+
{ type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
|
296
303
|
end
|
297
304
|
|
298
305
|
def arithmetic_type(num1, num2)
|
@@ -303,6 +310,23 @@ module Sparkql::ParserTools
|
|
303
310
|
end
|
304
311
|
end
|
305
312
|
|
313
|
+
def escape_arithmetic_value(expression)
|
314
|
+
case expression[:type]
|
315
|
+
when :decimal
|
316
|
+
BigDecimal.new(expression[:value])
|
317
|
+
else
|
318
|
+
escape_value(expression)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def unescape_arithmetic(value)
|
323
|
+
if value.is_a?(BigDecimal)
|
324
|
+
value.round(20).to_s('F')
|
325
|
+
else
|
326
|
+
value.to_s
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
306
330
|
def zero_error?(number)
|
307
331
|
return unless escape_value(number) == 0
|
308
332
|
|
@@ -565,4 +565,19 @@ class ParserCompatabilityTest < Test::Unit::TestCase
|
|
565
565
|
assert parser.send(:check_type!, expression, :datetime)
|
566
566
|
assert_equal '3', parser.escape_value(expression)
|
567
567
|
end
|
568
|
+
|
569
|
+
test "function with field and arithmetic" do
|
570
|
+
filter = "year(CloseDate) add 1 Eq 2017"
|
571
|
+
parser = Parser.new
|
572
|
+
expression = parser.tokenize(filter).first
|
573
|
+
assert parser.send(:check_type!, expression, :datetime)
|
574
|
+
end
|
575
|
+
|
576
|
+
test "Cannot perform arithmetic on a String field" do
|
577
|
+
filter = "City Add 3.0 Eq 'Fargo'"
|
578
|
+
parser = Parser.new
|
579
|
+
expression = parser.tokenize(filter).first
|
580
|
+
# Type mismatch
|
581
|
+
assert !parser.send(:check_type!, expression, :datetime)
|
582
|
+
end
|
568
583
|
end
|
data/test/unit/parser_test.rb
CHANGED
@@ -232,6 +232,54 @@ class ParserTest < Test::Unit::TestCase
|
|
232
232
|
assert_equal 'Mod', field_manipulations[:op]
|
233
233
|
end
|
234
234
|
|
235
|
+
test 'Mod returns decimal precision' do
|
236
|
+
@parser = Parser.new
|
237
|
+
filter = "Baths Eq 32.7 Mod 20.7"
|
238
|
+
expressions = @parser.parse(filter)
|
239
|
+
assert !@parser.errors?, @parser.errors.inspect
|
240
|
+
assert_equal '12.0', expressions.first[:value]
|
241
|
+
end
|
242
|
+
|
243
|
+
test 'Adding returns decimal precision' do
|
244
|
+
@parser = Parser.new
|
245
|
+
filter = "Baths Eq 0.1 Add 0.2"
|
246
|
+
expressions = @parser.parse(filter)
|
247
|
+
assert !@parser.errors?, @parser.errors.inspect
|
248
|
+
assert_equal '0.3', expressions.first[:value]
|
249
|
+
end
|
250
|
+
|
251
|
+
test 'Subtracting returns decimal precision' do
|
252
|
+
@parser = Parser.new
|
253
|
+
filter = "Baths Eq 0.3 Sub 0.1"
|
254
|
+
expressions = @parser.parse(filter)
|
255
|
+
assert !@parser.errors?, @parser.errors.inspect
|
256
|
+
assert_equal '0.2', expressions.first[:value]
|
257
|
+
end
|
258
|
+
|
259
|
+
test 'Division returns decimal precision' do
|
260
|
+
@parser = Parser.new
|
261
|
+
filter = "Baths Eq 0.6 Div 0.2"
|
262
|
+
expressions = @parser.parse(filter)
|
263
|
+
assert !@parser.errors?, @parser.errors.inspect
|
264
|
+
assert_equal '3.0', expressions.first[:value]
|
265
|
+
end
|
266
|
+
|
267
|
+
test 'Arithmetic rounds to 20 decimal places' do
|
268
|
+
@parser = Parser.new
|
269
|
+
filter = "Baths Eq 7 Div 10.1"
|
270
|
+
expressions = @parser.parse(filter)
|
271
|
+
assert !@parser.errors?, @parser.errors.inspect
|
272
|
+
assert_equal '0.69306930693069306931', expressions.first[:value]
|
273
|
+
end
|
274
|
+
|
275
|
+
test 'Multiplication returns decimal precision' do
|
276
|
+
@parser = Parser.new
|
277
|
+
filter = "Baths Eq 7 Mul 0.1"
|
278
|
+
expressions = @parser.parse(filter)
|
279
|
+
assert !@parser.errors?, @parser.errors.inspect
|
280
|
+
assert_equal '0.7', expressions.first[:value]
|
281
|
+
end
|
282
|
+
|
235
283
|
test 'arithmetic with field function' do
|
236
284
|
@parser = Parser.new
|
237
285
|
filter = "floor(Baths) Add 2 Eq 1"
|
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.2.
|
4
|
+
version: 1.2.4
|
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-12-
|
11
|
+
date: 2018-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: georuby
|