Skip to content
15 changes: 10 additions & 5 deletions .palantir/revapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1025,14 +1025,11 @@ acceptedBreaks:
new: "class org.apache.iceberg.types.Types.NestedField"
justification: "new Constructor added"
org.apache.iceberg:iceberg-core:
- code: "java.method.visibilityReduced"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for other reviewers: I've checked and these blocked have been moved when running

./gradlew :iceberg-core:revapiAcceptBreak --justification "Serialization across versions is not supported"  \
          --code "java.class.defaultSerializationChanged" \
          --old "class org.apache.iceberg.GenericManifestFile" \
          --new "class org.apache.iceberg.GenericManifestFile"

old: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
new: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
justification: "Deprecations for 1.6.0 release"
- code: "java.element.noLongerDeprecated"
old: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
new: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
justification: "Constructor became private as part of deprecations cleanup for 1.6.0 release"
justification: "Constructor became private as part of deprecations cleanup for\
\ 1.6.0 release"
- code: "java.element.noLongerDeprecated"
old: "method void org.apache.iceberg.rest.auth.OAuth2Util.AuthSession::<init>(java.util.Map<java.lang.String,\
\ java.lang.String>, java.lang.String, java.lang.String, java.lang.String,\
Expand All @@ -1056,6 +1053,10 @@ acceptedBreaks:
- code: "java.method.removed"
old: "method org.apache.iceberg.DataFiles.Builder org.apache.iceberg.DataFiles.Builder::withEqualityFieldIds(java.util.List<java.lang.Integer>)"
justification: "Deprecations for 1.6.0 release"
- code: "java.method.visibilityReduced"
old: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
new: "method void org.apache.iceberg.encryption.PlaintextEncryptionManager::<init>()"
justification: "Deprecations for 1.6.0 release"
"1.6.0":
org.apache.iceberg:iceberg-common:
- code: "java.method.removed"
Expand Down Expand Up @@ -1083,6 +1084,10 @@ acceptedBreaks:
\ java.lang.Object[]) throws java.lang.Exception"
justification: "Reduced visibility and scoped to package"
org.apache.iceberg:iceberg-core:
- code: "java.class.defaultSerializationChanged"
old: "class org.apache.iceberg.GenericManifestFile"
new: "class org.apache.iceberg.GenericManifestFile"
justification: "Serialization across versions is not supported"
- code: "java.class.removed"
old: "enum org.apache.iceberg.BaseMetastoreTableOperations.CommitStatus"
justification: "Removing deprecated code"
Expand Down
41 changes: 39 additions & 2 deletions core/src/main/java/org/apache/iceberg/AllManifestsTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.iceberg.expressions.Literal;
import org.apache.iceberg.io.CloseableIterable;
import org.apache.iceberg.io.FileIO;
import org.apache.iceberg.relocated.com.google.common.annotations.VisibleForTesting;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.relocated.com.google.common.collect.Iterables;
import org.apache.iceberg.relocated.com.google.common.collect.Maps;
Expand All @@ -52,7 +53,8 @@ public class AllManifestsTable extends BaseMetadataTable {
public static final Types.NestedField REF_SNAPSHOT_ID =
Types.NestedField.required(18, "reference_snapshot_id", Types.LongType.get());

private static final Schema MANIFEST_FILE_SCHEMA =
@VisibleForTesting
static final Schema MANIFEST_FILE_SCHEMA =
new Schema(
Types.NestedField.required(14, "content", Types.IntegerType.get()),
Types.NestedField.required(1, "path", Types.StringType.get()),
Expand Down Expand Up @@ -119,6 +121,7 @@ protected TableScan newRefinedScan(Table table, Schema schema, TableScanContext
protected CloseableIterable<FileScanTask> doPlanFiles() {
FileIO io = table().io();
Map<Integer, PartitionSpec> specs = Maps.newHashMap(table().specs());
Schema dataTableSchema = table().schema();
Expression filter = shouldIgnoreResiduals() ? Expressions.alwaysTrue() : filter();

SnapshotEvaluator snapshotEvaluator =
Expand All @@ -132,7 +135,13 @@ protected CloseableIterable<FileScanTask> doPlanFiles() {
snap -> {
if (snap.manifestListLocation() != null) {
return new ManifestListReadTask(
io, schema(), specs, snap.manifestListLocation(), filter, snap.snapshotId());
dataTableSchema,
io,
schema(),
specs,
snap.manifestListLocation(),
filter,
snap.snapshotId());
} else {
return StaticDataTask.of(
io.newInputFile(
Expand All @@ -149,6 +158,7 @@ protected CloseableIterable<FileScanTask> doPlanFiles() {
}

static class ManifestListReadTask implements DataTask {
private final Schema dataTableSchema;
private final FileIO io;
private final Schema schema;
private final Map<Integer, PartitionSpec> specs;
Expand All @@ -158,12 +168,14 @@ static class ManifestListReadTask implements DataTask {
private DataFile lazyDataFile = null;

ManifestListReadTask(
Schema dataTableSchema,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs the table schema to be able to parse partition spec.

the other arg schema is for the MANIFEST_FILE_SCHEMA.

FileIO io,
Schema schema,
Map<Integer, PartitionSpec> specs,
String manifestListLocation,
Expression residual,
long referenceSnapshotId) {
this.dataTableSchema = dataTableSchema;
this.io = io;
this.schema = schema;
this.specs = specs;
Expand Down Expand Up @@ -244,6 +256,31 @@ public Expression residual() {
public Iterable<FileScanTask> split(long splitSize) {
return ImmutableList.of(this); // don't split
}

@Override
public Schema schema() {
return schema;
}

Schema dataTableSchema() {
return dataTableSchema;
}

FileIO io() {
return io;
}

Map<Integer, PartitionSpec> specsById() {
return specs;
}

String manifestListLocation() {
return manifestListLocation;
}

long referenceSnapshotId() {
return referenceSnapshotId;
}
}

static StaticDataTask.Row manifestFileToRow(
Expand Down
107 changes: 107 additions & 0 deletions core/src/main/java/org/apache/iceberg/AllManifestsTableTaskParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.iceberg;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.util.Map;
import org.apache.iceberg.expressions.Expression;
import org.apache.iceberg.expressions.ExpressionParser;
import org.apache.iceberg.io.FileIO;
import org.apache.iceberg.io.FileIOParser;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.util.JsonUtil;
import org.apache.iceberg.util.PartitionUtil;

class AllManifestsTableTaskParser {
private static final String DATA_TABLE_SCHEMA = "data-table-schema";
private static final String FILE_IO = "file-io";
private static final String SCHEMA = "schema";
private static final String SPECS = "partition-specs";
private static final String MANIFEST_LIST_LOCATION = "manifest-list-Location";
private static final String RESIDUAL = "residual-filter";
private static final String REFERENCE_SNAPSHOT_ID = "reference-snapshot-id";

private AllManifestsTableTaskParser() {}

static void toJson(AllManifestsTable.ManifestListReadTask task, JsonGenerator generator)
throws IOException {
Preconditions.checkArgument(task != null, "Invalid manifest task: null");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add unit tests to cover the scenarios where the task/generator is null

Preconditions.checkArgument(generator != null, "Invalid JSON generator: null");

generator.writeFieldName(DATA_TABLE_SCHEMA);
SchemaParser.toJson(task.dataTableSchema(), generator);

@RussellSpitzer RussellSpitzer Jul 22, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding the (base) table schema here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the dataTableSchema to the AllManifestsTable.ManifestListReadTask because we need it to parse partition spec. See line 89 below.

PartitionSpec spec = PartitionSpecParser.fromJson(dataTableSchema, specNode);

also mentioned in https://github.com/apache/iceberg/pull/10735/files#r1685644366


generator.writeFieldName(FILE_IO);
FileIOParser.toJson(task.io(), generator);

generator.writeFieldName(SCHEMA);
SchemaParser.toJson(task.schema(), generator);

generator.writeArrayFieldStart(SPECS);
for (PartitionSpec spec : task.specsById().values()) {
PartitionSpecParser.toJson(spec, generator);
}

generator.writeEndArray();

generator.writeStringField(MANIFEST_LIST_LOCATION, task.manifestListLocation());

generator.writeFieldName(RESIDUAL);
ExpressionParser.toJson(task.residual(), generator);

generator.writeNumberField(REFERENCE_SNAPSHOT_ID, task.referenceSnapshotId());
}

static AllManifestsTable.ManifestListReadTask fromJson(JsonNode jsonNode) {
Comment thread
nastra marked this conversation as resolved.
Preconditions.checkArgument(jsonNode != null, "Invalid JSON node for manifest task: null");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please also add unit tests to cover these here (same as for toJson). This should also be done for all the other JSON parsers

Preconditions.checkArgument(
jsonNode.isObject(), "Invalid JSON node for manifest task: non-object (%s)", jsonNode);

Schema dataTableSchema = SchemaParser.fromJson(JsonUtil.get(DATA_TABLE_SCHEMA, jsonNode));
FileIO fileIO = FileIOParser.fromJson(JsonUtil.get(FILE_IO, jsonNode), null);
Schema schema = SchemaParser.fromJson(JsonUtil.get(SCHEMA, jsonNode));

JsonNode specsArray = JsonUtil.get(SPECS, jsonNode);
Preconditions.checkArgument(
specsArray.isArray(), "Invalid JSON node for partition specs: non-array (%s)", specsArray);

ImmutableList.Builder<PartitionSpec> specsBuilder = ImmutableList.builder();
for (JsonNode specNode : specsArray) {
PartitionSpec spec = PartitionSpecParser.fromJson(dataTableSchema, specNode);
specsBuilder.add(spec);
}

Map<Integer, PartitionSpec> specsById = PartitionUtil.indexSpecs(specsBuilder.build());
String manifestListLocation = JsonUtil.getString(MANIFEST_LIST_LOCATION, jsonNode);
Expression residualFilter = ExpressionParser.fromJson(JsonUtil.get(RESIDUAL, jsonNode));
long referenceSnapshotId = JsonUtil.getLong(REFERENCE_SNAPSHOT_ID, jsonNode);

return new AllManifestsTable.ManifestListReadTask(
dataTableSchema,
fileIO,
schema,
specsById,
manifestListLocation,
residualFilter,
referenceSnapshotId);
}
}
52 changes: 35 additions & 17 deletions core/src/main/java/org/apache/iceberg/BaseEntriesTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.apache.iceberg.expressions.ResidualEvaluator;
import org.apache.iceberg.io.CloseableIterable;
import org.apache.iceberg.io.FileIO;
import org.apache.iceberg.relocated.com.google.common.annotations.VisibleForTesting;
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList;
import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.relocated.com.google.common.collect.Sets;
Expand Down Expand Up @@ -92,15 +91,9 @@ static CloseableIterable<FileScanTask> planFiles(
evalCache.get(manifest.partitionSpecId()).eval(manifest)
&& manifestContentEvaluator.eval(manifest));

String schemaString = SchemaParser.toJson(projectedSchema);
String specString = PartitionSpecParser.toJson(PartitionSpec.unpartitioned());
ResidualEvaluator residuals = ResidualEvaluator.unpartitioned(filter);

return CloseableIterable.transform(
filteredManifests,
manifest ->
new ManifestReadTask(
table, manifest, projectedSchema, schemaString, specString, residuals));
manifest -> new ManifestReadTask(table, manifest, projectedSchema, filter));
}

/**
Expand Down Expand Up @@ -283,19 +276,29 @@ static class ManifestReadTask extends BaseFileScanTask implements DataTask {
private final ManifestFile manifest;
private final Map<Integer, PartitionSpec> specsById;

private ManifestReadTask(
Table table, ManifestFile manifest, Schema projection, Expression filter) {
this(table.schema(), table.io(), table.specs(), manifest, projection, filter);
}

ManifestReadTask(
Table table,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did a bit refactoring. this constructor can be used by the JSON parser. the other constructor above is used for the previous purpose in the planFiles method above.

Schema dataTableSchema,
FileIO io,
Map<Integer, PartitionSpec> specsById,
ManifestFile manifest,
Schema projection,
String schemaString,
String specString,
ResidualEvaluator residuals) {
super(DataFiles.fromManifest(manifest), null, schemaString, specString, residuals);
Expression filter) {
super(
DataFiles.fromManifest(manifest),
null,
SchemaParser.toJson(projection),
PartitionSpecParser.toJson(PartitionSpec.unpartitioned()),
ResidualEvaluator.unpartitioned(filter));
this.projection = projection;
this.io = table.io();
this.io = io;
this.manifest = manifest;
this.specsById = Maps.newHashMap(table.specs());
this.dataTableSchema = table.schema();
this.specsById = Maps.newHashMap(specsById);
this.dataTableSchema = dataTableSchema;

Type fileProjectionType = projection.findType("data_file");
this.fileProjection =
Expand All @@ -311,7 +314,6 @@ public long estimatedRowsCount() {
+ (long) manifest.existingFilesCount();
}

@VisibleForTesting

@stevenzwu stevenzwu Jul 21, 2024

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not just for testing anymore. JSON parser needs this getter too

ManifestFile manifest() {
return manifest;
}
Expand Down Expand Up @@ -403,5 +405,21 @@ private MetricsUtil.ReadableMetricsStruct readableMetrics(
public Iterable<FileScanTask> split(long splitSize) {
return ImmutableList.of(this); // don't split
}

FileIO io() {
return io;
}

Map<Integer, PartitionSpec> specsById() {
return specsById;
}

Schema dataTableSchema() {
return dataTableSchema;
}

Schema projection() {
return projection;
}
}
}
Loading