7474import java .util .stream .Stream ;
7575
7676import static org .apache .fluss .lake .paimon .testutils .PaimonTestUtils .adjustToLegacyV1Table ;
77+ import static org .apache .fluss .lake .paimon .utils .PaimonConversions .LAKESTREAM_ENABLED_OPTION_KEY ;
7778import static org .apache .fluss .lake .paimon .utils .PaimonConversions .PAIMON_UNSETTABLE_OPTIONS ;
7879import static org .apache .fluss .metadata .TableDescriptor .BUCKET_COLUMN_NAME ;
7980import static org .apache .fluss .metadata .TableDescriptor .OFFSET_COLUMN_NAME ;
@@ -177,6 +178,7 @@ void testCreateLakeEnabledTable() throws Exception {
177178 new String [] {"log_c1" , "log_c2" }),
178179 "log_c1,log_c2" ,
179180 BUCKET_NUM );
181+ assertThat (paimonLogTable .options ()).containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
180182
181183 TableDescriptor logNoBucketKeyTable =
182184 TableDescriptor .builder ()
@@ -234,6 +236,7 @@ void testCreateLakeEnabledTable() throws Exception {
234236 new String [] {"pk_c1" , "pk_c2" }),
235237 "pk_c1" ,
236238 BUCKET_NUM );
239+ assertThat (paimonPkTable .options ()).containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
237240
238241 // test partitioned table
239242 TablePath partitionedTablePath = TablePath .of (DATABASE , "partitioned_table" );
@@ -708,6 +711,9 @@ void testAlterLakeEnabledLogTable() throws Exception {
708711
709712 Identifier paimonTablePath = Identifier .create (DATABASE , logTablePath .getTableName ());
710713 Table enabledPaimonLogTable = paimonCatalog .getTable (paimonTablePath );
714+ // enabling lake acceleration on a clean table sets lakestream.enabled=true
715+ assertThat (enabledPaimonLogTable .options ())
716+ .containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
711717
712718 Map <String , String > updatedProperties = new HashMap <>();
713719 updatedProperties .put (ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" );
@@ -735,6 +741,9 @@ void testAlterLakeEnabledLogTable() throws Exception {
735741
736742 // verify LogTablet datalake status is disabled
737743 verifyLogTabletDataLakeEnabled (tableId , false );
744+ // disabling lake acceleration removes lakestream.enabled instead of storing false
745+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
746+ .doesNotContainKey (LAKESTREAM_ENABLED_OPTION_KEY );
738747
739748 // try to enable lake table again
740749 enableLake = TableChange .set (ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" );
@@ -743,6 +752,9 @@ void testAlterLakeEnabledLogTable() throws Exception {
743752
744753 // verify LogTablet datalake status is enabled again
745754 verifyLogTabletDataLakeEnabled (tableId , true );
755+ // re-enabling lake acceleration adds lakestream.enabled=true again
756+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
757+ .containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
746758
747759 // write some data to the lake table
748760 writeData (paimonCatalog .getTable (paimonTablePath ));
@@ -765,6 +777,143 @@ void testAlterLakeEnabledLogTable() throws Exception {
765777 verifyLogTabletDataLakeEnabled (tableId , true );
766778 }
767779
780+ @ Test
781+ void testAlterLakeEnabledPrimaryKeyTable () throws Exception {
782+ // create pk table with lake disabled
783+ TableDescriptor pkTable =
784+ TableDescriptor .builder ()
785+ .schema (
786+ Schema .newBuilder ()
787+ .column ("pk_c1" , DataTypes .INT ())
788+ .column ("pk_c2" , DataTypes .STRING ())
789+ .primaryKey ("pk_c1" )
790+ .build ())
791+ .property (ConfigOptions .TABLE_DATALAKE_ENABLED , false )
792+ .distributedBy (BUCKET_NUM )
793+ .build ();
794+ TablePath pkTablePath = TablePath .of (DATABASE , "pk_table_alter" );
795+ admin .createTable (pkTablePath , pkTable , false ).get ();
796+ Identifier paimonTablePath = Identifier .create (DATABASE , pkTablePath .getTableName ());
797+
798+ // lake table not created yet while lake is disabled
799+ assertThatThrownBy (() -> paimonCatalog .getTable (paimonTablePath ))
800+ .isInstanceOf (Catalog .TableNotExistException .class );
801+
802+ // enable lake acceleration sets lakestream.enabled=true
803+ admin .alterTable (
804+ pkTablePath ,
805+ Collections .singletonList (
806+ TableChange .set (
807+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" )),
808+ false )
809+ .get ();
810+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
811+ .containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
812+
813+ // disable lake acceleration removes lakestream.enabled instead of storing false
814+ admin .alterTable (
815+ pkTablePath ,
816+ Collections .singletonList (
817+ TableChange .set (
818+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "false" )),
819+ false )
820+ .get ();
821+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
822+ .doesNotContainKey (LAKESTREAM_ENABLED_OPTION_KEY );
823+
824+ // re-enable lake acceleration adds lakestream.enabled=true again
825+ admin .alterTable (
826+ pkTablePath ,
827+ Collections .singletonList (
828+ TableChange .set (
829+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" )),
830+ false )
831+ .get ();
832+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
833+ .containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
834+
835+ // resetting datalake.enabled is equivalent to disabling acceleration, and removes the
836+ // key from the table descriptor entirely (unlike SetOption "false")
837+ admin .alterTable (
838+ pkTablePath ,
839+ Collections .singletonList (
840+ TableChange .reset (ConfigOptions .TABLE_DATALAKE_ENABLED .key ())),
841+ false )
842+ .get ();
843+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
844+ .doesNotContainKey (LAKESTREAM_ENABLED_OPTION_KEY );
845+
846+ // re-enabling after a reset must still sync lakestream.enabled=true: since the reset
847+ // removed the key from the descriptor, MetadataManager must not rely solely on "the old
848+ // descriptor already had the key" to decide whether to sync to the lake table
849+ admin .alterTable (
850+ pkTablePath ,
851+ Collections .singletonList (
852+ TableChange .set (
853+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" )),
854+ false )
855+ .get ();
856+ assertThat (paimonCatalog .getTable (paimonTablePath ).options ())
857+ .containsEntry (LAKESTREAM_ENABLED_OPTION_KEY , "true" );
858+ }
859+
860+ @ Test
861+ void testLegacyTableLakeStreamOptionUntouched () throws Exception {
862+ // create a clean, lake-enabled table, then turn it into a legacy table carrying the three
863+ // system columns. Old-layout tables are outside the scope of lakestream.enabled: altering
864+ // datalake.enabled must not add or remove the option on them.
865+ TablePath tablePath = TablePath .of (DATABASE , "legacy_lakestream_table" );
866+ TableDescriptor tableDescriptor =
867+ TableDescriptor .builder ()
868+ .schema (
869+ Schema .newBuilder ()
870+ .column ("c1" , DataTypes .INT ())
871+ .column ("c2" , DataTypes .STRING ())
872+ .build ())
873+ .property (ConfigOptions .TABLE_DATALAKE_ENABLED , true )
874+ .distributedBy (BUCKET_NUM , "c1" )
875+ .build ();
876+ admin .createTable (tablePath , tableDescriptor , false ).get ();
877+ Identifier paimonTablePath = Identifier .create (DATABASE , tablePath .getTableName ());
878+
879+ adjustToLegacyV1Table (tablePath , paimonCatalog );
880+ String lakeStreamValueBeforeAlter =
881+ paimonCatalog
882+ .getTable (paimonTablePath )
883+ .options ()
884+ .get (LAKESTREAM_ENABLED_OPTION_KEY );
885+
886+ // disable lake acceleration on a legacy table leaves the option untouched
887+ admin .alterTable (
888+ tablePath ,
889+ Collections .singletonList (
890+ TableChange .set (
891+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "false" )),
892+ false )
893+ .get ();
894+ assertThat (
895+ paimonCatalog
896+ .getTable (paimonTablePath )
897+ .options ()
898+ .get (LAKESTREAM_ENABLED_OPTION_KEY ))
899+ .isEqualTo (lakeStreamValueBeforeAlter );
900+
901+ // re-enable lake acceleration on a legacy table also leaves the option untouched
902+ admin .alterTable (
903+ tablePath ,
904+ Collections .singletonList (
905+ TableChange .set (
906+ ConfigOptions .TABLE_DATALAKE_ENABLED .key (), "true" )),
907+ false )
908+ .get ();
909+ assertThat (
910+ paimonCatalog
911+ .getTable (paimonTablePath )
912+ .options ()
913+ .get (LAKESTREAM_ENABLED_OPTION_KEY ))
914+ .isEqualTo (lakeStreamValueBeforeAlter );
915+ }
916+
768917 @ Test
769918 void testThrowExceptionWhenConflictWithSystemColumn () {
770919 for (String systemColumn :
0 commit comments