Modèles courants pour le filtrage de lignes et le masquage des colonnes

Cette page décrit les modèles courants d’implémentation des stratégies de filtre de lignes et de masque de colonne ABAC.

Fonctions de masquage compatibles Cast

Azure Databricks caste automatiquement la sortie de la fonction de masquage pour qu'elle corresponde au type de données de la colonne cible. Consultez Conversion automatique des types pour les masques de colonne.

Les modèles suivants vous aident à concevoir des fonctions de masquage compatibles avec cast.

Retourner un type castable

Lors du masquage d’une colonne, retournez le même type de données ou un type qui est convertible. Vérifiez les types de données des colonnes que votre stratégie cible et vérifiez que chaque branche de la fonction retourne une valeur 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;

Éviter le dépassement de capacité numérique

Lorsqu’une fonction de masque accepte et retourne un type numérique plus large que la colonne cible, le résultat est automatiquement converti en type de colonne. Si la valeur retournée dépasse la plage du type plus étroit, le cast provoque un débordement et la requête échoue pendant l'exécution.

-- 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;

Utiliser VARIANT pour plusieurs types de colonnes

Voir Masquer plusieurs types de colonnes avec une seule fonction.

Compatibilité des casts de test

Testez les fonctions de masquage avec différents modèles de données.

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;

Masquer plusieurs types de colonnes avec une seule fonction

Une seule fonction UDF de masquage qui accepte et renvoie un VARIANT peut masquer des colonnes de nombreux types de données, ce qui réduit le nombre d’UDF et de politiques que vous devez gérer. Azure Databricks convertit la valeur de la colonne en VARIANT avant l'exécution de la fonction, puis reconvertit la valeur de retour dans le type de la colonne conformément aux règles ANSI SQL.

À l’intérieur de la fonction, utilisez schema_of_variant() pour inspecter la valeur et effectuer un branchement selon son type. Dans chaque branche, renvoyez une valeur pouvant être convertie vers le type de la colonne cible.

Masquez plusieurs types numériques

Le masque VARIANT le plus simple renvoie une seule constante qu’Azure Databricks convertit dans le type de chaque colonne. Une politique utilisant cette fonction peut masquer INT, DOUBLE, et DECIMAL les colonnes, sans fonction distincte pour chaque précision :

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

Pour faire varier la valeur masquée selon le type, effectuez un branchement sur schema_of_variant() et renvoyez une valeur appropriée pour chaque type :

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;

Les types entiers sont étendus à VARIANT dans un BIGINT, donc testez INT plutôt que TINYINT ou BIGINT.

Masquer les colonnes STRUCT, ARRAY et MAP

L’approche VARIANT masque également les colonnes de type complexe, étendant l’exemple numérique ci-dessus aux données imbriquées. Une colonne STRUCT, MAP ou VARIANT est transmise à la fonction en tant que ARRAY, et Azure Databricks convertit ensuite la valeur renvoyée par la fonction dans le type déclaré de la colonne. La valeur retournée doit avoir le même schéma que l’entrée, sinon le casting échoue et les erreurs de requête.

Note

Les colonnes STRUCT sont prises en charge sur Databricks Runtime 18.1 et versions ultérieures. les colonnes ARRAY et MAP sont prises en charge à partir de Databricks Runtime 19. Ce casting fonctionne uniquement au sein des politiques de masques de colonne et de filtres de ligne ABAC, pas dans les masques SQL ou au niveau tableau en général.

L’exemple suivant masque des types spécifiques STRUCT, ARRAY, et MAP spécifiques. Ajoutez une WHEN branche pour chaque schéma que vous devez masquer :

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;

Chaque WHEN branche fait deux choses, décrites dans les sections suivantes :

  1. Identifiez le schéma VARIANT. Chaque schéma VARIANT a besoin de sa propre logique de masquage ; la fonction effectue donc un branchement selon la chaîne renvoyée par schema_of_variant(val) afin d’appliquer le masquage approprié à chacun d’eux.
  2. Reconstruis la valeur masquée. Créez une valeur caviardée du type de la colonne et placez-la dans to_variant_object() pour renvoyer une VARIANT.

Une valeur dont le schéma ne correspond à aucune branche tombe vers ELSE.

Identifier le schéma VARIANT

Faire correspondre au schéma de la valeur VARIANT, et non au type déclaré de la colonne : la conversion en VARIANT normalise les données, de sorte que le schéma peut différer de la définition de la colonne. Les exemples suivants montrent des cas courants :

Type de colonne Résultat schema_of_variant() Notes
ARRAY<STRING> ARRAY<STRING>
ARRAY<INT> ARRAY<BIGINT> Les types entiers s’élargissent à BIGINT.
STRUCT<id: INT, name: STRING> OBJECT<id: BIGINT, name: STRING> Les colonnes STRUCT et MAP deviennent toutes deux OBJECT.
STRUCT<name: STRING, id: INT> OBJECT<id: BIGINT, name: STRING> Les champs sont triés alphabétiquement par clé, et non par ordre de déclaration.
ARRAY<STRUCT<id: INT, name: STRING>> ARRAY<OBJECT<id: BIGINT, name: STRING>>
MAP<STRING, STRING> holding {a: 'x'} OBJECT<a: STRING> Cela varie selon la ligne : les clés de chaque ligne déterminent le schéma.
MAP<STRING, STRING> holding {a: 'x', b: 'y'} OBJECT<a: STRING, b: STRING> Une ligne différente de la même colonne produit un schéma différent.
MAP<STRING, STRING> holding {a: null} OBJECT<a: VOID> Une valeur nulle devient VOID.
MAP<STRING, STRUCT<id: INT, name: STRING>> holding {a: ...} OBJECT<a: OBJECT<id: BIGINT, name: STRING>, ...>

Reconstruire la valeur masquée

Pour chaque schéma que vous correspondez, construisez une valeur expurgée avec la même structure, puis encapsulez-la dans to_variant_object():

  • Utilisez-le named_struct() pour reconstruire un STRUCT, gardez les champs que vous souhaitez et remplacez le reste.
  • Utilisez array() pour reconstruire un ARRAY.
  • Utilisez map() pour reconstruire un MAP avec des valeurs masquées.

Azure Databricks reconvertit le VARIANT renvoyé dans le type déclaré de la colonne, de sorte que chaque valeur reconstruite doit être convertible dans ce type.

Tester un masque VARIANT

Avant d’attacher une fonction à une politique, vous pouvez la tester dans une requête simple pour confirmer la sortie masquée. La fonction suivante masque une ARRAY<STRUCT<id: BIGINT, value: FLOAT>> colonne et génère une erreur pour tout autre schéma :

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;

Convertissez la colonne avec to_variant_object(), appliquez la fonction de masquage et utilisez variant_get() pour reconvertir le VARIANT masqué dans le type de la colonne. Cela reflète ce que fait la politique à l’exécution :

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

Limites

  • Une colonne complexe qui contient CHAR, VARCHAR, GEOMETRY, GEOGRAPHY, ou TIME ne peut pas être masquée par VARIANT.
  • MAP Les clés doivent être STRING. Les colonnes tapées MAP<INT, ...>, MAP<DATE, ...>, etc. ne sont pas converties.

Empêcher l’accès jusqu’à ce que les colonnes sensibles soient marquées

Un modèle de gouvernance courant consiste à contrôler l’accès en fonction de la classification des données. Vous pouvez l’implémenter avec une balise et des stratégies restrictives par défaut qui appliquent différents niveaux de protection en fonction de l’état de classification.

  1. Appliquez une balise comme classification : unverified à tous les nouveaux objets par défaut, par le biais de l’automatisation ou de l’héritage des étiquettes en appliquant la balise au niveau du catalogue ou du schéma, afin que toutes les nouvelles tables ajoutées au catalogue ou au schéma héritent automatiquement de la balise.
  2. Créez une stratégie de filtre de lignes qui bloque l’accès aux tables marquées classification : unverified.
  3. Créez une stratégie de masque de colonne qui masque les colonnes sensibles sur les tables où la classification : unverified balise n’est plus présente.
  4. Lorsqu’un gestionnaire de données termine la classification, il met à jour la balise. La stratégie de blocage ne correspond plus et la stratégie de masquage prend effet.
-- 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');

Pour protéger les données sensibles une fois qu’elles ont été classifiées, définissez une stratégie de masque de colonne qui prend effet lorsque la classification : unverified balise n’est plus présente :

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;

Révélation partielle sans regex

Révéler une partie d’une valeur sensible à l’aide d’opérations de chaîne au lieu d’une expression régulière. Le masquage basé sur les expressions régulières examine la valeur complète de chaque ligne, ce qui est coûteux sur les champs de texte volumineux (voir Éviter le masquage par expressions régulières sur les champs de texte volumineux).

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

Hachage cohérent (pseudonymisation déterministe)

