FirebirdSQL logo

Nouveaux types de données

Concerne : lors de la migration à partir des versions 2.5 et 3.0 de Firebird.

Comme indiqué précédemment, certaines expressions peuvent renvoyer de nouveaux types de données qui ne peuvent pas être interprétés par votre application sans la modifier.Un tel affinage peut prendre beaucoup de temps ou peut être au-delà de vos capacités.Pour simplifier la migration vers de nouvelles versions, vous pouvez définir le paramètre DataTypeCompatibility pour qu’il soit compatible avec la version souhaitée dans firebird.conf ou databases.conf.

DataTypeCompatibility = 3.0

ou

DataTypeCompatibility = 2.5

C’est le moyen le plus rapide d’assurer la compatibilité avec les nouveaux types de données.Cependant, au fil du temps, vous pouvez commencer à intégrer la prise en charge de nouveaux types dans votre application.Naturellement, cela se fera progressivement - d’abord un type, puis un autre et ainsi de suite.Dans ce cas, vous devez configurer le mappage des types dont vous n’avez pas encore finalisé la prise en charge avec d’autres types de données.L’opérateur SET BIND OF est utilisé à cette fin.

Syntaxe
SET BIND OF { <type-from> | TIME ZONE } TO { <type-to> | LEGACY | NATIVE | EXTENDED }

Le mot-clé LEGACY dans la partie TO est utilisé lorsqu’un type de données non présent dans une version précédente de Firebird doit être représenté d’une manière compréhensible pour les anciens logiciels clients (une certaine perte de données peut se produire). Il existe les conversions suivantes vers les types LEGACY :

Table 1. Conversions vers les anciens types
DataTypeCompatibility Native type Legacy type

2.5

BOOLEAN

CHAR(5)

2.5 ou 3.0

DECFLOAT

DOUBLE PRECISION

2.5 ou 3.0

INT128

BIGINT

2.5 ou 3.0

TIME WITH TIME ZONE

TIME WITHOUT TIME ZONE

2.5 ou 3.0

TIMESTAMP WITH TIME ZONE

TIMESTAMP WITHOUT TIME ZONE

Lorsque le paramètre DataTypeCompatibility est défini, les nouveaux types de données sont convertis en types hérités selon le tableau décrit ci-dessus.

Une description détaillée de cette déclaration est disponible dans "Firebird 4.0 Release Notes" et "Firebird 5.0 SQL Language Guide".Grâce à lui, vous pouvez contrôler l’affichage des nouveaux types dans votre application en exécutant la requête appropriée immédiatement après la connexion, et même écrire un déclencheur AFTER CONNECT dans lequel vous pouvez utiliser plusieurs de ces opérateurs.

Par exemple, supposons que vous ayez ajouté le support de la date et de l’heure avec les fuseaux horaires à votre application, mais que vous n’ayez toujours pas le support des types INT128 et DECFLOAT. Dans ce cas, vous pouvez écrire le trigger suivant.

create or alter trigger tr_ac_set_bind
on connect
as
begin
  set bind of int128 to legacy;
  set bind of decfloat to legacy;
end

Lecture agréée dans les transactions READ COMMITTED

Concerne : lors de la migration à partir des versions 2.5 et 3.0 de Firebird.

Firebird 4 introduit non seulement la cohérence en lecture (READ CONSISTENCY) pour les requêtes dans les transactions READ COMMITTED, mais en fait aussi le mode par défaut pour toutes les transactions READ COMMITTED, indépendamment de leurs propriétés RECORD VERSION ou NO RECORD VERSION.

L’objectif est d’offrir aux utilisateurs un meilleur comportement, à la fois conforme à la spécification SQL et moins sujet aux conflits. Toutefois, ce nouveau comportement peut également avoir des effets secondaires inattendus.

Le plus important d’entre eux est sans doute le redémarrage lors de la gestion des conflits de mise à jour. Certains codes qui ne sont pas soumis au contrôle transactionnel peuvent ainsi être exécutés plusieurs fois dans PSQL. Voici quelques exemples de ce type de code :

  • l’utilisation de tableaux, de séquences ou de variables contextuelles externes ;

  • l’envoi de courriers électroniques à l’aide d’UDF ;

  • Utilisation de transactions hors ligne ou de requêtes externes.

Note

Dans le mode d’isolation READ COMMITTED READ CONSISTENCY, le conflit de mise à jour est géré différemment. Si pendant l’exécution de UPDATE ou DELETE un enregistrement est trouvé qui a déjà été modifié ou supprimé par une autre transaction (transaction confirmée), toutes les modifications faites dans la requête courante sont annulées et la requête est exécutée à nouveau. C’est ce qu’on appelle redémarrer la requête.

Vous pouvez en savoir plus sur les lectures cohérentes dans les transactions READ COMMITTED dans les "Firebird 4.0 Release Notes".

