FirebirdSQL logo

Pseudo-colonne RDB$DB_KEY

Chaque table et vue contient une pseudo-colonne RDB$DB_KEY.

La colonne RDB$DB_KEY est une clé interne qui indique la position d’un enregistrement dans une table. Elle est maintenue par le serveur de base de données pour un usage interne et peut être utilisée par vous dans certains cas comme le moyen le plus rapide de trouver un enregistrement dans une table.

RDB$DB_KEY est un pointeur logique vers une entrée de la table, et en aucun cas vers une adresse physique sur le disque. Les valeurs de RDB$DB_KEY ne suivent pas une séquence prévisible, donc n’essayez pas d’utiliser directement des calculs impliquant leurs positions relatives. Cela ne peut être fait qu’avec la fonction MAKE_DBKEY.

Important

Les valeurs de RDB$DB_KEY ne sont pas stables. Ils sont modifiés après une sauvegarde et une restauration ultérieure, et parfois après la confirmation d’une transaction. Par conséquent, considérez RDB$DB_KEY comme une valeur éphémère et n’essayez pas de l’utiliser lorsque la transaction au cours de laquelle elle a été reçue est terminée ou annulée.

Taille RDB$DB_KEY

Pour les tables, le champ RDB$DB_KEY utilise 8 octets et est de type BINARY(8). Pour les vues, le champ RDB$DB_KEY utilise une taille de 8 octets fois le nombre de tables utilisées dans la vue. Par exemple, si une vue joint trois tables, la RDB$DB_KEY utilise 24 octets. Ceci est important lorsque vous travaillez avec des modules PSQL et que vous avez l’intention de stocker RDB$DB_KEY dans des variables. Vous devez utiliser le type de données BINARY(n) de la bonne longueur. La valeur exacte de la longueur du champ RDB$DB_KEY peut être trouvée en utilisant la table système RDB$RELATIONS. Il est stocké dans le champ RDB$DBKEY_LENGTH.

Example 1. Détermination de la longueur de la RDB$DB_KEY pour la vue V_FARM.
SELECT
  RDB$DBKEY_LENGTH
FROM RDB$RELATIONS
WHERE RDB$RELATION_NAME = 'V_FARM'

Utilisation de la clé RDB$DB_KEY

Puisque RDB$DB_KEY pointe directement vers l’endroit où l’enregistrement est stocké, la recherche sera plus rapide qu’avec une clé primaire. Si, pour une raison quelconque, la table n’a pas de clé primaire ou d’index unique actif, ou si l’index unique autorise les valeurs NULL, il est possible que des enregistrements en double existent. Dans ces circonstances, RDB$DB_KEY est le seul moyen d’identifier précisément chaque enregistrement.

Le serveur Firebird utilise RDB$DB_KEY pour optimiser certaines méthodes d’accès. Par exemple, pour optimiser le tri externe. Si la largeur des enregistrements à trier est très grande (dépasse la valeur spécifiée dans le paramètre de configuration InlineSortThreshold), alors au lieu du tri externe classique, seul le tri par clé est utilisé, en sauvegardant RDB$DB_KEY pour les tables liées, et en effectuant ensuite un Refetch pour récupérer les enregistrements de ces tables en utilisant le RDB$DB_KEY sauvegardé.

Une autre utilisation de RDB$DB_KEY et de la fonction MAKE_DBKEY est de diviser les grandes tables en parties à peu près égales, ce qui est utilisé dans les sauvegardes parallèles.

Example 2. Diviser une grande table en plusieurs parties
-- Récupérer les données de la table SOMETABLE contenues dans les pages de données (DP),
-- désignées par la première page de pointeurs (PP).
select * from SOMETABLE
where rdb$db_key >= make_dbkey('SOMETABLE', 0, 0, 0)
  and rdb$db_key <  make_dbkey('SOMETABLE', 0, 0, 1);

-- Récupérer les données de la table SOMETABLE contenues dans les pages de données (DP),
-- désignées par la deuxième page de pointeurs (PP).
select * from SOMETABLE
where rdb$db_key >= make_dbkey('SOMETABLE', 0, 0, 1)
  and rdb$db_key <  make_dbkey('SOMETABLE', 0, 0, 2);

...

Durée de vie de RDB$DB_KEY

Par défaut, la portée de RDB$DB_KEY est la transaction courante. Vous pouvez supposer qu’elle reste correcte tant que la transaction courante est en cours. Confirmer ou annuler une transaction rendra les valeurs de RDB$DB_KEY imprévisibles. Si vous utilisez COMMIT RETAINING, le contexte de la transaction est préservé, ce qui bloque la collecte des déchets et empêche donc l’ancienne clé_db d’être "réaffectée". Dans ces conditions, les valeurs RDB$DB_KEY pour tous les enregistrements utilisés dans votre transaction sont maintenues valides jusqu’à ce qu’une validation "forcé"(commit) ou un rollback se produise.

