Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
61e242d
Spec: Add secondary index specification (from PR #16961)
osscm Jul 20, 2026
9cf6c40
Core: Add IndexMetadata and IndexSnapshot for secondary index spec
osscm Jul 20, 2026
0e9106c
Core: Add IndexCatalog, IndexMetadataIO, and InMemoryIndexCatalog
osscm Jul 20, 2026
8898830
Core: Add TrackingFileWriter and TrackingFileReader (Avro)
osscm Jul 21, 2026
c80847e
Core: Add HashTransform, LeafFileMetadata, ScalarIndexCommitter
osscm Jul 21, 2026
47c28ce
Core: Fix errorprone blockers, add tests for metadata IO, removeSnaps…
osscm Aug 19, 2026
9233255
Data: Add LeafFileEntry, LeafFileWriter, LeafFileReader for SCALAR in…
osscm Aug 20, 2026
9c3c6e2
Data: Validate leaf file entry order in LeafFileWriter (option 2)
osscm Aug 20, 2026
56de9f2
Data: Add end-to-end test wiring build, commit, and lookup together
osscm Aug 22, 2026
e241af0
Spark: Add build_scalar_index procedure
osscm Aug 22, 2026
b224a5f
Spark: Add read-path SCALAR index pruning to SparkScanBuilder
osscm Aug 24, 2026
52f7e2a
Scripts: Rewrite taxi demo to call the real build_scalar_index procedure
osscm Aug 24, 2026
72d01b4
Spark: Fix UUID/String type mismatch in build_scalar_index
osscm Aug 25, 2026
88c14c3
Spark: Fix stale column reference in transform_value computation
osscm Aug 25, 2026
bb2b814
Spark: Make LeafFileWriteResult public for Encoders.javaSerialization
osscm Aug 25, 2026
7293200
Core: Make HashTransform Serializable
osscm Aug 25, 2026
7938153
Spark: Fix Integer/Long mismatch in position computation
osscm Aug 25, 2026
dfbcfec
Spark: Fix ValidationException from _file metadata column in scan pru…
osscm Aug 25, 2026
2067494
Spark: Fix IndexIdentifier mismatch in TestBuildScalarIndexProcedure
osscm Aug 25, 2026
0928561
Spark: Load table through Spark catalog in TestBuildScalarIndexProcedure
osscm Aug 25, 2026
2c9bba2
Spark: Enforce SCALAR index file-level pruning via a Scan decorator
osscm Aug 25, 2026
3462c30
Spark: Fix flaky pruning tests, add isolated FileScanTaskFilteringSca…
osscm Aug 25, 2026
e10f220
Spark: Fix unreported IOException in TestFileScanTaskFilteringScan
osscm Aug 26, 2026
94fc4a2
Spark: Adopt covered/uncovered-files staleness model for SCALAR pruning
osscm Sep 21, 2026
042b592
Spark: Add incremental (append-only) build_scalar_index mode
osscm Sep 21, 2026
b345ea2
Core, Spark: Add DurableIndexCatalog so registrations survive a restart
osscm Sep 21, 2026
6a3d850
Core, Spark: Scope DurableIndexCatalog registry files by table UUID
osscm Sep 22, 2026
b1d2809
Spark: Resolve range predicates against IDENTITY-transform SCALAR ind…
osscm Sep 22, 2026
5e33c2e
Spark: Resolve IN predicates against SCALAR indexes (HASH or IDENTITY)
osscm Sep 22, 2026
2fe250b
Spark: Fix missing ImmutableList import in SparkScanBuilder
osscm Sep 22, 2026
4ce87c0
Spark: Add a real test for IN-predicate leaf-file dedup, not just cor…
osscm Sep 23, 2026
0c08cfe
Spark: Add partition-column warning and a planning-cost bound (Open Q…
osscm Sep 23, 2026
9a7332e
Spark: Resolve SCALAR index via listIndexes(), not a name convention
osscm Sep 30, 2026
992f70f
Format index sources and fix javadoc and license CI failures
osscm Oct 2, 2026
9ad4fa6
Spark: Guard SCALAR index pruning against key-column type promotion
osscm Oct 2, 2026
f015123
Spark: Format v3.5 index sources
osscm Oct 3, 2026
2cec6c9
Core, Spark: Fix checkstyle violations in index sources
osscm Oct 7, 2026
0992e4d
Spark: Restructure and dedup the SCALAR index pruning path
osscm Oct 7, 2026
dea2475
Core, Spark: Write SCALAR index leaf files with small row groups
osscm Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions api/src/main/java/org/apache/iceberg/index/IndexCatalog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.iceberg.index;

import java.util.List;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.exceptions.AlreadyExistsException;
import org.apache.iceberg.exceptions.NoSuchTableException;

/**
* Catalog interface for managing secondary indexes on Iceberg tables.
*
* <p>An index catalog stores a pointer to the current index metadata file for each registered
* index. Commits are atomic: {@link #updateIndex} replaces the metadata pointer only if the current
* pointer still matches the base that the caller read.
*/
public interface IndexCatalog {

/**
* Register a new index in the catalog.
*
* @param identifier the index identifier
* @param metadata the initial index metadata (must have a metadataFileLocation set)
* @throws AlreadyExistsException if an index with the same identifier already exists
*/
void createIndex(IndexIdentifier identifier, IndexMetadata metadata);

/**
* Load the current metadata for an index.
*
* @param identifier the index identifier
* @return the current index metadata
* @throws NoSuchTableException if the index does not exist
*/
IndexMetadata loadIndex(IndexIdentifier identifier);

/**
* Atomically replace the current index metadata.
*
* <p>The update succeeds only if the current metadata file location matches {@code
* base.metadataFileLocation()}. If another writer has already committed a newer version, this
* call throws {@link java.util.ConcurrentModificationException}.
*
* @param identifier the index identifier
* @param base the metadata the caller read (used for optimistic concurrency check)
* @param updated the new metadata to commit (must have a new metadataFileLocation)
*/
void updateIndex(IndexIdentifier identifier, IndexMetadata base, IndexMetadata updated);

/**
* Remove an index from the catalog.
*
* <p>Does not delete the underlying index files — callers are responsible for cleanup.
*
* @param identifier the index identifier
* @throws NoSuchTableException if the index does not exist
*/
void dropIndex(IndexIdentifier identifier);

/**
* Check if an index exists.
*
* @param identifier the index identifier
* @return true if the index exists
*/
boolean indexExists(IndexIdentifier identifier);

/**
* List all indexes registered for a table.
*
* @param tableIdentifier the table identifier
* @return list of index metadata for all indexes on the table, may be empty
*/
List<IndexMetadata> listIndexes(TableIdentifier tableIdentifier);
}
77 changes: 77 additions & 0 deletions api/src/main/java/org/apache/iceberg/index/IndexIdentifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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.iceberg.index;

import java.util.Objects;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;

/**
* Identifies a secondary index by its source table and index name.
*
* <p>Format: {@code namespace.table.indexName}
*
* <p>Example: {@code db.orders.order_id_idx}
*/
public class IndexIdentifier {

private final TableIdentifier tableIdentifier;
private final String name;

private IndexIdentifier(TableIdentifier tableIdentifier, String name) {
this.tableIdentifier = tableIdentifier;
this.name = name;
}

public static IndexIdentifier of(TableIdentifier tableIdentifier, String name) {
Preconditions.checkNotNull(tableIdentifier, "tableIdentifier is required");
Preconditions.checkArgument(name != null && !name.isEmpty(), "index name must be non-empty");
return new IndexIdentifier(tableIdentifier, name);
}

public TableIdentifier tableIdentifier() {
return tableIdentifier;
}

public String name() {
return name;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof IndexIdentifier)) {
return false;
}
IndexIdentifier that = (IndexIdentifier) o;
return Objects.equals(tableIdentifier, that.tableIdentifier) && Objects.equals(name, that.name);
}

@Override
public int hashCode() {
return Objects.hash(tableIdentifier, name);
}

@Override
public String toString() {
return tableIdentifier + "." + name;
}
}
138 changes: 138 additions & 0 deletions api/src/main/java/org/apache/iceberg/index/IndexMetadata.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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.iceberg.index;

