Développons une nouvelle fonctionnalité pour PostgreSQL (8/13)

J'ai oublié un point important. Il faut aussi modifier l'OID du tablespace par défaut dans la table pg_database pour que le prochain objet créé le soit dans le répertoire du nouveau tablespace. Si on se réfère à ce qu'on a déjà appris dans cette série, il faut utiliser les fonctions head_beginscan, heap_getnext et heap_endscan pour lire le catalogue système, récupérer la ligne correspondant à la base de données à modifier, modifier l'info et terminer la lecture. En lisant un peu plus le fichier src/backend/commands/dbcommands.c, qu'on commence à connaître par coeur, on voit comment l'option connectionlimit est mise à jour dans ce catalogue système. On va donc récupérer ce bout de code et l'adapter pour notre cas.

On prépare le filtre sur le nom de la base de données (Anum_pg_database_datname). Ce filtre ne veut récupérer que la ligne correspondant à la base cible (opérateur d'égalité BTEqualStrategyNumber, sur un champ de type name, dont le nom est dbname).

 ScanKeyInit(&scankey,
     Anum_pg_database_datname,
     BTEqualStrategyNumber, F_NAMEEQ,
     NameGetDatum(dbname));

On lance le parcours du catalogue système (remarquez qu'on n'utilise pas heap_beginscan mais systable_beginscan).

 sysscan = systable_beginscan(pgdbrel,
     DatabaseNameIndexId, true,
     SnapshotNow, 1, &scankey);

On récupère la première (et normalement seule) ligne de ce parcours.

 oldtuple = systable_getnext(sysscan);

On prépare un emplacement mémoire permettant de construire le nouvel enregistrement :

 MemSet(new_record, 0, sizeof(new_record));
 MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
 MemSet(new_record_repl, ' ', sizeof(new_record_repl));

On modifie le champ dattablespace (constante Anum_pg_database_dattablespace) avec la valeur tblspcoid (passée auparavant dans la fonction de casting ObjectIdGetDatum :

 new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(tblspcoid);
 new_record_repl[Anum_pg_database_dattablespace - 1] = 'r';

On exécute la fonction de modification du tuple.

 newtuple = heap_modifytuple(oldtuple, RelationGetDescr(pgdbrel), new_record,
 							new_record_nulls, new_record_repl);
 simple_heap_update(pgdbrel, &oldtuple->t_self, newtuple);

Et on met à jour les index systèmes :

 CatalogUpdateIndexes(pgdbrel, newtuple);

Enfin, on termine le parcours.

 systable_endscan(sysscan);

Simple. Le pire, c'est que ça fonctionne. Là, plus rien ne manque (en dehors de la documentation, de la gestion des objets comme les index et les tables TOAST, mais bon, c'est broutille). Du coup, j'ai pris mon courage à deux mains pour envoyer un mail sur la liste pgsql-hackers. En effet, il me reste quelques soucis à régler :

  1. je trouve toujours bizarre, voire carrément contre nature, d'avoir à exécuter le ALTER DATABASE depuis la base de données à modifier
  2. le fait de ne pas pouvoir bouger les objets systèmes est vraiment un problème plus important car cela empêche d'aller au bout de cette fonctionnalité
  3. enfin, il se trouve qu'avec le nouveau code ci-dessus, PostgreSQL se met à chercher un fichier PG_VERSION dans le répertoire pg_tblspc/<oid tablespace>/<oid database>... ce qui génère un certain nombre d'erreurs fatales.

Bref, des conseils avisés sont toujours bons à prendre. Et j'ai eu ma réponse. Tom Lane. Directement. Quel honneur. Et je ne me suis même pas fait dégommé. Bizarre... Enfin bon, sa réponse est très intéressante mais va nous obliger à revoir une bonne partie du code déjà réalisé. Ce sera pour le prochain billet.

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet