Skip to content

Commit 97dc739

Browse files
authored
[Kernel] Handle the preview and graduated table features when updating protocol through metadata (#4539)
## Description Example preview and graduated features are: * (typeWidening-preview -> typeWidening) * (variantType-preview -> variant) Currently we enable both the preview and non-preview feature names if the feature enable criteria is true. For example if the delta.enableTypeWidening is set to true, the table will have both typeWidening-preview and typeWidening Expected behavior: * if the table has no typeWidening or typeWidening-preview * enable only the typeWidening feature * if the table has typeWidening-preview * keep it as is and don't add typeWidening * If the table has typeWidening feature * keep it as is Resolves #4532 ## How was this patch tested? Unit and integration tests. Integration tests only for typeWiderning as Kernel doesn't yet support writes with variant.
1 parent 60c8893 commit 97dc739

File tree

5 files changed

+144
-40
lines changed

5 files changed

+144
-40
lines changed

kernel/kernel-api/src/main/java/io/delta/kernel/internal/icebergcompat/IcebergWriterCompatV1MetadataValidatorAndUpdater.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public static Optional<Metadata> validateAndUpdateIcebergWriterCompatV1Metadata(
171171
CLUSTERING_W_FEATURE,
172172
TIMESTAMP_NTZ_RW_FEATURE,
173173
TYPE_WIDENING_RW_FEATURE,
174-
TYPE_WIDENING_PREVIEW_TABLE_FEATURE)
174+
TYPE_WIDENING_RW_PREVIEW_FEATURE)
175175
.collect(toSet());
176176

177177
/** Checks that all features supported in the protocol are in {@link #ALLOWED_TABLE_FEATURES} */

kernel/kernel-api/src/main/java/io/delta/kernel/internal/tablefeatures/TableFeatures.java

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -183,28 +183,46 @@ public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata me
183183
}
184184
}
185185

