@@ -420,7 +420,8 @@ public ListTablesResponse listTables(Namespace namespace) {
420420 */
421421 public LoadTableResponse createTableDirect (Namespace namespace , CreateTableRequest request ) {
422422 return createTableDirect (
423- namespace , request , EnumSet .noneOf (AccessDelegationMode .class ), Optional .empty ());
423+ namespace , request , EnumSet .noneOf (AccessDelegationMode .class ), Optional .empty ())
424+ .response ();
424425 }
425426
426427 /**
@@ -435,7 +436,8 @@ public LoadTableResponse createTableDirectWithWriteDelegation(
435436 CreateTableRequest request ,
436437 Optional <String > refreshCredentialsEndpoint ) {
437438 return createTableDirect (
438- namespace , request , EnumSet .of (VENDED_CREDENTIALS ), refreshCredentialsEndpoint );
439+ namespace , request , EnumSet .of (VENDED_CREDENTIALS ), refreshCredentialsEndpoint )
440+ .response ();
439441 }
440442
441443 public void authorizeCreateTableDirect (
@@ -456,7 +458,7 @@ public void authorizeCreateTableDirect(
456458 }
457459 }
458460
459- public LoadTableResponse createTableDirect (
461+ public ETaggedLoadTableResponse createTableDirect (
460462 Namespace namespace ,
461463 CreateTableRequest request ,
462464 EnumSet <AccessDelegationMode > delegationModes ,
@@ -488,16 +490,18 @@ public LoadTableResponse createTableDirect(
488490
489491 if (table instanceof BaseTable baseTable ) {
490492 TableMetadata tableMetadata = baseTable .operations ().current ();
491- return buildLoadTableResponseWithDelegationCredentials (
492- tableIdentifier ,
493- tableMetadata ,
494- resolvedMode ,
495- Set .of (
496- PolarisStorageActions .READ ,
497- PolarisStorageActions .WRITE ,
498- PolarisStorageActions .LIST ),
499- refreshCredentialsEndpoint )
500- .build ();
493+ return withETag (
494+ buildLoadTableResponseWithDelegationCredentials (
495+ tableIdentifier ,
496+ tableMetadata ,
497+ resolvedMode ,
498+ Set .of (
499+ PolarisStorageActions .READ ,
500+ PolarisStorageActions .WRITE ,
501+ PolarisStorageActions .LIST ),
502+ refreshCredentialsEndpoint )
503+ .build (),
504+ tableIdentifier );
501505 } else if (table instanceof BaseMetadataTable ) {
502506 // metadata tables are loaded on the client side, return NoSuchTableException for now
503507 throw notFoundExceptionForTableLikeEntity (
@@ -610,12 +614,13 @@ public LoadTableResponse createTableStaged(
610614 * @param request the register table request
611615 * @return ETagged {@link LoadTableResponse} to uniquely identify the table metadata
612616 */
613- public LoadTableResponse registerTable (Namespace namespace , RegisterTableRequest request ) {
617+ public ETaggedLoadTableResponse registerTable (Namespace namespace , RegisterTableRequest request ) {
614618 PolarisAuthorizableOperation op = PolarisAuthorizableOperation .REGISTER_TABLE ;
615- authorizeCreateTableLikeUnderNamespaceOperationOrThrow (
616- op , TableIdentifier . of ( namespace , request . name ()) );
619+ TableIdentifier tableIdentifier = TableIdentifier . of ( namespace , request . name ());
620+ authorizeCreateTableLikeUnderNamespaceOperationOrThrow ( op , tableIdentifier );
617621
618- return catalogHandlerUtils ().registerTable (baseCatalog , namespace , request );
622+ return withETag (
623+ catalogHandlerUtils ().registerTable (baseCatalog , namespace , request ), tableIdentifier );
619624 }
620625
621626 public boolean sendNotification (TableIdentifier identifier , NotificationRequest request ) {
@@ -708,11 +713,12 @@ public LoadTableResponse loadTable(TableIdentifier tableIdentifier, String snaps
708713 public Optional <LoadTableResponse > loadTableIfStale (
709714 TableIdentifier tableIdentifier , IfNoneMatch ifNoneMatch , String snapshots ) {
710715 return loadTable (
711- tableIdentifier ,
712- snapshots ,
713- ifNoneMatch ,
714- EnumSet .noneOf (AccessDelegationMode .class ),
715- Optional .empty ());
716+ tableIdentifier ,
717+ snapshots ,
718+ ifNoneMatch ,
719+ EnumSet .noneOf (AccessDelegationMode .class ),
720+ Optional .empty ())
721+ .map (ETaggedLoadTableResponse ::response );
716722 }
717723
718724 public LoadTableResponse loadTableWithAccessDelegation (
@@ -740,11 +746,12 @@ public Optional<LoadTableResponse> loadTableWithAccessDelegationIfStale(
740746 String snapshots ,
741747 Optional <String > refreshCredentialsEndpoint ) {
742748 return loadTable (
743- tableIdentifier ,
744- snapshots ,
745- ifNoneMatch ,
746- EnumSet .of (VENDED_CREDENTIALS ),
747- refreshCredentialsEndpoint );
749+ tableIdentifier ,
750+ snapshots ,
751+ ifNoneMatch ,
752+ EnumSet .of (VENDED_CREDENTIALS ),
753+ refreshCredentialsEndpoint )
754+ .map (ETaggedLoadTableResponse ::response );
748755 }
749756
750757 /**
@@ -852,7 +859,7 @@ private Set<PolarisStorageActions> authorizeLoadTable(
852859 return actionsRequested ;
853860 }
854861
855- public Optional <LoadTableResponse > loadTable (
862+ public Optional <ETaggedLoadTableResponse > loadTable (
856863 TableIdentifier tableIdentifier ,
857864 String snapshots ,
858865 IfNoneMatch ifNoneMatch ,
@@ -897,7 +904,7 @@ public Optional<LoadTableResponse> loadTable(
897904 actionsRequested ,
898905 refreshCredentialsEndpoint )
899906 .build ();
900- return Optional .of (filterResponseToSnapshots (response , snapshots ));
907+ return Optional .of (withETag ( filterResponseToSnapshots (response , snapshots ), tableIdentifier ));
901908 } else if (table instanceof BaseMetadataTable ) {
902909 // metadata tables are loaded on the client side, return NoSuchTableException for now
903910 throw notFoundExceptionForTableLikeEntity (
@@ -907,6 +914,27 @@ public Optional<LoadTableResponse> loadTable(
907914 throw new IllegalStateException ("Cannot wrap catalog that does not produce BaseTable" );
908915 }
909916
917+ /**
918+ * Pair a {@link LoadTableResponse} with an entity tag derived from its metadata location. When
919+ * the response has no metadata location (e.g. staged create or external catalogs that don't
920+ * expose one) the etag is omitted and a warning is logged.
921+ */
922+ private ETaggedLoadTableResponse withETag (
923+ LoadTableResponse response , TableIdentifier tableIdentifier ) {
924+ if (response .metadataLocation () != null ) {
925+ return new ETaggedLoadTableResponse (
926+ response ,
927+ Optional .of (
928+ IcebergHttpUtil .generateETagForMetadataFileLocation (response .metadataLocation ())));
929+ }
930+ LOGGER
931+ .atWarn ()
932+ .addKeyValue ("namespace" , tableIdentifier .namespace ())
933+ .addKeyValue ("tableName" , tableIdentifier .name ())
934+ .log ("Response has null metadataLocation; omitting etag" );
935+ return new ETaggedLoadTableResponse (response , Optional .empty ());
936+ }
937+
910938 private LoadTableResponse .Builder buildLoadTableResponseWithDelegationCredentials (
911939 TableIdentifier tableIdentifier ,
912940 TableMetadata tableMetadata ,
0 commit comments