Documentation PostgreSQL 9.3.25 > Internes > Écrire un wrapper de données distantes > Planification de la requête avec un wrapper de données distantes | |
Fonctions d'aide pour les wrapper de données distantes | Optimiseur génétique de requêtes (Genetic Query Optimizer) |
Les fonctions d'appels d'un wrapper de données distantes, GetForeignRelSize, GetForeignPaths, GetForeignPlan et PlanForeignModify doivent s'intégrer au fonctionnement du planificateur de PostgreSQL™. Voici quelques notes sur ce qu'elles doivent faire.
Les informations dans root et baserel peuvent être utilisées pour réduire la quantité d'informations qui doivent être récupérées sur la table distante (et donc réduire le coût) baserel->baserestrictinfo est tout particulièrement intéressant car il contient les qualificatifs de restriction (clauses WHERE) qui doivent être utilisées pour filtrer les lignes à récupérer. (Le wrapper lui-même n'est pas requis de respecter ces clauses car l'exécuteur du moteur peut les vérifier à sa place.) baserel->reltargetlist peut être utilisé pour déterminer les colonnes à récupérer ; mais notez qu'il liste seulement les colonnes qui doivent être émises par le nœud ForeignScan, et non pas les colonnes qui sont utilisées pour satisfaire l'évaluation des qualificatifs et non renvoyées par la requête.
Divers champs privés sont disponibles pour que les fonctions de planification du wrapper de données distantes conservent les informations. Habituellement, tout ce que vous stockez dans les champs privées doit avoir été alloué avec la fonction palloc, pour que l'espace soit récupéré à la fin de la planification.
baserel->fdw_private est un pointeur void disponible pour que les fonctions de planification du wrapper y stockent des informations correspondant à la table distante spécifique. Le planificateur du moteur n'y touche pas sauf lors de son initialisation à NULL quand le nœud baserel est créé. Il est utile de passer des informations de GetForeignRelSize à GetForeignPaths et/ou GetForeignPaths à GetForeignPlan, évitant du coup un recalcul.
GetForeignPaths peut identifier la signification de chemins d'accès différents pour enregistrer des informations privées dans le champ fdw_private des nœuds ForeignPath. fdw_private est déclaré comme un pointeur List mais peut contenir réellement n'importe quoi car le planificateur du moteur n'y touche pas. Néanmoins, une bonne pratique est d'utiliser une représentation qui est affichable par nodeToString, pour son utilisation avec le support du débogage disponible dans le processus.
GetForeignPlan peut examiner le champ fdw_private du nœud ForeignPath, et peut générer les listes fdw_exprs et fdw_private à placer dans le nœud de plan ForeignScan, où elles seront disponibles au moment de l'exécution. Les deux listes doivent être représentées sous une forme que copyObject sait copier. La liste fdw_private n'a pas d'autres restrictions et n'est pas interprétée par le processus moteur. La liste fdw_exprs, si non NULL, devrait contenir les arbres d'expressions qui devront être exécutées. Ces arbres passeront par un post-traitement par le planificateur qui les rend complètement exécutables.
Dans GetForeignPlan, habituellement, la liste cible fournie peut être copiée dans le nœud du plan tel quel. La liste scan_clauses fournie contient les mêmes clauses que baserel->baserestrictinfo mais ces clauses pourraient être ré-ordonnées pour une meilleure efficacité à l'exécution. Dans les cas simples, le wrapper peut seulement supprimer les nœuds RestrictInfo de la liste scan_clauses (en utilisant extract_actual_clauses) et placer toutes les clauses dans la liste des qualificatifs du nœud. Cela signifie que toutes les clauses seront vérifiées par l'exécuteur au moment de l'exécution. Les wrappers les plus complexes peuvent être capables de vérifier certaines clauses en interne, auquel cas ces clauses peuvent être supprimées de la liste de qualificatifs du nœud du plan pour que le planificateur ne perde pas de temps à les vérifier de nouveau.
Comme exemple, le wrapper peut identifier certaines clauses de restriction de la forme variable_distante = sous_expression, qui, d'après lui, peut être exécuté sir le serveur distant en donnant la valeur évaluée localement de la sous_expression. L'identification réelle d'une telle clause doit survenir lors de l'exécution de GetForeignPaths car cela va affecter l'estimation ddu coût pour le chemin. Le champ fdw_private du chemin pourrait probablement inclure un pointeur vers le nœud RestrictInfo de la clause identifiée. Puis, GetForeignPlan pourrait supprimer cette clause de scan_clauses et ajouter la sous_expression à fdw_exprs pour s'assurer qu'elle soit convertie en une forme exécutable. Il pourrait aussi placer des informations de contrôle dans le champ fdw_private du nœud pour dire aux fonctions d'exécution ce qu'il faudra faire au moment de l'exécution. La requête transmise au serveur distant va impliquer quelque chose comme WHERE variable_distante = $1, avec la valeur du paramètre obtenu à l'exécution à partir de l'évaluation de l'arbre d'expression fdw_exprs.
Le wrapper de données distantes devrait toujours construire au moins un chemin qui dépend seulement des clauses de restriction de la table. Dans les requêtes de jointure, il pourrait aussi choisir de construire des chemins qui dépendent des clauses de jointures. Par exemple, variable_distante = variable_local. De telles clauses ne se trouveront pas dans baserel->baserestrictinfo mais doivent être dans les listes de jointures des relations. Un chemin utilisant une telle clause est appelé un « parameterized path ». Il doit identifier les autres relations utilisées dans le(s) clause(s) de jointure sélectionnée(s) avec une valeur convenable pour param_info ; utilisez get_baserel_parampathinfo pour calculer cette valeur. Dans GetForeignPlan, la portion local_variable de la clause de jointure pourra être ajoutée à fdw_exprs, et ensuite à l'exécution, cela fonctionne de la même façon que pour une clause de restriction standard.
Lors de la planification d'un UPDATE ou d'un DELETE, PlanForeignModify peut rechercher la structure RelOptInfo pour la table distante et utiliser la donnée baserel->fdw_private créée précédemment par les fonctions de planification de parcours. Néanmoins, pour un INSERT, la table cible n'est pas parcourue, donc il n'existe aucun RelOptInfo pour elle. La structure List renvoyée par PlanForeignModify a les mêmes restrictions que la liste fdw_private d'un nœud de plan ForeignScan, c'est-à-dire qu'elle doit contenir seulement les structures que copyObject sait copier.
Pour un UPDATE ou DELETE vers une source de données externe qui supporte les mises à jour concurrentes, il est recommandé que l'opération ForeignScan verrouille les lignes qu'il récupère, par exemple via l'équivalent d'un SELECT FOR UPDATE. Le FDW doit aussi choisir de verrouiller les lignes à leur lecture quand la table distante est référencée dans un SELECT FOR UPDATE/SHARE ; dans le cas contraire, la clause FOR UPDATE ou FOR SHARE sont essentiellement une opération vide au niveau de la table distante concernée. Ce comportement peut amener des sémantiques légèrement différentes par rapport aux opérations sur des tables locales où le verrouillage des lignes est retardé le plus longtemps possible : les lignes distantes peuvent devenir verrouillées même si elles échouent ensuite à appliquer la restriction locale ou à joindre les conditions. Néanmoins, faire correspondre les sémantiques locales réclamerait un accès distant supplémentaire et pourrait être impossible suivant la sémantique du verrouillage fournit par la source de données externe.