in service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java [1069:1183]
private void validateNoLocationOverlap(
String location, List<PolarisEntity> parentPath, String name) {
ListEntitiesResult siblingNamespacesResult =
getMetaStoreManager()
.listEntities(
callContext.getPolarisCallContext(),
parentPath.stream().map(PolarisEntity::toCore).collect(Collectors.toList()),
PolarisEntityType.NAMESPACE,
PolarisEntitySubType.ANY_SUBTYPE);
if (!siblingNamespacesResult.isSuccess()) {
throw new IllegalStateException(
"Unable to resolve siblings entities to validate location - could not list namespaces");
}
// if the entity path has more than just the catalog, check for tables as well as other
// namespaces
Optional<NamespaceEntity> parentNamespace =
parentPath.size() > 1
? Optional.of(NamespaceEntity.of(parentPath.getLast()))
: Optional.empty();
List<TableIdentifier> siblingTables =
parentNamespace
.map(
ns -> {
ListEntitiesResult siblingTablesResult =
getMetaStoreManager()
.listEntities(
callContext.getPolarisCallContext(),
parentPath.stream()
.map(PolarisEntity::toCore)
.collect(Collectors.toList()),
PolarisEntityType.TABLE_LIKE,
PolarisEntitySubType.ANY_SUBTYPE);
if (!siblingTablesResult.isSuccess()) {
throw new IllegalStateException(
"Unable to resolve siblings entities to validate location - could not list tables");
}
return siblingTablesResult.getEntities().stream()
.map(tbl -> TableIdentifier.of(ns.asNamespace(), tbl.getName()))
.collect(Collectors.toList());
})
.orElse(List.of());
List<Namespace> siblingNamespaces =
siblingNamespacesResult.getEntities().stream()
.map(
ns -> {
String[] nsLevels =
parentNamespace
.map(parent -> parent.asNamespace().levels())
.orElse(new String[0]);
String[] newLevels = Arrays.copyOf(nsLevels, nsLevels.length + 1);
newLevels[nsLevels.length] = ns.getName();
return Namespace.of(newLevels);
})
.toList();
LOGGER.debug(
"Resolving {} sibling entities to validate location",
siblingTables.size() + siblingNamespaces.size());
PolarisResolutionManifest resolutionManifest =
new PolarisResolutionManifest(
callContext, entityManager, securityContext, parentPath.getFirst().getName());
siblingTables.forEach(
tbl ->
resolutionManifest.addPath(
new ResolverPath(
PolarisCatalogHelpers.tableIdentifierToList(tbl), PolarisEntityType.TABLE_LIKE),
tbl));
siblingNamespaces.forEach(
ns ->
resolutionManifest.addPath(
new ResolverPath(Arrays.asList(ns.levels()), PolarisEntityType.NAMESPACE), ns));
ResolverStatus status = resolutionManifest.resolveAll();
if (!status.getStatus().equals(ResolverStatus.StatusEnum.SUCCESS)) {
String message =
"Unable to resolve sibling entities to validate location - " + status.getStatus();
if (status.getStatus().equals(ResolverStatus.StatusEnum.ENTITY_COULD_NOT_BE_RESOLVED)) {
message += ". Could not resolve entity: " + status.getFailedToResolvedEntityName();
}
throw new IllegalStateException(message);
}
StorageLocation targetLocation = StorageLocation.of(location);
Stream.concat(
siblingTables.stream()
.filter(tbl -> !tbl.name().equals(name))
.map(
tbl -> {
PolarisResolvedPathWrapper resolveTablePath =
resolutionManifest.getResolvedPath(tbl);
return IcebergTableLikeEntity.of(resolveTablePath.getRawLeafEntity())
.getBaseLocation();
}),
siblingNamespaces.stream()
.filter(ns -> !ns.level(ns.length() - 1).equals(name))
.map(
ns -> {
PolarisResolvedPathWrapper resolveNamespacePath =
resolutionManifest.getResolvedPath(ns);
return NamespaceEntity.of(resolveNamespacePath.getRawLeafEntity())
.getBaseLocation();
}))
.filter(java.util.Objects::nonNull)
.map(StorageLocation::of)
.forEach(
siblingLocation -> {
if (targetLocation.isChildOf(siblingLocation)
|| siblingLocation.isChildOf(targetLocation)) {
throw new org.apache.iceberg.exceptions.ForbiddenException(
"Unable to create table at location '%s' because it conflicts with existing table or namespace at location '%s'",
targetLocation, siblingLocation);
}
});
}