u70b3 commented on code in PR #67630:
URL: https://github.com/apache/doris/pull/67630#discussion_r4003109439
##########
fe/fe-common/src/main/java/org/apache/doris/common/Config.java:
##########
@@ -4226,4 +4226,44 @@ public void handle(Field field, String value) throws
Exception {
+ "可降低导入内存压力并提升随机分桶表的吞吐量,覆盖所有导入类型。"})
public static boolean enable_adaptive_random_bucket_load = true;
+ @ConfField(mutable = true, masterOnly = true, varType =
VariableAnnotation.EXPERIMENTAL, description = {
Review Comment:
Keeping `mutable=true` for now: this is the same seam the dispatch and
FORCE_RELEASE slices use as the production gate (roadmap:
https://github.com/apache/doris/issues/66497#issuecomment-5595163744), and the
regression suite enables it via ADMIN SET. A hard lock can land together with
FORCE_RELEASE instead of freezing the seam twice — see
https://github.com/apache/doris/pull/67630#issuecomment-5635777574.
##########
fe/fe-common/src/main/java/org/apache/doris/common/Config.java:
##########
@@ -4226,4 +4226,44 @@ public void handle(Field field, String value) throws
Exception {
+ "可降低导入内存压力并提升随机分桶表的吞吐量,覆盖所有导入类型。"})
public static boolean enable_adaptive_random_bucket_load = true;
+ @ConfField(mutable = true, masterOnly = true, varType =
VariableAnnotation.EXPERIMENTAL, description = {
+ "是否启用 Lance 外表索引变更(CREATE/CREATE OR REPLACE/DROP INDEX)的
admission。默认关闭;"
+ + "启用前需确认未决 job 配额均为正值。注意:在 dispatch(后续版本)与
FORCE_RELEASE(后续版本)就绪前"
+ + "开启本开关会产生不可回收的 PENDING job 并冻结对应 catalog 的身份属性变更与 DROP
CATALOG。",
+ "Enable admission of Lance index mutations (CREATE/CREATE OR
REPLACE/DROP INDEX). "
+ + "Disabled by default; unresolved-job quotas must be
positive before enabling. "
+ + "WARNING: enabling before dispatch and FORCE_RELEASE
land in a later release creates "
+ + "PENDING jobs that cannot be resolved and freezes
catalog identity changes and DROP CATALOG."})
+ public static boolean enable_lance_index_mutation = false;
+
+ @ConfField(mutable = true, masterOnly = true,
+ callback =
LanceIndexConfigValidator.PositiveLongConfigHandler.class,
+ description = {"单个 Lance 数据表(locator 身份)允许的最大未决索引 job 数。",
+ "Max unresolved Lance index jobs per table (locator
identity)."})
+ public static long lance_index_job_max_unresolved_per_table = 8;
+
+ @ConfField(mutable = true, masterOnly = true,
+ callback =
LanceIndexConfigValidator.PositiveLongConfigHandler.class,
+ description = {"单个 Lance catalog 允许的最大未决索引 job 数。",
+ "Max unresolved Lance index jobs per catalog."})
+ public static long lance_index_job_max_unresolved_per_catalog = 64;
+
+ @ConfField(mutable = true, masterOnly = true,
+ callback =
LanceIndexConfigValidator.PositiveLongConfigHandler.class,
+ description = {"全部 catalog 合计允许的最大未决 Lance 索引 job 数。",
+ "Max unresolved Lance index jobs across all catalogs
(global)."})
+ public static long lance_index_job_max_unresolved_global = 256;
+
+ @ConfField(mutable = true, masterOnly = false,
Review Comment:
Fixed in 4fba663dc4 — both ANN bounds are now `masterOnly=true`, matching
where the validation actually runs.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogMgr.java:
##########
@@ -87,6 +90,17 @@ public class CatalogMgr implements Writable,
GsonPostProcessable {
public static final String METADATA_REFRESH_INTERVAL_SEC =
"metadata_refresh_interval_sec";
public static final String CATALOG_TYPE_PROP = "type";
+ /**
+ * The Lance catalog properties whose change moves the persisted target
identity
+ * (provider, stable locator, or namespace mapping) out from under
unresolved index
+ * jobs. Credential and other properties are not target-changing and stay
unguarded.
+ * Keys match case-insensitively because the catalog property chain
performs no key
+ * normalization.
+ */
+ private static final Set<String> LANCE_TARGET_IDENTITY_KEYS =
ImmutableSet.of(
Review Comment:
Fixed in 4fba663dc4 — the guard now fingerprints the 18 storage-routing keys
the Lance property chain actually consumes (endpoint/region families across
s3/oss/cos/obs/minio/gs/ozone); credentials stay rotatable.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/LanceIndexAdmission.java:
##########
@@ -0,0 +1,504 @@
+// 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.doris.datasource.lance;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.datasource.CatalogMgr;
+import org.apache.doris.datasource.lance.job.LanceIndexDatasetLocator;
+import org.apache.doris.datasource.lance.job.LanceIndexFenceKey;
+import org.apache.doris.datasource.lance.job.LanceIndexJob;
+import org.apache.doris.datasource.lance.job.LanceIndexJobMutationType;
+import org.apache.doris.datasource.lance.job.LanceIndexNameNormalizer;
+import org.apache.doris.datasource.lance.job.LanceIndexSchemaContract;
+import org.apache.doris.nereids.trees.plans.commands.info.IndexDefinition;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.annotation.Nullable;
+
+/**
+ * Lance index admission (design sections 2.2 and 4.1): the single place where
a statically
+ * validated top-level CREATE [OR REPLACE]/DROP INDEX statement against a
Lance catalog table is
+ * turned into a durable job. The whole flow runs against one pinned admission
snapshot — the
+ * IF preflight never takes a second metadata read, and no catalog/db/table
metadata lock is held
+ * while the snapshot loader does its JNI work (design section 5.1).
+ *
+ * <p>The step order is the correctness contract: name normalization and
reserved prefix come
+ * first, before target capture and the snapshot read (fail cheap-first — a
reserved name
+ * rejected at admission depth costs no remote read), then case-only collision
analysis, IF
+ * preflight (including the two-stage {@code matches}: requested-algorithm
equality plus
+ * physical-family corroboration), schema contract from the stored column
name, locator
+ * normalization, deterministic properties JSON, positive-quota assertion, and
only then exactly
+ * one id allocation and the durable {@code createJob} transfer.
+ * Every rejection before {@code createJob} leaves no job, no fence, no quota
charge, no journal
+ * record, and no id allocation; the manager's own fence/quota rejections pass
through verbatim
+ * (an id burned by them is accepted — ids are never required to be
contiguous).
+ */
+public final class LanceIndexAdmission {
+
+ /**
+ * The snapshot read seam. Tests inject a prepared snapshot here so
admission runs without
+ * FE startup or JNI; the production default delegates to the catalog's
merged-snapshot read.
+ */
+ public interface SnapshotLoader {
+ LanceIndexAdmissionSnapshot load(LanceExternalCatalog catalog, String
dbName, String tableName)
+ throws Exception;
+ }
+
+ /** The admission result: the durable job id, or null for an IF no-op (no
job created). */
+ public static final class Outcome {
+ private final Long jobId;
+
+ private Outcome(Long jobId) {
+ this.jobId = jobId;
+ }
+
+ /**
+ * The admitted job id, or null when the IF preflight made the
statement an immediate
+ * no-op (design section 2.2: "returns an immediate no-op, without
creating a job").
+ */
+ @Nullable
+ public Long getJobId() {
+ return jobId;
+ }
+ }
+
+ private static final SnapshotLoader DEFAULT_LOADER = new SnapshotLoader() {
+ @Override
+ public LanceIndexAdmissionSnapshot load(LanceExternalCatalog catalog,
String dbName,
+ String tableName) throws Exception {
+ return catalog.loadTableIndexAdmissionSnapshot(dbName, tableName);
+ }
+ };
+
+ private LanceIndexAdmission() {
+ }
+
+ /**
+ * Admits a top-level CREATE [OR REPLACE] INDEX. Static validation
+ * ({@link LanceIndexMutationValidator#validateCreateIndex}) must already
have passed for
+ * {@code def}.
+ */
+ public static Outcome admitCreate(LanceExternalCatalog catalog,
LanceExternalDatabase db,
+ LanceExternalTable table, IndexDefinition def, boolean
ifNotExists) throws Exception {
+ return admitCreate(DEFAULT_LOADER, catalog, db, table, def,
ifNotExists);
+ }
+
+ static Outcome admitCreate(SnapshotLoader loader, LanceExternalCatalog
catalog,
+ LanceExternalDatabase db, LanceExternalTable table,
IndexDefinition def, boolean ifNotExists)
+ throws Exception {
+ // 1. Display/normalized names and the reserved system prefix, checked
before any metadata
+ // read so a reserved name rejected at admission depth costs no remote
snapshot read (fail
+ // cheap-first). The prefix is rejected for CREATE and REPLACE exactly
as for DROP; the
+ // static layer rejects it first and this is the defense-in-depth copy
at admission depth.
+ String displayName = def.getIndexName();
+ String normalizedName =
LanceIndexNameNormalizer.normalize(displayName);
+ LanceIndexMutationValidator.rejectIfReservedIndexName(displayName);
+ // 2. One pinned snapshot for every authoritative decision below.
+ CatalogMgr catalogMgr = Env.getCurrentEnv().getCatalogMgr();
+ CatalogMgr.LanceIndexTarget target =
catalogMgr.captureLanceIndexTarget(catalog);
+ LanceIndexAdmissionSnapshot snapshot = loader.load(catalog,
db.getRemoteName(), table.getRemoteName());
Review Comment:
Fixed in de4daa43f7 — top-level field names colliding under the table's
`equalsIgnoreCase` lookup relation now fail closed before the IF preflight and
contract selection.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/LanceIndexAdmission.java:
##########
@@ -0,0 +1,504 @@
+// 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.doris.datasource.lance;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.datasource.CatalogMgr;
+import org.apache.doris.datasource.lance.job.LanceIndexDatasetLocator;
+import org.apache.doris.datasource.lance.job.LanceIndexFenceKey;
+import org.apache.doris.datasource.lance.job.LanceIndexJob;
+import org.apache.doris.datasource.lance.job.LanceIndexJobMutationType;
+import org.apache.doris.datasource.lance.job.LanceIndexNameNormalizer;
+import org.apache.doris.datasource.lance.job.LanceIndexSchemaContract;
+import org.apache.doris.nereids.trees.plans.commands.info.IndexDefinition;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.annotation.Nullable;
+
+/**
+ * Lance index admission (design sections 2.2 and 4.1): the single place where
a statically
+ * validated top-level CREATE [OR REPLACE]/DROP INDEX statement against a
Lance catalog table is
+ * turned into a durable job. The whole flow runs against one pinned admission
snapshot — the
+ * IF preflight never takes a second metadata read, and no catalog/db/table
metadata lock is held
+ * while the snapshot loader does its JNI work (design section 5.1).
+ *
+ * <p>The step order is the correctness contract: name normalization and
reserved prefix come
+ * first, before target capture and the snapshot read (fail cheap-first — a
reserved name
+ * rejected at admission depth costs no remote read), then case-only collision
analysis, IF
+ * preflight (including the two-stage {@code matches}: requested-algorithm
equality plus
+ * physical-family corroboration), schema contract from the stored column
name, locator
+ * normalization, deterministic properties JSON, positive-quota assertion, and
only then exactly
+ * one id allocation and the durable {@code createJob} transfer.
+ * Every rejection before {@code createJob} leaves no job, no fence, no quota
charge, no journal
+ * record, and no id allocation; the manager's own fence/quota rejections pass
through verbatim
+ * (an id burned by them is accepted — ids are never required to be
contiguous).
+ */
+public final class LanceIndexAdmission {
+
+ /**
+ * The snapshot read seam. Tests inject a prepared snapshot here so
admission runs without
+ * FE startup or JNI; the production default delegates to the catalog's
merged-snapshot read.
+ */
+ public interface SnapshotLoader {
+ LanceIndexAdmissionSnapshot load(LanceExternalCatalog catalog, String
dbName, String tableName)
+ throws Exception;
+ }
+
+ /** The admission result: the durable job id, or null for an IF no-op (no
job created). */
+ public static final class Outcome {
+ private final Long jobId;
+
+ private Outcome(Long jobId) {
+ this.jobId = jobId;
+ }
+
+ /**
+ * The admitted job id, or null when the IF preflight made the
statement an immediate
+ * no-op (design section 2.2: "returns an immediate no-op, without
creating a job").
+ */
+ @Nullable
+ public Long getJobId() {
+ return jobId;
+ }
+ }
+
+ private static final SnapshotLoader DEFAULT_LOADER = new SnapshotLoader() {
+ @Override
+ public LanceIndexAdmissionSnapshot load(LanceExternalCatalog catalog,
String dbName,
+ String tableName) throws Exception {
+ return catalog.loadTableIndexAdmissionSnapshot(dbName, tableName);
+ }
+ };
+
+ private LanceIndexAdmission() {
+ }
+
+ /**
+ * Admits a top-level CREATE [OR REPLACE] INDEX. Static validation
+ * ({@link LanceIndexMutationValidator#validateCreateIndex}) must already
have passed for
+ * {@code def}.
+ */
+ public static Outcome admitCreate(LanceExternalCatalog catalog,
LanceExternalDatabase db,
+ LanceExternalTable table, IndexDefinition def, boolean
ifNotExists) throws Exception {
+ return admitCreate(DEFAULT_LOADER, catalog, db, table, def,
ifNotExists);
+ }
+
+ static Outcome admitCreate(SnapshotLoader loader, LanceExternalCatalog
catalog,
+ LanceExternalDatabase db, LanceExternalTable table,
IndexDefinition def, boolean ifNotExists)
+ throws Exception {
+ // 1. Display/normalized names and the reserved system prefix, checked
before any metadata
+ // read so a reserved name rejected at admission depth costs no remote
snapshot read (fail
+ // cheap-first). The prefix is rejected for CREATE and REPLACE exactly
as for DROP; the
+ // static layer rejects it first and this is the defense-in-depth copy
at admission depth.
+ String displayName = def.getIndexName();
+ String normalizedName =
LanceIndexNameNormalizer.normalize(displayName);
+ LanceIndexMutationValidator.rejectIfReservedIndexName(displayName);
+ // 2. One pinned snapshot for every authoritative decision below.
+ CatalogMgr catalogMgr = Env.getCurrentEnv().getCatalogMgr();
+ CatalogMgr.LanceIndexTarget target =
catalogMgr.captureLanceIndexTarget(catalog);
+ LanceIndexAdmissionSnapshot snapshot = loader.load(catalog,
db.getRemoteName(), table.getRemoteName());
+ // 3. Case-only analysis (design section 4.1): ambiguous external
collisions fail closed;
+ // a unique match resolves to the stored display name.
+ List<String> storedNames = logicalIndexNames(snapshot);
+ if (LanceIndexFamilies.isAmbiguousCaseCollision(storedNames,
normalizedName)) {
+ rejectInvalid("index name '" + displayName
+ + "' is ambiguous: multiple Lance indexes differ only by
case");
+ }
+ String storedName = LanceIndexFamilies.uniqueMatch(storedNames,
normalizedName);
+ // 4. IF preflight (design section 2.2).
+ boolean orReplace = def.isOrReplace();
+ if (!orReplace && storedName != null) {
+ if (!ifNotExists) {
+ rejectInvalid("index '" + displayName + "' already exists");
+ }
+ if (!matchesExistingDefinition(snapshot, storedName, def)) {
+ rejectInvalid("index '" + displayName + "' already exists with
a different definition");
+ }
+ return catalogMgr.withLanceIndexAdmission(catalog, target, () ->
new Outcome(null));
+ }
+ // 5. Schema contract v1 from the stored column name (never the raw
user spelling).
+ String storedColumnName = storedColumnName(table,
def.getCols().get(0));
+ LanceIndexSchemaContract contract =
+ LanceSchemaContractBuilder.build(snapshot.getTopLevelFields(),
storedColumnName);
+ // 6. The fence locator is the normalized dataset uri of the same
pinned snapshot.
+ String locator = normalizeLocator(snapshot);
+ // 7. Deterministic normalized properties JSON for ANN; scalar
families persist null.
+ boolean ann = def.getLanceIndexType() == null;
+ String indexType = ann ? annIndexType(def) : def.getLanceIndexType();
+ String propertiesJson = ann ? buildAnnPropertiesJson(def) : null;
+ // D7 backstop: quota values from fe.conf bypass the ADMIN SET
callback, so admission
+ // re-asserts positivity before any id allocation or durable transfer.
+ assertPositiveQuotas();
+ return catalogMgr.withLanceIndexAdmission(catalog, target, () -> {
+ // 8. Exactly one id allocation, after every preflight above has
passed.
+ long jobId = Env.getCurrentEnv().getNextId();
+ String creator = ConnectContext.get().getQualifiedUser();
+ // 9. REPLACE on an existing name persists the stored display name
(section 4.1) so the
+ // worker locates the case-sensitive target; a fresh REPLACE keeps
the user's spelling.
+ String persistedDisplayName = (orReplace && storedName != null) ?
storedName : displayName;
+ LanceIndexJob job;
+ try {
+ job = new LanceIndexJob(jobId, creator, catalog.getId(),
db.getFullName(), table.getName(),
+ LanceIndexFenceKey.PROVIDER_DIRECTORY, locator,
persistedDisplayName, normalizedName,
+ orReplace ? LanceIndexJobMutationType.REPLACE :
LanceIndexJobMutationType.CREATE,
+ ifNotExists, false, indexType, storedColumnName,
propertiesJson,
+ snapshot.getDatasetVersion(), contract);
+ } catch (IllegalArgumentException e) {
+ throw invalidAdmission(e.getMessage());
+ }
+ Env.getCurrentEnv().getLanceIndexJobManager().createJob(job,
+ Config.lance_index_job_max_unresolved_per_table,
+ Config.lance_index_job_max_unresolved_per_catalog,
+ Config.lance_index_job_max_unresolved_global);
+ // 10. The job and its fence are durable once createJob returns.
+ return new Outcome(jobId);
+ });
+ }
+
+ /**
+ * Admits a top-level DROP INDEX. The static name bounds
+ * ({@link LanceIndexMutationValidator#validateDropIndex}) must already
have passed.
+ */
+ public static Outcome admitDrop(LanceExternalCatalog catalog,
LanceExternalDatabase db,
+ LanceExternalTable table, String indexName, boolean ifExists)
throws Exception {
+ return admitDrop(DEFAULT_LOADER, catalog, db, table, indexName,
ifExists);
+ }
+
+ static Outcome admitDrop(SnapshotLoader loader, LanceExternalCatalog
catalog,
+ LanceExternalDatabase db, LanceExternalTable table, String
indexName, boolean ifExists)
+ throws Exception {
+ // Fail cheap-first: the reserved prefix is rejected before target
capture and the
+ // snapshot read, so it costs no remote read.
+ String normalizedName = LanceIndexNameNormalizer.normalize(indexName);
+ LanceIndexMutationValidator.rejectIfReservedIndexName(indexName);
+ CatalogMgr catalogMgr = Env.getCurrentEnv().getCatalogMgr();
+ CatalogMgr.LanceIndexTarget target =
catalogMgr.captureLanceIndexTarget(catalog);
+ LanceIndexAdmissionSnapshot snapshot = loader.load(catalog,
db.getRemoteName(), table.getRemoteName());
+ List<String> storedNames = logicalIndexNames(snapshot);
+ if (LanceIndexFamilies.isAmbiguousCaseCollision(storedNames,
normalizedName)) {
+ rejectInvalid("index name '" + indexName
+ + "' is ambiguous: multiple Lance indexes differ only by
case");
+ }
+ String storedName = LanceIndexFamilies.uniqueMatch(storedNames,
normalizedName);
+ if (storedName == null) {
+ if (ifExists) {
+ return catalogMgr.withLanceIndexAdmission(catalog, target, ()
-> new Outcome(null));
+ }
+ rejectInvalid("index '" + indexName + "' not found");
+ }
+ String locator = normalizeLocator(snapshot);
+ assertPositiveQuotas();
+ return catalogMgr.withLanceIndexAdmission(catalog, target, () -> {
+ long jobId = Env.getCurrentEnv().getNextId();
+ String creator = ConnectContext.get().getQualifiedUser();
+ // DROP only runs past the preflight with a unique match, so the
stored display name is
+ // always persisted (section 4.1); definition fields stay null on
a DROP job record.
+ LanceIndexJob job;
+ try {
+ job = new LanceIndexJob(jobId, creator, catalog.getId(),
db.getFullName(), table.getName(),
+ LanceIndexFenceKey.PROVIDER_DIRECTORY, locator,
storedName, normalizedName,
+ LanceIndexJobMutationType.DROP, false, ifExists, null,
null, null,
+ snapshot.getDatasetVersion(), null);
+ } catch (IllegalArgumentException e) {
+ throw invalidAdmission(e.getMessage());
+ }
+ Env.getCurrentEnv().getLanceIndexJobManager().createJob(job,
+ Config.lance_index_job_max_unresolved_per_table,
+ Config.lance_index_job_max_unresolved_per_catalog,
+ Config.lance_index_job_max_unresolved_global);
+ return new Outcome(jobId);
+ });
+ }
+
+ /**
+ * The section 2.2 definition match, two stages: (a) the requested
algorithm must equal the
+ * stored logical algorithm under family normalization — a same-name
different-algorithm
+ * request is a mismatch, never a no-op; (b) the physical entry of the
same name must exist
+ * and back the logical algorithm (snapshot self-consistency, failing
closed); (c) the single
+ * normalized column must be equal; (d) whitelist properties are compared
per property — a
+ * value the request sets and the snapshot exposes must be equal, an
unexposed snapshot value
+ * is skipped, and a property the request omits is never compared.
+ */
+ private static boolean
matchesExistingDefinition(LanceIndexAdmissionSnapshot snapshot,
+ String storedName, IndexDefinition def) {
+ LanceLogicalIndex logical = null;
+ for (LanceLogicalIndex index : snapshot.getLogicalIndexes()) {
+ if (index.getName().equals(storedName)) {
+ logical = index;
+ break;
+ }
+ }
+ if (logical == null) {
+ return false;
+ }
+ String requestAlgorithm = requestedAlgorithm(def);
+ if (requestAlgorithm == null ||
!LanceIndexFamilies.normalize(logical.getIndexType())
+ .equals(LanceIndexFamilies.normalize(requestAlgorithm))) {
+ return false;
+ }
+ LanceIndexAdmissionSnapshot.PhysicalIndexInfo physical = null;
+ for (LanceIndexAdmissionSnapshot.PhysicalIndexInfo entry :
snapshot.getPhysicalIndexes()) {
+ if (entry.getName().equals(storedName)) {
+ physical = entry;
+ break;
+ }
+ }
+ if (physical == null
+ || !LanceIndexFamilies.isCompatible(logical.getIndexType(),
physical.getIndexTypeName())) {
+ return false;
+ }
+ if (logical.getColumns().size() != 1) {
+ return false;
+ }
+ String requestColumn =
LanceIndexNameNormalizer.normalize(def.getCols().get(0));
Review Comment:
Fixed in de4daa43f7 — the request column is converted to the same loader
path-segment representation before comparison, so a name the parser unquotes
compares equal to its escaped logical form.
--
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]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]