Skip to content
Closed
Show file tree
Hide file tree
Changes from 20 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 @@ -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 @@ -19,6 +19,8 @@
package org.apache.paimon.table;

import org.apache.paimon.CoreOptions;
import org.apache.paimon.annotation.VisibleForTesting;
import org.apache.paimon.catalog.TableQueryAuthResult;
import org.apache.paimon.codegen.CodeGenUtils;
import org.apache.paimon.codegen.RecordComparator;
import org.apache.paimon.data.BinaryRow;
Expand All @@ -36,7 +38,9 @@
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.table.source.DataTableScan;
import org.apache.paimon.table.source.InnerTableRead;
import org.apache.paimon.table.source.QueryAuthSplit;
import org.apache.paimon.table.source.Split;
import org.apache.paimon.table.source.TableQueryAuth;
import org.apache.paimon.table.source.TableRead;
import org.apache.paimon.types.RowType;
import org.apache.paimon.utils.ChainPartitionProjector;
Expand All @@ -45,6 +49,8 @@
import org.apache.paimon.utils.Pair;
import org.apache.paimon.utils.RowDataToObjectArrayConverter;

import javax.annotation.Nullable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -119,6 +125,13 @@ private DataTableScan newDeltaScan(Function<FileStoreTable, DataTableScan> scanC
return scanCreator.apply(other());
}

static FileStoreTable withoutQueryAuth(FileStoreTable table) {
if (!table.coreOptions().queryAuthEnabled()) {
return table;
}
return table.copy(Collections.singletonMap(CoreOptions.QUERY_AUTH_ENABLED.key(), "false"));
}

