StaticAssertThrows a FieldSecurityError when FindViolation refuses sql. The error names
entityName, the entity the caller is reading, never the entity a subquery reaches, so a
refusal says nothing a "no such column" error would not.
StaticAssertChecks a whole statement, such as a saved query's rendered SQL, against the denied fields of
every entity it reads. The entities come from the base views and tables the text names, plus
readEntities. References are checked wherever they appear: the select list, WHERE, JOIN,
GROUP BY, ORDER BY and HAVING. For a user carrying any field denial, a relation the catalog
does not know is refused too (FieldSecuritySQLContext.RefuseUnknownRelations).
Throws a FieldSecurityError on the first violation, naming the entity whose denial
it reached (or fallbackEntityName when none).
OptionalreadEntities: readonly EntityInfo[]StaticCatalogEvery field user may not read across catalog: one entityID:field,field entry per
entity on which the user is denied something, sorted. '' for a user denied nothing.
Memoized exactly as CatalogHasDenialsForUser.
StaticCatalogTrue when user is denied at least one field on any entity of catalog.
Memoized per (catalog, user): after the first call it costs a few comparisons. The memo is
discarded when the catalog array changes length, when the field-security generation moves
(an entity rebuilt, a flag assigned, a cache invalidated; see
CurrentFieldSecurityGeneration) or when the user's roles change, which are everything the
answer depends on. A catalog swapped for a new array starts a new memo.
StaticErrorFor a user carrying field denials on entity, the FieldSecurityError a database's
"no such column" rejection becomes, so a column that does not exist and a column that is
denied produce the same error. Null when the user carries no denials there, or the error is
not a "no such column" rejection.
Reads SQL Server's Invalid column name 'x' and The multi-part identifier "a.x" could not be bound, and PostgreSQL's column "x" does not exist and column a.x does not exist.
StaticErrorThe error a violation raises, naming entityName.
StaticFindThe first reason the sort fragment orderBy must be refused for context.User, or null.
Everything FindViolation refuses, and, for a user denied a field on
context.TargetEntity, a sort item that is a column position (3 DESC). A position names a
column of the select list without spelling it, and an entity-object read selects every
column, so ORDER BY 3 can sort by a denied field. It is refused as a whole-row construct.
StaticFindThe first reason sql must be refused for context.User, or null when it may run.
A filter, sort or aggregate fragment, or a whole statement
The user, dialect, catalog and the entities the SQL is known to read
StaticRelationThe entities whose base view or base table sql reads as a relation: a name after FROM, JOIN
or APPLY, or in a FROM list, wherever it appears (subqueries and CTE bodies included), with
schema qualifiers skipped. A name in a literal or a comment is not a relation. A bare name
shared by entities in several schemas yields each of them, so the set never misses a read.
A relation inside a parenthesized join and PostgreSQL's FROM ONLY x count too. Null when
the text cannot be read the way the database reads it.
StaticReportedThe name a "no such column" error would give for name as sql writes it, so a denied
column is reported exactly as an unknown one. On PostgreSQL, MJ quotes a capitalized or
dot-qualified word before the database sees it (AutoQuoteLeavesWordBare), so the error
names such a word as written; a word left bare is folded to lower case. A quoted name, and
every name on SQL Server, is reported as written. The first token spelled name decides.
The caller's SQL
The reference as the database reads it
The dialect the SQL runs under
StaticUserTrue when user is denied at least one field on at least one of entities.
SECURITY — the field-level security gate for SQL text a caller authors: RunView filters, sorts and aggregate expressions, a saved view's stored clauses, dataset item filters, and the rendered SQL of a saved query.
Output projection alone is not enough. A user denied
Salarywho can writeExtraFilter: "Salary > 200000", sort by it, or aggregateMIN(Salary)reconstructs its values from which rows come back. This gate refuses such text before it reaches the cache or the database, for every caller: client requests, agents and actions alike.How it reads the text
The text is split into tokens with the database's own lexical rules (
ScanSQLForScreening), so a name inside a string literal or a comment is not a reference, and a[bracketed]or"quoted"name is read as the bare name it is. Every unquoted word and quoted identifier is then compared with every denied field of every entity the SQL reads:A name matches a denied field by the rule
IdentifierMatchesstates, or when the two are equal after Unicode compatibility folding, accent stripping and case folding, the comparisons a database collation can make. So a look-alike spelling that the database would still resolve to the denied column is refused too. Each name is compared on its own, so one denied name inside another can never hide it.A reference qualified by an alias or view name that binds only to entities whose denied set does not hold that name is not a reference to a denied field. An unknown qualifier is checked against every restricted entity.
Whole-row constructs
For a user denied something on an entity the SQL reads, constructs that read a whole row without naming its columns are refused:
SELECT *andalias.*(except directly insideEXISTS (…)whose body has no set operation or row-picking ORDER BY),FOR JSON,FOR XML, any function of*other thanCOUNT(*)/COUNT_BIG(*)(for exampleCHECKSUM(*)),NATURAL JOIN, a column alias list on such a relation, and a relation or its alias used as a value (PostgreSQL's whole-row reference, as inrow_to_json(e)orTABLE vw). A user with no denials on the entities read is not affected; ordinary subqueries keep working.Failing closed
Text the scanner cannot read the way the database does is refused for any user who carries a denial on an entity the SQL could read. So is a name holding a control, format or default-ignorable character (a zero-width joiner, a direction mark), which a collation may give no weight to.
Unknown columns
A reference to a column that does not exist reaches the database, which rejects it. For a user carrying denials, FieldSecuritySQLGate.ErrorForInvalidColumn turns that rejection into the same FieldSecurityError a denied column gets, so the two cannot be told apart.