Skip to content

Commit eae37a7

Browse files
authored
feat: allow AppendOnly, ChangeDataFeed, and TypeWidening in CREATE TABLE (delta-io#2279)
## What changes are proposed in this pull request? Add three simple protocol-only features to the CREATE TABLE allow lists: AppendOnly, ChangeDataFeed, TypeWidening No additional auto-enablement code is needed. `maybe_auto_enable_property_driven_features` (from delta-io#2245) derives enablement from `FeatureInfo::enablement_check`. ## How was this change tested? Parameterized integration tests in kernel/tests/create_table/main.rs: - `test_create_table_with_feature_signal` extended with `is_reader_writer` parameter and cases for all three features (WriterOnly features only check `writer_features()`) - `test_create_table_with_enablement_property` generalized to cover all property-driven features (DV, TypeWidening, CDF, AppendOnly) with true/false cases via `#[values]` Co-authored-by: Sanuj Basu <sanujbasu@users.noreply.github.com>
1 parent 2ee3dde commit eae37a7

2 files changed

Lines changed: 65 additions & 44 deletions

File tree

kernel/src/transaction/builder/create_table.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ use crate::table_features::{
2828
SET_TABLE_FEATURE_SUPPORTED_VALUE,
2929
};
3030
use crate::table_properties::{
31-
TableProperties, CHECKPOINT_WRITE_STATS_AS_JSON, CHECKPOINT_WRITE_STATS_AS_STRUCT,
31+
TableProperties, APPEND_ONLY, CHECKPOINT_WRITE_STATS_AS_JSON, CHECKPOINT_WRITE_STATS_AS_STRUCT,
3232
COLUMN_MAPPING_MAX_COLUMN_ID, COLUMN_MAPPING_MODE, DELTA_PROPERTY_PREFIX,
33-
ENABLE_DELETION_VECTORS, ENABLE_IN_COMMIT_TIMESTAMPS, SET_TRANSACTION_RETENTION_DURATION,
33+
ENABLE_CHANGE_DATA_FEED, ENABLE_DELETION_VECTORS, ENABLE_IN_COMMIT_TIMESTAMPS,
34+
ENABLE_TYPE_WIDENING, SET_TRANSACTION_RETENTION_DURATION,
3435
};
3536
use crate::transaction::create_table::CreateTableTransaction;
3637
use crate::transaction::data_layout::DataLayout;
@@ -56,6 +57,12 @@ const ALLOWED_DELTA_FEATURES: &[TableFeature] = &[
5657
// specifying clustering columns via `with_data_layout()`.
5758
TableFeature::DeletionVectors,
5859
TableFeature::V2Checkpoint,
60+
// Simple protocol-only features: enabling these only updates the protocol action.
61+
// They can also be auto-enabled via their enablement properties (e.g. delta.appendOnly=true)
62+
// through `maybe_auto_enable_property_driven_features`.
63+
TableFeature::AppendOnly,
64+
TableFeature::ChangeDataFeed,
65+
TableFeature::TypeWidening,
5966
];
6067

6168
/// Delta properties allowed to be set during CREATE TABLE.
@@ -71,8 +78,11 @@ const ALLOWED_DELTA_PROPERTIES: &[&str] = &[
7178
// Checkpoint stats format properties
7279
CHECKPOINT_WRITE_STATS_AS_JSON,
7380
CHECKPOINT_WRITE_STATS_AS_STRUCT,
74-
// DeletionVectors enablement property: auto-enables the DeletionVectors feature
81+
// Property-driven feature enablement properties
7582
ENABLE_DELETION_VECTORS,
83+
ENABLE_CHANGE_DATA_FEED,
84+
ENABLE_TYPE_WIDENING,
85+
APPEND_ONLY,
7686
// Set transaction retention duration: controls expiration of txn identifiers
7787
SET_TRANSACTION_RETENTION_DURATION,
7888
];
@@ -719,6 +729,7 @@ mod tests {
719729
use super::*;
720730
use crate::expressions::ColumnName;
721731
use crate::schema::{DataType, StructField, StructType};
732+
use crate::table_properties::ENABLE_ICEBERG_COMPAT_V1;
722733
use crate::utils::test_utils::assert_result_error_with_message;
723734

724735
fn test_schema() -> SchemaRef {
@@ -830,14 +841,12 @@ mod tests {
830841

831842
#[test]
832843
fn test_validate_unsupported_properties() {
833-
use crate::table_properties::{APPEND_ONLY, ENABLE_CHANGE_DATA_FEED};
834-
835844
// Delta properties not on allow list are rejected
836845
let mut properties = HashMap::new();
837-
properties.insert(ENABLE_CHANGE_DATA_FEED.to_string(), "true".to_string());
846+
properties.insert(ENABLE_ICEBERG_COMPAT_V1.to_string(), "true".to_string());
838847
assert_result_error_with_message(
839848
validate_extract_table_features_and_properties(properties),
840-
"Setting delta property 'delta.enableChangeDataFeed' is not supported",
849+
"Setting delta property 'delta.enableIcebergCompatV1' is not supported",
841850
);
842851

843852
// Feature signals for features not in ALLOWED_DELTA_FEATURES are rejected
@@ -863,10 +872,10 @@ mod tests {
863872
// Mixed properties with unsupported delta property are rejected
864873
let mut properties = HashMap::new();
865874
properties.insert("myapp.version".to_string(), "1.0".to_string());
866-
properties.insert(APPEND_ONLY.to_string(), "true".to_string());
875+
properties.insert(ENABLE_ICEBERG_COMPAT_V1.to_string(), "true".to_string());
867876
assert_result_error_with_message(
868877
validate_extract_table_features_and_properties(properties),
869-
"Setting delta property 'delta.appendOnly' is not supported",
878+
"Setting delta property 'delta.enableIcebergCompatV1' is not supported",
870879
);
871880
}
872881

kernel/tests/create_table/main.rs

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -342,14 +342,19 @@ async fn test_create_table_txn_debug() -> DeltaResult<()> {
342342
}
343343

344344
#[rstest]
345-
#[case("vacuumProtocolCheck", TableFeature::VacuumProtocolCheck, true)]
346-
#[case("v2Checkpoint", TableFeature::V2Checkpoint, true)]
347-
// DV uses EnabledIf: the feature signal alone makes it supported but not enabled
348-
// (requires delta.enableDeletionVectors=true property to be enabled)
349-
#[case("deletionVectors", TableFeature::DeletionVectors, false)]
345+
// ReaderWriter features (AlwaysIfSupported)
346+
#[case("vacuumProtocolCheck", TableFeature::VacuumProtocolCheck, true, true)]
347+
#[case("v2Checkpoint", TableFeature::V2Checkpoint, true, true)]
348+
// ReaderWriter features (EnabledIf -- feature signal alone does not enable)
349+
#[case("deletionVectors", TableFeature::DeletionVectors, true, false)]
350+
#[case("typeWidening", TableFeature::TypeWidening, true, false)]
351+
// WriterOnly features (EnabledIf -- feature signal alone does not enable)
352+
#[case("appendOnly", TableFeature::AppendOnly, false, false)]
353+
#[case("changeDataFeed", TableFeature::ChangeDataFeed, false, false)]
350354
fn test_create_table_with_feature_signal(
351355
#[case] feature_name: &str,
352356
#[case] feature: TableFeature,
357+
#[case] is_reader_writer: bool,
353358
#[case] enabled_when_supported: bool,
354359
) -> DeltaResult<()> {
355360
let (_temp_dir, table_path, engine) = test_table_setup()?;
@@ -379,12 +384,14 @@ fn test_create_table_with_feature_signal(
379384
.is_some_and(|f| f.contains(&feature)),
380385
"{feature_name} should be in writer features"
381386
);
382-
assert!(
383-
protocol
384-
.reader_features()
385-
.is_some_and(|f| f.contains(&feature)),
386-
"{feature_name} should be in reader features"
387-
);
387+
if is_reader_writer {
388+
assert!(
389+
protocol
390+
.reader_features()
391+
.is_some_and(|f| f.contains(&feature)),
392+
"{feature_name} should be in reader features"
393+
);
394+
}
388395

389396
Ok(())
390397
}
@@ -419,51 +426,56 @@ fn test_create_table_with_checkpoint_stats_properties(
419426
}
420427

421428
#[rstest]
422-
#[case("true", true)]
423-
#[case("false", false)]
424-
fn test_create_table_with_deletion_vectors_property(
425-
#[case] value: &str,
426-
#[case] expect_enabled: bool,
429+
// ReaderWriter features
430+
#[case("delta.enableDeletionVectors", TableFeature::DeletionVectors, true)]
431+
#[case("delta.enableTypeWidening", TableFeature::TypeWidening, true)]
432+
// WriterOnly features
433+
#[case("delta.enableChangeDataFeed", TableFeature::ChangeDataFeed, false)]
434+
#[case("delta.appendOnly", TableFeature::AppendOnly, false)]
435+
fn test_create_table_with_enablement_property(
436+
#[case] property: &str,
437+
#[case] feature: TableFeature,
438+
#[case] is_reader_writer: bool,
439+
#[values(true, false)] expect_enabled: bool,
427440
) -> DeltaResult<()> {
428441
let (_temp_dir, table_path, engine) = test_table_setup()?;
442+
let value = expect_enabled.to_string();
429443

430444
let _ = create_table(&table_path, simple_schema()?, "Test/1.0")
431-
.with_table_properties([("delta.enableDeletionVectors", value)])
445+
.with_table_properties([(property, value.as_str())])
432446
.build(engine.as_ref(), Box::new(FileSystemCommitter::new()))?
433447
.commit(engine.as_ref())?;
434448

435449
let snapshot = Snapshot::builder_for(&table_path).build(engine.as_ref())?;
436450
let table_config = snapshot.table_configuration();
437451

438452
assert_eq!(
439-
snapshot.table_properties().enable_deletion_vectors,
440-
Some(value.parse::<bool>().unwrap())
441-
);
442-
assert_eq!(
443-
table_config.is_feature_supported(&TableFeature::DeletionVectors),
453+
table_config.is_feature_supported(&feature),
444454
expect_enabled,
445-
"DeletionVectors supported should be {expect_enabled}"
455+
"{property}={value}: feature supported should be {expect_enabled}"
446456
);
447457
assert_eq!(
448-
table_config.is_feature_enabled(&TableFeature::DeletionVectors),
458+
table_config.is_feature_enabled(&feature),
449459
expect_enabled,
450-
"DeletionVectors enabled should be {expect_enabled}"
460+
"{property}={value}: feature enabled should be {expect_enabled}"
451461
);
452462
let protocol = table_config.protocol();
453463
assert_eq!(
454464
protocol
455465
.writer_features()
456-
.is_some_and(|f| f.contains(&TableFeature::DeletionVectors)),
457-
expect_enabled,
458-
"DeletionVectors in writer features should be {expect_enabled}"
459-
);
460-
assert_eq!(
461-
protocol
462-
.reader_features()
463-
.is_some_and(|f| f.contains(&TableFeature::DeletionVectors)),
466+
.is_some_and(|f| f.contains(&feature)),
464467
expect_enabled,
465-
"DeletionVectors in reader features should be {expect_enabled}"
466-
);
468+
"{property}={value}: in writer features should be {expect_enabled}"
469+
);
470+
if is_reader_writer {
471+
assert_eq!(
472+
protocol
473+
.reader_features()
474+
.is_some_and(|f| f.contains(&feature)),
475+
expect_enabled,
476+
"{property}={value}: in reader features should be {expect_enabled}"
477+
);
478+
}
467479

468480
Ok(())
469481
}

0 commit comments

Comments
 (0)