-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Support Iceberg row-level delete and update #10075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,13 +27,17 @@ | |
|
|
||
| import javax.inject.Inject; | ||
|
|
||
| import java.io.Externalizable; | ||
| import java.io.IOException; | ||
| import java.io.ObjectInput; | ||
| import java.io.ObjectOutput; | ||
| import java.util.Optional; | ||
|
|
||
| import static com.google.common.base.MoreObjects.toStringHelper; | ||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| public class HdfsEnvironment | ||
| implements Externalizable | ||
| { | ||
| static { | ||
| HadoopNative.requireHadoopNative(); | ||
|
|
@@ -102,7 +106,29 @@ public void doAs(ConnectorIdentity identity, Runnable action) | |
| hdfsAuthentication.doAs(identity, action); | ||
| } | ||
|
|
||
| public HdfsEnvironment() | ||
| { | ||
| this.hdfsConfiguration = null; | ||
| this.hdfsAuthentication = null; | ||
| this.newDirectoryPermissions = null; | ||
| this.newFileInheritOwnership = false; | ||
| this.verifyChecksum = false; | ||
| } | ||
|
|
||
| @Override | ||
| public void writeExternal(ObjectOutput out) | ||
| throws IOException | ||
| { | ||
| } | ||
|
|
||
| @Override | ||
| public void readExternal(ObjectInput in) | ||
| throws IOException, ClassNotFoundException | ||
| { | ||
| } | ||
|
|
||
| public static class HdfsContext | ||
| implements Externalizable | ||
| { | ||
| private final ConnectorIdentity identity; | ||
|
|
||
|
|
@@ -130,5 +156,22 @@ public String toString() | |
| .add("user", identity) | ||
| .toString(); | ||
| } | ||
|
|
||
| public HdfsContext() | ||
| { | ||
| identity = null; | ||
| } | ||
|
|
||
| @Override | ||
| public void writeExternal(ObjectOutput out) | ||
| throws IOException | ||
| { | ||
| } | ||
|
|
||
| @Override | ||
| public void readExternal(ObjectInput in) | ||
| throws IOException, ClassNotFoundException | ||
| { | ||
| } | ||
|
Comment on lines
+166
to
+175
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above. This is neither correct, nor desired. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,11 +25,26 @@ | |
| import java.util.Objects; | ||
| import java.util.Optional; | ||
|
|
||
| import static io.trino.plugin.iceberg.ColumnIdentity.primitiveColumnIdentity; | ||
| import static io.trino.spi.type.BigintType.BIGINT; | ||
| import static java.util.Objects.requireNonNull; | ||
| import static org.apache.iceberg.MetadataColumns.IS_DELETED; | ||
| import static org.apache.iceberg.MetadataColumns.ROW_POSITION; | ||
|
|
||
| public class IcebergColumnHandle | ||
| implements ColumnHandle | ||
| { | ||
| public static final IcebergColumnHandle ROW_POSITION_HANDLE = new IcebergColumnHandle( | ||
| primitiveColumnIdentity(ROW_POSITION.fieldId(), ROW_POSITION.name()), | ||
| BIGINT, | ||
| ImmutableList.of(), | ||
| BIGINT, | ||
| Optional.empty()); | ||
|
|
||
| // use Integer.MIN_VALUE as $row_id field ID, which is currently not reserved by Iceberg | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would we know if iceberg starts using this id internally? Would sth break?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because Iceberg column name and type could change, comparing column ID is needed for equality check. Using a duplicated column ID would cause issue in that area. All the Iceberg metadata columns have IDs counting down from |
||
| public static final int TRINO_ROW_ID_COLUMN_ID = Integer.MIN_VALUE; | ||
| public static final String TRINO_ROW_ID_COLUMN_NAME = "$row_id"; | ||
|
|
||
| private final ColumnIdentity baseColumnIdentity; | ||
| private final Type baseType; | ||
| // The list of field ids to indicate the projected part of the top-level column represented by baseColumnIdentity | ||
|
|
@@ -138,6 +153,21 @@ public boolean isBaseColumn() | |
| return path.isEmpty(); | ||
| } | ||
|
|
||
| public boolean isIcebergRowPositionMetadataColumn() | ||
| { | ||
| return id == ROW_POSITION.fieldId(); | ||
| } | ||
|
|
||
| public boolean isIcebergIsDeletedMetadataColumn() | ||
| { | ||
| return id == IS_DELETED.fieldId(); | ||
| } | ||
|
|
||
| public boolean isTrinoRowIdColumn() | ||
| { | ||
| return id == TRINO_ROW_ID_COLUMN_ID; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| import io.trino.plugin.hive.HiveCompressionCodec; | ||
| import org.apache.iceberg.FileFormat; | ||
|
|
||
| import javax.validation.constraints.Max; | ||
| import javax.validation.constraints.Min; | ||
| import javax.validation.constraints.NotNull; | ||
|
|
||
|
|
@@ -29,6 +30,9 @@ | |
|
|
||
| public class IcebergConfig | ||
| { | ||
| public static final int FORMAT_VERSION_SUPPORT_MIN = 1; | ||
| public static final int FORMAT_VERSION_SUPPORT_MAX = 2; | ||
|
Comment on lines
+33
to
+34
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: MIN/MAX_SUPPORTED_VERSION |
||
|
|
||
| private IcebergFileFormat fileFormat = ORC; | ||
| private HiveCompressionCodec compressionCodec = GZIP; | ||
| private boolean useFileSizeFromMetadata = true; | ||
|
|
@@ -38,6 +42,7 @@ public class IcebergConfig | |
| private Duration dynamicFilteringWaitTimeout = new Duration(0, SECONDS); | ||
| private boolean tableStatisticsEnabled = true; | ||
| private boolean projectionPushdownEnabled = true; | ||
| private int formatVersion = FORMAT_VERSION_SUPPORT_MIN; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not 2? document. |
||
|
|
||
| public CatalogType getCatalogType() | ||
| { | ||
|
|
@@ -167,4 +172,19 @@ public IcebergConfig setProjectionPushdownEnabled(boolean projectionPushdownEnab | |
| this.projectionPushdownEnabled = projectionPushdownEnabled; | ||
| return this; | ||
| } | ||
|
|
||
| @Min(FORMAT_VERSION_SUPPORT_MIN) | ||
| @Max(FORMAT_VERSION_SUPPORT_MAX) | ||
| public int getFormatVersion() | ||
| { | ||
| return formatVersion; | ||
| } | ||
|
|
||
| @Config("iceberg.format-version") | ||
| @ConfigDescription("Iceberg table format version to use when creating a table") | ||
| public IcebergConfig setFormatVersion(int formatVersion) | ||
| { | ||
| this.formatVersion = formatVersion; | ||
| return this; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| import org.apache.hadoop.fs.FileSystem; | ||
| import org.apache.hadoop.fs.Path; | ||
| import org.apache.hadoop.mapred.JobConf; | ||
| import org.apache.iceberg.FileContent; | ||
| import org.apache.iceberg.FileFormat; | ||
| import org.apache.iceberg.Schema; | ||
| import org.apache.iceberg.types.Types; | ||
|
|
@@ -110,11 +111,12 @@ public IcebergFileWriter createFileWriter( | |
| JobConf jobConf, | ||
| ConnectorSession session, | ||
| HdfsContext hdfsContext, | ||
| FileFormat fileFormat) | ||
| FileFormat fileFormat, | ||
| FileContent fileContent) | ||
| { | ||
| switch (fileFormat) { | ||
| case PARQUET: | ||
| return createParquetWriter(outputPath, icebergSchema, jobConf, session, hdfsContext); | ||
| return createParquetWriter(outputPath, icebergSchema, jobConf, session, hdfsContext, fileContent); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why at least add |
||
| case ORC: | ||
| return createOrcWriter(outputPath, icebergSchema, jobConf, session); | ||
| default: | ||
|
|
@@ -127,7 +129,8 @@ private IcebergFileWriter createParquetWriter( | |
| Schema icebergSchema, | ||
| JobConf jobConf, | ||
| ConnectorSession session, | ||
| HdfsContext hdfsContext) | ||
| HdfsContext hdfsContext, | ||
| FileContent fileContent) | ||
| { | ||
| List<String> fileColumnNames = icebergSchema.columns().stream() | ||
| .map(Types.NestedField::name) | ||
|
|
@@ -162,7 +165,8 @@ private IcebergFileWriter createParquetWriter( | |
| nodeVersion.toString(), | ||
| outputPath, | ||
| hdfsEnvironment, | ||
| hdfsContext); | ||
| hdfsContext, | ||
| fileContent); | ||
| } | ||
| catch (IOException e) { | ||
| throw new TrinoException(ICEBERG_WRITER_OPEN_ERROR, "Error creating Parquet file", e); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's unlikely correct serialization of
HdfsEnvironmentstate, since it doesn't seem to store anything.Anyway, we should not make this class serializable (nor Externalizable) at all