@@ -526,7 +526,11 @@ private void authorizeGrantOnTableLikeOperationOrThrow(
526526 PolarisResolvedPathWrapper tableLikeWrapper =
527527 resolutionManifest .getResolvedPath (
528528 identifier , PolarisEntityType .TABLE_LIKE , PolarisEntitySubType .ANY_SUBTYPE , true );
529- if (!resolutionManifest .getIsPassthroughFacade ()
529+ boolean rbacForFederatedCatalogsEnabled =
530+ getCurrentPolarisContext ()
531+ .getRealmConfig ()
532+ .getConfig (FeatureConfiguration .ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS );
533+ if (!(resolutionManifest .getIsPassthroughFacade () && rbacForFederatedCatalogsEnabled )
530534 && !subTypes .contains (tableLikeWrapper .getRawLeafEntity ().getSubType ())) {
531535 CatalogHandler .throwNotFoundExceptionForTableLikeEntity (identifier , subTypes );
532536 }
@@ -1697,7 +1701,11 @@ public boolean grantPrivilegeOnNamespaceToRole(
16971701 PolarisResolvedPathWrapper resolvedPathWrapper = resolutionManifest .getResolvedPath (namespace );
16981702 if (resolvedPathWrapper == null
16991703 || !resolvedPathWrapper .isFullyResolvedNamespace (catalogName , namespace )) {
1700- if (resolutionManifest .getIsPassthroughFacade ()) {
1704+ boolean rbacForFederatedCatalogsEnabled =
1705+ getCurrentPolarisContext ()
1706+ .getRealmConfig ()
1707+ .getConfig (FeatureConfiguration .ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS );
1708+ if (resolutionManifest .getIsPassthroughFacade () && rbacForFederatedCatalogsEnabled ) {
17011709 resolvedPathWrapper =
17021710 createSyntheticNamespaceEntities (catalogEntity , namespace , resolvedPathWrapper );
17031711 if (resolvedPathWrapper == null
@@ -1773,26 +1781,25 @@ private PolarisResolvedPathWrapper createSyntheticNamespaceEntities(
17731781 PolarisEntity currentParent = existingPath .getRawLeafEntity ();
17741782
17751783 String [] allNamespaceLevels = namespace .levels ();
1776- int matchingLevel = - 1 ;
1784+ int numMatchingLevels = 0 ;
17771785 for (PolarisEntity entity : completePath .subList (1 , completePath .size ())) {
1778- if (entity .getName ().equals (allNamespaceLevels [matchingLevel + 1 ])) {
1779- matchingLevel ++;
1780- } else {
1786+ if (!entity .getName ().equals (allNamespaceLevels [numMatchingLevels ])) {
17811787 break ;
17821788 }
1789+ numMatchingLevels ++;
17831790 }
17841791
1785- for (int i = matchingLevel + 1 ; i < allNamespaceLevels .length ; i ++) {
1786- String namespacePart = allNamespaceLevels [i ];
1792+ for (int i = numMatchingLevels ; i < allNamespaceLevels .length ; i ++) {
1793+ String [] namespacePart = Arrays .copyOfRange (allNamespaceLevels , 0 , i + 1 );
1794+ String leafNamespace = namespacePart [namespacePart .length - 1 ];
1795+ Namespace currentNamespace = Namespace .of (namespacePart );
17871796
17881797 // TODO: Instead of creating synthetic entitties, rely on external catalog mediated backfill.
17891798 PolarisEntity syntheticNamespace =
1790- new PolarisEntity .Builder ()
1799+ new NamespaceEntity .Builder (currentNamespace )
17911800 .setId (metaStoreManager .generateNewEntityId (getCurrentPolarisContext ()).getId ())
17921801 .setCatalogId (catalogEntity .getId ())
17931802 .setParentId (currentParent .getId ())
1794- .setType (PolarisEntityType .NAMESPACE )
1795- .setName (namespacePart )
17961803 .setCreateTimestamp (System .currentTimeMillis ())
17971804 .build ();
17981805
@@ -1810,12 +1817,12 @@ private PolarisResolvedPathWrapper createSyntheticNamespaceEntities(
18101817 resolutionManifest .getResolvedPath (partialNamespace );
18111818 PolarisEntity partialLeafEntity = partialPath .getRawLeafEntity ();
18121819 if (partialLeafEntity == null
1813- || !(partialLeafEntity .getName ().equals (namespacePart )
1820+ || !(partialLeafEntity .getName ().equals (leafNamespace )
18141821 && partialLeafEntity .getType () == PolarisEntityType .NAMESPACE )) {
18151822 throw new RuntimeException (
18161823 String .format (
18171824 "Failed to create or find namespace entity '%s' in federated catalog '%s'" ,
1818- namespacePart , catalogEntity .getName ()));
1825+ leafNamespace , catalogEntity .getName ()));
18191826 }
18201827 syntheticNamespace = partialLeafEntity ;
18211828 }
@@ -2115,7 +2122,11 @@ private boolean grantPrivilegeOnTableLikeToRole(
21152122 identifier , PolarisEntityType .TABLE_LIKE , PolarisEntitySubType .ANY_SUBTYPE );
21162123 if (resolvedPathWrapper == null
21172124 || !subTypes .contains (resolvedPathWrapper .getRawLeafEntity ().getSubType ())) {
2118- if (resolutionManifest .getIsPassthroughFacade ()) {
2125+ boolean rbacForFederatedCatalogsEnabled =
2126+ getCurrentPolarisContext ()
2127+ .getRealmConfig ()
2128+ .getConfig (FeatureConfiguration .ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS );
2129+ if (resolutionManifest .getIsPassthroughFacade () && rbacForFederatedCatalogsEnabled ) {
21192130 resolvedPathWrapper =
21202131 createSyntheticTableLikeEntities (
21212132 catalogEntity , identifier , subTypes , resolvedPathWrapper );
@@ -2175,17 +2186,20 @@ private PolarisResolvedPathWrapper createSyntheticTableLikeEntities(
21752186 namespace .toString (), catalogEntity .getName ()));
21762187 }
21772188
2178- // TODO: Instead of creating a synthetic table-like entity, rely on external catalog mediated
2179- // backfill.
21802189 PolarisEntity parentNamespaceEntity = resolvedNamespacePathWrapper .getRawLeafEntity ();
2190+
2191+ // TODO: Once we support GENERIC_TABLE federation, select the intended type depending on the
2192+ // callsite; if it is instantiated via an Iceberg RESTCatalog factory or a different factory
2193+ // for GenericCatalogs.
2194+ PolarisEntitySubType syntheticEntitySubType = selectEntitySubType (subTypes );
2195+
2196+ // TODO: Instead of creating a synthetic table-like entity, rely on external catalog mediated
2197+ // backfill and use the metadata location from the external catalog.
21812198 PolarisEntity syntheticTableEntity =
2182- new PolarisEntity .Builder ()
2199+ new IcebergTableLikeEntity .Builder (identifier , "" )
21832200 .setId (metaStoreManager .generateNewEntityId (getCurrentPolarisContext ()).getId ())
21842201 .setCatalogId (parentNamespaceEntity .getCatalogId ())
2185- .setParentId (parentNamespaceEntity .getId ())
2186- .setType (PolarisEntityType .TABLE_LIKE )
2187- .setSubType (subTypes .get (0 ))
2188- .setName (identifier .name ())
2202+ .setSubType (syntheticEntitySubType )
21892203 .setCreateTimestamp (System .currentTimeMillis ())
21902204 .build ();
21912205
@@ -2306,4 +2320,25 @@ private boolean revokePrivilegeOnPolicyEntityFromRole(
23062320 privilege )
23072321 .isSuccess ();
23082322 }
2323+
2324+ /**
2325+ * Selects the appropriate entity subtype for synthetic entities in external catalogs.
2326+ *
2327+ * @param subTypes list of candidate subtypes
2328+ * @return the selected subtype for the synthetic entity
2329+ * @throws IllegalStateException if no supported subtype is found
2330+ */
2331+ private static PolarisEntitySubType selectEntitySubType (List <PolarisEntitySubType > subTypes ) {
2332+ if (subTypes .contains (PolarisEntitySubType .ICEBERG_TABLE )) {
2333+ return PolarisEntitySubType .ICEBERG_TABLE ;
2334+ } else if (subTypes .contains (PolarisEntitySubType .ICEBERG_VIEW )) {
2335+ return PolarisEntitySubType .ICEBERG_VIEW ;
2336+ } else {
2337+ throw new IllegalStateException (
2338+ String .format (
2339+ "No supported subtype found in %s. Only ICEBERG_TABLE and ICEBERG_VIEW are"
2340+ + " supported for synthetic entities in external catalogs." ,
2341+ subTypes ));
2342+ }
2343+ }
23092344}
0 commit comments