Patrones comunes para el filtrado de filas y el enmascaramiento de columnas

En esta página se describen patrones comunes para implementar directivas de filtro de fila y máscara de columna de ABAC.

Funciones de enmascaramiento compatibles con conversión

Azure Databricks convierte automáticamente la salida de la función de enmascaramiento para que coincida con el tipo de datos de la columna de destino. Consulte Conversión automática de tipos para máscaras de columna.

Los patrones siguientes ayudan a diseñar funciones de enmascaramiento compatibles con conversión.

Devolver un tipo que se puede convertir

Al enmascarar una columna, devuelva el mismo tipo de datos o un tipo que se pueda convertir a él. Compruebe los tipos de datos de las columnas que tiene como destino la directiva y compruebe que cada rama de la función devuelve un valor compatible.

-- Succeeds: Masks a DOUBLE column, returns DOUBLE in every branch
CREATE FUNCTION mask_salary(salary DOUBLE, user_role STRING)
RETURNS DOUBLE
RETURN CASE
  WHEN user_role IN ('admin', 'hr') THEN salary
  WHEN user_role = 'manager' THEN ROUND(salary / 1000) * 1000
  ELSE 0.0
END;

-- Fails: 'CONFIDENTIAL' cannot be cast to a DOUBLE column type
CREATE FUNCTION mask_salary_as_text(salary DOUBLE, user_role STRING)
RETURNS STRING
RETURN CASE
  WHEN user_role IN ('admin', 'hr') THEN CAST(salary AS STRING)
  ELSE 'CONFIDENTIAL'
END;

Evitar desbordamiento numérico

Cuando una función mask acepta y devuelve un tipo numérico más amplio que la columna de destino, el resultado se devuelve automáticamente al tipo de columna. Si el valor devuelto supera el rango del tipo más estrecho, la conversión se desborda y la consulta falla en tiempo de ejecución.

-- The target column is TINYINT (max 127). The input is upcast to BIGINT
-- for the function. Adding 1000 produces a BIGINT result that overflows
-- when cast back to TINYINT.
CREATE FUNCTION mask_score(score BIGINT)
RETURNS BIGINT
RETURN score + 1000;

Uso de VARIANT para varios tipos de columna

Véase Enmascarar múltiples tipos de columna con una sola función.

Compatibilidad de conversión de pruebas

Pruebe las funciones de enmascaramiento con diferentes patrones de datos.

SELECT CAST(mask_salary(salary, 'admin') AS DOUBLE) FROM employees;
SELECT CAST(mask_salary(salary, 'manager') AS DOUBLE) FROM employees;
SELECT CAST(mask_salary(salary, 'viewer') AS DOUBLE) FROM employees;

Enmascarar múltiples tipos de columnas con una sola función

Una única UDF de enmascaramiento que acepta y devuelve un VARIANT puede enmascarar columnas de diversos tipos de datos, lo que reduce la cantidad de UDF y políticas que debe mantener. Azure Databricks convierte el valor de columna a VARIANT antes de que se ejecute la función, luego convierte el valor de retorno al tipo de columna siguiendo las reglas ANSI SQL.

Dentro de la función, usa schema_of_variant() para inspeccionar el valor y ramificar según su tipo. En cada rama, devuelves un valor que sea castible al tipo de columna de destino.

Enmascarar múltiples tipos numéricos

La máscara VARIANT más sencilla devuelve una única constante que Azure Databricks convierte al tipo de datos de cada columna. Una política que utiliza esta función puede enmascarar INT, DOUBLE, y DECIMAL columnas, sin una función separada para cada precisión:

CREATE FUNCTION mask_numeric(val VARIANT)
RETURNS VARIANT
DETERMINISTIC
RETURN 0::VARIANT;

Para variar el valor enmascarado por tipo, haz una bifurcación en función de schema_of_variant() y devuelve un valor adecuado para cada caso:

CREATE FUNCTION flexible_mask(data VARIANT)
RETURNS VARIANT
RETURN CASE
  WHEN schema_of_variant(data) = 'BIGINT' THEN 0::VARIANT
  WHEN schema_of_variant(data) = 'DATE' THEN DATE'1970-01-01'::VARIANT
  WHEN schema_of_variant(data) = 'DOUBLE' THEN 0.00::VARIANT
  ELSE NULL::VARIANT
