avro 1.10.1 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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)
|