Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
68229cf
[core] Handle query auth wrappers in split consumers
Jul 17, 2026
37bf952
[core] Bypass query auth for index bootstrap
Jul 17, 2026
478edc9
[core] Preserve query auth in chain table scans
Jul 17, 2026
b1e6b86
[core] Preserve query auth in data evolution scans
Jul 17, 2026
070a17c
fix
Jul 20, 2026
e756304
rebaseMaster
Jul 20, 2026
60243a3
fix
Jul 20, 2026
359e30d
[core] Apply query auth after chain split assembly
Jul 20, 2026
cdad524
[flink] Unwrap query auth split for task assignment
Jul 20, 2026
9555d89
[flink] Read snapshot id from query auth split
Jul 20, 2026
1385429
[flink] Support query auth splits in aligned mode
Jul 20, 2026
d316fa3
[flink] Support query auth in dynamic partition pruning
Jul 20, 2026
35b4d3d
[flink] Handle query auth splits in dedicated source
Jul 20, 2026
29e9d59
[flink-cdc] Handle query auth splits in enumerator
Jul 20, 2026
efa166b
[spark] Read metadata from query auth splits
Jul 20, 2026
bc5e71d
[spark] Preserve query auth in streaming splits
Jul 20, 2026
e5b5e70
[spark] Handle query auth in primary key vector search
Jul 20, 2026
ab5d2cd
[flink] Use full cache lookup with query auth
Jul 20, 2026
a3f092a
[core] Bypass query auth for compact bucket scans
Jul 20, 2026
712b461
[core] Bypass query auth for file monitor scans
Jul 20, 2026
1175f29
fix
Jul 20, 2026
d96d196
[common] Preserve score readers through query auth
Jul 20, 2026
be05bcf
[flink] Unwrap query auth splits for lag metrics
Jul 20, 2026
1eec687
[spark] Apply query auth to metadata-only vector search
Jul 20, 2026
63cd8e1
[core] Avoid TopN pruning before chain query auth
Jul 20, 2026
33fb8c6
[spark] Apply query auth before lateral vector limit
Jul 20, 2026
705616d
[flink] Reject lookup joins with query auth
Jul 20, 2026
57df19d
[spark] Fix lateral vector search column auth
Jul 20, 2026
66eaaa5
[core] Stabilize index bootstrap reader cleanup
Jul 20, 2026
45e78aa
[core] Handle query auth in maintenance and system tables
Jul 20, 2026
6761e1b
[spark] Preserve query auth through split planning
Jul 20, 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,53 @@

package org.apache.paimon.reader;

import org.apache.paimon.utils.Filter;

import javax.annotation.Nullable;

import java.io.IOException;
import java.util.function.Function;

/** A {@link RecordReader} whose records expose vector-search scores and row identifiers. */
public interface ScoreRecordReader<T> extends RecordReader<T> {

@Nullable
@Override
ScoreRecordIterator<T> readBatch() throws IOException;

@Override
default <R> ScoreRecordReader<R> transform(Function<T, R> function) {
ScoreRecordReader<T> thisReader = this;
return new ScoreRecordReader<R>() {
@Nullable
@Override
public ScoreRecordIterator<R> readBatch() throws IOException {
ScoreRecordIterator<T> iterator = thisReader.readBatch();
return iterator == null ? null : iterator.transform(function);
}

@Override
public void close() throws IOException {
thisReader.close();
}
};
}

@Override
default ScoreRecordReader<T> filter(Filter<T> filter) {
ScoreRecordReader<T> thisReader = this;
return new ScoreRecordReader<T>() {
@Nullable
@Override
public ScoreRecordIterator<T> readBatch() throws IOException {
ScoreRecordIterator<T> iterator = thisReader.readBatch();
return iterator == null ? null : iterator.filter(filter);
}

@Override
public void close() throws IOException {
thisReader.close();
}
};
}
}
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.paimon.reader;

import org.junit.jupiter.api.Test;

import javax.annotation.Nullable;

import java.io.IOException;

import static org.assertj.core.api.Assertions.assertThat;

