Aller au contenu

Estimer la taille des Large Objects par table

Problématique

Les Large Objects ne sont pas « attribués » à une table dans le dictionnaire de données. Ce sont des objets indépendants, stockés dans pg_largeobject, et indexés par un oid, qui est manipulé par les fonctions lo_*. Le lien avec la/les table stockant ces oid est donc très faible (pas de relation de clé étrangère). Par ailleurs, tous les Large Objects sont stockés dans une seule et même table, ce qui rend difficile le chiffrage de la volumétrie des Large Objects associés à une table particulière.

Solution

Il est impossible de résoudre le problème avec une simple requête, il faut absolument utiliser une fonction : on ne sait pas dans quelles tables se trouvent les champs oid susceptibles de contenir des liens vers des large objects. Cette fonction retourne donc les oid contenus dans la base, et la table d’où ils proviennent :

CREATE OR REPLACE FUNCTION lo_oids ( )
 RETURNS TABLE ( loid oid, tableoid oid)
 LANGUAGE plpgsql
AS $function$
DECLARE
  vcurseur record;
  vloid oid;
BEGIN
    FOR vcurseur IN SELECT attrelid, attname
                    FROM pg_attribute 
                    WHERE atttypid IN (SELECT oid from pg_type where typname in ('oid', 'lo')) 
                    AND attnum > 0 
                    AND NOT attisdropped 
                    AND attrelid IN (SELECT oid 
                                     FROM pg_class 
                                     WHERE relnamespace NOT IN (SELECT oid 
                                                                FROM pg_namespace 
                                                                WHERE nspname IN ('pg_catalog')
                                                               ) 
                                     AND relkind = 'r'
                                    ) 
                   LOOP
        -- On a un curseur qui contient l'id de la table, et le nom de l'attribut
        tableoid = vcurseur.attrelid;
        FOR loid IN EXECUTE 'SELECT ' || vcurseur.attname || ' FROM ' || (vcurseur.attrelid)::regclass LOOP
            RETURN NEXT;
        END LOOP;
    END LOOP;
END
$function$;

Il suffit ensuite d’une jointure entre cette fonction et la table pg_largeobject :

SELECT oids.tableoid::regclass, sum(octet_length(data)) 
FROM lo_oids() oids 
JOIN pg_largeobject ON (oids.loid = pg_largeobject.loid)
GROUP BY tableoid;

Cette solution est bien entendu facile à adapter, si besoin, pour sortir ces informations par colonne de table, par exemple.

Cependant, attention : les valeurs retournées sont les tailles des Large Objects, pas la place qu’ils occupent sur le disque.