Les fonctions d'appels d'un wrapper de données distantes, GetForeignRelSize
,
GetForeignPaths
, GetForeignPlan
,
PlanForeignModify
, GetForeignJoinPaths
,
GetForeignUpperPaths
et PlanDirectModify
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 RelOptInfo
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
, avec la valeur du paramètre obtenu à l'exécution à partir de
l'évaluation de l'arbre d'expression variable_distante
=
$1fdw_exprs
.
Toutes les clauses enlevées de la liste des qualificatifs
du nœud du plan doivent être à la place ajoutées à
fdw_recheck_quals
ou vérifiées à nouveau par
RecheckForeignScan
pour permettre un
fonctionnement correct au niveau d'isolation READ
COMMITED
. Lorsqu'une mise à jour concurrente survient
pour une autre table concernée par la requête, l'exécuteur peut
avoir besoin de vérifier que tous les qualificatifs originaux
sont encore satisfaits pour la ligne, éventuellement avec un
ensemble différent de valeurs pour les paramètres. L'utilisation
de fdw_recheck_quals
est typiquement plus
facile que de mettre en place les vérifications à l'intérieur
de RecheckForeignScan
, mais cette méthode sera
insuffisante lorsque des jointures externes ont été poussées, dans
la mesure où les lignes jointes dans ce cas peuvent avoir certaines
colonnes à NULL sans rejeter la ligne entièrement.
Un autre champ ForeignScan
qui peut être
rempli par les FDW est fdw_scan_tlist
,
qui décrit les lignes renvoyées par le FDW pour ce nœud du
plan. Pour les parcours simples de tables distantes, il peut être
positionné à NIL
, impliquant que les lignes
renvoyées ont le type de ligne déclaré pour la table distante. Une
valeur différente de NIL
doit être une liste cible (liste
de TargetEntry
) contenant des variables
et/ou expressions représentant les colonnes renvoyées. Ceci peut
être utilisé, par exemple, pour montrer que le FDW a omis certaines
colonnes qu'il a noté comme non nécessaire à la requête. Aussi,
si le FDW peut calculer des expressions utilisées par la requête
de manière moins coûteuse que localement, il pourrait ajouter ces
expressions à fdw_scan_tlist
. Notez que
les plans de jointure (créés à partir des chemins construits par
GetForeignJoinPaths
) doivent toujours fournir
fdw_scand_tlist
pour décrire l'ensemble
des colonnes qu'ils retourneront.
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.
Si un FDW supporte les jointures distantes,
GetForeignJoinPaths
devrait produire
ForeignPath
pour les jointures
distantes potentielles essentiellement de la même manière que
GetForeignPaths
le fait pour les tables
de base. L'information à propos de la jointure envisagée
peut être passée à GetForeignPlan
de la même manière que décrit ci-dessus. Cependant,
baserestrictinfo
n'est pas applicable
pour les tables d'une jointure ; à la place, les clauses de jointure
applicables pour une jointure particulière sont passées à
GetForeignJoinPaths
comme un paramètre séparé
(extra->restrictlist
).
Un FDW pourrait supporter en plus l'exécution direct de certaines actions
d'un plan, qui sont au-dessus du niveau d'un parcours ou d'une jointure,
comme par exemple un regroupement ou un agrégat. Pour proposer ce genre
d'options, le FDW doit générer des chemins et les insérer dans la
relation de niveau supérieur appropriée. Par
exemple, un chemin représentant un agrégat distant doit être inséré dans
la relation UPPERREL_GROUP_AGG
, en utilisant
add_path
. Ce chemin sera comparé suivant son coût et
celui d'un agrégat local réalisé en lisant un chemin de parcours simple
de la relation externe (notez qu'un tel chemin doit aussi être fourni...
dans le cas contraire, une erreur est renvoyée lors de l'optimisation).
Si le chemin de l'agrégat distant gagne (ce qui sera généralement le
cas), il sera converti en un plan standard en appelant
GetForeignPlan
. L'endroit recommendé pour générer de
tels chemins est dans la fonction callback
GetForeignUpperPaths
, qui est appelée pour chaque
relation supérieure (autrement dit, chaque étape de traitement
post-parcours/jointure) si toutes les relations de base de la requête
viennent du même FDW.
PlanForeignModify
et les autres callbacks décrits
dans Section 57.2.4 sont conçus autour de la
supposition que la relation externe sera parcourue de la façon standard
et qu'ensuite, les mises à jour individuelles de lignes seront réalisées
par un nœud local ModifyTable
. Cette approche est
nécessaire dans le cas général où une mise à jour nécessite de lire des
tables locales ainsi que des tables externes. Néanmoins, si l'opération
pouvoit être exécutée entièrement par le serveur distant, le FDW pourrait
générer un plan représentant cela et l'insérer dans la relation de niveau
supérieur UPPERREL_FINAL
, où il serait comparé avec
l'approche ModifyTable
. Cette approche pourrait être
utilisé pour implémenter un SELECT FOR UPDATE
distant,
plutôt que d'utiliser les callbacks de verrouillage de ligne décrits dans
Section 57.2.6. Gardez à l'exprit qu'un
chemin inséré dans UPPERREL_FINAL
est responsable de
l'implémentation de tout le comportement de cette
requête.
Lors de la planification d'un UPDATE
ou d'un
DELETE
, PlanForeignModify
et PlanDirectModify
peuvent
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.
Une commande INSERT
avec une clause ON
CONFLICT
ne supporte pas la spécification d'une
cible de conflit, dans la mesure où les contraintes uniques
ou les contraintes d'exclusion sur les tables distantes ne
sont pas localement connues. Ceci entraîne également que
ON CONFLICT DO UPDATE
n'est pas supporté
car la spécification est obligatoire ici.