/** Tests for {@link ScoreRecordReader}. */
public class ScoreRecordReaderTest {

@Test
public void testFilterAndTransformPreserveScoreMetadata() throws IOException {
ScoreRecordReader<Integer> reader =
new ScoreRecordReader<Integer>() {
private boolean emitted;

@Nullable
@Override
public ScoreRecordIterator<Integer> readBatch() {
if (emitted) {
return null;
}
emitted = true;
return new TestingScoreRecordIterator();
}

@Override
public void close() {}
};

ScoreRecordReader<String> transformed =
reader.filter(value -> value == 1).transform(String::valueOf);
ScoreRecordIterator<String> iterator = transformed.readBatch();

assertThat(iterator).isNotNull();
assertThat(iterator.next()).isEqualTo("1");
assertThat(iterator.returnedRowId()).isEqualTo(11L);
assertThat(iterator.returnedScore()).isEqualTo(0.75f);
assertThat(iterator.next()).isNull();
assertThat(transformed.readBatch()).isNull();
}

private static class TestingScoreRecordIterator implements ScoreRecordIterator<Integer> {

private final int[] values = {0, 1};
private final long[] rowIds = {10L, 11L};
private final float[] scores = {0.25f, 0.75f};
private int index = -1;

@Nullable
@Override
public Integer next() {
index++;
return index < values.length ? values[index] : null;
}

@Override
public float returnedScore() {
return scores[index];
}

@Override
public long returnedRowId() {
return rowIds[index];
}

@Override
public void releaseBatch() {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.data.JoinedRow;
import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.options.Options;
import org.apache.paimon.reader.RecordReader;
import org.apache.paimon.schema.TableSchema;
import org.apache.paimon.table.FileStoreTable;
Expand All @@ -41,12 +42,12 @@
import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.apache.paimon.CoreOptions.QUERY_AUTH_ENABLED;
import static org.apache.paimon.CoreOptions.SCAN_MODE;
import static org.apache.paimon.CoreOptions.StartupMode.LATEST;
import static org.apache.paimon.io.SplitsParallelReadUtil.parallelExecute;
Expand Down Expand Up @@ -80,11 +81,12 @@ public RecordReader<InternalRow> bootstrap(int numAssigners, int assignId) throw
.mapToInt(Integer::intValue)
.toArray();

// force using the latest scan mode
// Force using the latest scan mode and bypass query auth for this internal index read.
Options bootstrapOptions = new Options();
bootstrapOptions.set(SCAN_MODE, LATEST);
bootstrapOptions.set(QUERY_AUTH_ENABLED, false);
ReadBuilder readBuilder =
table.copy(Collections.singletonMap(SCAN_MODE.key(), LATEST.toString()))
.newReadBuilder()
.withProjection(keyProjection);
table.copy(bootstrapOptions.toMap()).newReadBuilder().withProjection(keyProjection);

DataTableScan tableScan = (DataTableScan) readBuilder.newScan();
List<Split> splits =
Expand Down Expand Up @@ -119,7 +121,7 @@ public RecordReader<InternalRow> bootstrap(int numAssigners, int assignId) throw
options.pageSize(),
options.crossPartitionUpsertBootstrapParallelism(),
split -> {
DataSplit dataSplit = ((DataSplit) split);
DataSplit dataSplit = (DataSplit) split;
int bucket = dataSplit.bucket();
return partBucketConverter.toGenericRow(
new JoinedRow(dataSplit.partition(), GenericRow.of(bucket)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.table.source.DataTableScan;
import org.apache.paimon.table.source.InnerTableScan;
import org.apache.paimon.table.source.QueryAuthSplit;
import org.apache.paimon.table.source.Split;
import org.apache.paimon.types.RowType;
import org.apache.paimon.utils.Filter;
Expand Down Expand Up @@ -332,12 +333,17 @@ private Optional<GlobalIndexResult> evalGlobalIndex() {
public static Plan wrapToIndexSplits(
List<Split> splits, RowRangeIndex rowRangeIndex, ScoreGetter scoreGetter) {
List<Split> indexedSplits = new ArrayList<>();
Function<Split, List<IndexedSplit>> process =
split ->
Collections.singletonList(
split instanceof IndexedSplit
? (IndexedSplit) split
: wrap((DataSplit) split, rowRangeIndex, scoreGetter));
Function<Split, List<Split>> process =
split -> {
Split wrappedSplit = QueryAuthSplit.unwrap(split);
if (wrappedSplit instanceof IndexedSplit) {
return Collections.singletonList(split);
}
IndexedSplit indexedSplit =
wrap((DataSplit) wrappedSplit, rowRangeIndex, scoreGetter);
return Collections.singletonList(
QueryAuthSplit.retainAuth(split, indexedSplit));
};
randomlyExecuteSequentialReturn(process, splits, null).forEachRemaining(indexedSplits::add);
return () -> indexedSplits;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

import static org.apache.paimon.CoreOptions.QUERY_AUTH_ENABLED;

/** List what data files recorded in manifests are missing from the filesystem. */
public class ListUnexistingFiles {

Expand All @@ -48,11 +50,14 @@ public class ListUnexistingFiles {
private final ThreadPoolExecutor executor;

public ListUnexistingFiles(FileStoreTable table) {
this.table = table;
this.pathFactory = table.store().pathFactory();
this.table =
table.coreOptions().queryAuthEnabled()
? table.copy(Collections.singletonMap(QUERY_AUTH_ENABLED.key(), "false"))
: table;
this.pathFactory = this.table.store().pathFactory();
this.executor =
FileOperationThreadPool.getExecutorService(
table.coreOptions().fileOperationThreadNum());
this.table.coreOptions().fileOperationThreadNum());
}

public Map<Integer, Map<String, DataFileMeta>> list(BinaryRow partition) throws Exception {
Expand Down
Loading
Loading