A query-auth.enabled table makes the server return a per-user row filter and column masking that a client is expected to apply. This client cannot apply them yet, so it refuses to read such a table at all — even for a user the server reports as unrestricted. This first slice fetches the authorization at scan-plan time and carries it to the read, so that user can read. A user with rules gets the same refusal as before.
Brief change log
Table::authorize_read asks the server once per plan, and TableScan::plan stamps the result on every split — Java wraps each split in a QueryAuthSplit for the same reason. TableRead::to_arrow then decides from the splits: each must carry a grant, obtained through this table's own handle, and unrestricted. Per split, not just the first, since split lists can be concatenated across plans.
Whether a table is query-auth is read from the server, not from the loaded handle: the option can be turned on after a table was loaded, and a cached false would skip authorization altogether. That check doubles as the first half of a freshness bracket around the auth call, which refuses a table that was re-created or evolved in the meantime, or whose identity the server no longer reports.
QueryAuthGrant keeps the response unparsed — parsing belongs with the code that applies it — and the request carries no explicit select, which the server expands to the real schema fields. Naming a reserved system column would fail its column-permission check and deny an otherwise authorized user, so a read that reaches one is refused client-side instead. A user authorized for a subset is therefore still refused, as today. AuthTableQueryResponse rejects unknown fields, unlike every other response: an absent field reads as "no rule", so a drifted protocol would otherwise look like an unrestricted grant.
Two refusals are deliberate: a restricted grant fails at planning, since a plan carries row counts and min/max that engines answer COUNT/MIN/MAX from without reading a row; and a time-travelled or branch copy is refused, since the server rules on the current schema. Only the query-auth part of ReadBuilder::new_read's gate moved to to_arrow, where the split's grant is visible; the engine-served type check stays there, since a table's declared type is known without a grant.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
A query-auth.enabled table makes the server return a per-user row filter and column masking that a client is expected to apply. This client cannot apply them yet, so it refuses to read such a table at all — even for a user the server reports as unrestricted. This first slice fetches the authorization at scan-plan time and carries it to the read, so that user can read. A user with rules gets the same refusal as before.
Brief change log
Table::authorize_read asks the server once per plan, and TableScan::plan stamps the result on every split — Java wraps each split in a QueryAuthSplit for the same reason. TableRead::to_arrow then decides from the splits: each must carry a grant, obtained through this table's own handle, and unrestricted. Per split, not just the first, since split lists can be concatenated across plans.
Whether a table is query-auth is read from the server, not from the loaded handle: the option can be turned on after a table was loaded, and a cached false would skip authorization altogether. That check doubles as the first half of a freshness bracket around the auth call, which refuses a table that was re-created or evolved in the meantime, or whose identity the server no longer reports.
QueryAuthGrant keeps the response unparsed — parsing belongs with the code that applies it — and the request carries no explicit select, which the server expands to the real schema fields. Naming a reserved system column would fail its column-permission check and deny an otherwise authorized user, so a read that reaches one is refused client-side instead. A user authorized for a subset is therefore still refused, as today. AuthTableQueryResponse rejects unknown fields, unlike every other response: an absent field reads as "no rule", so a drifted protocol would otherwise look like an unrestricted grant.
Two refusals are deliberate: a restricted grant fails at planning, since a plan carries row counts and min/max that engines answer COUNT/MIN/MAX from without reading a row; and a time-travelled or branch copy is refused, since the server rules on the current schema. Only the query-auth part of ReadBuilder::new_read's gate moved to to_arrow, where the split's grant is visible; the engine-served type check stays there, since a table's declared type is known without a grant.