herringbone 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +209 -0
- data/bin/herringbone +54 -0
- data/lib/herringbone/active_record.rb +131 -0
- data/lib/herringbone/byte_values.rb +144 -0
- data/lib/herringbone/codecs/lz4.rb +305 -0
- data/lib/herringbone/codecs/snappy.rb +375 -0
- data/lib/herringbone/compression.rb +75 -0
- data/lib/herringbone/encodings/delta.rb +153 -0
- data/lib/herringbone/encodings/plain.rb +87 -0
- data/lib/herringbone/encodings/rle.rb +203 -0
- data/lib/herringbone/format.rb +309 -0
- data/lib/herringbone/io_buffer_support.rb +25 -0
- data/lib/herringbone/reader.rb +469 -0
- data/lib/herringbone/schema.rb +551 -0
- data/lib/herringbone/thrift.rb +334 -0
- data/lib/herringbone/types.rb +454 -0
- data/lib/herringbone/version.rb +5 -0
- data/lib/herringbone/writer.rb +634 -0
- data/lib/herringbone.rb +45 -0
- metadata +104 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Herringbone
|
|
4
|
+
# Parquet file metadata structures, mirroring parquet.thrift.
|
|
5
|
+
# Enums are plain i32 on the wire; constants below give them names.
|
|
6
|
+
module Format
|
|
7
|
+
module Type
|
|
8
|
+
BOOLEAN = 0
|
|
9
|
+
INT32 = 1
|
|
10
|
+
INT64 = 2
|
|
11
|
+
INT96 = 3
|
|
12
|
+
FLOAT = 4
|
|
13
|
+
DOUBLE = 5
|
|
14
|
+
BYTE_ARRAY = 6
|
|
15
|
+
FIXED_LEN_BYTE_ARRAY = 7
|
|
16
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ConvertedType
|
|
20
|
+
UTF8 = 0
|
|
21
|
+
MAP = 1
|
|
22
|
+
MAP_KEY_VALUE = 2
|
|
23
|
+
LIST = 3
|
|
24
|
+
ENUM = 4
|
|
25
|
+
DECIMAL = 5
|
|
26
|
+
DATE = 6
|
|
27
|
+
TIME_MILLIS = 7
|
|
28
|
+
TIME_MICROS = 8
|
|
29
|
+
TIMESTAMP_MILLIS = 9
|
|
30
|
+
TIMESTAMP_MICROS = 10
|
|
31
|
+
UINT_8 = 11
|
|
32
|
+
UINT_16 = 12
|
|
33
|
+
UINT_32 = 13
|
|
34
|
+
UINT_64 = 14
|
|
35
|
+
INT_8 = 15
|
|
36
|
+
INT_16 = 16
|
|
37
|
+
INT_32 = 17
|
|
38
|
+
INT_64 = 18
|
|
39
|
+
JSON = 19
|
|
40
|
+
BSON = 20
|
|
41
|
+
INTERVAL = 21
|
|
42
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module Repetition
|
|
46
|
+
REQUIRED = 0
|
|
47
|
+
OPTIONAL = 1
|
|
48
|
+
REPEATED = 2
|
|
49
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module Encoding
|
|
53
|
+
PLAIN = 0
|
|
54
|
+
PLAIN_DICTIONARY = 2
|
|
55
|
+
RLE = 3
|
|
56
|
+
BIT_PACKED = 4
|
|
57
|
+
DELTA_BINARY_PACKED = 5
|
|
58
|
+
DELTA_LENGTH_BYTE_ARRAY = 6
|
|
59
|
+
DELTA_BYTE_ARRAY = 7
|
|
60
|
+
RLE_DICTIONARY = 8
|
|
61
|
+
BYTE_STREAM_SPLIT = 9
|
|
62
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module Codec
|
|
66
|
+
UNCOMPRESSED = 0
|
|
67
|
+
SNAPPY = 1
|
|
68
|
+
GZIP = 2
|
|
69
|
+
LZO = 3
|
|
70
|
+
BROTLI = 4
|
|
71
|
+
LZ4 = 5
|
|
72
|
+
ZSTD = 6
|
|
73
|
+
LZ4_RAW = 7
|
|
74
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
module PageType
|
|
78
|
+
DATA_PAGE = 0
|
|
79
|
+
INDEX_PAGE = 1
|
|
80
|
+
DICTIONARY_PAGE = 2
|
|
81
|
+
DATA_PAGE_V2 = 3
|
|
82
|
+
NAMES = constants.to_h { |c| [const_get(c), c] }.freeze
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
S = Thrift::Struct
|
|
86
|
+
|
|
87
|
+
class SizeStatistics < S
|
|
88
|
+
field 1, :unencoded_byte_array_data_bytes, :i64
|
|
89
|
+
field 2, :repetition_level_histogram, [:list, :i64]
|
|
90
|
+
field 3, :definition_level_histogram, [:list, :i64]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class Statistics < S
|
|
94
|
+
field 1, :max, :binary
|
|
95
|
+
field 2, :min, :binary
|
|
96
|
+
field 3, :null_count, :i64
|
|
97
|
+
field 4, :distinct_count, :i64
|
|
98
|
+
field 5, :max_value, :binary
|
|
99
|
+
field 6, :min_value, :binary
|
|
100
|
+
field 7, :is_max_value_exact, :bool
|
|
101
|
+
field 8, :is_min_value_exact, :bool
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Empty marker structs used inside unions
|
|
105
|
+
class StringType < S; end
|
|
106
|
+
class UUIDType < S; end
|
|
107
|
+
class MapType < S; end
|
|
108
|
+
class ListType < S; end
|
|
109
|
+
class EnumType < S; end
|
|
110
|
+
class DateType < S; end
|
|
111
|
+
class Float16Type < S; end
|
|
112
|
+
class NullType < S; end
|
|
113
|
+
class JsonType < S; end
|
|
114
|
+
class BsonType < S; end
|
|
115
|
+
class MilliSeconds < S; end
|
|
116
|
+
class MicroSeconds < S; end
|
|
117
|
+
class NanoSeconds < S; end
|
|
118
|
+
class TypeDefinedOrder < S; end
|
|
119
|
+
class IndexPageHeader < S; end
|
|
120
|
+
|
|
121
|
+
class VariantType < S
|
|
122
|
+
field 1, :specification_version, :byte
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class DecimalType < S
|
|
126
|
+
field 1, :scale, :i32
|
|
127
|
+
field 2, :precision, :i32
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class TimeUnit < S
|
|
131
|
+
field 1, :millis, MilliSeconds
|
|
132
|
+
field 2, :micros, MicroSeconds
|
|
133
|
+
field 3, :nanos, NanoSeconds
|
|
134
|
+
|
|
135
|
+
def self.millis = new(millis: MilliSeconds.new)
|
|
136
|
+
def self.micros = new(micros: MicroSeconds.new)
|
|
137
|
+
def self.nanos = new(nanos: NanoSeconds.new)
|
|
138
|
+
|
|
139
|
+
def to_sym
|
|
140
|
+
if millis then :millis
|
|
141
|
+
elsif micros then :micros
|
|
142
|
+
elsif nanos then :nanos
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
class TimestampType < S
|
|
148
|
+
field 1, :is_adjusted_to_utc, :bool
|
|
149
|
+
field 2, :unit, TimeUnit
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
class TimeType < S
|
|
153
|
+
field 1, :is_adjusted_to_utc, :bool
|
|
154
|
+
field 2, :unit, TimeUnit
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
class IntType < S
|
|
158
|
+
field 1, :bit_width, :byte
|
|
159
|
+
field 2, :is_signed, :bool
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
class LogicalType < S
|
|
163
|
+
field 1, :string, StringType
|
|
164
|
+
field 2, :map, MapType
|
|
165
|
+
field 3, :list, ListType
|
|
166
|
+
field 4, :enum, EnumType
|
|
167
|
+
field 5, :decimal, DecimalType
|
|
168
|
+
field 6, :date, DateType
|
|
169
|
+
field 7, :time, TimeType
|
|
170
|
+
field 8, :timestamp, TimestampType
|
|
171
|
+
field 10, :integer, IntType
|
|
172
|
+
field 11, :unknown, NullType
|
|
173
|
+
field 12, :json, JsonType
|
|
174
|
+
field 13, :bson, BsonType
|
|
175
|
+
field 14, :uuid, UUIDType
|
|
176
|
+
field 15, :float16, Float16Type
|
|
177
|
+
field 16, :variant, VariantType
|
|
178
|
+
|
|
179
|
+
# Returns [kind_symbol, payload] for whichever union member is set
|
|
180
|
+
def kind
|
|
181
|
+
self.class.fields.each do |f|
|
|
182
|
+
v = instance_variable_get(f.ivar)
|
|
183
|
+
return [f.name, v] if v
|
|
184
|
+
end
|
|
185
|
+
nil
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
class SchemaElement < S
|
|
190
|
+
field 1, :type, :i32
|
|
191
|
+
field 2, :type_length, :i32
|
|
192
|
+
field 3, :repetition_type, :i32
|
|
193
|
+
field 4, :name, :string
|
|
194
|
+
field 5, :num_children, :i32
|
|
195
|
+
field 6, :converted_type, :i32
|
|
196
|
+
field 7, :scale, :i32
|
|
197
|
+
field 8, :precision, :i32
|
|
198
|
+
field 9, :field_id, :i32
|
|
199
|
+
field 10, :logical_type, LogicalType
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class DataPageHeader < S
|
|
203
|
+
field 1, :num_values, :i32
|
|
204
|
+
field 2, :encoding, :i32
|
|
205
|
+
field 3, :definition_level_encoding, :i32
|
|
206
|
+
field 4, :repetition_level_encoding, :i32
|
|
207
|
+
field 5, :statistics, Statistics
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
class DictionaryPageHeader < S
|
|
211
|
+
field 1, :num_values, :i32
|
|
212
|
+
field 2, :encoding, :i32
|
|
213
|
+
field 3, :is_sorted, :bool
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
class DataPageHeaderV2 < S
|
|
217
|
+
field 1, :num_values, :i32
|
|
218
|
+
field 2, :num_nulls, :i32
|
|
219
|
+
field 3, :num_rows, :i32
|
|
220
|
+
field 4, :encoding, :i32
|
|
221
|
+
field 5, :definition_levels_byte_length, :i32
|
|
222
|
+
field 6, :repetition_levels_byte_length, :i32
|
|
223
|
+
field 7, :is_compressed, :bool
|
|
224
|
+
field 8, :statistics, Statistics
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class PageHeader < S
|
|
228
|
+
field 1, :type, :i32
|
|
229
|
+
field 2, :uncompressed_page_size, :i32
|
|
230
|
+
field 3, :compressed_page_size, :i32
|
|
231
|
+
field 4, :crc, :i32
|
|
232
|
+
field 5, :data_page_header, DataPageHeader
|
|
233
|
+
field 6, :index_page_header, IndexPageHeader
|
|
234
|
+
field 7, :dictionary_page_header, DictionaryPageHeader
|
|
235
|
+
field 8, :data_page_header_v2, DataPageHeaderV2
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
class KeyValue < S
|
|
239
|
+
field 1, :key, :string
|
|
240
|
+
field 2, :value, :string
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
class SortingColumn < S
|
|
244
|
+
field 1, :column_idx, :i32
|
|
245
|
+
field 2, :descending, :bool
|
|
246
|
+
field 3, :nulls_first, :bool
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
class PageEncodingStats < S
|
|
250
|
+
field 1, :page_type, :i32
|
|
251
|
+
field 2, :encoding, :i32
|
|
252
|
+
field 3, :count, :i32
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
class ColumnMetaData < S
|
|
256
|
+
field 1, :type, :i32
|
|
257
|
+
field 2, :encodings, [:list, :i32]
|
|
258
|
+
field 3, :path_in_schema, [:list, :string]
|
|
259
|
+
field 4, :codec, :i32
|
|
260
|
+
field 5, :num_values, :i64
|
|
261
|
+
field 6, :total_uncompressed_size, :i64
|
|
262
|
+
field 7, :total_compressed_size, :i64
|
|
263
|
+
field 8, :key_value_metadata, [:list, KeyValue]
|
|
264
|
+
field 9, :data_page_offset, :i64
|
|
265
|
+
field 10, :index_page_offset, :i64
|
|
266
|
+
field 11, :dictionary_page_offset, :i64
|
|
267
|
+
field 12, :statistics, Statistics
|
|
268
|
+
field 13, :encoding_stats, [:list, PageEncodingStats]
|
|
269
|
+
field 14, :bloom_filter_offset, :i64
|
|
270
|
+
field 15, :bloom_filter_length, :i32
|
|
271
|
+
field 16, :size_statistics, SizeStatistics
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
class ColumnChunk < S
|
|
275
|
+
field 1, :file_path, :string
|
|
276
|
+
field 2, :file_offset, :i64
|
|
277
|
+
field 3, :meta_data, ColumnMetaData
|
|
278
|
+
field 4, :offset_index_offset, :i64
|
|
279
|
+
field 5, :offset_index_length, :i32
|
|
280
|
+
field 6, :column_index_offset, :i64
|
|
281
|
+
field 7, :column_index_length, :i32
|
|
282
|
+
field 9, :encrypted_column_metadata, :binary
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
class RowGroup < S
|
|
286
|
+
field 1, :columns, [:list, ColumnChunk]
|
|
287
|
+
field 2, :total_byte_size, :i64
|
|
288
|
+
field 3, :num_rows, :i64
|
|
289
|
+
field 4, :sorting_columns, [:list, SortingColumn]
|
|
290
|
+
field 5, :file_offset, :i64
|
|
291
|
+
field 6, :total_compressed_size, :i64
|
|
292
|
+
field 7, :ordinal, :i16
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
class ColumnOrder < S
|
|
296
|
+
field 1, :type_order, TypeDefinedOrder
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
class FileMetaData < S
|
|
300
|
+
field 1, :version, :i32
|
|
301
|
+
field 2, :schema, [:list, SchemaElement]
|
|
302
|
+
field 3, :num_rows, :i64
|
|
303
|
+
field 4, :row_groups, [:list, RowGroup]
|
|
304
|
+
field 5, :key_value_metadata, [:list, KeyValue]
|
|
305
|
+
field 6, :created_by, :string
|
|
306
|
+
field 7, :column_orders, [:list, ColumnOrder]
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Herringbone
|
|
4
|
+
# IO::Buffer lets the decompressors copy bytes around without allocating a String per copy.
|
|
5
|
+
# It still carries an "experimental" warning, which is silenced once here: the gem only uses
|
|
6
|
+
# new/for/copy/get_string/free, and falls back to String operations where it is missing.
|
|
7
|
+
module IOBufferSupport
|
|
8
|
+
AVAILABLE = begin
|
|
9
|
+
if defined?(IO::Buffer) && IO::Buffer.method_defined?(:copy) && IO::Buffer.method_defined?(:get_string)
|
|
10
|
+
previous = Warning[:experimental]
|
|
11
|
+
begin
|
|
12
|
+
Warning[:experimental] = false
|
|
13
|
+
IO::Buffer.new(1).free
|
|
14
|
+
ensure
|
|
15
|
+
Warning[:experimental] = previous
|
|
16
|
+
end
|
|
17
|
+
ENV["HERRINGBONE_NO_IO_BUFFER"].nil?
|
|
18
|
+
else
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
rescue StandardError
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|