Row-Level and Column-Level Permissions in Internal Database Tools Without Full DB Access
Enforce permissions at the database level so exports and AI queries can't bypass access controls.

Least-privilege access control for internal database tools requires row-level rules for which records people can see, column-level rules for which fields they can read, and user identity passed through every query path. These controls let sales and support work with production data without giving them full database access. Dashboard permissions alone cannot protect sensitive records when exports, scheduled reports, or AI-generated queries use broader permissions.
Why giving non-technical teams direct database access is structurally risky without granular permissions
Self-serve access has a good purpose. Product, sales, support, and ops teams need a way to get answers without taking every question to a data or engineering team. Giving them a way to explore production data removes that dependency, but it also changes who runs queries. Access once concentrated among analysts spreads across people with different jobs and different reasons to see a record. The permission model has to express those differences.
A read-only credential lowers write risk but leaves read risk open, so someone who cannot change a salary may still be able to read it. A shared BI login has a related defect because it treats different people as the same reader. Neither model distinguishes access to a tool from entitlement to the data behind it. Read-only is not the same as need-to-know.
Hiding a dashboard or removing a menu item offers a narrower kind of protection. What happens when the same user exports data, explores another view, or asks a natural-language question? If the restriction exists only in the presentation layer, the next interface can step around it. The structural weakness is the distance between the visible control and the query that retrieves the data. Granular permissions close that distance by controlling access to records and fields as well as the screens that show them.
What row-level security actually does at the query level
Row-level security, or RLS, filters records according to a user's identity, role, or attributes. Its working part is a predicate, a condition a row must satisfy to be visible. The filter belongs to the access policy. A sales rep does not need to remember to request only their accounts, because the rule limits which accounts the query can return.
PostgreSQL defines policies per table and evaluates them at query time. A USING expression governs visible rows, while WITH CHECK governs which rows a user can insert or update. These expressions can reference the current user, session variables, or application-supplied context. Snowflake attaches a row access policy to a table or view, using a policy expression that can consult a mapping table of roles and entitlements or context functions such as CURRENT_ROLE(). Neither approach needs a separate copy of the table for each audience.
The location of that predicate changes the guarantee. When it runs inside the database against the rep's identity, a dashboard, spreadsheet export, and AI query encounter the same restriction. When an application instead appends a WHERE clause, its code is responsible for remembering and constructing the rule. A missing or malformed clause can return unauthorized rows. The database cannot enforce an application rule it never receives.
There is a subtler limitation, however. Zachary Espiritu and David Cash's USENIX paper describes how post-filtering access control can leak hidden data through query timing. PostgreSQL optimizer behavior can reveal whether queries match unseen rows, and rich queries can amplify that signal into full record reconstruction through binary search. Database-native RLS remains much stronger than application filtering, but high-sensitivity deployments cannot treat it as perfect isolation.
How do you restrict which columns a non-technical team can see?
Column-level security answers a different question from RLS. A support agent may be allowed to see a customer's record without being allowed to read every field in it. Row filtering sets which records a user can see, while column privileges limit which fields they can read, such as salary, SSN, or credit card number. Neither mechanism substitutes for the other. Both need deliberate configuration.
Database column privileges provide a way to grant access only to permitted fields. Supabase offers column-level security through PostgreSQL privileges as a Feature Preview, which it does not recommend for most users. This design avoids a costly workaround that uses CDC replication to strip sensitive fields into separate tables for each audience. That workaround turns an authorization decision into a replication job, with duplicated tables, drift risk, and operational overhead.
Sometimes the field must stay in the result. SQL Server's Dynamic Data Masking obscures column values at query time without encrypting the stored data, allowing a developer to see masked SSNs while an authorized application sees real values. Exclusion instead withholds access to the field itself. Encryption addresses protection of the stored values, so these options are not simply interchangeable settings on one security dial.
Thales CipherTrust Database Protection uses database views and triggers for transparent field-level encryption. It supports local encryption on the database server or remote encryption within CipherTrust Manager, but remote encryption does not mean plaintext can never be processed on the database server. ALTR addresses a different scaling problem through tag-based masking, applying a matching policy to columns tagged as PII or PCI that are connected and monitored by the platform. Tags replace repeated policy-per-column work while leaving row-level rules in place.
Where enforcement should live — database engine, tool layer, or semantic layer — and why the answer changes by threat model
Enforcement choices depend on which failure the design must withstand. Database-engine enforcement offers the strongest protection against tool-layer bugs because a misconfigured dashboard query, direct SQL session, and AI-generated statement all have to pass through the database policy. That makes database RLS the right base for everyday internal access rules, such as keeping reps within their own accounts. The timing side-channel qualifies that choice for high-sensitivity multi-tenant systems.
Tool-layer filtering has a different weak point. Its authorization depends on application code adding the right clause to every relevant query. Avoiding engine-side policy evaluation may offer a performance advantage, but that trade-off leaves governance dependent on the application never omitting the filter. A semantic layer instead defines metrics, dimensions, joins, and row rules in a shared model. Dashboards, spreadsheet exports, and natural-language interfaces can then inherit consistent rules when they read through that model.
Yet a shared model is not an unavoidable gate. A tool connected directly to the warehouse can bypass semantic enforcement unless database policies back it. For most internal-tool deployments, database RLS and column privileges therefore form the baseline, while semantic enforcement adds consistency across surfaces. The semantic layer provides defense in depth.
Identity is the hinge between these layers. If the tool connects as one service account without passing user context, the database evaluates access using that account's permissions. SAML, OIDC, or JWT identity mapping must connect to session variables or application roles that the database policy can use. A buyer comparing platforms should ask how identity gets to the policies used for interactive queries, exports, scheduled jobs, and AI requests. A permissions badge in a feature table cannot answer that question.
How internal tools create new enforcement gaps that raw database permissions don't solve on their own
An internal tool is more than a report viewer. Editable records, field-level actions, and multi-table joins create an interaction model that raw database permissions do not explain to users. A support agent may see a record and assume every visible field is editable, only to encounter a rejected write. PostgreSQL's WITH CHECK can enforce the row constraint, but the interface still needs to make permitted edits intelligible.
Joins also widen the scope of permission design. Limits on sensitive columns in one table leave a coverage gap for fields brought from another table into a unified view. The tool's combined view needs permissions that account for all its source fields. Workspace Admin, Member, and Contributor roles likewise govern interface actions only meaningfully when mapped to the appropriate database identities and roles.
Exports and scheduled reports expose an especially common discontinuity. A dashboard may show only a user's accounts while an export job uses a service account that can see more. The screen is filtered, but the delivery path is not. AI interfaces pose the same identity question when they generate and execute SQL. Query accuracy cannot repair that mismatch, because even correct SQL can retrieve too much when it runs under the wrong identity.
How to design permissions before users accumulate habits and dashboards multiply
Define what a sales rep, support agent, finance analyst, and product manager should see by table, column, and row predicate. Make those decisions before dashboards multiply and users build workarounds around broad access. Retrofitting restrictions then becomes an integration and rework problem. The first deliverable is an access model.
Next, connect those roles to identity provider groups through SAML groups, OIDC claims, or automatically provisioned roles. Access can then follow a person's group when they change teams instead of waiting for manual updates across reports. Write policies that use session variables, application roles, or entitlement mappings. A mapping table can support hundreds of users without embedding each username in the policy. Classify PII, PCI, and other sensitive columns during schema design so field restrictions are part of that same model.
For Snowflake cross-account sharing, check the context function explicitly. CURRENT_ROLE() returns NULL for consumer accounts inside a secure UDF, secure view, or masking or row access policy assigned to a shared table or view. Use CURRENT_ACCOUNT() and an account entitlement mapping in the row access policy instead. Snowflake also recommends IS_DATABASE_ROLE_IN_SESSION() as an alternative workaround. This is a design-time distinction with production consequences.
Before production, test common query patterns with PostgreSQL's EXPLAIN ANALYZE or Snowflake query profiling. Look for unindexed policy columns or entitlement lookups, volatile function calls, and correlated subqueries. Indexing resolves the index-related bottleneck. Establish ongoing validation at this stage too. Supabase's Security Advisor uses the open-source Splinter linter to scan for tables without RLS, overly permissive policies, and exposed sensitive columns, providing a concrete model for catching configuration gaps.
What ongoing governance looks like once granular permissions are live
Permissions do not stay correct by standing still. Roles, users, and schemas change around them, so audit logging and regular access reviews are the operational counterpart to policy enforcement. Logs record who queried what, when, and through which surface, making anomalies investigable without relying on users to report them. Reviews should follow the identity provider groups used to grant access.
Changes to schemas need as much attention as changes to staff. New columns can introduce sensitive data without the restrictions that protected existing fields, depending on how privileges are configured. Reviewing new columns against the sensitivity classification before production catches that drift at its source. GDPR and CCPA data minimization requirements give this work a compliance purpose as well. Returning only records and fields needed for a user's job makes minimization an output of access control.
The brief's regulatory milestones include EU AI Act high-risk enforcement beginning December 2, 2027, and January 1, 2027 effective dates for Colorado's AI Act and New York's RAISE Act. Organizations covered by these requirements need to audit AI-generated queries too. The operational value extends beyond compliance, however. In a database-native internal tool, the same logs show common queries, heavily accessed tables, and places where permissions need refinement.
When non-technical teams can explore within trustworthy boundaries, they can use the data without turning every question into an engineering ticket. Governance lets self-service grow without granting everyone the keys to every record.


