sparkql 1.1.15 → 1.1.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +4 -0
- data/VERSION +1 -1
- data/lib/sparkql/function_resolver.rb +94 -3
- data/lib/sparkql/parser_tools.rb +7 -2
- data/test/unit/function_resolver_test.rb +106 -1
- data/test/unit/parser_test.rb +16 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MDI5NzJhMzM4MGY2MWFlNGM0YjE3N2Y1OWI2NDBhYzhhYTA5ODU2NQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MmJlZTY5NzRlZGEwY2RkYTcyNzU1NDUyMzBiZGY1MDdjN2IwZjI1OA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NWY3MDFkNWIyYmM2ZjNmODMwMTA3MWQzZTZlN2NhMmEzNTA2YzE5M2Q4NzEw
|
10
|
+
MmE0YWJhM2ZlYTkxZWE5ZDAyYWU0NDNiZTVlZjZlM2FkMzgwZTA0ZTI0OTEy
|
11
|
+
ZDAxNzc5NzdmNTgzMWYxN2U4MjAzZDJkYmJjMTdlMGMyNDE2ZmY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTI4YmQwYzFkNjllNDc0Yzg0YWFiYzVjMGZmNDJkNDNlODc5MTcyMzVhYzcy
|
14
|
+
MjhmMjUyMTQwYmUwMjY4N2U1MzIzNzI5MWVjMmFjNmI0MDg3ZDBmMmZiZDFl
|
15
|
+
YzZhNjkxYTAxZTJiZGI1MTZjY2NlMjlmNjYyZGExNmRkMDdjYTU=
|
data/CHANGELOG.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.1.
|
1
|
+
1.1.16
|
@@ -16,6 +16,8 @@ class Sparkql::FunctionResolver
|
|
16
16
|
VALID_REGEX_FLAGS = ["", "i"]
|
17
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
|
+
VALID_CAST_TYPES = [:field, :character, :decimal, :integer]
|
20
|
+
|
19
21
|
SUPPORTED_FUNCTIONS = {
|
20
22
|
:polygon => {
|
21
23
|
:args => [:character],
|
@@ -69,6 +71,10 @@ class Sparkql::FunctionResolver
|
|
69
71
|
:args => [[:field, :character], :character],
|
70
72
|
:return_type => :integer
|
71
73
|
},
|
74
|
+
:cast => {
|
75
|
+
:args => [[:field, :character, :decimal, :integer, :null], :character],
|
76
|
+
:resolve_for_type => true,
|
77
|
+
},
|
72
78
|
:round => {
|
73
79
|
:args => [[:field, :decimal]],
|
74
80
|
:resolve_for_type => true,
|
@@ -190,7 +196,7 @@ class Sparkql::FunctionResolver
|
|
190
196
|
end
|
191
197
|
|
192
198
|
# Validate the function instance prior to calling it. All validation failures will show up in the
|
193
|
-
# errors array.
|
199
|
+
# errors array.
|
194
200
|
def validate()
|
195
201
|
name = @name.to_sym
|
196
202
|
unless support.has_key?(name)
|
@@ -219,10 +225,26 @@ class Sparkql::FunctionResolver
|
|
219
225
|
end
|
220
226
|
count +=1
|
221
227
|
end
|
228
|
+
|
229
|
+
if name == :cast
|
230
|
+
type = @args.last[:value]
|
231
|
+
if !VALID_CAST_TYPES.include?(type.to_sym)
|
232
|
+
@errors << Sparkql::ParserError.new(:token => @name,
|
233
|
+
:message => "Function call '#{@name}' requires a castable type.",
|
234
|
+
:status => :fatal )
|
235
|
+
return
|
236
|
+
end
|
237
|
+
end
|
222
238
|
end
|
223
239
|
|
224
240
|
def return_type
|
225
|
-
|
241
|
+
name = @name.to_sym
|
242
|
+
|
243
|
+
if name == :cast
|
244
|
+
@args.last[:value].to_sym
|
245
|
+
else
|
246
|
+
support[@name.to_sym][:return_type]
|
247
|
+
end
|
226
248
|
end
|
227
249
|
|
228
250
|
def errors
|
@@ -752,7 +774,76 @@ class Sparkql::FunctionResolver
|
|
752
774
|
:value => [start_str.to_s, end_str.to_s]
|
753
775
|
}
|
754
776
|
end
|
755
|
-
|
777
|
+
|
778
|
+
def cast_field(value, type)
|
779
|
+
{
|
780
|
+
:type => :function,
|
781
|
+
:value => "cast",
|
782
|
+
:args => [value, type]
|
783
|
+
}
|
784
|
+
end
|
785
|
+
|
786
|
+
def cast(value, type)
|
787
|
+
if value == 'NULL'
|
788
|
+
value = nil
|
789
|
+
end
|
790
|
+
|
791
|
+
new_type = type.to_sym
|
792
|
+
{
|
793
|
+
type: new_type,
|
794
|
+
value: cast_literal(value, new_type)
|
795
|
+
}
|
796
|
+
rescue
|
797
|
+
{
|
798
|
+
type: :null,
|
799
|
+
value: 'NULL'
|
800
|
+
}
|
801
|
+
end
|
802
|
+
|
803
|
+
def valid_cast_type?(type)
|
804
|
+
if VALID_CAST_TYPES.key?(type.to_sym)
|
805
|
+
true
|
806
|
+
else
|
807
|
+
@errors << Sparkql::ParserError.new(:token => coords,
|
808
|
+
:message => "Function call 'cast' requires a castable type.",
|
809
|
+
:status => :fatal )
|
810
|
+
false
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
def cast_null(value, type)
|
815
|
+
cast(value, type)
|
816
|
+
end
|
817
|
+
|
818
|
+
def cast_decimal(value, type)
|
819
|
+
cast(value, type)
|
820
|
+
end
|
821
|
+
|
822
|
+
def cast_character(value, type)
|
823
|
+
cast(value, type)
|
824
|
+
end
|
825
|
+
|
826
|
+
def cast_literal(value, type)
|
827
|
+
case type
|
828
|
+
when :character
|
829
|
+
"'#{value.to_s}'"
|
830
|
+
when :integer
|
831
|
+
if value.nil?
|
832
|
+
'0'
|
833
|
+
else
|
834
|
+
Integer(Float(value)).to_s
|
835
|
+
end
|
836
|
+
when :decimal
|
837
|
+
if value.nil?
|
838
|
+
'0.0'
|
839
|
+
else
|
840
|
+
Float(value).to_s
|
841
|
+
end
|
842
|
+
when :null
|
843
|
+
'NULL'
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
756
847
|
private
|
757
848
|
|
758
849
|
def is_coords?(coord_string)
|
data/lib/sparkql/parser_tools.rb
CHANGED
@@ -31,15 +31,20 @@ module Sparkql::ParserTools
|
|
31
31
|
function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[field[:value].to_sym]
|
32
32
|
if !function.nil?
|
33
33
|
field_args[:field_function] = field[:value]
|
34
|
-
field_args[:field_function_type] = function[:return_type]
|
35
34
|
field_args[:args] = field[:args]
|
35
|
+
|
36
|
+
if field_args[:field_function] == 'cast'
|
37
|
+
field_args[:field_function_type] = field[:args].last.to_sym
|
38
|
+
else
|
39
|
+
field_args[:field_function_type] = function[:return_type]
|
40
|
+
end
|
36
41
|
else
|
37
42
|
tokenizer_error(:token => field[:value],
|
38
43
|
:message => "Unsupported function type", :status => :fatal )
|
39
44
|
end
|
40
45
|
field = field[:args].first
|
41
46
|
end
|
42
|
-
custom_field = field.start_with?('"')
|
47
|
+
custom_field = !field.nil? && field.start_with?('"')
|
43
48
|
block_group = (@lexer.level == 0) ? 0 : @lexer.block_group_identifier
|
44
49
|
expression = {:field => field, :operator => operator, :conjunction => 'And',
|
45
50
|
:conjunction_level => 0, :level => @lexer.level,
|
@@ -451,7 +451,112 @@ class FunctionResolverTest < Test::Unit::TestCase
|
|
451
451
|
:value => 1.0}])
|
452
452
|
assert_equal :shape, f.return_type
|
453
453
|
end
|
454
|
-
|
454
|
+
|
455
|
+
test 'return_type for cast()' do
|
456
|
+
f = FunctionResolver.new('cast', [{:type => :character,
|
457
|
+
:value => "1"},{:type => :character,
|
458
|
+
:value => 'decimal'}])
|
459
|
+
|
460
|
+
assert_equal :decimal, f.return_type
|
461
|
+
|
462
|
+
f = FunctionResolver.new('cast', [{:type => :character,
|
463
|
+
:value => "1"},{:type => :character,
|
464
|
+
:value => 'integer'}])
|
465
|
+
|
466
|
+
assert_equal :integer, f.return_type
|
467
|
+
end
|
468
|
+
|
469
|
+
test "cast() decimal to integer" do
|
470
|
+
f = FunctionResolver.new('cast', [{:type => :decimal, :value => '1.2'}, {type: :character, :value => 'integer'}])
|
471
|
+
f.validate
|
472
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
473
|
+
value = f.call
|
474
|
+
|
475
|
+
assert_equal :integer, value[:type]
|
476
|
+
assert_equal '1', value[:value]
|
477
|
+
end
|
478
|
+
|
479
|
+
test "cast() integer to decimal" do
|
480
|
+
f = FunctionResolver.new('cast', [{:type => :decimal, :value => '1'}, {type: :character, :value => 'decimal'}])
|
481
|
+
f.validate
|
482
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
483
|
+
value = f.call
|
484
|
+
|
485
|
+
assert_equal :decimal, value[:type]
|
486
|
+
assert_equal '1.0', value[:value]
|
487
|
+
end
|
488
|
+
|
489
|
+
test "cast() nil to integer" do
|
490
|
+
f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'integer'}])
|
491
|
+
f.validate
|
492
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
493
|
+
value = f.call
|
494
|
+
|
495
|
+
assert_equal :integer, value[:type]
|
496
|
+
assert_equal '0', value[:value]
|
497
|
+
end
|
498
|
+
|
499
|
+
test "cast() nil to decimal" do
|
500
|
+
f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'decimal'}])
|
501
|
+
f.validate
|
502
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
503
|
+
value = f.call
|
504
|
+
|
505
|
+
assert_equal :decimal, value[:type]
|
506
|
+
assert_equal '0.0', value[:value]
|
507
|
+
end
|
508
|
+
|
509
|
+
test "cast() nil to character" do
|
510
|
+
f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'character'}])
|
511
|
+
f.validate
|
512
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
513
|
+
value = f.call
|
514
|
+
|
515
|
+
assert_equal :character, value[:type]
|
516
|
+
assert_equal "''", value[:value]
|
517
|
+
end
|
518
|
+
|
519
|
+
test "cast() character to decimal" do
|
520
|
+
f = FunctionResolver.new('cast', [{:type => :character, :value => "1.1"}, {type: :character, :value => 'decimal'}])
|
521
|
+
f.validate
|
522
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
523
|
+
value = f.call
|
524
|
+
|
525
|
+
assert_equal :decimal, value[:type]
|
526
|
+
assert_equal "1.1", value[:value]
|
527
|
+
end
|
528
|
+
|
529
|
+
test "cast() character to integer" do
|
530
|
+
f = FunctionResolver.new('cast', [{:type => :character, :value => "1"}, {type: :character, :value => 'integer'}])
|
531
|
+
f.validate
|
532
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
533
|
+
value = f.call
|
534
|
+
|
535
|
+
assert_equal :integer, value[:type]
|
536
|
+
assert_equal "1", value[:value]
|
537
|
+
end
|
538
|
+
|
539
|
+
test "cast() Field" do
|
540
|
+
f = FunctionResolver.new('cast', [{:type => :field, :value => 'Bedrooms'}, {type: :character, :value => 'character'}])
|
541
|
+
f.validate
|
542
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
543
|
+
value = f.call
|
544
|
+
|
545
|
+
assert_equal :function, value[:type]
|
546
|
+
assert_equal 'cast', value[:value]
|
547
|
+
assert_equal ['Bedrooms', 'character'], value[:args]
|
548
|
+
end
|
549
|
+
|
550
|
+
test "invalid cast returns null" do
|
551
|
+
f = FunctionResolver.new('cast', [{:type => :character, :value => '1.1.1'}, {type: :character, :value => 'integer'}])
|
552
|
+
f.validate
|
553
|
+
assert !f.errors?, "Errors #{f.errors.inspect}"
|
554
|
+
value = f.call
|
555
|
+
|
556
|
+
assert_equal :null, value[:type]
|
557
|
+
assert_equal 'NULL', value[:value]
|
558
|
+
end
|
559
|
+
|
455
560
|
test "invalid function" do
|
456
561
|
f = FunctionResolver.new('then', [])
|
457
562
|
f.validate
|
data/test/unit/parser_test.rb
CHANGED
@@ -753,6 +753,22 @@ class ParserTest < Test::Unit::TestCase
|
|
753
753
|
assert_equal(["FieldName"], expression[:function_parameters])
|
754
754
|
end
|
755
755
|
|
756
|
+
def test_cast_with_field
|
757
|
+
filter = "cast(ListPrice, 'character') Eq '100000'"
|
758
|
+
@parser = Parser.new
|
759
|
+
expression = @parser.parse(filter).first
|
760
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
761
|
+
|
762
|
+
assert_equal 'cast', expression[:field_function]
|
763
|
+
assert_equal "'100000'", expression[:condition]
|
764
|
+
assert_equal(:character, expression[:field_function_type])
|
765
|
+
end
|
766
|
+
|
767
|
+
def test_cast_with_invalid_type
|
768
|
+
parser_errors("cast(ListPrice, 'bogus') Eq '10'")
|
769
|
+
parser_errors("ListPrice Eq cast('10', 'bogus')")
|
770
|
+
end
|
771
|
+
|
756
772
|
private
|
757
773
|
|
758
774
|
def parser_errors(filter)
|
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.16
|
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-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: georuby
|