Après une validation(commit) ou un retour en arrière(rollback), une autre transaction peut supprimer un enregistrement qui a été isolé dans le contexte de votre transaction et donc traité comme "existant" dans votre application. Toute valeur RDB$DB_KEY peut maintenant pointer vers un enregistrement inexistant ou vers un autre enregistrement placé à cet endroit.

Pseudo-colonne RDB$RECORD_VERSION

Chaque table contient une pseudo-colonne RDB$RECORD_VERSION de type BIGINT. La pseudo-colonne nommée RDB$RECORD_VERSION renvoie le numéro de la transaction qui a créé la version actuelle de l’enregistrement.

Champ RDB$VALID_BLR

Les tables système RDB$PROCEDURES, RDB$FUNCTIONS et RDB$TRIGGERS contiennent un champ RDB$VALID_BLR. Il est destiné à signaler qu’un module PSQL (procédure ou trigger) peut être invalide après que les domaines ou les colonnes de table dont il dépend aient été modifiés. Dans ce cas, le champ RDB$VALID_BLR est mis à 0 pour les procédures ou les triggers dont le code peut être invalide.

Comment cela fonctionne

Dans les déclencheurs, les procédures et les fonctions, les dépendances portent sur les colonnes de la table à laquelle ils font référence, ainsi que sur tout paramètre ou variable défini dans le module à l’aide de la clause TYPE OF.

Après que le noyau Firebird ait modifié un domaine, y compris les domaines implicites créés en interne lors de la définition des colonnes ou des paramètres, Firebird recompile en interne toutes les dépendances.

Note

L’invalidation se produit pour les procédures, les fonctions, les packages et les triggers, mais pas pour les blocs d’instructions DML, qui sont exécutés avec EXECUTE BLOCK.