Le hachage cohérent (également appelé pseudonyme déterministe) remplace les données sensibles par une valeur hachée identique sur plusieurs tables. Le marquage d’une fonction indique DETERMINISTIC au moteur que la fonction retourne toujours le même résultat pour la même entrée, ce qui lui permet d’optimiser la requête. Consultez Utiliser des expressions déterministes et sans risque d’erreur.

La fonction suivante hache de manière cohérente une valeur de chaîne et utilise un paramètre version pour permettre la rotation des clés. Incrémentez le versionnombre à travers la clause de stratégie USING COLUMNS pour générer de nouveaux hachages sans affecter les données historiques qui utilisaient la version précédente. La fonction concatène la valeur d’origine avec le numéro de version avant le hachage. Par conséquent, la même entrée avec la même version produit toujours le même hachage.

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

Masquez une colonne en fonction des attributs de l’utilisateur interrogateur

Important

Les attributs d’identité des politiques ABAC sont en bêta. Pour les utiliser, un administrateur de compte doit :

Une politique de masque en colonne peut utiliser les attributs d’identité de l’utilisateur interrogé pour masquer des données sensibles sans nécessiter de groupes dédiés. Par exemple, il peut garder les données non masquées pour les utilisateurs department = HR et les masquer pour tous les autres.

Ces schémas nécessitent des attributs d’identité fournis à vos utilisateurs par votre fournisseur d’identité, et les fonctions se comportent différemment des conditions uniquement par tag, ce qui affecte la manière dont vous rédigez la politique. Avant de les utiliser, examinez les fonctions d’attributs d’identité et les attributs d’identité.

Important

Les fonctions renvoient false lorsque l’utilisateur n’a aucune valeur pour l’attribut ou que la clé d’attribut n’existe pas. Écrivez la condition de façon à ce que ce false résultat limite l’accès plutôt que de l’accorder. Inversez la correspondance avec NOT afin que le masque s’applique sauf si l’attribut correspond. Par exemple, WHEN NOT has_identity_attribute_value('department', 'HR') masque la colonne pour tout le monde sauf les utilisateurs dont le département est HR, et parce qu’une valeur manquante est aussi false, les utilisateurs sans attribut département sont aussi masqués. Évitez l’inverse : une condition qui ne masque que lorsque l’attribut correspond laisse non masqués les utilisateurs pour lesquels l’attribut n’a aucune valeur.

Pour le comportement d’évaluation, voir Conditions d’attribut d’identité. Pour connaître les limitations, voir Attributs d’identité dans les conditions de stratégie.

Correspond à une valeur fixe

Masquer ssn pour tous ceux dont le département n’est pas 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;

Dans cet exemple, un utilisateur dont le département appartient HR voit de vraies valeurs. Un utilisateur dans un autre département, et un utilisateur sans attribut département, voient tous deux le masque.

Comparer à une étiquette régie

L’exemple précédent nomme une valeur d’attribut spécifique (HR) dans la politique, donc couvrir plusieurs départements signifierait rédiger une politique distincte pour chacun. Pour couvrir tous les départements ayant une seule politique, identifiez chaque table avec le département qui en est propriétaire, puis comparez l’attribut de department l’utilisateur interrogateur avec ce tag. La colonne n’est révélée que lorsque le département de l’utilisateur correspond à la valeur du dept_tag tableau :

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;

Les clés et valeurs d’attribut sont toutes deux sensibles à la majuscule, et les valeurs sont comparées exactement : Finance et finance ne correspondent pas.

Restreignez l’accès aux agents externes agissant au nom d’un utilisateur

Important

Les attributs de contexte dans les politiques ABAC sont en version Bêta. Pour les utiliser, un administrateur de compte doit activer l’aperçu des attributs contextuels UC ABAC depuis la page Aperçu de la console de compte. Consultez Gérer les versions préliminaires du compte.

Les attributs de contexte peuvent être utilisés pour restreindre l’accès aux données pour les requêtes effectuées au nom d’un utilisateur via une application OAuth (autorisation utilisateur-à-machine (U2M)). Si les agents sont connectés via OAuth, cette configuration peut être utilisée pour les empêcher d’accéder aux données lorsqu’ils agissent au nom d’un utilisateur, même si l’utilisateur peut toujours lire les données lorsqu’il les interroge directement dans l’espace de travail.

Tout accès authentifié par OAuth via la CLI d’Azure Databricks, les SDKs ou l’API d’exécution de données SQL se définit request.is_on_behalf_of à 'true', même lorsqu’un utilisateur interroge manuellement. L’accès authentifié avec un jeton d’accès personnel (PAT) ne le fait pas. L’accès Genie ne peut pas être pris en charge via ce mécanisme.

