PostgreSQL: Utiliser le principe du type “varlena”

PostgreSQL utilise depuis Postgres le type varlena, qui signifie variable length array en anglais, soit tableau à taille variable en français. Ce type permet de stocker des données à taille variable dans la base de données. Le principe est très simple: stocker en mémoire une ou plusieurs données de façon contiguë. De plus, il ne doit pas y avoir de pointeur dans le type.

La structure “de base” de varlena est définie dans le fichier src/include/c.h, line 401 pour PostgreSQL 8.4 par:

Note: le premier tableau de 4 caractères peu très bien est remplacé par un int32, ça revient au même: 4 octets.

Ce type représente le principe: un champ vl_dat, qui contiendra la donnée et d’autres champs qui ne doivent pas être des pointeurs, mais des entiers le plus souvent (ici un tableau de 4 caractères), qui contiennent des informations sur cette donnée. Cela permet à PostgreSQL de pouvoir stocker sur le disque et en mémoire toutes les données en une fois, pouvant ainsi les déplacer comme bon lui semble.

Attention: la propriété qui contient les données doit être la dernière propriété de la structure.

Tout type de donnée un peu complexe doit utiliser ce principe lorsqu’il stocke des données de taille variable en base afin d’assurer une intégrité aux données stockées. Nous allons voir, à l’aide de la librairie parse_url pour PostgreSQL, comment créer un type de données stockant de nombreuses données de taille variable dans une seule et même donnée, dans le but d’être utilisé comme type de colonne dans une table par exemple.

Problématique

Ce que l’on veut faire, c’est stocker des informations (des chaines de caractères) à taille variable à propos d’une adresse URL. Pour cela, il nous suffit de faire une fonction qui va parser une chaine de caractères entrante, qui est l’URL, et nous retourner un type de données url qui contient des propriétés char* scheme, host, path, query

Un type de données “classique”

Pour ça, nous allons créer un type de donnée url, classique, qui contient des pointeurs vers des tableaux (chaines) de caractères alloués en mémoire:

Ainsi, nous allons appeler la fonction parse_url_exec, qui va retourner un url*, contenant les valeurs demandées. Le problème, c’est que les données ne seront pas réparties de manière contiguë en mémoire mais séparées à l’aide des pointeurs. En effet, la valeur de url->scheme peut se trouver à l’adresse mémoire X, alors que la valeur de url->query à l’adresse mémoire Y et ainsi de suite pour toutes les valeurs. C’est pourquoi nous allons chercher à rassembler ces données dans un seul et même bloc mémoire.

Un type de données “varlena”, contiguë en mémoire

Nous allons maintenant créer un nouveau type, url_internal qui est constitué d’un seul tableau de caractère qui contiendra toutes les valeurs, mises bout à bout et de plusieurs entiers, qui correspondront à l’index des données dans le tableau de caractères et à leur taille.

Note: On pourrait faire autrement, en séparant les données par '', en n’utilisant qu’un entier qui correspond à la taille ou à l’index de la valeur dans la chaine de caractères mais j’ai choisi, pour des questions pratiques, de faire comme ça, il n’y a pas vraiment de convention, uniquement un principe.

Nous utilisons un tableau de 1 caractère pour la compilation. Cela permet de dire que c’est bien un tableau de caractères, et comme le compilateur ne vérifie pas la taille des données insérées, nous allons pouvoir lui réserver plus de mémoire, pour y stocker des données plus longues. Ainsi, pour transformer une donnée du type url vers une donnée du type url_internal, nous allons allouer lors de la création de ce url_internal un bloc de mémoire de la taille de la structure + de la taille de la donnée à stocker dans data.

Ensuite, nous n’avons qu’a mettre bout à bout à l’aide de strcat le contenu des chaines de caractère de url pour en faire une nouvelle chaine data qui sera copiée dans internal->data à l’aide de memcpy:

Pour récupérer ces valeurs depuis un type url_internal vers un type url, rien de plus simple, il vous suffit, pour chaque donnée (host, path, …) d’allouer en mémoire la longueur de la donnée + 1 pour '' de fin de chaine, puis de récupérer les données depuis internal->data. Voici un exemple pour récuperer l’host (où object est un url*):

On récupère internal->host_len caractères dans internal->data à partir du caractère numéro internal->host.

En espérant vous avoir apporter des informations sur ce type de données et comment le manipuler, selon mes conventions. Surtout, n’hésitez pas à allez voir dans le code source de la librairie parse_url v2 pour PostgreSQL, en particulier pour la fonction make_varlena, appelée lors du renvoi de url_internal par la fonction url_in.

facebooktwittergoogle_plusredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">