Un autre effet important est que les curseurs sous-déclarés dans les transactions READ COMMITTED READ CONSISTENCY en mode lecture seule tiennent maintenant la collecte des déchets.Nous vous recommandons d’éviter d’utiliser une seule longue transaction READ COMMITTED READ ONLY dans votre application, et de la remplacer par plusieurs transactions de ce type, chacune d’entre elles étant active exactement pendant la durée nécessaire.

Si les caractéristiques du mode READ CONSISTENCY ne sont pas souhaitables pour une raison quelconque, le paramètre de configuration ReadConsistency doit être mis à 0 pour revenir à l’ancien comportement.

Changements dans l’optimiseur

L’optimiseur change dans chaque version de Firebird. La plupart de ces changements sont positifs, c’est-à-dire que vos requêtes devraient s’exécuter plus rapidement, mais certaines requêtes peuvent ralentir. Vous devez donc tester les performances de votre application, et s’il y a un ralentissement quelque part, l’intervention du programmeur est nécessaire.

Pour la plupart des modifications de l’optimiseur, vous ne pouvez pas affecter le plan de requête en modifiant la configuration du serveur. Dans ce cas, vous pouvez procéder comme suit :

  • réécrire la requête SQL pour qu’elle s’exécute plus rapidement sur la nouvelle version du serveur ;

  • créer ou supprimer des index ;

  • Si aucune des solutions ci-dessus ne vous a aidé, créez un ticket de régression à l’adresse suivante FirebirdSQL ticket.

Il y a quelques éléments de l’optimiseur qui peuvent être affectés par la modification de la configuration :

Utilisation de Refetch pour le tri de grands ensembles de données

Concerne : lors de la migration à partir des versions 2.5 et 3.0 de Firebird.

Depuis Firebird 4.0, une nouvelle méthode d’accès Refetch a été introduite pour optimiser le tri des ensembles de données larges. Un ensemble de données large est un ensemble de données dans lequel la longueur totale des champs d’enregistrement est importante.

Historiquement, lorsqu’il effectue un tri externe, Firebird écrit à la fois les champs clés (c’est-à-dire ceux spécifiés dans la phrase ORDER BY ou GROUP BY) et les champs non-clés (tous les autres champs référencés dans la requête) dans des blocs de tri qui sont soit stockés en mémoire, soit dans des fichiers temporaires. Une fois le tri terminé, ces champs sont relus à partir des blocs de tri.Cette approche est généralement considérée comme plus rapide car les enregistrements sont lus à partir des fichiers temporaires dans l’ordre des enregistrements triés, plutôt que d’être sélectionnés de manière aléatoire à partir de la page de données.Cependant, si les champs non-clés sont grands (par exemple, de longs VARCHARs sont utilisés), cela augmente la taille des blocs de tri et résulte donc en plus d’opérations d’entrées-sorties pour les fichiers temporaires. Firebird 4 offre une approche alternative (méthode d’accès Refetch) où seuls les champs clés et les enregistrements DBKEY sont stockés à l’intérieur des blocs de tri, et les champs non-clés sont récupérés à partir des pages de données après le tri. Cela permet d’améliorer les performances de tri dans le cas de longs champs non clés.

Ainsi, les plans de vos requêtes qui utilisent le tri peuvent changer. Un nouveau paramètre de configuration InlineSortThreshold est introduit pour contrôler cette méthode d’accès. La valeur spécifiée pour InlineSortThreshold définit la taille maximale d’un enregistrement de tri (en octets) qui peut être stocké en ligne, c’est-à-dire à l’intérieur d’un bloc de tri. Zéro signifie que les enregistrements sont toujours rechargés. La valeur optimale de ce paramètre doit être choisie de manière expérimentale. La valeur par défaut est de 1000 octets.

Prenons l’exemple suivant :

SELECT
  field_1, field_2, field_3, field_4
FROM SomeTable
ORDER BY field_1

Avant Firebird 4.0, les 4 champs étaient toujours inclus dans les blocs de tri. Depuis Firebird 4.0, si la longueur totale des champs field_1 . field_4 dépasse la valeur de InlineSortThreshold, seul field_1 sera inclus dans les blocs de tri et ensuite Refetch sera exécuté.

Conversion des JOIN OUTER en JOIN INNER

L’optimisation des JOIN OUTER dans Firebird pose un certain nombre de problèmes.

Tout d’abord, le OUTER JOIN ne peut actuellement être exécuté que par un seul algorithme de connexion NESTED LOOP JOIN, ce qui pourrait être modifié dans les versions futures.

Deuxièmement, lorsque les threads sont reliés par des connexions externes, l’ordre de connexion est strictement fixe, c’est-à-dire que l’optimiseur ne peut pas le modifier pour que le résultat reste correct.