END;

Los tipos enteros se ensanchan a BIGINT en un VARIANT, por lo que se ramifica en BIGINT lugar de INT o TINYINT.

Enmascarar columnas STRUCT, ARRAY y MAP

El VARIANT enfoque también enmascara columnas de tipo complejo, extendiendo el ejemplo numérico anterior a datos anidados. Una columna STRUCT, MAP o VARIANT llega a la función como un ARRAY, y Azure Databricks convierte lo que devuelve la función de nuevo al tipo declarado de la columna. El valor devuelto debe tener el mismo esquema que el valor de entrada; de lo contrario, la conversión falla y la consulta produce un error.

Note

Las columnas STRUCT son compatibles en Databricks Runtime 18.1 y superiores. Las columnas ARRAY y MAP se admiten en Databricks Runtime 19 y versiones posteriores. Este casting funciona únicamente dentro de políticas de mascarillas de columna y filtro de fila ABAC, no en SQL general ni en máscaras a nivel de tabla.

La siguiente función de ejemplo enmascara tipos específicos STRUCT, ARRAY, y MAP . Añade una WHEN rama para cada esquema que necesites enmascarar:

CREATE OR REPLACE FUNCTION generic_mask(val VARIANT)
RETURNS VARIANT
RETURN CASE
  -- STRUCT<id: INT, ssn: STRING>: keep id, redact ssn
  WHEN schema_of_variant(val) = 'OBJECT<id: BIGINT, ssn: STRING>' THEN
    to_variant_object(named_struct('id', val:id, 'ssn', 'xxx-xx-xxxx'))
  -- ARRAY<STRING>: return a single redacted element
  WHEN schema_of_variant(val) = 'ARRAY<STRING>' THEN
    to_variant_object(array('redacted'))
  -- MAP<STRING, STRING>: redact every value
  WHEN schema_of_variant(val) = 'OBJECT<key1: STRING, key2: STRING>' THEN
    to_variant_object(map('key1', 'redacted', 'key2', 'redacted'))
  ELSE NULL::VARIANT
END;

Cada WHEN rama hace dos cosas, descritas en las siguientes secciones:

  1. Identifica el esquema VARIANT. Cada esquema VARIANT necesita su propia lógica de enmascaramiento, por lo que la función se bifurca según la cadena del esquema que devuelve schema_of_variant(val) para aplicar la redacción adecuada a cada esquema.
  2. Reconstruye el valor enmascarado. Crea un valor redactado del tipo de la columna y envuélvelo en to_variant_object() para devolver un VARIANT.

Un valor cuyo esquema no coincide con ninguna rama cae a través de ELSE.

Identificar el esquema VARIANT

Hacer coincidir con el esquema del valor VARIANT, no con el tipo declarado de la columna: la conversión a VARIANT normaliza los datos, por lo que el esquema puede diferir de la definición de la columna. Los siguientes ejemplos muestran casos comunes:

Tipo de columna schema_of_variant() resultado Notes
ARRAY<STRING> ARRAY<STRING>
ARRAY<INT> ARRAY<BIGINT> Los tipos enteros se ensanchan a BIGINT.
STRUCT<id: INT, name: STRING> OBJECT<id: BIGINT, name: STRING> las columnas STRUCT y MAP pasan a ser OBJECT.
STRUCT<name: STRING, id: INT> OBJECT<id: BIGINT, name: STRING> Los campos se ordenan alfabéticamente por clave, no por orden de declaración.
ARRAY<STRUCT<id: INT, name: STRING>> ARRAY<OBJECT<id: BIGINT, name: STRING>>
MAP<STRING, STRING> Decisión {a: 'x'} OBJECT<a: STRING> Varía según la fila: las claves de cada fila determinan el esquema.
MAP<STRING, STRING> Decisión {a: 'x', b: 'y'} OBJECT<a: STRING, b: STRING> Una fila diferente de la misma columna produce un esquema distinto.
MAP<STRING, STRING> Decisión {a: null} OBJECT<a: VOID> Un valor nulo se convierte VOIDen .
MAP<STRING, STRUCT<id: INT, name: STRING>> Decisión {a: ...} OBJECT<a: OBJECT<id: BIGINT, name: STRING>, ...>

