Skip to content

feat(auth): apply query-auth masks to non-BLOB columns - #1077

Merged
JingsongLi merged 2 commits into
apache:mainfrom
plusplusjiajia:query-auth-column-masking
Oct 10, 2026
Merged

JingsongLi merged 2 commits into
apache:mainfrom
plusplusjiajia:query-auth-column-masking

Conversation

@plusplusjiajia

@plusplusjiajia plusplusjiajia commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Purpose

#513 applies the server's row filter but still refuses users with column masks. This adds masking for non-BLOB columns. BLOB column masks remain explicitly unsupported and will be handled separately.

Brief change log

The grant also carries the masks, parsed as Java does: a mask on an unknown column, one that reads another masked column, or one that changes the column type or can make a NOT NULL column null fails closed, and a mask on a system column is inert. A CAST mask is accepted only where Arrow and Java convert alike, including exact signed integer widening into a compatible target column.

Planning excludes masked-column predicates from file, bucket and partition pruning. Those predicates are evaluated after masking, including partition-key predicates, and limits are not pushed past them. DataFusion treats query-auth filters as inexact at the provider boundary. to_arrow masks after filtering on stored values, applies the caller's predicates to masked values, and projects back.

Chunk shuffle continues to accept unmasked partition filters on authorized tables. Predicates on masked columns are refused after the catalog grant is known.

Tables containing BLOB columns can use masks on their ordinary columns. Safe ordinary predicates run after authorization and before masking; existing BLOB payload resolution stays after row authorization.

String masks borrow inputs and build concatenated output directly in Arrow buffers. ASCII case conversion operates on the column buffer; Unicode conversion preserves context-sensitive casing.

Tests

Unit tests cover mask transforms, refused casts, integer bounds, nulls, Unicode and sliced arrays. Mock-server tests cover masked predicates, bucket and partition keys, merged primary-key rows, a row filter and mask sharing a column, and source-column retention across overlapping partial files. Chunk-shuffle tests cover append and data-evolution tables, both planning APIs, and unrestricted and restricted grants. BLOB masks are rejected even when the masked column is not projected. DataFusion queries verify that WHERE and aggregates see masked values.

API and Format

No API or format change.

Documentation

The Query Authorization section in docs/src/sql.md covers masks.

@plusplusjiajia
plusplusjiajia force-pushed the query-auth-column-masking branch 8 times, most recently from a8a2091 to 3c1d459 Compare October 9, 2026 08:29
@plusplusjiajia plusplusjiajia changed the title feat(auth): apply query-auth column masking on read feat(auth): apply query-auth masks to non-BLOB columns Oct 9, 2026
@plusplusjiajia
plusplusjiajia force-pushed the query-auth-column-masking branch from 3c1d459 to 4bd05f2 Compare October 9, 2026 09:43
@plusplusjiajia
plusplusjiajia marked this pull request as ready for review October 9, 2026 09:53

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one compatibility regression; the verified reproduction and suggested fix are inline.

Comment on lines +69 to 70
if partition_keys.is_empty() || table.schema().core_options().query_auth_enabled() {
(None, filter.split_and())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve partition-only filters when enabling chunk shuffle

With query-auth.enabled=true, this branch leaves even a pure partition predicate in data_predicates. TableScan::with_chunk_shuffle() still rejects any nonempty data_predicates, so a REST-loaded append table with an unrestricted grant now fails on builder.with_filter(partition_predicate).new_scan().with_chunk_shuffle(7, 2) with chunk_shuffle only supports partition predicates, before authorization can restore partition pruning. Python's with_chunk_shuffle is affected as well.

I verified that the same test using real Parquet files and a mock REST catalog passes on base d8205a0 (including planning and reading) and fails on head 4bd05f26. Please check shuffle compatibility after authorization has separated the safe partition predicates, or distinguish partition-only residuals from actual data predicates, so unmasked partition filters remain supported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and added regression tests for append and data-evolution tables.

@plusplusjiajia
plusplusjiajia force-pushed the query-auth-column-masking branch from 5e6faa4 to f9769f7 Compare October 10, 2026 00:11

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@JingsongLi
JingsongLi merged commit 637386b into apache:main Oct 10, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants