dimas-b commented on code in PR #514:
URL: https://github.com/apache/polaris/pull/514#discussion_r1878762038


##########
polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/ResolverBuilderImpl.java:
##########
@@ -0,0 +1,797 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.core.persistence.resolver;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import jakarta.annotation.Nonnull;
+import jakarta.annotation.Nullable;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.polaris.core.PolarisCallContext;
+import org.apache.polaris.core.PolarisDiagnostics;
+import org.apache.polaris.core.entity.PolarisBaseEntity;
+import org.apache.polaris.core.entity.PolarisChangeTrackingVersions;
+import org.apache.polaris.core.entity.PolarisEntityConstants;
+import org.apache.polaris.core.entity.PolarisEntityId;
+import org.apache.polaris.core.entity.PolarisEntityType;
+import org.apache.polaris.core.entity.PolarisGrantRecord;
+import org.apache.polaris.core.entity.PolarisPrivilege;
+import org.apache.polaris.core.persistence.cache.EntityCache;
+import org.apache.polaris.core.persistence.cache.EntityCacheByNameKey;
+import org.apache.polaris.core.persistence.cache.EntityCacheEntry;
+import org.apache.polaris.core.persistence.cache.EntityCacheLookupResult;
+import org.apache.polaris.core.persistence.cache.PolarisRemoteCache;
+import 
org.apache.polaris.core.persistence.cache.PolarisRemoteCache.ChangeTrackingResult;
+
+public final class ResolverBuilderImpl implements ResolverBuilder {
+
+  // we stash the Polaris call context here
+  private final @Nonnull PolarisCallContext polarisCallContext;
+
+  // the diagnostic services
+  private final @Nonnull PolarisDiagnostics diagnostics;
+
+  // the polaris metastore manager
+  private final @Nonnull PolarisRemoteCache polarisRemoteCache;
+
+  // the cache of entities
+  private final @Nonnull EntityCache cache;
+
+  // the id of the principal making the call or 0 if unknown
+  private final long callerPrincipalId;
+
+  // the name of the principal making the call or null if unknown. If 0, the 
principal name will be
+  // not null
+  private final String callerPrincipalName;
+
+  // reference catalog name for name resolution
+  private final String referenceCatalogName;
+
+  // if not null, subset of principal roles to activate
+  private final @Nullable Set<String> callerPrincipalRoleNamesScope;
+
+  // set of entities to resolve given their name. This does not include 
namespaces or table_like
+  // entities which are
+  // part of a path
+  private final AbstractSet<ResolverEntityName> entitiesToResolve;
+
+  // list of paths to resolve
+  private final List<ResolverPath> pathsToResolve;
+
+  // caller principal
+  private EntityCacheEntry resolvedCallerPrincipal;
+
+  // all principal roles which have been resolved
+  private List<EntityCacheEntry> resolvedCallerPrincipalRoles;
+
+  // catalog to use as the reference catalog for role activation
+  private EntityCacheEntry resolvedReferenceCatalog;
+
+  // all catalog roles which have been activated
+  private final Map<Long, EntityCacheEntry> resolvedCatalogRoles;
+
+  // all resolved paths
+  private List<List<EntityCacheEntry>> resolvedPaths;
+
+  // all entities which have been successfully resolved, by name
+  private final Map<EntityCacheByNameKey, EntityCacheEntry> 
resolvedEntriesByName;
+
+  // all entities which have been fully resolved, by id
+  private final Map<Long, EntityCacheEntry> resolvedEntriesById;
+
+  private boolean resolved;
+
+  /**
+   * Constructor, effectively starts an entity resolver session
+   *
+   * @param polarisCallContext the polaris call context
+   * @param polarisRemoteCache meta store manager
+   * @param callerPrincipalId if not 0, the id of the principal calling the 
service
+   * @param callerPrincipalName if callerPrincipalId is 0, the name of the 
principal calling the
+   *     service
+   * @param callerPrincipalRoleNamesScope if not null, scope principal roles
+   * @param cache shared entity cache
+   * @param referenceCatalogName if not null, specifies the name of the 
reference catalog. The
+   *     reference catalog is the catalog used to resolve catalog roles and 
catalog path. Also, if a
+   *     catalog reference is added, we will determine all catalog roles which 
are activated by the
+   *     caller. Note that when a catalog name needs to be resolved because 
the principal creates or
+   *     drop a catalog, it should not be specified here. Instead, it should 
be resolved by calling
+   *     {@link #addEntityByName(PolarisEntityType, String)}. Generally, any 
DDL executed as a
+   *     service admin should use null for that parameter.
+   */
+  public ResolverBuilderImpl(
+      @Nonnull PolarisCallContext polarisCallContext,
+      @Nonnull PolarisRemoteCache polarisRemoteCache,
+      long callerPrincipalId,
+      @Nullable String callerPrincipalName,
+      @Nullable Set<String> callerPrincipalRoleNamesScope,
+      @Nonnull EntityCache cache,
+      @Nullable String referenceCatalogName) {
+    this.polarisCallContext = polarisCallContext;
+    this.diagnostics = polarisCallContext.getDiagServices();
+    this.polarisRemoteCache = polarisRemoteCache;
+    this.cache = cache;
+    this.callerPrincipalName = callerPrincipalName;
+    this.callerPrincipalId = callerPrincipalId;
+    this.referenceCatalogName = referenceCatalogName;
+
+    // scoped principal role names
+    this.callerPrincipalRoleNamesScope = callerPrincipalRoleNamesScope;
+
+    // validate inputs
+    this.diagnostics.checkNotNull(polarisRemoteCache, 
"unexpected_null_polarisRemoteCache");
+    this.diagnostics.checkNotNull(cache, "unexpected_null_cache");
+    this.diagnostics.check(
+        callerPrincipalId != 0 || callerPrincipalName != null, 
"principal_must_be_specified");
+
+    // paths to resolve
+    this.pathsToResolve = new ArrayList<>();
+    this.resolvedPaths = new ArrayList<>();
+
+    // all entities we need to resolve by name
+    this.entitiesToResolve = new HashSet<>();
+
+    // will contain all principal roles which we were able to resolve
+    this.resolvedCallerPrincipalRoles = new ArrayList<>();
+
+    // remember if a reference catalog name was specified
+    if (referenceCatalogName != null) {
+      this.resolvedCatalogRoles = new HashMap<>();
+    } else {
+      this.resolvedCatalogRoles = null;
+    }
+
+    // all resolved entities, by name and by if
+    this.resolvedEntriesByName = new HashMap<>();
+    resolvedEntriesById = new HashMap<>();
+  }
+
+  @Override
+  public ResolverBuilder addEntityByName(
+      @Nonnull PolarisEntityType entityType, @Nonnull String entityName) {
+    checkState(!resolved, "resolver_called");
+    diagnostics.checkNotNull(entityType, "entity_type_is_null");
+    diagnostics.checkNotNull(entityName, "entity_name_is_null");
+    this.addEntityByName(entityType, entityName, false);
+    return this;
+  }
+
+  @Override
+  public ResolverBuilder addOptionalEntityByName(
+      @Nonnull PolarisEntityType entityType, @Nonnull String entityName) {
+    checkState(!resolved, "resolver_called");
+    diagnostics.checkNotNull(entityType, "entity_type_is_null");
+    diagnostics.checkNotNull(entityName, "entity_name_is_null");
+    this.addEntityByName(entityType, entityName, true);
+    return this;
+  }
+
+  @Override
+  public ResolverBuilder addPath(@Nonnull ResolverPath path) {
+    checkState(!resolved, "resolver_called");
+    diagnostics.checkNotNull(path, "unexpected_null_entity_path");
+    this.pathsToResolve.add(path);
+    return this;
+  }
+
+  @Override
+  public Resolver buildResolved() {
+    checkState(!resolved, "resolver_called");
+
+    resolved = true;
+
+    // retry until a pass terminates, or we reached the maximum iteration 
count. Note that we should
+    // finish normally in no more than few passes so the 1000 limit is really 
to avoid spinning
+    // forever if there is a bug.
+    //
+    // TODO Note: this for-i-1000-loop looks suspicious (code smell) and at 
best just very expensive
+    for (int count = 0; ; count++) {
+      try {
+        runResolvePass();
+        break;
+      } catch (ResolverException ex) {
+        if (count < 1000) {
+          continue;
+        }
+        throw ex;
+      }
+    }
+    ;

Review Comment:
   nit: spurious `;`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to