Reconstruir el valor enmascarado

Para cada esquema que emparejes, construye un valor redactado con la misma estructura y luego envuélvelo en to_variant_object():

  • Úsalo named_struct() para reconstruir un STRUCT, manteniendo los campos que quieres y reemplazando el resto.
  • Use array() para reconstruir un ARRAY.
  • Úsalo map() para reconstruir un MAP con valores censurados.

Azure Databricks convierte de nuevo el valor devuelto VARIANT al tipo declarado de la columna, por lo que cada valor reconstruido debe poder convertirse a ese tipo.

Prueba una máscara VARIANT

Antes de adjuntar una función a una política, puedes probarla en una consulta sencilla para confirmar la salida enmascarada. La siguiente función enmascara una ARRAY<STRUCT<id: BIGINT, value: FLOAT>> columna y genera un error para cualquier otro esquema:

CREATE OR REPLACE FUNCTION mask_points(v VARIANT)
RETURNS VARIANT
RETURN CASE
  WHEN schema_of_variant(v) = 'ARRAY<OBJECT<id: BIGINT, value: FLOAT>>' THEN
    to_variant_object(array(named_struct('id', 1, 'value', 2.1)))
  ELSE raise_error('Unexpected VARIANT schema: ' || schema_of_variant(v))
END;

Convierte la columna con to_variant_object(), aplica la función de enmascaramiento y usa variant_get() para convertir el enmascarado VARIANT de nuevo al tipo de columna. Esto refleja lo que hace la política en tiempo de ejecución:

SELECT variant_get(mask_points(to_variant_object(points)), '$', typeof(points)) AS masked
FROM my_catalog.my_schema.my_table;

Limitations

  • Una columna compleja que contiene TIME, VARIANT, GEOGRAPHY, GEOMETRY o VARCHAR no se puede enmascarar con CHAR.
  • MAP las claves deben ser STRING. Las columnas escritas MAP<INT, ...>, MAP<DATE, ...>, y así sucesivamente no se convierten.

Impedir el acceso hasta que se etiquetan las columnas confidenciales

Un patrón de gobernanza común es controlar el acceso en función de si los datos se han clasificado. Puede implementarlo con una etiqueta y directivas restrictivas predeterminadas que apliquen distintos niveles de protección en función del estado de clasificación.

  1. Aplique una etiqueta como classification : unverified a todos los objetos nuevos de forma predeterminada, mediante la automatización o la herencia de etiquetas aplicando la etiqueta en el nivel de catálogo o esquema, de modo que las nuevas tablas agregadas al catálogo o esquema hereden automáticamente la etiqueta.
  2. Cree una directiva de filtro de fila que bloquee el acceso a las tablas etiquetadas classification : unverified.
  3. Cree una directiva de máscara de columna que enmascara columnas confidenciales en tablas donde la classification : unverified etiqueta ya no esté presente.
  4. Cuando un administrador de datos completa la clasificación, actualiza la etiqueta. La directiva de bloqueo ya no coincide y la directiva de enmascaramiento surte efecto.
-- Block access to unverified tables for all non-admin users
CREATE FUNCTION catalog.schema.block_all() RETURNS BOOLEAN
  RETURN FALSE;

CREATE POLICY block_unverified
ON CATALOG my_catalog
ROW FILTER catalog.schema.block_all
TO `account users` EXCEPT `data_admins`
FOR TABLES
WHEN has_tag_value('classification', 'unverified');

Para proteger los datos confidenciales después de clasificarlos, defina una directiva de máscara de columna que surte efecto cuando la classification : unverified etiqueta ya no esté presente:

CREATE FUNCTION catalog.schema.mask_pii(val STRING)
RETURNS STRING
RETURN '***';

CREATE POLICY mask_reviewed_pii
ON CATALOG my_catalog
COLUMN MASK catalog.schema.mask_pii
TO `account users`
EXCEPT `data_admins`
FOR TABLES
WHEN NOT has_tag_value('classification', 'unverified')
MATCH COLUMNS (has_tag_value('pii', 'name') OR has_tag_value('pii', 'address')) AS m
ON COLUMN m;

