-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Spark 4.2: Support CREATE TABLE ... LIKE ... #17954
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
base: main
Are you sure you want to change the base?
Changes from all commits
d1fbb55
67b0aa5
a6f0bbd
10d8007
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 |
|---|---|---|
|
|
@@ -36,6 +36,10 @@ | |
| import org.apache.iceberg.HasTableOperations; | ||
| import org.apache.iceberg.MetadataTableType; | ||
| import org.apache.iceberg.Schema; | ||
| import org.apache.iceberg.SortField; | ||
| import org.apache.iceberg.SortOrder; | ||
| import org.apache.iceberg.TableProperties; | ||
| import org.apache.iceberg.TableUtil; | ||
| import org.apache.iceberg.Transaction; | ||
| import org.apache.iceberg.catalog.Catalog; | ||
| import org.apache.iceberg.catalog.Namespace; | ||
|
|
@@ -44,6 +48,7 @@ | |
| import org.apache.iceberg.catalog.ViewCatalog; | ||
| import org.apache.iceberg.exceptions.AlreadyExistsException; | ||
| import org.apache.iceberg.exceptions.ValidationException; | ||
| import org.apache.iceberg.expressions.Expressions; | ||
| import org.apache.iceberg.hadoop.HadoopCatalog; | ||
| import org.apache.iceberg.hadoop.HadoopTables; | ||
| import org.apache.iceberg.relocated.com.google.common.base.Preconditions; | ||
|
|
@@ -82,6 +87,7 @@ | |
| import org.apache.spark.sql.connector.catalog.TableChange.ColumnChange; | ||
| import org.apache.spark.sql.connector.catalog.TableChange.RemoveProperty; | ||
| import org.apache.spark.sql.connector.catalog.TableChange.SetProperty; | ||
| import org.apache.spark.sql.connector.catalog.TableInfo; | ||
| import org.apache.spark.sql.connector.catalog.TableSummary; | ||
| import org.apache.spark.sql.connector.catalog.View; | ||
| import org.apache.spark.sql.connector.expressions.Transform; | ||
|
|
@@ -206,11 +212,55 @@ public Table createTable( | |
| Identifier ident, StructType schema, Transform[] transforms, Map<String, String> properties) | ||
| throws TableAlreadyExistsException { | ||
| Schema icebergSchema = SparkSchemaUtil.convert(schema); | ||
| return createTable(ident, icebergSchema, transforms, properties, SortOrder.unsorted()); | ||
| } | ||
|
|
||
| @Override | ||
| public Table createTableLike(Identifier ident, TableInfo tableInfo, Table sourceTable) | ||
| throws TableAlreadyExistsException, NoSuchNamespaceException { | ||
| // Spark intentionally excludes the source table's properties from tableInfo and leaves it to | ||
| // the connector to decide which to clone via sourceTable. Clone the source Iceberg table's | ||
| // schema, properties and sort order, then let user-specified LIKE options (in tableInfo) take | ||
| // precedence. | ||
| Schema icebergSchema; | ||
| Map<String, String> properties = Maps.newHashMap(); | ||
| SortOrder sortOrder = SortOrder.unsorted(); | ||
|
|
||
| if (sourceTable instanceof SparkTable) { | ||
| org.apache.iceberg.Table sourceIcebergTable = ((SparkTable) sourceTable).table(); | ||
| icebergSchema = sourceIcebergTable.schema(); | ||
| properties.putAll(sourceIcebergTable.properties()); | ||
|
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. Please carry the source table's format version into the target properties unless explicitly overridden.
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. Please remove source location-bearing properties such as
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. Please rebuild or omit |
||
| properties.remove(TableProperties.WRITE_METADATA_LOCATION); | ||
| properties.remove(TableProperties.WRITE_DATA_LOCATION); | ||
| properties.remove(TableProperties.OBJECT_STORE_PATH); | ||
| properties.remove(TableProperties.WRITE_FOLDER_STORAGE_LOCATION); | ||
| properties.put( | ||
| TableProperties.FORMAT_VERSION, | ||
| String.valueOf(TableUtil.formatVersion(sourceIcebergTable))); | ||
| sortOrder = | ||
| copySortOrder(sourceIcebergTable.schema(), icebergSchema, sourceIcebergTable.sortOrder()); | ||
| } else { | ||
| icebergSchema = SparkSchemaUtil.convert(tableInfo.schema()); | ||
| } | ||
|
|
||
| properties.putAll(tableInfo.properties()); | ||
|
|
||
| return createTable(ident, icebergSchema, tableInfo.partitions(), properties, sortOrder); | ||
|
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. Please copy the source Iceberg partition spec directly instead of round-tripping through Spark transforms. That conversion drops custom partition-field names such as |
||
| } | ||
|
|
||
| private Table createTable( | ||
| Identifier ident, | ||
| Schema icebergSchema, | ||
| Transform[] transforms, | ||
| Map<String, String> properties, | ||
| SortOrder sortOrder) | ||
| throws TableAlreadyExistsException { | ||
| try { | ||
| Catalog.TableBuilder builder = newBuilder(ident, icebergSchema); | ||
| org.apache.iceberg.Table icebergTable = | ||
| builder | ||
| .withPartitionSpec(Spark3Util.toPartitionSpec(icebergSchema, transforms)) | ||
| .withSortOrder(sortOrder) | ||
| .withLocation(properties.get("location")) | ||
| .withProperties(Spark3Util.rebuildCreateProperties(properties)) | ||
| .create(); | ||
|
|
@@ -220,6 +270,24 @@ public Table createTable( | |
| } | ||
| } | ||
|
|
||
| private static SortOrder copySortOrder( | ||
| Schema sourceSchema, Schema targetSchema, SortOrder sourceSortOrder) { | ||
| if (sourceSortOrder.isUnsorted()) { | ||
| return SortOrder.unsorted(); | ||
| } | ||
|
|
||
| SortOrder.Builder builder = SortOrder.builderFor(targetSchema); | ||
| for (SortField field : sourceSortOrder.fields()) { | ||
| String sourceName = sourceSchema.findColumnName(field.sourceId()); | ||
| builder.sortBy( | ||
| Expressions.transform(sourceName, field.transform()), | ||
| field.direction(), | ||
| field.nullOrder()); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| @Override | ||
| public StagedTable stageCreate( | ||
| Identifier ident, StructType schema, Transform[] transforms, Map<String, String> properties) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,7 @@ | |
| import org.apache.spark.sql.connector.catalog.Table; | ||
| import org.apache.spark.sql.connector.catalog.TableCatalog; | ||
| import org.apache.spark.sql.connector.catalog.TableChange; | ||
| import org.apache.spark.sql.connector.catalog.TableInfo; | ||
| import org.apache.spark.sql.connector.catalog.TableSummary; | ||
| import org.apache.spark.sql.connector.catalog.View; | ||
| import org.apache.spark.sql.connector.catalog.ViewCatalog; | ||
|
|
@@ -252,6 +253,19 @@ public Table createTable( | |
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Table createTableLike(Identifier ident, TableInfo tableInfo, Table sourceTable) | ||
| throws TableAlreadyExistsException, NoSuchNamespaceException { | ||
| checkViewNotExists(ident); | ||
|
|
||
| String provider = tableInfo.properties().get("provider"); | ||
|
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. Please fall back to the source table’s provider when |
||
| if (useIceberg(provider)) { | ||
| return icebergCatalog.createTableLike(ident, tableInfo, sourceTable); | ||
| } else { | ||
| return getSessionCatalog().createTableLike(ident, tableInfo, sourceTable); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public StagedTable stageCreate( | ||
| Identifier ident, StructType schema, Transform[] partitions, Map<String, String> properties) | ||
|
|
||
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.
Please preserve the effective Iceberg schema represented by
SparkTable, rather than always using the current table schema. For atag_*,snapshot_id_*, or timestamp selector,table().schema()can differ from the selected snapshot’s schema.