Ces motifs utilisent les fonctions d’attribut contextuel. Pour les attributs et comportements disponibles, voir Fonctions d’attributs contextuelles (Bêta).

Connectez l’agent à Azure Databricks

Pour utiliser les attributs contextuels, connectez l’agent à l’aide d’une application OAuth personnalisée :

  1. Un administrateur de compte active l’aperçu des attributs contextuels UC ABAC depuis la console de compte. Consultez Gérer les préversions d’Azure Databricks.
  2. Un administrateur de compte enregistre une application OAuth personnalisée dans la console de compte et note son identifiant client.
  3. Connectez l’agent au MCP géré par Azure Databricks via cette application OAuth. Voir la configuration du client MCP.

Un agent qui utilise le client intégré databricks-cli authentifie toujours via OAuth, donc request.is_on_behalf_of lit 'true'. Cependant, vous ne pouvez pas distinguer ses requêtes de l’utilisation manuelle de la ligne de commande, car les deux partagent l’identifiant databricks-cli client. Pour gouverner une application spécifique, enregistrez une application OAuth personnalisée et connectez l’agent via celle-ci.

Warning

Assurez-vous qu’un agent ne puisse pas accéder aux données par un chemin que votre police ne couvre pas :

  • Si vous restreignez l’accès en fonction de request.is_on_behalf_of, assurez-vous que l’agent ne peut pas s’authentifier avec un PAT. Un PAT ne définit pas request.is_on_behalf_of sur 'true', donc une condition sur cet attribut ne le limite pas.
  • Si vous restreignez l’accès en fonction de request.client_id, assurez-vous que l’agent ne peut pas se connecter via un client que votre condition ne couvre pas, comme le client générique databricks-cli .

Masquez une colonne pour les requêtes effectuées pour le compte de quelqu’un

Masque ssn pour les requêtes qui s’exécutent au nom d’un utilisateur, comme un agent agissant via une application OAuth enregistrée, tout en la laissant non masquée pour les requêtes directes :

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;

Dans cet exemple, une requête directe retourne des valeurs réelles, et une requête au nom de l’utilisateur retourne les valeurs masquées. Avec la CLI et l’API d’exécution des instructions SQL, request.is_on_behalf_of lit également 'true', donc cette politique masque aussi la colonne pour ces requêtes. Pour cibler une application spécifique, comparez request.client_id avec l’identifiant client de cette application.

Restreindre une colonne à une demande approuvée

Masque ssn pour toutes les requêtes externes sauf celles de votre application approuvée, identifiées par son identifiant client 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;

Pour voir quelle application a fait une demande, inspectez le identity_metadata.acting_resource champ dans les journaux d’audit.

Filtrage de lignes avec prédicats en colonnes uniquement

Filtrez des lignes à l’aide d’une logique booléenne simple qui référence uniquement des colonnes de table. Les prédicats de colonne uniquement activent l'optimisation des prédicats, ce qui permet au moteur d'ignorer les données non pertinentes pendant les scans (voir Comprendre l'optimisation des prédicats sur les tables protégées).

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

Utilisez une stratégie qui transmet les régions autorisées en tant que 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');

Filtrage de lignes à travers plusieurs colonnes associées

Lorsqu’une table comporte plusieurs colonnes représentant des attributs associés (par exemple, ship_to_country et bill_to_country), vous pouvez les mettre en correspondance avec des conditions d’étiquette distinctes et passer les deux à une seule fonction UDF. Cela évite de créer des stratégies distinctes pour chaque colonne. Une politique de filtre de lignes peut inclure jusqu’à six expressions de colonne, et une politique de masque de colonne peut inclure jusqu’à trois expressions de colonne dans la clause MATCH COLUMNS (voir quotas de politique).

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 analyste voit uniquement les commandes où le pays d’expédition ou de facturation se trouve dans leur liste autorisée.

Tables de recherche dans les UDF de stratégie ABAC

Lorsque les règles d’accès varient par utilisateur et ne peuvent pas être exprimées uniquement par les clauses de la stratégie TO/EXCEPT, vous pouvez vérifier les droits d’accès sur une petite table de recherche. Utilisez TO/EXCEPT le cas échéant, car il s’agit de l’approche recommandée pour le ciblage des principaux (voir Approche pour le ciblage des principaux). Conserver la table de recherche de petite taille afin que l'optimiseur convertisse la sous-requête en jointure de hachage par diffusion (voir Conserver les tables de recherche de petite taille).

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);