Row-level security and column-level security are two distinct mechanisms for controlling access to data within a data lakehouse. Row-level security restricts which rows a user can see based on their identity or role, while column-level security restricts which columns are visible or queryable. Together, they form the foundation of fine-grained access control in modern data platforms.
Both mechanisms address a different dimension of the same problem: ensuring that users see only the data they are authorized to access. Which one you need, and when, depends on your data model, your compliance requirements, and how sensitive information is structured across your tables. The sections below walk through each in detail, along with the tools and architectural decisions that make them work in an open-source lakehouse.
How do row-level and column-level security differ from each other?
Row-level security filters the records a user can access, while column-level security filters the fields they can see. Both operate on the same underlying table, but they cut across different axes of that data. A user subject to row-level security might see all columns but only the rows belonging to their region. A user subject to column-level security might see all rows but with salary or national ID columns hidden or masked.
The distinction matters because the threat model is different in each case. Row-level security is primarily about data partitioning: making sure that a sales representative in Germany does not see records belonging to the French team, or that a healthcare provider only accesses records for their own patients. Column-level security is primarily about data sensitivity: protecting personally identifiable information (PII), financial figures, or regulated attributes that should not be visible to most users even if they have legitimate access to the rest of the table.
In practice, you will often need both. A table containing customer transactions might require row-level filtering by region and column-level masking of payment card details at the same time.
Why does a data lakehouse need fine-grained access control?
A data lakehouse consolidates large volumes of structured and semi-structured data in a single platform. That makes broad access permissions a real security and compliance risk. Without row- and column-level controls, one misconfigured permission can expose sensitive records across an entire dataset to users who should only see a fraction of it.
Lakehouses are built to serve many different consumers at once: analysts, data scientists, ML engineers, and operational applications. Each of these consumers has a different authorization scope. Coarse-grained controls, like granting read access to an entire table or schema, cannot express those distinctions accurately.
Regulatory frameworks add further pressure. Organizations operating under GDPR, HIPAA, or sector-specific rules in financial services must demonstrate that access to personal or sensitive data is restricted to authorized parties. Fine-grained access control is what makes those guarantees enforceable at the data layer, not just at the application layer. Without it, data governance stays a policy document rather than an enforced reality.
How does row-level security work in a data lakehouse?
Row-level security works by attaching a filter condition to a query at execution time, based on the identity of the requesting user or their assigned role. The query engine evaluates this filter before returning results, so the user never sees rows they are not permitted to access, regardless of what SQL they write.
In a lakehouse context, this is typically implemented in one of two ways. The first uses policies defined at the catalog or metastore level, which the query engine then enforces when it plans and executes a query. The second embeds the filter logic directly in views or materialized access layers, so users query a view rather than the base table.
A common pattern is to store a mapping of user attributes to data partitions, a table that maps each user or role to a set of allowed values in a given column, such as a region code or a tenant identifier. The query engine joins this mapping against the base table at query time and returns only the matching rows. This keeps the access logic centralized and auditable, rather than scattered across application code.
How does column-level security work in a data lakehouse?
Column-level security works by either blocking access to specific columns entirely or by masking their values based on the requesting user’s role. A blocked column returns an error or is simply absent from the result set. A masked column returns a transformed value, a redacted string, a hash, or null, instead of the actual data.
Masking is particularly useful when a table is shared across teams with different authorization levels. An analyst might see a customer’s name and purchase history, while the same query run by a less privileged user returns the name as a masked string. The underlying data is unchanged; only what is returned to the user differs.
Column-level controls are defined either in the query engine’s access policy layer or in the table format metadata. Some table formats allow column encryption at the storage level, which means the data is physically protected even if someone bypasses the query engine and reads the raw files directly. This matters when your lakehouse stores data in open file formats on object storage, where direct file access is technically possible.
What tools enforce row and column security in open-source lakehouses?
Several open-source tools enforce row-level and column-level security in a lakehouse environment. The most widely used are Apache Ranger, Open Policy Agent (OPA), and the access control layers built into query engines such as Trino.
- Apache Ranger provides a centralized policy management framework that integrates with Hive Metastore, HDFS, and various query engines. It supports both row-level filtering and column masking through its policy engine, and logs access decisions for audit purposes.
- Trino implements column-level security natively through its connector framework and supports row filtering and column masking via system access control plugins. Policies can be defined in configuration files or delegated to an external policy engine.
- Open Policy Agent (OPA) is a general-purpose policy engine that integrates with query engines and catalog services to evaluate access decisions at runtime. It decouples policy logic from application code and supports complex, attribute-based rules.
- Apache Iceberg supports column-level encryption at the table format level, meaning sensitive columns can be encrypted before data reaches object storage. This protects data from direct file access, independent of whatever controls the query engine applies.
The right combination depends on your query engine, your catalog, and whether you need enforcement at the storage layer, the catalog layer, or both. Most production lakehouses use at least two of these layers together.
What’s the difference between security at the catalog layer versus the query engine?
Security enforced at the catalog layer controls what metadata and table definitions a user can discover and access. Security enforced at the query engine layer controls what data is actually returned when a query runs. These are complementary but distinct enforcement points, and relying on only one of them leaves gaps.
Catalog-layer security, implemented through tools such as Apache Polaris or a Hive Metastore with Ranger integration, prevents unauthorized users from even seeing that a table exists. This is useful for multi-tenant environments where different teams should have isolated namespaces. But catalog-layer controls do not protect the underlying files if a user has direct access to object storage.
Query engine-layer security, implemented in Trino or Apache Spark™, enforces row- and column-level policies at query execution time. It operates on the data itself, not just the metadata. The limitation is that it only protects data accessed through that specific query engine. If data is stored in an open format such as Apache Parquet on object storage, a user with storage credentials can bypass the query engine entirely and read the raw files.
This is why production lakehouse deployments typically combine both layers: catalog-level controls for namespace isolation and discoverability, query engine-level controls for runtime filtering and masking, and optionally storage-level encryption for physical data protection.
When should you use row-level versus column-level security?
Use row-level security when different users should access different subsets of records within the same table, based on attributes such as region, tenant, department, or data classification. Use column-level security when all users may legitimately query the same records, but specific fields within those records contain sensitive data that should be restricted or masked for certain roles.
A few concrete scenarios help clarify the choice:
- Row-level security fits when you have a multi-tenant SaaS platform storing all customer data in shared tables, a financial institution where analysts should only see transactions from their own business unit, or a healthcare system where clinicians access only their own patients’ records.
- Column-level security fits when a data table contains PII columns such as national ID numbers or email addresses that analysts do not need for their work, when salary or compensation data lives in the same table as headcount metrics, or when regulatory requirements mandate that certain fields are only accessible to compliance officers.
- Both together are appropriate when you have a customer dataset where support agents should see only their assigned accounts and should never see payment card details, regardless of which account they are viewing.
The deciding factor is usually the shape of your data model and the nature of the sensitivity. Row-level controls address who owns or is associated with a record. Column-level controls address what kind of information a field contains.
How Stackable supports fine-grained access control in your lakehouse
The Stackable Data Platform (SDP) provides the infrastructure layer for deploying and operating the open-source tools that enforce row-level and column-level security in a Kubernetes-native environment. Rather than locking you into a proprietary access control model, the SDP lets you compose the right combination of tools for your specific requirements.
- The SDP includes a Stackable Operator for Apache Kafka® and operators for Trino, Apache Spark™, and other components, each configured to integrate with your chosen policy engine, whether that is Apache Ranger, OPA, or native connector-level controls.
- Access policies can be managed as code alongside your platform configuration, making them auditable, version-controlled, and reproducible across environments.
- Because the SDP is 100% open source and cloud-agnostic, your access control policies and data remain under your control, whether you deploy on-premises, in a private cloud, or in a hybrid setup.
- The platform supports data sovereignty by design: no data leaves your infrastructure, and no vendor holds the keys to your policy engine.
If you are building or migrating a data lakehouse and need to implement fine-grained access control without inheriting a proprietary stack, talk to the Stackable team about how the SDP fits your architecture.
Related Articles
- How does a data lakehouse differ from a data warehouse?
- How do data contracts enable interoperability between domains?
- How do you migrate a data platform with minimal disruption to analysts?
- How do you avoid vendor lock-in when choosing a new data platform?
- How to migrate a legacy data platform step by step?