Tout module qui n’a pas pu être recompilé en raison d’une incompatibilité résultant d’un changement de domaine est marqué comme invalide (le champ RDB$VALID_BLR est mis à 0 dans l’entrée de la table système correspondante (RDB$PROCEDURES ou RDB$TRIGGERS).

La réinitialisation (mise à 1 de RDB$VALID_BLR) se produit lorsque

  1. le domaine est à nouveau modifié et sa nouvelle définition est compatible avec la définition du module précédemment invalide ; ou

  2. le module précédemment invalide est modifié de manière à correspondre à la nouvelle définition du domaine.

La requête ci-dessous recherche les procédures et les triggers dépendant d’un domaine particulier (dans l’exemple, il s’agit du domaine 'MYDOMAIN') et affiche l’état du champ RDB$VALID_BLR :

WITH VALID_PSQL (
    PSQL_TYPE,
    ROUTE_NAME,
    VALID)
AS (SELECT
        'Procedure',
        RDB$PROCEDURE_NAME,
        RDB$VALID_BLR
    FROM
        RDB$PROCEDURES
    WHERE
          RDB$PROCEDURES.RDB$PACKAGE_NAME IS NULL
    UNION ALL
    SELECT
        'Function',
        RDB$FUNCTION_NAME,
        RDB$VALID_BLR
    FROM
        RDB$FUNCTIONS
    WHERE
          RDB$FUNCTIONS.RDB$PACKAGE_NAME IS NULL
    UNION ALL
    SELECT
        'Package',
        RDB$PACKAGE_NAME,
        RDB$VALID_BODY_FLAG
    FROM
        RDB$PACKAGES
    UNION ALL
    SELECT
        'Trigger',
        RDB$TRIGGER_NAME,
        RDB$VALID_BLR
    FROM
        RDB$TRIGGERS
    WHERE
          RDB$TRIGGERS.RDB$SYSTEM_FLAG = 0)
SELECT
    PSQL_TYPE,
    ROUTE_NAME,
    VALID
FROM
    VALID_PSQL
WHERE
      EXISTS(SELECT
                 *
             FROM
                 RDB$DEPENDENCIES
             WHERE
                   RDB$DEPENDENT_NAME = VALID_PSQL.ROUTE_NAME
               AND RDB$DEPENDED_ON_NAME = 'MYDOMAIN');

/*
  Remplacez MYDOMAIN par le nom réel du domaine à tester. Utilisez des lettres majuscules si le domaine a été créé sans tenir compte de la casse - sinon, utilisez l'orthographe exacte du nom de domaine en tenant compte de la casse.
*/

La requête suivante recherche les procédures et les déclencheurs qui dépendent d’une certaine colonne de table (dans l’exemple, il s’agit de la colonne 'MYCOLUMN' de la table 'MYTABLE') et fournit des informations sur l’état du champ RDB$VALID_BLR :

WITH VALID_PSQL (
    PSQL_TYPE,
    ROUTE_NAME,
    VALID)
AS (SELECT
        'Procedure',
        RDB$PROCEDURE_NAME,
        RDB$VALID_BLR
    FROM
        RDB$PROCEDURES
    WHERE
          RDB$PROCEDURES.RDB$PACKAGE_NAME IS NULL
    UNION ALL
    SELECT
        'Function',
        RDB$FUNCTION_NAME,
        RDB$VALID_BLR
    FROM
        RDB$FUNCTIONS
    WHERE
          RDB$FUNCTIONS.RDB$PACKAGE_NAME IS NULL
    UNION ALL
    SELECT
        'Package',
        RDB$PACKAGE_NAME,
        RDB$VALID_BODY_FLAG
    FROM
        RDB$PACKAGES
    UNION ALL
    SELECT
        'Trigger',
        RDB$TRIGGER_NAME,
        RDB$VALID_BLR
    FROM
        RDB$TRIGGERS
    WHERE
          RDB$TRIGGERS.RDB$SYSTEM_FLAG = 0)
SELECT
    PSQL_TYPE,
    ROUTE_NAME,
    VALID
FROM
    VALID_PSQL
WHERE
      EXISTS(SELECT
                 *
             FROM
                 RDB$DEPENDENCIES D
             WHERE
                   D.RDB$DEPENDENT_NAME = VALID_PSQL.ROUTE_NAME
               AND D.RDB$DEPENDED_ON_NAME = 'MYTABLE'
               AND D.RDB$FIELD_NAME = 'MYCOLUMN');

/*
  Remplacez MYTABLE et MYCOLUMN par les noms réels de la table et de sa colonne à vérifier.
  Utilisez des lettres majuscules si la table et sa colonne ont été créées sans tenir compte de la casse - sinon, utilisez l'orthographe exacte du nom de la table et de sa colonne en tenant compte de la casse.
*/
Important

Tous les modules invalides causés par les changements de domaine/colonne sont reflétés dans le champ RDB$VALID_BLR. En revanche, d’autres types de modifications, telles que des modifications du nombre de paramètres d’entrée ou de sortie des procédures, etc., n’affectent pas le champ de validation, même si elles peuvent potentiellement invalider le module. Les scénarios typiques pourraient être les suivants :

  1. Une procédure (B) est définie de telle sorte qu’elle appelle une autre procédure (A) et en lit les paramètres de sortie. Dans ce cas, la dépendance sera enregistrée dans RDB$DEPENDENCIES. La procédure appelée (A) peut ensuite être modifiée pour changer ou supprimer un ou plusieurs paramètres de sortie. L’instruction ALTER PROCEDURE A provoquera une erreur lorsque la transaction sera validée.

  2. La procédure (B) appelle la procédure (A) en lui transmettant des valeurs comme paramètres d’entrée. Aucune dépendance ne sera enregistrée dans RDB$DEPENDENCIES. Les modifications ultérieures des paramètres d’entrée de la procédure A seront autorisées. Un échec se produira au moment de l’exécution si B appelle A avec un ensemble incohérent de paramètres d’entrée.

Note
Autres commentaires
  • Pour les modules PSQL hérités de versions antérieures de Firebird (y compris de nombreux triggers système, même si la base de données a été créée sous Firebird version 2.1 ou ultérieure), le champ RDB$VALID_BLR est NULL. Cela ne signifie pas que leur BLR est invalide.

  • Les commandes SHOW PROCEDURES, SHOW FUNCTIONS et SHOW TRIGGERS de l’utilitaire de ligne de commande isql marqueront les modules d’un astérisque lors de la sortie d’informations dont le champ RDB$VALID_BLR est égal à 0. Les commandes SHOW PROCEDURE procname, SHOW FUNCTION funcname et SHOW TRIGGER trigname affichant le code du module PSQL n’alarmeront pas l’utilisateur avec un BLR invalide.

Note sur l’égalité

Important

Cette observation sur les opérateurs d’égalité et d’inégalité s’applique à tout Firebird.

L’opérateur =, qui est utilisé dans de nombreuses conditions, ne compare que des valeurs entre elles. Selon la norme SQL, NULL n’est pas une valeur et, par conséquent, deux valeurs NULL ne sont ni égales ni inégales l’une à l’autre. Si vous voulez que les valeurs NULL correspondent l’une à l’autre lorsqu’elles sont combinées, utilisez l’opérateur IS NOT DISTINCT FROM.

SELECT *
FROM A
JOIN B ON A.id IS NOT DISTINCT FROM B.code

De même, si vous voulez que les valeurs NULL soient différentes de toute valeur et que deux valeurs NULL soient traitées comme égales, utilisez l’opérateur IS DISTINCT FROM au lieu de l’opérateur <>.

SELECT *
FROM A
JOIN B ON A.id IS DISTINCT FROM B.code