Skip to content

Commit 1b15939

Browse files
committed
Add Indexes and Stats format reader and writer
Add reader and writer for the Index and Statistics File Format.
1 parent 454e246 commit 1b15939

20 files changed

Lines changed: 1232 additions & 0 deletions

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ project(':iceberg-core') {
217217
exclude group: 'org.tukaani' // xz compression is not supported
218218
}
219219

220+
implementation 'io.airlift:aircompressor'
220221
implementation 'org.apache.httpcomponents.client5:httpclient5'
221222
implementation "com.fasterxml.jackson.core:jackson-databind"
222223
implementation "com.fasterxml.jackson.core:jackson-core"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.stats;
21+
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
import java.util.Set;
25+
import javax.annotation.Nullable;
26+
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
27+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableSet;
28+
29+
public class BlobMetadata {
30+
private final String type;
31+
private final Set<Integer> columns;
32+
private final long offset;
33+
private final long length;
34+
private final String compressionCodec;
35+
36+
@JsonCreator
37+
public BlobMetadata(
38+
@JsonProperty("type") String type,
39+
@JsonProperty("columns") Set<Integer> columns,
40+
@JsonProperty("offset") long offset,
41+
@JsonProperty("length") long length,
42+
@JsonProperty("compression_codec") @Nullable String compressionCodec) {
43+
this.type = Preconditions.checkNotNull(type, "type is null");
44+
this.columns = ImmutableSet.copyOf(Preconditions.checkNotNull(columns, "columns is null"));
45+
this.offset = offset;
46+
this.length = length;
47+
this.compressionCodec = compressionCodec;
48+
}
49+
50+
public String type() {
51+
return type;
52+
}
53+
54+
public Set<Integer> columns() {
55+
return columns;
56+
}
57+
58+
/**
59+
* Offset in the file
60+
*/
61+
public long offset() {
62+
return offset;
63+
}
64+
65+
/**
66+
* Length in the file
67+
*/
68+
public long length() {
69+
return length;
70+
}
71+
72+
@Nullable
73+
public String compressionCodec() {
74+
return compressionCodec;
75+
}
76+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.stats;
21+
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
import java.util.List;
25+
import java.util.Map;
26+
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
27+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
28+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
29+
30+
public class FileMetadata {
31+
private final List<BlobMetadata> blobs;
32+
private final Map<String, String> properties;
33+
34+
@JsonCreator
35+
public FileMetadata(
36+
@JsonProperty("blobs") List<BlobMetadata> blobs,
37+
@JsonProperty("properties") Map<String, String> properties) {
38+
this.blobs = ImmutableList.copyOf(Preconditions.checkNotNull(blobs, "blobs is null"));
39+
this.properties = ImmutableMap.copyOf(Preconditions.checkNotNull(properties, "properties is null"));
40+
}
41+
42+
public List<BlobMetadata> blobs() {
43+
return blobs;
44+
}
45+
46+
public Map<String, String> properties() {
47+
return properties;
48+
}
49+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.stats;
21+
22+
import com.fasterxml.jackson.core.JsonGenerator;
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
import java.io.IOException;
25+
import java.io.StringWriter;
26+
import java.io.UncheckedIOException;
27+
import java.util.Map;
28+
import java.util.Set;
29+
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
30+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
31+
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
32+
import org.apache.iceberg.util.JsonUtil;
33+
34+
public final class FileMetadataParser {
35+
36+
private FileMetadataParser() {
37+
}
38+
39+
private static final String BLOBS = "blobs";
40+
private static final String PROPERTIES = "properties";
41+
42+
private static final String TYPE = "type";
43+
private static final String COLUMNS = "columns";
44+
private static final String OFFSET = "offset";
45+
private static final String LENGTH = "length";
46+
private static final String COMPRESSION_CODEC = "compression_codec";
47+
48+
public static String toJson(FileMetadata fileMetadata) {
49+
try {
50+
StringWriter writer = new StringWriter();
51+
JsonGenerator generator = JsonUtil.factory().createGenerator(writer);
52+
generator.useDefaultPrettyPrinter();
53+
toJson(fileMetadata, generator);
54+
generator.flush();
55+
return writer.toString();
56+
} catch (IOException e) {
57+
throw new UncheckedIOException("Failed to write json for: " + fileMetadata, e);
58+
}
59+
}
60+
61+
public static FileMetadata fromJson(String json) {
62+
try {
63+
return fromJson(JsonUtil.mapper().readValue(json, JsonNode.class));
64+
} catch (IOException e) {
65+
throw new UncheckedIOException(e);
66+
}
67+
}
68+
69+
public static FileMetadata fromJson(JsonNode json) {
70+
return fileMetadataFromJson(json);
71+
}
72+
73+
static void toJson(FileMetadata fileMetadata, JsonGenerator generator) throws IOException {
74+
generator.writeStartObject();
75+
76+
generator.writeArrayFieldStart(BLOBS);
77+
for (BlobMetadata blobMetadata : fileMetadata.blobs()) {
78+
toJson(blobMetadata, generator);
79+
}
80+
generator.writeEndArray();
81+
82+
generator.writeObjectFieldStart(PROPERTIES);
83+
for (Map.Entry<String, String> entry : fileMetadata.properties().entrySet()) {
84+
generator.writeStringField(entry.getKey(), entry.getValue());
85+
}
86+
generator.writeEndObject();
87+
88+
generator.writeEndObject();
89+
}
90+
91+
static FileMetadata fileMetadataFromJson(JsonNode json) {
92+
93+
ImmutableList.Builder<BlobMetadata> blobs = ImmutableList.builder();
94+
JsonNode blobsJson = json.get(BLOBS);
95+
Preconditions.checkArgument(blobsJson != null && blobsJson.isArray(),
96+
"Cannot parse blobs from non-array: %s", blobsJson);
97+
for (JsonNode blobJson : blobsJson) {
98+
blobs.add(blobMetadataFromJson(blobJson));
99+
}
100+
101+
Map<String, String> properties = ImmutableMap.of();
102+
JsonNode propertiesJson = json.get(PROPERTIES);
103+
if (propertiesJson != null) {
104+
properties = JsonUtil.getStringMap(PROPERTIES, json);
105+
}
106+
107+
return new FileMetadata(
108+
blobs.build(),
109+
properties);
110+
}
111+
112+
static void toJson(BlobMetadata blobMetadata, JsonGenerator generator) throws IOException {
113+
generator.writeStartObject();
114+
115+
generator.writeStringField(TYPE, blobMetadata.type());
116+
117+
generator.writeArrayFieldStart(COLUMNS);
118+
for (int column : blobMetadata.columns()) {
119+
generator.writeNumber(column);
120+
}
121+
generator.writeEndArray();
122+
123+
generator.writeNumberField(OFFSET, blobMetadata.offset());
124+
generator.writeNumberField(LENGTH, blobMetadata.length());
125+
126+
if (blobMetadata.compressionCodec() != null) {
127+
generator.writeStringField(COMPRESSION_CODEC, blobMetadata.compressionCodec());
128+
}
129+
130+
generator.writeEndObject();
131+
}
132+
133+
static BlobMetadata blobMetadataFromJson(JsonNode json) {
134+
String type = JsonUtil.getString(TYPE, json);
135+
Set<Integer> columns = JsonUtil.getIntegerSet(COLUMNS, json);
136+
long offset = JsonUtil.getLong(OFFSET, json);
137+
long length = JsonUtil.getLong(LENGTH, json);
138+
String compressionCodec = JsonUtil.getStringOrNull(COMPRESSION_CODEC, json);
139+
140+
return new BlobMetadata(
141+
type,
142+
columns,
143+
offset,
144+
length,
145+
compressionCodec);
146+
}
147+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.stats;
21+
22+
public final class StandardBlobTypes {
23+
private StandardBlobTypes() {
24+
}
25+
26+
/**
27+
* 8-bytes integer stored little-endian and representing number of distinct values
28+
*/
29+
public static final String NDV_LONG_LITTLE_ENDIAN = "ndv-long-little-endian";
30+
31+
/**
32+
* A serialized form of a "compact" Theta sketch produced by the <a href="https://datasketches.apache.org/">Apache DataSketches</a> library
33+
*/
34+
public static final String APACHE_DATASKETCHES_THETA_V1 = "apache-datasketches-theta-v1";
35+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iceberg.stats;
21+
22+
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
23+
24+
public enum StatsCompressionCodec {
25+
/**
26+
* LZ4 single compression frame with content size present
27+
*/
28+
LZ4("lz4"),
29+
30+
/**
31+
* Zstandard single compression frame with content size present
32+
*/
33+
ZSTD("zstd"),
34+
/**/;
35+
36+
private final String codecName;
37+
38+
StatsCompressionCodec(String codecName) {
39+
this.codecName = codecName;
40+
}
41+
42+
public String getCodecName() {
43+
return codecName;
44+
}
45+
46+
public static StatsCompressionCodec forName(String codecName) {
47+
Preconditions.checkNotNull(codecName, "codecName is null");
48+
for (StatsCompressionCodec value : values()) {
49+
if (value.getCodecName().equals(codecName)) {
50+
return value;
51+
}
52+
}
53+
throw new IllegalArgumentException("Unrecognized codec name " + codecName);
54+
}
55+
}

0 commit comments

Comments
 (0)