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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZWExMjRhNmFjYzM3NTdjYWNkMjdmOWRjYzBhYTJiNGVjMTRjOTViZg==
4
+ MDI5NzJhMzM4MGY2MWFlNGM0YjE3N2Y1OWI2NDBhYzhhYTA5ODU2NQ==
5
5
  data.tar.gz: !binary |-
6
- YzhiYTlhMWM0OTRkNWE4ZTFjMTQzYzE4MmNjYzJiZWQ3MDdhMmFiYQ==
6
+ MmJlZTY5NzRlZGEwY2RkYTcyNzU1NDUyMzBiZGY1MDdjN2IwZjI1OA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Mzk5MGUxM2M0ZWQ2M2QxMTQ2ZDM3MTMxNTEwODVhZTc0NzJlZWJhYzkzODA4
10
- NDIxZTUxNzE4OTYxYmI3MTJiNzkxMGU1MzVjMGQ3ZDdkMzIzZDQ5OGExMmI5
11
- YmM1NDU2M2IzM2JhMWU2OTY3Y2NhYzM5MDNiMjllNmVkYjA2NDM=
9
+ NWY3MDFkNWIyYmM2ZjNmODMwMTA3MWQzZTZlN2NhMmEzNTA2YzE5M2Q4NzEw
10
+ MmE0YWJhM2ZlYTkxZWE5ZDAyYWU0NDNiZTVlZjZlM2FkMzgwZTA0ZTI0OTEy
11
+ ZDAxNzc5NzdmNTgzMWYxN2U4MjAzZDJkYmJjMTdlMGMyNDE2ZmY=
12
12
  data.tar.gz: !binary |-
13
- MTRjZDEyN2U5N2IwZTA5NGEwZjZjMmEyMzM5ZTA4YWUwMDcxNWEwZTZjODli
14
- OWNmZDBjNmU0YzNiNWZiNjc3MWRkYmRjNjc1ZTg4MjNlNWVlNzAzYTVhY2Uz
15
- NzU2OTg2NzAzZTA4ZjdmMDFkZDdhMzY3NTU4ZTQxNTRhODkyNDQ=
13
+ YTI4YmQwYzFkNjllNDc0Yzg0YWFiYzVjMGZmNDJkNDNlODc5MTcyMzVhYzcy
14
+ MjhmMjUyMTQwYmUwMjY4N2U1MzIzNzI5MWVjMmFjNmI0MDg3ZDBmMmZiZDFl
15
+ YzZhNjkxYTAxZTJiZGI1MTZjY2NlMjlmNjYyZGExNmRkMDdjYTU=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ v1.1.16, 2018-07-26
2
+ -------------------
3
+ * [IMPROVEMENT] New Function: cast()
4
+
1
5
  v1.1.15, 2018-07-12
2
6
  -------------------
3
7
  * [IMPROVEMENT] New Functions: ceiling(), floor()
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.15
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
- support[@name.to_sym][:return_type]
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)
@@ -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
@@ -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.15
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-07-19 00:00:00.000000000 Z
11
+ date: 2018-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: georuby