Revelado parcial sin regex

Mostrar parte de un valor confidencial mediante operaciones de cadena en lugar de regex. El enmascaramiento basado en expresiones regulares examina todo el valor de cada fila, lo cual es costoso en campos de texto grandes (consulte Evitar enmascaramiento de expresiones regulares en campos de texto grandes).

CREATE FUNCTION mask_ssn(ssn STRING, show_last INT) RETURNS STRING
DETERMINISTIC
  RETURN CONCAT('***-**-', RIGHT(ssn, show_last));

Hash coherente (pseudonimización determinista)

El hash coherente (también denominado seudonimización determinista) reemplaza los datos confidenciales por un valor hash que es el mismo en varias tablas. Al marcar una función como DETERMINISTIC se indica al motor que la función siempre devuelve el mismo resultado para la misma entrada, lo que ayuda a optimizar la consulta. Consulte Uso de expresiones deterministas y seguras para errores.

La siguiente función aplica un hash coherente a un valor de cadena y usa un version parámetro para admitir la rotación de claves. Incremente el version número mediante la cláusula de la directiva USING COLUMNS para generar nuevos hashes sin interrumpir los datos históricos que usaron la versión anterior. La función concatena el valor original con el número de versión antes de aplicar hash, por lo que la misma entrada con la misma versión siempre genera el mismo hash.

CREATE FUNCTION pseudonymize(val STRING, version INT) RETURNS STRING
DETERMINISTIC
  RETURN SHA2(CONCAT(val, CAST(version AS STRING)), 256);

Enmascarar una columna basada en los atributos del usuario que consulta

Importante

Los atributos de identidad en las políticas ABAC están en Beta. Para utilizarlas, un administrador de cuenta debe:

Una política de mascarilla en columna puede usar los atributos de identidad del usuario que consulta para enmascarar datos sensibles sin requerir grupos dedicados. Por ejemplo, puede mantener los datos sin enmascarar para los usuarios con department = HR y enmascararlos para todos los demás.

Estos patrones requieren atributos de identidad provisibles a tus usuarios desde tu proveedor de identidad, y las funciones se comportan de forma diferente a las condiciones solo de etiqueta en aspectos que afectan a cómo escribes la política. Antes de usarlos, revisa las funciones de atributos de identidad y atributos de identidad.

Importante

Las funciones devuelven false cuando el usuario no tiene ningún valor para el atributo o cuando la clave del atributo no existe. Escribe la condición de modo que este false resultado restrinja el acceso en lugar de concederlo. Niega la coincidencia con NOT para que la máscara se aplique a menos que el atributo coincida. Por ejemplo, WHEN NOT has_identity_attribute_value('department', 'HR') enmascara la columna para todos excepto para los usuarios cuyo departamento es HR, y, como un valor ausente también es false, los usuarios que no tienen atributo de departamento también quedan enmascarados. Evita lo contrario: una condición que enmascara solo cuando el atributo coincide deja sin enmascarar a los usuarios que no tienen valor para el atributo.

Para el comportamiento de evaluación, véase Condiciones de atributo de identidad. Para limitaciones, véase Atributos de identidad en condiciones de política.

Coincidir con un valor fijo

Ocultar ssn para todos cuyo departamento no sea HR:

CREATE FUNCTION hr_catalog.people.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';

CREATE OR REPLACE POLICY mask_ssn_non_hr
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_identity_attribute_value('department', 'HR')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;

En este ejemplo, un usuario cuyo departamento HR pertenece ve valores reales. Un usuario de cualquier otro departamento, y un usuario sin atributo de departamento, ambos ven la máscara.

Coincidencia con una etiqueta regulada

El ejemplo anterior nombra un valor de atributo específico (HR) en la política, por lo que cubrir varios departamentos significaría redactar una política separada para cada uno. Para cubrir todos los departamentos con una única política, etiqueta cada tabla con el departamento que la posee y luego compara el atributo del department usuario que consulta con esa etiqueta. La columna se muestra solo cuando el departamento del usuario coincide con el valor de dept_tag de la tabla:

CREATE FUNCTION prod.sales.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';