186-
public static final TableFeature VARIANT_RW_FEATURE = new VariantTypeTableFeature("variantType");
187-
public static final TableFeature VARIANT_RW_PREVIEW_FEATURE =
188-
new VariantTypeTableFeature("variantType-preview");
189-
190-
private static class VariantTypeTableFeature extends TableFeature.ReaderWriterFeature
191-
implements FeatureAutoEnabledByMetadata {
192-
VariantTypeTableFeature(String featureName) {
193-
super(
194-
/* featureName = */ featureName, /* minReaderVersion = */ 3, /* minWriterVersion = */ 7);
186+
/* ---- Start: variantType ---- */
187+
// Base class for variantType and variantType-preview features. Both features are same in terms
188+
// of behavior and given the feature is graduated, we will enable the `variantType` by default
189+
// if the metadata requirements are satisfied and the table doesn't already contain the
190+
// `variantType-preview` feature. Also to note, with this version of Kernel, one can't
191+
// auto upgrade to `variantType-preview` with metadata requirements. It can only be set
192+
// manually using `delta.feature.variantType-preview=supported` property.
193+
private static class VariantTypeTableFeatureBase extends TableFeature.ReaderWriterFeature {
194+
VariantTypeTableFeatureBase(String featureName) {
195+
super(featureName, /* minReaderVersion = */ 3, /* minWriterVersion = */ 7);
195196
}
196197

197198
@Override
198-
public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata metadata) {
199-
return hasTypeColumn(metadata.getSchema(), VARIANT);
199+
public boolean hasKernelWriteSupport(Metadata metadata) {
200+
return false; // TODO: yet to be implemented in Kernel
201+
}
202+
}
203+
204+
private static class VariantTypeTableFeature extends VariantTypeTableFeatureBase
205+
implements FeatureAutoEnabledByMetadata {
206+
VariantTypeTableFeature() {
207+
super("variantType");
200208
}
201209

202210
@Override
203-
public boolean hasKernelWriteSupport(Metadata metadata) {
204-
return false; // TODO: yet to be implemented in Kernel
211+
public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata metadata) {
212+
return hasTypeColumn(metadata.getSchema(), VARIANT)
213+
&&
214+
// Don't automatically enable the stable feature if the preview feature is
215+
// already supported, to avoid possibly breaking old clients that only
216+
// support the preview feature.
217+
!protocol.supportsFeature(VARIANT_RW_PREVIEW_FEATURE);
205218
}
206219
}
207220

221+
public static final TableFeature VARIANT_RW_FEATURE = new VariantTypeTableFeature();
222+
public static final TableFeature VARIANT_RW_PREVIEW_FEATURE =
223+
new VariantTypeTableFeatureBase("variantType-preview");
224+
/* ---- End: variantType ---- */
225+
208226
public static final TableFeature DOMAIN_METADATA_W_FEATURE = new DomainMetadataFeature();
209227

210228
private static class DomainMetadataFeature extends TableFeature.WriterFeature {
@@ -286,28 +304,42 @@ public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata me
286304
}
287305
}
288306

289-
public static final TableFeature TYPE_WIDENING_RW_FEATURE =
290-
new TypeWideningTableFeature("typeWidening");
291-
public static final TableFeature TYPE_WIDENING_PREVIEW_TABLE_FEATURE =
292-
new TypeWideningTableFeature("typeWidening-preview");
293-
294-
private static class TypeWideningTableFeature extends TableFeature.ReaderWriterFeature
295-
implements FeatureAutoEnabledByMetadata {
296-
TypeWideningTableFeature(String featureName) {
307+
/* ---- Start: type widening ---- */
308+
// Base class for typeWidening and typeWidening-preview features. Both features are same in terms
309+
// of behavior and given the feature is graduated, we will enable the `typeWidening` by default
310+
// if the metadata requirements are satisfied and the table doesn't already contain the
311+
// `typeWidening-preview` feature. Also to note, with this version of Kernel, one can't
312+
// auto upgrade to `typeWidening-preview` with metadata requirements. It can only be set
313+
// manually using `delta.feature.typeWidening-preview=supported` property.
314+
private static class TypeWideningTableFeatureBase extends TableFeature.ReaderWriterFeature {
315+
TypeWideningTableFeatureBase(String featureName) {
297316
super(featureName, /* minReaderVersion = */ 3, /* minWriterVersion = */ 7);
298317
}
318+
}
299319

300-
@Override
301-
public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata metadata) {
302-
return TableConfig.TYPE_WIDENING_ENABLED.fromMetadata(metadata);
320+
private static class TypeWideningTableFeature extends TypeWideningTableFeatureBase
321+
implements FeatureAutoEnabledByMetadata {
322+
TypeWideningTableFeature() {
323+
super("typeWidening");
303324
}
304325

305326
@Override
306-
public boolean hasKernelWriteSupport(Metadata metadata) {
307-
return true;
327+
public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata metadata) {
328+
return TableConfig.TYPE_WIDENING_ENABLED.fromMetadata(metadata)
329+
&&
330+
// Don't automatically enable the stable feature if the preview feature is already
331+
// supported, to
332+
// avoid possibly breaking old clients that only support the preview feature.
333+
!protocol.supportsFeature(TYPE_WIDENING_RW_PREVIEW_FEATURE);
308334
}
309335
}
310336

337+
public static final TableFeature TYPE_WIDENING_RW_FEATURE = new TypeWideningTableFeature();
338+
339+
public static final TableFeature TYPE_WIDENING_RW_PREVIEW_FEATURE =
340+
new TypeWideningTableFeatureBase("typeWidening-preview");
341+
/* ---- End: type widening ---- */
342+
311343
public static final TableFeature IN_COMMIT_TIMESTAMP_W_FEATURE =
312344
new InCommitTimestampTableFeature();
313345

@@ -414,7 +446,7 @@ public boolean metadataRequiresFeatureToBeEnabled(Protocol protocol, Metadata me
414446
INVARIANTS_W_FEATURE,
415447
ROW_TRACKING_W_FEATURE,
416448
TIMESTAMP_NTZ_RW_FEATURE,
417-
TYPE_WIDENING_PREVIEW_TABLE_FEATURE,
449+
TYPE_WIDENING_RW_PREVIEW_FEATURE,
418450
TYPE_WIDENING_RW_FEATURE,
419451
VACUUM_PROTOCOL_CHECK_RW_FEATURE,
420452
VARIANT_RW_FEATURE,

kernel/kernel-api/src/test/scala/io/delta/kernel/internal/icebergcompat/IcebergCompatV2MetadataValidatorAndUpdaterSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import io.delta.kernel.exceptions.KernelException
2121
import io.delta.kernel.internal.actions.{Metadata, Protocol}
2222
import io.delta.kernel.internal.icebergcompat.IcebergCompatV2MetadataValidatorAndUpdater.validateAndUpdateIcebergCompatV2Metadata
2323
import io.delta.kernel.internal.tablefeatures.TableFeature
24-
import io.delta.kernel.internal.tablefeatures.TableFeatures.{COLUMN_MAPPING_RW_FEATURE, DELETION_VECTORS_RW_FEATURE, ICEBERG_COMPAT_V2_W_FEATURE, TYPE_WIDENING_PREVIEW_TABLE_FEATURE, TYPE_WIDENING_RW_FEATURE}
24+
import io.delta.kernel.internal.tablefeatures.TableFeatures.{COLUMN_MAPPING_RW_FEATURE, DELETION_VECTORS_RW_FEATURE, ICEBERG_COMPAT_V2_W_FEATURE, TYPE_WIDENING_RW_FEATURE, TYPE_WIDENING_RW_PREVIEW_FEATURE}
2525
import io.delta.kernel.internal.util.ColumnMappingSuiteBase
2626
import io.delta.kernel.test.VectorTestUtils
2727
import io.delta.kernel.types._

kernel/kernel-api/src/test/scala/io/delta/kernel/internal/tablefeatures/TableFeaturesSuite.scala

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package io.delta.kernel.internal.tablefeatures
1717

18-
import java.util
1918
import java.util.{Collections, Optional}
2019
import java.util.Collections.{emptySet, singleton}
2120
import java.util.stream.Collectors.toList
@@ -25,7 +24,7 @@ import scala.collection.JavaConverters._
2524
import io.delta.kernel.data.{ArrayValue, ColumnVector, MapValue}
2625
import io.delta.kernel.exceptions.KernelException
2726
import io.delta.kernel.internal.actions.{Format, Metadata, Protocol}
28-
import io.delta.kernel.internal.tablefeatures.TableFeatures.{validateKernelCanReadTheTable, validateKernelCanWriteToTable, CLUSTERING_W_FEATURE, DOMAIN_METADATA_W_FEATURE, TABLE_FEATURES}
27+
import io.delta.kernel.internal.tablefeatures.TableFeatures.{validateKernelCanReadTheTable, validateKernelCanWriteToTable, CLUSTERING_W_FEATURE, DOMAIN_METADATA_W_FEATURE, TABLE_FEATURES, TYPE_WIDENING_RW_FEATURE}
2928
import io.delta.kernel.internal.util.InternalUtils.singletonStringColumnVector
3029
import io.delta.kernel.internal.util.VectorUtils.buildColumnVector
3130
import io.delta.kernel.types._
@@ -119,16 +118,10 @@ class TableFeaturesSuite extends AnyFunSuite {
119118
("identityColumns", testMetadata(includeIdentityColumn = false), false),
120119
("columnMapping", testMetadata(tblProps = Map("delta.columnMapping.mode" -> "id")), true),
121120
("columnMapping", testMetadata(tblProps = Map("delta.columnMapping.mode" -> "none")), false),
122-
(
123-
"typeWidening-preview",
124-
testMetadata(tblProps = Map("delta.enableTypeWidening" -> "true")),
125-
true),
126-
(
127-
"typeWidening-preview",
128-
testMetadata(tblProps = Map("delta.enableTypeWidening" -> "false")),
129-
false),
130121
("typeWidening", testMetadata(tblProps = Map("delta.enableTypeWidening" -> "true")), true),
131122
("typeWidening", testMetadata(tblProps = Map("delta.enableTypeWidening" -> "false")), false),
123+
("variantType", testMetadata(includeVariantTypeCol = true), true),
124+
("variantType", testMetadata(includeVariantTypeCol = false), false),
132125
// Disable this until we have support to enable row tracking through metadata
133126
// ("rowTracking", testMetadata(tblProps = Map("delta.enableRowTracking" -> "true")), true),
134127
("rowTracking", testMetadata(tblProps = Map("delta.enableRowTracking" -> "false")), false),
@@ -183,7 +176,24 @@ class TableFeaturesSuite extends AnyFunSuite {
183176
}
184177
}
185178

186-
test("row tracking enable throguh metadata property is not supported") {
179+
Seq(
180+
("variantType", testMetadata(includeVariantTypeCol = true)),
181+
("typeWidening", testMetadata(tblProps = Map("delta.enableTypeWidening" -> "true")))).foreach {
182+
case (feature, metadataEnablingFeature) =>
183+
test("special handling of tables containing preview features: " + feature) {
184+
val protocolWithPreviewFeature = new Protocol(3, 7)
185+
.withFeature(TableFeatures.getTableFeature(s"$feature-preview"))
186+
187+
val enable = TableFeatures.getTableFeature(feature)
188+
.asInstanceOf[FeatureAutoEnabledByMetadata]
189+
.metadataRequiresFeatureToBeEnabled(
190+
protocolWithPreviewFeature,
191+
metadataEnablingFeature)
192+
assert(!enable, "shouldn't enable non-preview feature")
193+
}
194+
}
195+
196+
test("row tracking enable through metadata property is not supported") {
187197
val tableFeature = TableFeatures.getTableFeature("rowTracking")
188198
val ex = intercept[UnsupportedOperationException] {
189199
tableFeature.asInstanceOf[FeatureAutoEnabledByMetadata]

kernel/kernel-defaults/src/test/scala/io/delta/kernel/defaults/DeltaTableFeaturesSuite.scala

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,68 @@ class DeltaTableFeaturesSuite extends DeltaTableWriteSuiteBase {
317317
}
318318
}
319319

320+
/* ---- Start: type widening tests ---- */
321+
test("only typeWidening feature is enabled when metadata supports it: new table") {
322+
withTempDirAndEngine { (tablePath, engine) =>
323+
createEmptyTable(
324+
engine,
325+
tablePath = tablePath,
326+
schema = testSchema,
327+
tableProperties = Map("delta.enableTypeWidening" -> "true"))
328+
329+
val protocolV0 = getProtocol(engine, tablePath)
330+
assert(!protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_PREVIEW_FEATURE))
331+
assert(protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_FEATURE))
332+
333+
// try enabling type widening again and expect no change in protocol
334+
updateTableMetadata(
335+
engine = engine,
336+
tablePath = tablePath,
337+
tableProperties = Map("delta.enableTypeWidening" -> "true"))
338+
val protocolV1 = getProtocol(engine, tablePath)
339+
assert(protocolV1 === protocolV0)
340+
}
341+
}
342+
343+
test("only typeWidening feature is enabled when new metadata supports it: existing table") {
344+
withTempDirAndEngine { (tablePath, engine) =>
345+
createEmptyTable(engine, tablePath = tablePath, schema = testSchema)
346+
val protocolV0 = getProtocol(engine, tablePath)
347+
assert(!protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_PREVIEW_FEATURE))
348+
assert(!protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_FEATURE))
349+
350+
// try enabling type widening and expect change in protocol
351+
updateTableMetadata(
352+
engine = engine,
353+
tablePath = tablePath,
354+
tableProperties = Map("delta.enableTypeWidening" -> "true"))
355+
val protocolV1 = getProtocol(engine, tablePath)
356+
assert(!protocolV1.supportsFeature(TableFeatures.TYPE_WIDENING_RW_PREVIEW_FEATURE))
357+
assert(protocolV1.supportsFeature(TableFeatures.TYPE_WIDENING_RW_FEATURE))
358+
}
359+
}
360+
361+
test("typeWidening-preview in existing table is respected") {
362+
withTempDirAndEngine { (tablePath, engine) =>
363+
spark.sql(s"CREATE TABLE delta.`$tablePath`(id INT) USING delta " +
364+
s"TBLPROPERTIES ('delta.feature.typeWidening-preview' = 'supported')")
365+
366+
val protocolV0 = getProtocol(engine, tablePath)
367+
require(protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_PREVIEW_FEATURE))
368+
require(!protocolV0.supportsFeature(TableFeatures.TYPE_WIDENING_RW_FEATURE))
369+
370+
// now through Kernel type enabling the type widening through table property
371+
updateTableMetadata(
372+
engine = engine,
373+
tablePath = tablePath,
374+
tableProperties = Map("delta.enableTypeWidening" -> "true"))
375+
val protocolV1 = getProtocol(engine, tablePath)
376+
assert(protocolV1.supportsFeature(TableFeatures.TYPE_WIDENING_RW_PREVIEW_FEATURE))
377+
assert(!protocolV1.supportsFeature(TableFeatures.TYPE_WIDENING_RW_FEATURE))
378+
}
379+
}
380+
/* ---- End: type widening tests ---- */
381+
320382
///////////////////////////////////////////////////////////////////////////
321383
// Helper methods
322384
///////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)