@Override
public FileStoreTable copy(Map<String, String> dynamicOptions) {
Map<String, String> wrappedOptions =
Expand Down Expand Up @@ -210,6 +223,8 @@ public static class ChainTableBatchScan extends FallbackReadScan {
private final ChainGroupReadTable chainGroupReadTable;
private final RecordComparator chainPartitionComparator;
private final ChainPartitionProjector partitionProjector;
private final TableQueryAuth queryAuth;
@Nullable private RowType readType;
private Predicate dataPredicate;
private Filter<Integer> bucketFilter;
protected boolean preloadTargetSnapshot = true;
Expand All @@ -219,17 +234,19 @@ public ChainTableBatchScan(
this(tableSchema, chainGroupReadTable, FileStoreTable::newScan);
}

private ChainTableBatchScan(
@VisibleForTesting
ChainTableBatchScan(
TableSchema tableSchema,
ChainGroupReadTable chainGroupReadTable,
Function<FileStoreTable, DataTableScan> scanCreator) {
super(
chainGroupReadTable.wrapped,
chainGroupReadTable.other(),
tableSchema,
scanCreator);
physicalScanCreator(scanCreator));
this.options = CoreOptions.fromMap(tableSchema.options());
this.chainGroupReadTable = chainGroupReadTable;
this.queryAuth = chainGroupReadTable.catalogEnvironment().tableQueryAuth(this.options);
this.partitionConverter =
new RowDataToObjectArrayConverter(tableSchema.logicalPartitionType());
this.partitionComparator =
Expand All @@ -248,6 +265,19 @@ private ChainTableBatchScan(
partitionProjector.chainPartitionType().getFieldTypes());
}

private static Function<FileStoreTable, DataTableScan> physicalScanCreator(
Function<FileStoreTable, DataTableScan> scanCreator) {
return table -> scanCreator.apply(ChainGroupReadTable.withoutQueryAuth(table));
}

@Override
public ChainTableBatchScan withReadType(@Nullable RowType readType) {
this.readType = readType;
mainScan.withReadType(readType);
fallbackScan.withReadType(readType);
return this;
}

@Override
public ChainTableBatchScan withFilter(Predicate predicate) {
super.withFilter(predicate);
Expand All @@ -265,6 +295,18 @@ public ChainTableBatchScan withFilter(Predicate predicate) {
return this;
}

@Override
public ChainTableBatchScan withLimit(int limit) {
// Query authorization is applied only after the physical chain inputs have been
// assembled. Pushing a limit into those raw inputs could exhaust the limit with rows
// which are later removed by authorization. ReadBuilderImpl applies the logical limit
// after query authorization instead.
if (!options.queryAuthEnabled()) {
super.withLimit(limit);
}
return this;
}

@Override
public ChainTableBatchScan withPartitionFilter(Map<String, String> partitionSpec) {
super.withPartitionFilter(partitionSpec);
Expand Down Expand Up @@ -332,6 +374,8 @@ public FallbackReadScan skipPreloadTargetSnapshot() {
*/
@Override
public Plan plan() {
TableQueryAuthResult queryAuthResult =
queryAuth.auth(readType == null ? null : readType.getFieldNames());
List<Split> splits = new ArrayList<>();
PredicateBuilder builder = new PredicateBuilder(tableSchema.logicalPartitionType());
Set<BinaryRow> snapshotPartitions = preloadTargetSnapshotSplits(splits);
Expand Down Expand Up @@ -359,6 +403,7 @@ public Plan plan() {
for (List<BinaryRow> deltaPartitionsInGroup : groupedDeltaPartitions.values()) {

// Sort delta by chain dimension ascending.
// chainPartitionForCompare avoids copying BinaryRow in the comparator hot path.
deltaPartitionsInGroup.sort(
(a, b) ->
chainPartitionComparator.compare(
Expand Down Expand Up @@ -430,18 +475,12 @@ public Plan plan() {
deltaScan.withPartitionFilter(selectedDeltaPartitions);
}

List<DataSplit> deltaSubSplits =
deltaScan.plan().splits().stream()
.map(s -> (DataSplit) s)
.collect(Collectors.toList());
List<DataSplit> snapshotSubSplits = new ArrayList<>();
List<Split> deltaSubSplits = deltaScan.plan().splits();
List<Split> snapshotSubSplits = Collections.emptyList();
if (partitionPairs.getValue() != null) {
snapshotScan.withPartitionFilter(
Collections.singletonList(partitionPairs.getValue()));
snapshotSubSplits =
snapshotScan.plan().splits().stream()
.map(s -> (DataSplit) s)
.collect(Collectors.toList());
snapshotSubSplits = snapshotScan.plan().splits();
}
splits.addAll(
ChainTableUtils.buildChainSplits(
Expand All @@ -453,7 +492,8 @@ public Plan plan() {
}
}
}
return new DataFilePlan<>(splits);
Plan plan = new DataFilePlan<>(splits);
return queryAuthResult == null ? plan : queryAuthResult.convertPlan(plan);
}

@Override
Expand Down Expand Up @@ -494,11 +534,11 @@ private Set<BinaryRow> preloadTargetSnapshotSplits(List<Split> splits) {
}

for (Split split : mainScan.plan().splits()) {
DataSplit dataSplit = (DataSplit) split;
DataSplit dataSplit = QueryAuthSplit.unwrapDataSplit(split);
HashMap<String, String> fileBucketPathMapping = new HashMap<>();
HashMap<String, String> fileBranchMapping = new HashMap<>();
for (DataFileMeta file : dataSplit.dataFiles()) {
fileBucketPathMapping.put(file.fileName(), ((DataSplit) split).bucketPath());
fileBucketPathMapping.put(file.fileName(), dataSplit.bucketPath());
fileBranchMapping.put(file.fileName(), options.scanFallbackSnapshotBranch());
}
splits.add(
Expand Down Expand Up @@ -526,6 +566,9 @@ private DataTableScan newFilteredScan(boolean snapshot) {
if (bucketFilter != null) {
scan.withBucketFilter(bucketFilter);
}
if (readType != null) {
scan.withReadType(readType);
}
return scan;
}
}
Expand Down Expand Up @@ -582,7 +625,8 @@ public TableRead withIOManager(IOManager ioManager) {

@Override
public RecordReader<InternalRow> createReader(Split split) throws IOException {
if (split instanceof ChainSplit || split instanceof DataSplit) {
Split wrappedSplit = QueryAuthSplit.unwrap(split);
if (wrappedSplit instanceof ChainSplit || wrappedSplit instanceof DataSplit) {
return fallbackRead.createReader(split);
}
throw new IllegalArgumentException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.paimon.table.source.ChainSplit;
import org.apache.paimon.table.source.DataSplit;
import org.apache.paimon.table.source.InnerTableRead;
import org.apache.paimon.table.source.QueryAuthSplit;
import org.apache.paimon.table.source.Split;
import org.apache.paimon.table.source.StreamDataTableScan;
import org.apache.paimon.table.source.TableRead;
Expand Down Expand Up @@ -209,7 +210,8 @@ public RecordReader<InternalRow> createReader(Split split) throws IOException {
// FallbackSplit (including FallbackDataSplit): use inherited fallback read logic
return fallbackRead.createReader(split);
}
if (split instanceof ChainSplit || split instanceof DataSplit) {
Split wrappedSplit = QueryAuthSplit.unwrap(split);
if (wrappedSplit instanceof ChainSplit || wrappedSplit instanceof DataSplit) {
return chainGroupRead.createReader(split);
}
// Other split types: use inherited fallback read logic
Expand Down
Loading
Loading