CREATE OR REPLACE POLICY mask_unless_dept_matches
ON SCHEMA prod.sales
COLUMN MASK prod.sales.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_identity_attribute_tag_match('department', 'dept_tag')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;

Tanto las claves como los valores de los atributos distinguen entre mayúsculas y minúsculas, y los valores se comparan de forma exacta: Finance y finance no coinciden.

Restringir el acceso para agentes externos que actúan en nombre de un usuario

Importante

Los atributos de contexto en las políticas ABAC están en Beta. Para utilizarlos, un administrador de cuenta debe activar la vista previa de Atributos de Contexto UC ABAC desde la página de Previsualizaciones de la consola de cuentas. Consulte Gestionar previsualizaciones a nivel de cuenta.

Los atributos de contexto pueden usarse para restringir el acceso a datos de solicitudes realizadas en nombre de un usuario a través de una aplicación OAuth (autorización usuario-a-máquina (U2M)). Si los agentes están conectados mediante OAuth, esta configuración puede usarse para evitar que accedan a los datos cuando actúan en nombre de un usuario, aunque el usuario pueda seguir leyendo los datos cuando los consulta directamente en el espacio de trabajo.

Cualquier acceso autenticado por OAuth a través de la CLI de Azure Databricks, los SDKs o la API de Ejecución de Sentencias SQL se establece request.is_on_behalf_of en 'true', incluso cuando un usuario consulta manualmente. El acceso autenticado con un token de acceso personal (PAT) no lo hace. No es posible registrar el acceso a Genie mediante este mecanismo.

Estos patrones utilizan las funciones de atributo contextual. Para los atributos y comportamientos disponibles, véase Funciones de atributos de contexto (Beta).

Conecte el agente a Azure Databricks

Para usar atributos de contexto, conecta el agente usando una aplicación OAuth personalizada:

  1. Un administrador de la cuenta activa la vista previa de atributos de contexto de UC ABAC en la consola de la cuenta. Consulte Administrar versiones preliminares de Azure Databricks.
  2. Un administrador de cuenta registra una aplicación OAuth personalizada en la consola de cuenta y anota su ID de cliente.
  3. Conecta el agente al MCP gestionado por Azure Databricks a través de esa aplicación OAuth. Consulta Configurar un cliente OAuth personalizado.

Un agente que utiliza el cliente integrado databricks-cli sigue autenticándose sobre OAuth, por lo que request.is_on_behalf_of lee 'true'. Sin embargo, no puedes distinguir sus peticiones del uso manual de la CLI, porque ambas comparten el ID del databricks-cli cliente. Para gobernar una aplicación específica, registra una aplicación OAuth personalizada y conecta el agente a través de ella.

Warning

Asegúrate de que un agente no pueda acceder a los datos por una ruta que tu póliza no cubra:

  • Si restringes el acceso en función de request.is_on_behalf_of, asegúrate de que el agente no pueda autenticarse con un PAT. Un PAT no configura request.is_on_behalf_of con el valor 'true', por lo que una condición sobre ese atributo no lo restringe.
  • Si restringes el acceso en función de request.client_id, asegúrate de que el agente no pueda conectarse a través de un cliente que tu condición no cubra, como el cliente genérico databricks-cli .

Ocultar una columna para solicitudes en nombre de otro

Oculte ssn para las solicitudes que se ejecutan en nombre de un usuario, como un agente que actúa a través de una aplicación OAuth registrada, mientras que lo deja visible para las consultas directas:

CREATE FUNCTION hr_catalog.people.mask_ssn(s STRING) RETURNS STRING RETURN '***-**-****';

CREATE OR REPLACE POLICY mask_ssn_for_agents
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN has_context_attribute_value('request.is_on_behalf_of', 'true')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;

En este ejemplo, una consulta directa devuelve valores reales y una solicitud en nombre de otro muestra los valores enmascarados. Usando la CLI y la API de ejecución de instrucciones SQL, request.is_on_behalf_of también lee 'true', así que esta política enmascara la columna para esas solicitudes también. Para dirigirse a una aplicación específica, compara request.client_id con el ID de cliente de esa aplicación.

Restringir una columna a una solicitud aprobada