import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* Metadata for a secondary index on an Iceberg table.
*
* <p>Index metadata is stored as a JSON file in the index's {@link #location()}. It contains the
* index definition (type, transform, key columns) and a list of {@link IndexSnapshot index
* snapshots}, each corresponding to a specific source table snapshot.
*
* <p>This interface matches the index metadata format defined in the Iceberg secondary index
* specification (format/index.md).
*/
public interface IndexMetadata {

int SUPPORTED_INDEX_FORMAT_VERSION = 1;
int DEFAULT_INDEX_FORMAT_VERSION = 1;

/** Index metadata format version. Currently 1. */
int formatVersion();

/** Stable UUID assigned when the index is created. */
String uuid();

/** UUID of the source table this index is built on. */
String tableUuid();

/** Base location used to create index file paths (metadata, tracking, leaf files). */
String location();

/**
* Logical index type, e.g. {@code "SCALAR"}, {@code "IVF"}, {@code "TERM"}.
*
* <p>Engines that do not recognize the type must skip this index and fall back to normal
* planning.
*/
String type();

/**
* Transform function applied to key columns, e.g. {@code "HASH"} or {@code "IDENTITY"}.
*
* <p>Determines how index entries are organized within leaf files.
*/
String transformFunction();

/**
* Source-table column IDs that the transform is applied to (key columns).
*
* <p>Leaf files are organized by the transform value derived from these columns.
*/
List<Integer> keyColumnIds();

/**
* Optional source-table column IDs copied into the index for read convenience (included columns).
*
* <p>These columns are stored in leaf files but do not affect index organization. Used to serve
* covering queries without reading the source table.
*/
List<Integer> includedColumnIds();

/**
* Optional index-level properties, e.g. {@code hash.num-buckets}, {@code ivf.distance-function}.
*
* @return an unmodifiable map of string properties, never null
*/
Map<String, String> properties();

/**
* ID of the current index snapshot, or null if no snapshot has been committed yet.
*
* @return the current snapshot ID, or null
*/
@Nullable
Long currentSnapshotId();

/** All index snapshots for this index, ordered by snapshot ID. */
List<IndexSnapshot> snapshots();

/**
* Location of the index metadata file, or null if not persisted yet.
*
* @return the metadata file location, or null
*/
@Nullable
String metadataFileLocation();

// ---------------------------------------------------------------------------
// Derived helpers
// ---------------------------------------------------------------------------

/** Returns the current {@link IndexSnapshot}, or null if no snapshot has been committed. */
default IndexSnapshot currentSnapshot() {
Long id = currentSnapshotId();
if (id == null) {
return null;
}
for (IndexSnapshot snap : snapshots()) {
if (snap.snapshotId() == id) {
return snap;
}
}
return null;
}

/**
* Returns the {@link IndexSnapshot} whose {@link IndexSnapshot#sourceTableSnapshotId()} matches
* the given table snapshot ID, or null if none exists.
*/
default IndexSnapshot snapshotForTableSnapshot(long tableSnapshotId) {
for (IndexSnapshot snap : snapshots()) {
if (snap.sourceTableSnapshotId() == tableSnapshotId) {
return snap;
}
}
return null;
}
}
65 changes: 65 additions & 0 deletions api/src/main/java/org/apache/iceberg/index/IndexSnapshot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.iceberg.index;

import java.nio.ByteBuffer;
import java.util.Map;
import javax.annotation.Nullable;

/**
* An immutable version of the index data corresponding to a specific source table snapshot.
*
* <p>Each index snapshot is tied to one table snapshot via {@link #sourceTableSnapshotId()}. The
* tracking file referenced by {@link #trackingFile()} lists all leaf files belonging to this
* snapshot.
*/
public interface IndexSnapshot {

/** Unique identifier for this index snapshot. */
long snapshotId();

/** The source table snapshot this index snapshot was built from. */
long sourceTableSnapshotId();

/** Timestamp in milliseconds when this index snapshot was created. */
long timestampMs();

/**
* Location of the tracking file for this index snapshot.
*
* <p>The tracking file is a Parquet file that lists all leaf files belonging to this snapshot,
* with transform-value min/max bounds per leaf file for planning-time pruning.
*/
String trackingFile();

/**
* Optional snapshot-level properties, supplied by the index maintenance process.
*
* @return an unmodifiable map of string properties, never null
*/
Map<String, String> properties();

/**
* Optional implementation-specific key metadata for tracking file encryption.
*
* @return key metadata bytes, or null if not set
*/
@Nullable
ByteBuffer keyMetadata();
}
Loading
Loading