Cependant, s’il y a un prédicat pour le champ WHERE de la table "de droite" (jointe) dans la condition WHERE qui ne gère pas explicitement la valeur NULL, il n’y a pas d’intérêt à faire une jointure externe. Dans ce cas, à partir de Firebird 5.0, une telle jointure sera convertie en jointure interne, permettant à l’optimiseur d’appliquer la gamme complète des algorithmes de jointure disponibles.

Supposons que vous ayez la requête suivante :

SELECT
  COUNT(*)
FROM
  HORSE
  LEFT JOIN FARM ON FARM.CODE_FARM = HORSE.CODE_FARM
WHERE FARM.CODE_COUNTRY = 1

Firebird 5.0 convertira implicitement une telle requête en une forme équivalente :

SELECT
  COUNT(*)
FROM
  HORSE
  JOIN FARM ON FARM.CODE_FARM = HORSE.CODE_FARM
WHERE FARM.CODE_COUNTRY = 1

Si LEFT JOIN a été utilisé comme un indice pour spécifier l’ordre de jointure de manière très active, il peut être problématique de réécrire de nombreuses requêtes d’une nouvelle manière. Pour ces développeurs, il existe un paramètre de configuration OuterJoinConversion dans firebird.conf ou database.conf. Mettre le paramètre OuterJoinConversion à false désactive la transformation des jointures externes en jointures internes. Notez que ce paramètre est une solution temporaire pour faciliter la migration et peut être supprimé dans les versions futures de Firebird.

RETURNING, retour d’un ensemble d’enregistrements

Depuis Firebird 5.0, les instructions de modification du client INSERT . SELECT, UPDATE, DELETE, UPDATE OR INSERT et MERGE contenant une clause RETURNING retournent un curseur, c’est à dire qu’elles sont capables de retourner plusieurs enregistrements au lieu de donner l’erreur "multiple rows in singleton select" comme auparavant.

Ces requêtes pendant la préparation sont maintenant décrites comme isc_info_sql_sql_stmt_select, alors que dans les versions précédentes elles étaient décrites comme isc_info_sql_sql_stmt_exec_procedure.

Les opérateurs singleton INSERT …​ VALUES, ainsi que les instructions UPDATE et DELETE positionnées (c’est-à-dire, qui contiennent une clause WHERE CURRENT OF) conservent le comportement existant et sont décrites comme isc_info_sql_sql_stmt_exec_procedure.

Cependant, toutes ces requêtes, si elles sont utilisées dans PSQL et que la phrase RETURNING est appliquée, sont toujours traitées comme des singletons.

Si votre application utilise des instructions de modification INSERT . SELECT, UPDATE, DELETE, UPDATE OR INSERT, et MERGE contenant une phrase RETURNING, elle peut provoquer des erreurs. Assurez-vous que votre pilote ou composant d’accès gère correctement ces requêtes, et si ce n’est pas le cas, modifiez le code (de l’application ou du composant) ou attendez une mise à jour du pilote/composant correspondant qui gère ces requêtes correctement.

Exemples de modification d’opérateurs contenant RETURNING, et de retour d’un ensemble de données :

INSERT INTO dest(name, val)
SELECT desc, num + 1 FROM src WHERE id_parent = 5
RETURNING id, name, val;

UPDATE dest
SET a = a + 1
WHERE id = ?
RETURNING id, a;

DELETE FROM dest
WHERE price < 0.52
RETURNING id;

MERGE INTO PRODUCT_INVENTORY AS TARGET
USING (
  SELECT
    SL.ID_PRODUCT,
    SUM(SL.QUANTITY)
  FROM
    SALES_ORDER_LINE SL
    JOIN SALES_ORDER S ON S.ID = SL.ID_SALES_ORDER
  WHERE S.BYDATE = CURRENT_DATE
    AND SL.ID_PRODUCT = :ID_PRODUCT
  GROUP BY 1
) AS SRC(ID_PRODUCT, QUANTITY)
ON TARGET.ID_PRODUCT = SRC.ID_PRODUCT
WHEN MATCHED AND TARGET.QUANTITY - SRC.QUANTITY <= 0 THEN
  DELETE
WHEN MATCHED THEN
  UPDATE SET
    TARGET.QUANTITY = TARGET.QUANTITY - SRC.QUANTITY,
    TARGET.BYDATE = CURRENT_DATE
RETURNING OLD.QUANTITY, NEW.QUANTITY, SRC.QUANTITY;

Conclusion

Dans cet article, j’ai essayé de décrire les problèmes les plus courants et leurs solutions lors de la migration vers Firebird 5.0 à partir de Firebird 2.5, 3.0 et 4.0. J’espère que cet article vous aidera à migrer vos bases de données et vos applications vers Firebird 5.0 et à profiter de tous les avantages de la nouvelle version.