Oculta ssn para todas las solicitudes externas, excepto las de tu aplicación aprobada, identificada por su ID de cliente OAuth:

CREATE OR REPLACE POLICY mask_ssn_unapproved_apps
ON SCHEMA hr_catalog.people
COLUMN MASK hr_catalog.people.mask_ssn
TO `account users`
FOR TABLES
WHEN NOT has_context_attribute_value('request.client_id', '<your-app-client-id>')
MATCH COLUMNS has_tag_value('pii', 'ssn') AS ssn_col
ON COLUMN ssn_col;

Para ver qué aplicación ha hecho una solicitud, inspecciona el identity_metadata.acting_resource campo en los registros de auditoría.

Filtrado de filas con predicados que utilizan columnas exclusivamente

Filtre las filas mediante una lógica booleana simple que haga referencia solo a columnas de tabla. Los predicados de solo columna habilitan la delegación de predicados, lo que permite al motor omitir datos irrelevantes durante los análisis (consulte Descripción de la delegación de predicados en tablas protegidas).

CREATE FUNCTION filter_by_region(region STRING, allowed STRING)
RETURNS BOOLEAN
DETERMINISTIC
  RETURN array_contains(split(allowed, ','), lower(region));

Use con una directiva que pase las regiones permitidas como constante:

CREATE POLICY regional_access
ON CATALOG analytics
ROW FILTER filter_by_region
TO 'emea_team'
FOR TABLES
MATCH COLUMNS has_tag('region') AS rgn
USING COLUMNS (rgn, 'emea,apac');

Filtrado de filas entre varias columnas relacionadas

Cuando una tabla tiene varias columnas que representan atributos relacionados (por ejemplo, ship_to_country y bill_to_country), puede coincidir con condiciones de etiqueta independientes y pasar ambas a una sola UDF. Esto evita la creación de directivas independientes para cada columna. Una política de filtro de filas puede incluir hasta seis expresiones de columna, y una política de máscara de columna puede incluir hasta tres expresiones de columna en la cláusula MATCH COLUMNS (véase cuotas de política).

CREATE FUNCTION filter_by_countries(ship_country STRING, bill_country STRING, allowed STRING)
RETURNS BOOLEAN
DETERMINISTIC
  RETURN array_contains(split(allowed, ','), lower(ship_country))
      OR array_contains(split(allowed, ','), lower(bill_country));

CREATE POLICY regional_orders
ON SCHEMA prod.orders
ROW FILTER filter_by_countries
TO analysts
FOR TABLES
WHEN has_tag_value('sensitivity', 'high')
MATCH COLUMNS
  has_tag('ship_country') AS ship,
  has_tag('bill_country') AS bill
USING COLUMNS (ship, bill, 'us,ca,mx');

Un analista solo ve pedidos en los que el país de envío o de facturación está en su lista permitida.

Tablas de búsqueda en las UDFs de políticas ABAC

Cuando las reglas de acceso varían por usuario y no se pueden expresar solo a través de las cláusulas de TO/EXCEPT la directiva, puede comprobar los derechos de acceso en una tabla de búsqueda pequeña. Utilice TO/EXCEPT siempre que sea posible, ya que es el enfoque preferido para las entidades de seguridad de destino (consulte Enfoque para entidades de seguridad de destino). Mantenga pequeña la tabla de búsqueda para que el optimizador convierta la subconsulta en una unión de hash de difusión (consulte Mantener pequeñas las tablas de búsqueda).

CREATE TABLE access_rules (
  principal VARCHAR(255),
  priority VARCHAR(64)
);

INSERT INTO access_rules VALUES
  ('alice@company.com', '1-URGENT'),
  ('alice@company.com', '2-HIGH'),
  ('bob@company.com', '1-URGENT');

CREATE FUNCTION priority_allowed(o_priority STRING) RETURNS BOOLEAN
RETURN EXISTS (
  SELECT 1 FROM access_rules
  WHERE principal = session_user() AND priority = o_priority
);

CREATE POLICY priority_filter
ON CATALOG operations
ROW FILTER priority_allowed
TO `account users`
FOR TABLES
MATCH COLUMNS has_tag('priority') AS pri
USING COLUMNS (pri);