avro 1.10.1 → 1.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Manifest +0 -1
- data/NOTICE +1 -1
- data/Rakefile +13 -19
- data/avro.gemspec +37 -29
- data/interop/test_interop.rb +2 -1
- data/lib/avro/VERSION.txt +1 -1
- data/lib/avro/data_file.rb +5 -4
- data/lib/avro/io.rb +18 -14
- data/lib/avro/ipc.rb +9 -5
- data/lib/avro/logical_types.rb +186 -2
- data/lib/avro/protocol.rb +1 -0
- data/lib/avro/schema.rb +71 -12
- data/lib/avro/schema_compatibility.rb +19 -13
- data/lib/avro/schema_normalization.rb +1 -0
- data/lib/avro/schema_validator.rb +40 -34
- data/lib/avro.rb +1 -0
- data/test/case_finder.rb +1 -0
- data/test/random_data.rb +5 -4
- data/test/sample_ipc_client.rb +1 -0
- data/test/sample_ipc_http_client.rb +1 -0
- data/test/sample_ipc_http_server.rb +1 -0
- data/test/sample_ipc_server.rb +1 -0
- data/test/test_datafile.rb +1 -0
- data/test/test_fingerprints.rb +1 -0
- data/test/test_help.rb +1 -0
- data/test/test_io.rb +31 -16
- data/test/test_logical_types.rb +138 -1
- data/test/test_protocol.rb +2 -1
- data/test/test_schema.rb +134 -4
- data/test/test_schema_compatibility.rb +108 -0
- data/test/test_schema_normalization.rb +1 -0
- data/test/test_schema_validator.rb +24 -3
- data/test/test_socket_transport.rb +1 -0
- data/test/tool.rb +8 -7
- metadata +30 -33
- data/CHANGELOG +0 -1
data/lib/avro/schema.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Licensed to the Apache Software Foundation (ASF) under one
|
2
3
|
# or more contributor license agreements. See the NOTICE file
|
3
4
|
# distributed with this work for additional information
|
@@ -29,13 +30,17 @@ module Avro
|
|
29
30
|
NAMED_TYPES_SYM = Set.new(NAMED_TYPES.map(&:to_sym))
|
30
31
|
VALID_TYPES_SYM = Set.new(VALID_TYPES.map(&:to_sym))
|
31
32
|
|
32
|
-
NAME_REGEX = /^([A-Za-z_][A-Za-z0-9_]*)(\.([A-Za-z_][A-Za-z0-9_]*))
|
33
|
+
NAME_REGEX = /^([A-Za-z_][A-Za-z0-9_]*)(\.([A-Za-z_][A-Za-z0-9_]*))*$/.freeze
|
33
34
|
|
34
35
|
INT_MIN_VALUE = -(1 << 31)
|
35
36
|
INT_MAX_VALUE = (1 << 31) - 1
|
36
37
|
LONG_MIN_VALUE = -(1 << 63)
|
37
38
|
LONG_MAX_VALUE = (1 << 63) - 1
|
38
39
|
|
40
|
+
DEFAULT_VALIDATE_OPTIONS = { recursive: true, encoded: false }.freeze
|
41
|
+
|
42
|
+
DECIMAL_LOGICAL_TYPE = 'decimal'
|
43
|
+
|
39
44
|
def self.parse(json_string)
|
40
45
|
real_parse(MultiJson.load(json_string), {})
|
41
46
|
end
|
@@ -73,7 +78,9 @@ module Avro
|
|
73
78
|
case type_sym
|
74
79
|
when :fixed
|
75
80
|
size = json_obj['size']
|
76
|
-
|
81
|
+
precision = json_obj['precision']
|
82
|
+
scale = json_obj['scale']
|
83
|
+
return FixedSchema.new(name, namespace, size, names, logical_type, aliases, precision, scale)
|
77
84
|
when :enum
|
78
85
|
symbols = json_obj['symbols']
|
79
86
|
doc = json_obj['doc']
|
@@ -109,7 +116,7 @@ module Avro
|
|
109
116
|
end
|
110
117
|
|
111
118
|
# Determine if a ruby datum is an instance of a schema
|
112
|
-
def self.validate(expected_schema, logical_datum, options =
|
119
|
+
def self.validate(expected_schema, logical_datum, options = DEFAULT_VALIDATE_OPTIONS)
|
113
120
|
SchemaValidator.validate!(expected_schema, logical_datum, options)
|
114
121
|
true
|
115
122
|
rescue SchemaValidator::ValidationError
|
@@ -129,7 +136,7 @@ module Avro
|
|
129
136
|
def type; @type_sym.to_s; end
|
130
137
|
|
131
138
|
def type_adapter
|
132
|
-
@type_adapter ||= LogicalTypes.type_adapter(type, logical_type) || LogicalTypes::Identity
|
139
|
+
@type_adapter ||= LogicalTypes.type_adapter(type, logical_type, self) || LogicalTypes::Identity
|
133
140
|
end
|
134
141
|
|
135
142
|
# Returns the MD5 fingerprint of the schema as an Integer.
|
@@ -173,7 +180,7 @@ module Avro
|
|
173
180
|
fp
|
174
181
|
end
|
175
182
|
|
176
|
-
SINGLE_OBJECT_MAGIC_NUMBER = [0xC3, 0x01]
|
183
|
+
SINGLE_OBJECT_MAGIC_NUMBER = [0xC3, 0x01].freeze
|
177
184
|
def single_object_encoding_header
|
178
185
|
[SINGLE_OBJECT_MAGIC_NUMBER, single_object_schema_fingerprint].flatten
|
179
186
|
end
|
@@ -281,6 +288,10 @@ module Avro
|
|
281
288
|
def match_fullname?(name)
|
282
289
|
name == fullname || fullname_aliases.include?(name)
|
283
290
|
end
|
291
|
+
|
292
|
+
def match_schema?(schema)
|
293
|
+
type_sym == schema.type_sym && match_fullname?(schema.fullname)
|
294
|
+
end
|
284
295
|
end
|
285
296
|
|
286
297
|
class RecordSchema < NamedSchema
|
@@ -411,7 +422,7 @@ module Avro
|
|
411
422
|
end
|
412
423
|
|
413
424
|
class EnumSchema < NamedSchema
|
414
|
-
SYMBOL_REGEX = /^[A-Za-z_][A-Za-z0-9_]
|
425
|
+
SYMBOL_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/.freeze
|
415
426
|
|
416
427
|
attr_reader :symbols, :doc, :default
|
417
428
|
|
@@ -465,14 +476,27 @@ module Avro
|
|
465
476
|
hsh = super
|
466
477
|
hsh.size == 1 ? type : hsh
|
467
478
|
end
|
479
|
+
|
480
|
+
def match_schema?(schema)
|
481
|
+
return type_sym == schema.type_sym
|
482
|
+
# TODO: eventually this could handle schema promotion for primitive schemas too
|
483
|
+
end
|
468
484
|
end
|
469
485
|
|
470
486
|
class BytesSchema < PrimitiveSchema
|
487
|
+
ERROR_INVALID_SCALE = 'Scale must be greater than or equal to 0'
|
488
|
+
ERROR_INVALID_PRECISION = 'Precision must be positive'
|
489
|
+
ERROR_PRECISION_TOO_SMALL = 'Precision must be greater than scale'
|
490
|
+
|
471
491
|
attr_reader :precision, :scale
|
492
|
+
|
472
493
|
def initialize(type, logical_type=nil, precision=nil, scale=nil)
|
473
494
|
super(type.to_sym, logical_type)
|
474
|
-
|
475
|
-
@
|
495
|
+
|
496
|
+
@precision = precision.to_i if precision
|
497
|
+
@scale = scale.to_i if scale
|
498
|
+
|
499
|
+
validate_decimal! if logical_type == DECIMAL_LOGICAL_TYPE
|
476
500
|
end
|
477
501
|
|
478
502
|
def to_avro(names=nil)
|
@@ -483,29 +507,64 @@ module Avro
|
|
483
507
|
avro['scale'] = scale if scale
|
484
508
|
avro
|
485
509
|
end
|
510
|
+
|
511
|
+
def match_schema?(schema)
|
512
|
+
return true if super
|
513
|
+
|
514
|
+
if logical_type == DECIMAL_LOGICAL_TYPE && schema.logical_type == DECIMAL_LOGICAL_TYPE
|
515
|
+
return precision == schema.precision && (scale || 0) == (schema.scale || 0)
|
516
|
+
end
|
517
|
+
|
518
|
+
false
|
519
|
+
end
|
520
|
+
|
521
|
+
private
|
522
|
+
|
523
|
+
def validate_decimal!
|
524
|
+
raise Avro::SchemaParseError, ERROR_INVALID_PRECISION unless precision.to_i.positive?
|
525
|
+
raise Avro::SchemaParseError, ERROR_INVALID_SCALE if scale.to_i.negative?
|
526
|
+
raise Avro::SchemaParseError, ERROR_PRECISION_TOO_SMALL if precision < scale.to_i
|
527
|
+
end
|
486
528
|
end
|
487
529
|
|
488
530
|
class FixedSchema < NamedSchema
|
489
|
-
attr_reader :size
|
490
|
-
def initialize(name, space, size, names=nil, logical_type=nil, aliases=nil)
|
531
|
+
attr_reader :size, :precision, :scale
|
532
|
+
def initialize(name, space, size, names=nil, logical_type=nil, aliases=nil, precision=nil, scale=nil)
|
491
533
|
# Ensure valid cto args
|
492
534
|
unless size.is_a?(Integer)
|
493
535
|
raise AvroError, 'Fixed Schema requires a valid integer for size property.'
|
494
536
|
end
|
495
537
|
super(:fixed, name, space, names, nil, logical_type, aliases)
|
496
538
|
@size = size
|
539
|
+
@precision = precision
|
540
|
+
@scale = scale
|
497
541
|
end
|
498
542
|
|
499
543
|
def to_avro(names=Set.new)
|
500
544
|
avro = super
|
501
|
-
avro
|
545
|
+
return avro if avro.is_a?(String)
|
546
|
+
|
547
|
+
avro['size'] = size
|
548
|
+
avro['precision'] = precision if precision
|
549
|
+
avro['scale'] = scale if scale
|
550
|
+
avro
|
551
|
+
end
|
552
|
+
|
553
|
+
def match_schema?(schema)
|
554
|
+
return true if super && size == schema.size
|
555
|
+
|
556
|
+
if logical_type == DECIMAL_LOGICAL_TYPE && schema.logical_type == DECIMAL_LOGICAL_TYPE
|
557
|
+
return precision == schema.precision && (scale || 0) == (schema.scale || 0)
|
558
|
+
end
|
559
|
+
|
560
|
+
false
|
502
561
|
end
|
503
562
|
end
|
504
563
|
|
505
564
|
class Field < Schema
|
506
565
|
attr_reader :type, :name, :default, :order, :doc, :aliases
|
507
566
|
|
508
|
-
def initialize(type, name, default=:no_default, order=nil, names=nil, namespace=nil, doc=nil, aliases=nil)
|
567
|
+
def initialize(type, name, default=:no_default, order=nil, names=nil, namespace=nil, doc=nil, aliases=nil) # rubocop:disable Lint/MissingSuper
|
509
568
|
@type = subparse(type, names, namespace)
|
510
569
|
@name = name
|
511
570
|
@default = default
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Licensed to the Apache Software Foundation (ASF) under one
|
2
3
|
# or more contributor license agreements. See the NOTICE file
|
3
4
|
# distributed with this work for additional information
|
@@ -15,6 +16,9 @@
|
|
15
16
|
# limitations under the License.
|
16
17
|
module Avro
|
17
18
|
module SchemaCompatibility
|
19
|
+
INT_COERCIBLE_TYPES_SYM = [:long, :float, :double].freeze
|
20
|
+
LONG_COERCIBLE_TYPES_SYM = [:float, :double].freeze
|
21
|
+
|
18
22
|
# Perform a full, recursive check that a datum written using the writers_schema
|
19
23
|
# can be read using the readers_schema.
|
20
24
|
def self.can_read?(writers_schema, readers_schema)
|
@@ -31,6 +35,9 @@ module Avro
|
|
31
35
|
# be read using the readers_schema. This check includes matching the types,
|
32
36
|
# including schema promotion, and matching the full name (including aliases) for named types.
|
33
37
|
def self.match_schemas(writers_schema, readers_schema)
|
38
|
+
# Bypass deeper checks if the schemas are the same Ruby objects
|
39
|
+
return true if writers_schema.equal?(readers_schema)
|
40
|
+
|
34
41
|
w_type = writers_schema.type_sym
|
35
42
|
r_type = readers_schema.type_sym
|
36
43
|
|
@@ -40,31 +47,25 @@ module Avro
|
|
40
47
|
end
|
41
48
|
|
42
49
|
if w_type == r_type
|
43
|
-
return
|
50
|
+
return readers_schema.match_schema?(writers_schema) if Schema::PRIMITIVE_TYPES_SYM.include?(r_type)
|
44
51
|
|
45
52
|
case r_type
|
46
|
-
when :record
|
47
|
-
return readers_schema.match_fullname?(writers_schema.fullname)
|
48
|
-
when :error
|
49
|
-
return readers_schema.match_fullname?(writers_schema.fullname)
|
50
53
|
when :request
|
51
54
|
return true
|
52
|
-
when :fixed
|
53
|
-
return readers_schema.match_fullname?(writers_schema.fullname) &&
|
54
|
-
writers_schema.size == readers_schema.size
|
55
|
-
when :enum
|
56
|
-
return readers_schema.match_fullname?(writers_schema.fullname)
|
57
55
|
when :map
|
58
56
|
return match_schemas(writers_schema.values, readers_schema.values)
|
59
57
|
when :array
|
60
58
|
return match_schemas(writers_schema.items, readers_schema.items)
|
59
|
+
else
|
60
|
+
return readers_schema.match_schema?(writers_schema)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
64
|
# Handle schema promotion
|
65
|
-
|
65
|
+
# rubocop:disable Lint/DuplicateBranch
|
66
|
+
if w_type == :int && INT_COERCIBLE_TYPES_SYM.include?(r_type)
|
66
67
|
return true
|
67
|
-
elsif w_type == :long &&
|
68
|
+
elsif w_type == :long && LONG_COERCIBLE_TYPES_SYM.include?(r_type)
|
68
69
|
return true
|
69
70
|
elsif w_type == :float && r_type == :double
|
70
71
|
return true
|
@@ -73,8 +74,13 @@ module Avro
|
|
73
74
|
elsif w_type == :bytes && r_type == :string
|
74
75
|
return true
|
75
76
|
end
|
77
|
+
# rubocop:enable Lint/DuplicateBranch
|
76
78
|
|
77
|
-
|
79
|
+
if readers_schema.respond_to?(:match_schema?)
|
80
|
+
readers_schema.match_schema?(writers_schema)
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
78
84
|
end
|
79
85
|
|
80
86
|
class Checker
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Licensed to the Apache Software Foundation (ASF) under one
|
2
3
|
# or more contributor license agreements. See the NOTICE file
|
3
4
|
# distributed with this work for additional information
|
@@ -16,22 +17,24 @@
|
|
16
17
|
|
17
18
|
module Avro
|
18
19
|
class SchemaValidator
|
19
|
-
ROOT_IDENTIFIER = '.'
|
20
|
-
PATH_SEPARATOR = '.'
|
21
|
-
INT_RANGE = Schema::INT_MIN_VALUE..Schema::INT_MAX_VALUE
|
22
|
-
LONG_RANGE = Schema::LONG_MIN_VALUE..Schema::LONG_MAX_VALUE
|
20
|
+
ROOT_IDENTIFIER = '.'
|
21
|
+
PATH_SEPARATOR = '.'
|
22
|
+
INT_RANGE = (Schema::INT_MIN_VALUE..Schema::INT_MAX_VALUE).freeze
|
23
|
+
LONG_RANGE = (Schema::LONG_MIN_VALUE..Schema::LONG_MAX_VALUE).freeze
|
23
24
|
COMPLEX_TYPES = [:array, :error, :map, :record, :request].freeze
|
24
25
|
BOOLEAN_VALUES = [true, false].freeze
|
26
|
+
DEFAULT_VALIDATION_OPTIONS = { recursive: true, encoded: false, fail_on_extra_fields: false }.freeze
|
27
|
+
RECURSIVE_SIMPLE_VALIDATION_OPTIONS = { encoded: true }.freeze
|
28
|
+
RUBY_CLASS_TO_AVRO_TYPE = {
|
29
|
+
NilClass => 'null',
|
30
|
+
String => 'string',
|
31
|
+
Float => 'float',
|
32
|
+
Hash => 'record'
|
33
|
+
}.freeze
|
25
34
|
|
26
35
|
class Result
|
27
|
-
attr_reader :errors
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
@errors = []
|
31
|
-
end
|
32
|
-
|
33
36
|
def <<(error)
|
34
|
-
|
37
|
+
errors << error
|
35
38
|
end
|
36
39
|
|
37
40
|
def add_error(path, message)
|
@@ -39,11 +42,16 @@ module Avro
|
|
39
42
|
end
|
40
43
|
|
41
44
|
def failure?
|
42
|
-
@errors.any?
|
45
|
+
defined?(@errors) && errors.any?
|
43
46
|
end
|
44
47
|
|
45
48
|
def to_s
|
46
|
-
errors.join("\n")
|
49
|
+
failure? ? errors.join("\n") : ''
|
50
|
+
end
|
51
|
+
|
52
|
+
def errors
|
53
|
+
# Use less memory for success results by lazily creating the errors array
|
54
|
+
@errors ||= []
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
@@ -63,12 +71,9 @@ module Avro
|
|
63
71
|
TypeMismatchError = Class.new(ValidationError)
|
64
72
|
|
65
73
|
class << self
|
66
|
-
def validate!(expected_schema, logical_datum, options =
|
67
|
-
options ||= {}
|
68
|
-
options[:recursive] = true unless options.key?(:recursive)
|
69
|
-
|
74
|
+
def validate!(expected_schema, logical_datum, options = DEFAULT_VALIDATION_OPTIONS)
|
70
75
|
result = Result.new
|
71
|
-
if options
|
76
|
+
if options.fetch(:recursive, true)
|
72
77
|
validate_recursive(expected_schema, logical_datum, ROOT_IDENTIFIER, result, options)
|
73
78
|
else
|
74
79
|
validate_simple(expected_schema, logical_datum, ROOT_IDENTIFIER, result, options)
|
@@ -79,10 +84,10 @@ module Avro
|
|
79
84
|
|
80
85
|
private
|
81
86
|
|
82
|
-
def validate_recursive(expected_schema, logical_datum, path, result, options
|
87
|
+
def validate_recursive(expected_schema, logical_datum, path, result, options)
|
83
88
|
datum = resolve_datum(expected_schema, logical_datum, options[:encoded])
|
84
89
|
|
85
|
-
validate_simple(expected_schema, datum, path, result,
|
90
|
+
validate_simple(expected_schema, datum, path, result, RECURSIVE_SIMPLE_VALIDATION_OPTIONS)
|
86
91
|
|
87
92
|
case expected_schema.type_sym
|
88
93
|
when :array
|
@@ -95,7 +100,8 @@ module Avro
|
|
95
100
|
fail TypeMismatchError unless datum.is_a?(Hash)
|
96
101
|
expected_schema.fields.each do |field|
|
97
102
|
deeper_path = deeper_path_for_hash(field.name, path)
|
98
|
-
|
103
|
+
nested_value = datum.key?(field.name) ? datum[field.name] : datum[field.name.to_sym]
|
104
|
+
validate_recursive(field.type, nested_value, deeper_path, result, options)
|
99
105
|
end
|
100
106
|
if options[:fail_on_extra_fields]
|
101
107
|
datum_fields = datum.keys.map(&:to_s)
|
@@ -109,7 +115,7 @@ module Avro
|
|
109
115
|
result.add_error(path, "expected type #{expected_schema.type_sym}, got #{actual_value_message(datum)}")
|
110
116
|
end
|
111
117
|
|
112
|
-
def validate_simple(expected_schema, logical_datum, path, result, options
|
118
|
+
def validate_simple(expected_schema, logical_datum, path, result, options)
|
113
119
|
datum = resolve_datum(expected_schema, logical_datum, options[:encoded])
|
114
120
|
validate_type(expected_schema)
|
115
121
|
|
@@ -127,7 +133,7 @@ module Avro
|
|
127
133
|
fail TypeMismatchError unless datum.is_a?(Integer)
|
128
134
|
result.add_error(path, "out of bound value #{datum}") unless LONG_RANGE.cover?(datum)
|
129
135
|
when :float, :double
|
130
|
-
fail TypeMismatchError unless datum.is_a?(Float) || datum.is_a?(Integer)
|
136
|
+
fail TypeMismatchError unless datum.is_a?(Float) || datum.is_a?(Integer) || datum.is_a?(BigDecimal)
|
131
137
|
when :fixed
|
132
138
|
if datum.is_a? String
|
133
139
|
result.add_error(path, fixed_string_message(expected_schema.size, datum)) unless datum.bytesize == expected_schema.size
|
@@ -163,14 +169,14 @@ module Avro
|
|
163
169
|
"expected enum with values #{symbols}, got #{actual_value_message(datum)}"
|
164
170
|
end
|
165
171
|
|
166
|
-
def validate_array(expected_schema, datum, path, result, options
|
172
|
+
def validate_array(expected_schema, datum, path, result, options)
|
167
173
|
fail TypeMismatchError unless datum.is_a?(Array)
|
168
174
|
datum.each_with_index do |d, i|
|
169
|
-
validate_recursive(expected_schema.items, d, path
|
175
|
+
validate_recursive(expected_schema.items, d, "#{path}[#{i}]", result, options)
|
170
176
|
end
|
171
177
|
end
|
172
178
|
|
173
|
-
def validate_map(expected_schema, datum, path, result, options
|
179
|
+
def validate_map(expected_schema, datum, path, result, options)
|
174
180
|
fail TypeMismatchError unless datum.is_a?(Hash)
|
175
181
|
datum.keys.each do |k|
|
176
182
|
result.add_error(path, "unexpected key type '#{ruby_to_avro_type(k.class)}' in map") unless k.is_a?(String)
|
@@ -181,7 +187,7 @@ module Avro
|
|
181
187
|
end
|
182
188
|
end
|
183
189
|
|
184
|
-
def validate_union(expected_schema, datum, path, result, options
|
190
|
+
def validate_union(expected_schema, datum, path, result, options)
|
185
191
|
if expected_schema.schemas.size == 1
|
186
192
|
validate_recursive(expected_schema.schemas.first, datum, path, result, options)
|
187
193
|
return
|
@@ -201,6 +207,9 @@ module Avro
|
|
201
207
|
|
202
208
|
def first_compatible_type(datum, expected_schema, path, failures, options = {})
|
203
209
|
expected_schema.schemas.find do |schema|
|
210
|
+
# Avoid expensive validation if we're just validating a nil
|
211
|
+
next datum.nil? if schema.type_sym == :null
|
212
|
+
|
204
213
|
result = Result.new
|
205
214
|
validate_recursive(schema, datum, path, result, options)
|
206
215
|
failures << { type: schema.type_sym, result: result } if result.failure?
|
@@ -209,7 +218,9 @@ module Avro
|
|
209
218
|
end
|
210
219
|
|
211
220
|
def deeper_path_for_hash(sub_key, path)
|
212
|
-
"#{path}#{PATH_SEPARATOR}#{sub_key}"
|
221
|
+
deeper_path = +"#{path}#{PATH_SEPARATOR}#{sub_key}"
|
222
|
+
deeper_path.squeeze!(PATH_SEPARATOR)
|
223
|
+
deeper_path.freeze
|
213
224
|
end
|
214
225
|
|
215
226
|
def actual_value_message(value)
|
@@ -226,12 +237,7 @@ module Avro
|
|
226
237
|
end
|
227
238
|
|
228
239
|
def ruby_to_avro_type(ruby_class)
|
229
|
-
|
230
|
-
NilClass => 'null',
|
231
|
-
String => 'string',
|
232
|
-
Float => 'float',
|
233
|
-
Hash => 'record'
|
234
|
-
}.fetch(ruby_class, ruby_class)
|
240
|
+
RUBY_CLASS_TO_AVRO_TYPE.fetch(ruby_class, ruby_class)
|
235
241
|
end
|
236
242
|
|
237
243
|
def ruby_integer_to_avro_type(value)
|
data/lib/avro.rb
CHANGED
data/test/case_finder.rb
CHANGED
data/test/random_data.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Licensed to the Apache Software Foundation (ASF) under one
|
2
3
|
# or more contributor license agreements. See the NOTICE file
|
3
4
|
# distributed with this work for additional information
|
@@ -5,9 +6,9 @@
|
|
5
6
|
# to you under the Apache License, Version 2.0 (the
|
6
7
|
# "License"); you may not use this file except in compliance
|
7
8
|
# with the License. You may obtain a copy of the License at
|
8
|
-
#
|
9
|
+
#
|
9
10
|
# https://www.apache.org/licenses/LICENSE-2.0
|
10
|
-
#
|
11
|
+
#
|
11
12
|
# Unless required by applicable law or agreed to in writing, software
|
12
13
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
14
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@@ -74,7 +75,7 @@ class RandomData
|
|
74
75
|
return nil if len == 0
|
75
76
|
symbols[rand(len)]
|
76
77
|
when :fixed
|
77
|
-
f = ""
|
78
|
+
f = +""
|
78
79
|
schm.size.times { f << BYTEPOOL[rand(BYTEPOOL.size), 1] }
|
79
80
|
f
|
80
81
|
end
|
@@ -95,7 +96,7 @@ class RandomData
|
|
95
96
|
BYTEPOOL = '12345abcd'
|
96
97
|
|
97
98
|
def randstr(chars=CHARPOOL, length=20)
|
98
|
-
str = ''
|
99
|
+
str = +''
|
99
100
|
rand(length+1).times { str << chars[rand(chars.size)] }
|
100
101
|
str
|
101
102
|
end
|
data/test/sample_ipc_client.rb
CHANGED
data/test/sample_ipc_server.rb
CHANGED
data/test/test_datafile.rb
CHANGED
data/test/test_fingerprints.rb
CHANGED
data/test/test_help.rb
CHANGED
data/test/test_io.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Licensed to the Apache Software Foundation (ASF) under one
|
2
3
|
# or more contributor license agreements. See the NOTICE file
|
3
4
|
# distributed with this work for additional information
|
@@ -217,7 +218,7 @@ EOS
|
|
217
218
|
[64, '80 01'],
|
218
219
|
[8192, '80 80 01'],
|
219
220
|
[-8193, '81 80 01'],
|
220
|
-
]
|
221
|
+
].freeze
|
221
222
|
|
222
223
|
def avro_hexlify(reader)
|
223
224
|
bytes = []
|
@@ -266,46 +267,46 @@ EOS
|
|
266
267
|
|
267
268
|
def test_utf8_string_encoding
|
268
269
|
[
|
269
|
-
"\xC3"
|
270
|
-
"\xC3\x83"
|
270
|
+
String.new("\xC3", encoding: 'ISO-8859-1'),
|
271
|
+
String.new("\xC3\x83", encoding: 'UTF-8')
|
271
272
|
].each do |value|
|
272
|
-
output =
|
273
|
+
output = String.new('', encoding: 'BINARY')
|
273
274
|
encoder = Avro::IO::BinaryEncoder.new(StringIO.new(output))
|
274
275
|
datum_writer = Avro::IO::DatumWriter.new(Avro::Schema.parse('"string"'))
|
275
276
|
datum_writer.write(value, encoder)
|
276
277
|
|
277
|
-
assert_equal "\x04\xc3\x83"
|
278
|
+
assert_equal String.new("\x04\xc3\x83", encoding: 'BINARY'), output
|
278
279
|
end
|
279
280
|
end
|
280
281
|
|
281
282
|
def test_bytes_encoding
|
282
283
|
[
|
283
|
-
"\xC3\x83"
|
284
|
-
"\xC3\x83"
|
285
|
-
"\xC3\x83"
|
284
|
+
String.new("\xC3\x83", encoding: 'BINARY'),
|
285
|
+
String.new("\xC3\x83", encoding: 'ISO-8859-1'),
|
286
|
+
String.new("\xC3\x83", encoding: 'UTF-8')
|
286
287
|
].each do |value|
|
287
|
-
output =
|
288
|
+
output = String.new('', encoding: 'BINARY')
|
288
289
|
encoder = Avro::IO::BinaryEncoder.new(StringIO.new(output))
|
289
290
|
datum_writer = Avro::IO::DatumWriter.new(Avro::Schema.parse('"bytes"'))
|
290
291
|
datum_writer.write(value, encoder)
|
291
292
|
|
292
|
-
assert_equal "\x04\xc3\x83"
|
293
|
+
assert_equal String.new("\x04\xc3\x83", encoding: 'BINARY'), output
|
293
294
|
end
|
294
295
|
end
|
295
296
|
|
296
297
|
def test_fixed_encoding
|
297
298
|
[
|
298
|
-
"\xC3\x83"
|
299
|
-
"\xC3\x83"
|
300
|
-
"\xC3\x83"
|
299
|
+
String.new("\xC3\x83", encoding: 'BINARY'),
|
300
|
+
String.new("\xC3\x83", encoding: 'ISO-8859-1'),
|
301
|
+
String.new("\xC3\x83", encoding: 'UTF-8')
|
301
302
|
].each do |value|
|
302
|
-
output =
|
303
|
+
output = String.new('', encoding: 'BINARY')
|
303
304
|
encoder = Avro::IO::BinaryEncoder.new(StringIO.new(output))
|
304
305
|
schema = '{"type": "fixed", "name": "TwoBytes", "size": 2}'
|
305
306
|
datum_writer = Avro::IO::DatumWriter.new(Avro::Schema.parse(schema))
|
306
307
|
datum_writer.write(value, encoder)
|
307
308
|
|
308
|
-
assert_equal "\
|
309
|
+
assert_equal String.new("\xC3\x83", encoding: 'BINARY'), output
|
309
310
|
end
|
310
311
|
end
|
311
312
|
|
@@ -489,6 +490,20 @@ EOS
|
|
489
490
|
assert_equal(datum_read, { 'field2' => 1 })
|
490
491
|
end
|
491
492
|
|
493
|
+
def test_big_decimal_datum_for_float
|
494
|
+
writers_schema = Avro::Schema.parse('"float"')
|
495
|
+
writer, * = write_datum(BigDecimal('1.2'), writers_schema)
|
496
|
+
datum_read = read_datum(writer, writers_schema)
|
497
|
+
assert_in_delta(1.2, datum_read)
|
498
|
+
end
|
499
|
+
|
500
|
+
def test_big_decimal_datum_for_double
|
501
|
+
writers_schema = Avro::Schema.parse('"double"')
|
502
|
+
writer, * = write_datum(BigDecimal("1.2"), writers_schema)
|
503
|
+
datum_read = read_datum(writer, writers_schema)
|
504
|
+
assert_in_delta(1.2, datum_read)
|
505
|
+
end
|
506
|
+
|
492
507
|
def test_snappy_backward_compat
|
493
508
|
# a snappy-compressed block payload without the checksum
|
494
509
|
# this has no back-references, just one literal so the last 9
|
@@ -567,7 +582,7 @@ EOS
|
|
567
582
|
datum = randomdata.next
|
568
583
|
assert validate(schm, datum), 'datum is not valid for schema'
|
569
584
|
w = Avro::IO::DatumWriter.new(schm)
|
570
|
-
writer = StringIO.new
|
585
|
+
writer = StringIO.new(+"", "w")
|
571
586
|
w.write(datum, Avro::IO::BinaryEncoder.new(writer))
|
572
587
|
r = datum_reader(schm)
|
573
588
|
reader = StringIO.new(writer.string)
|