PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Créer une copie de la fonction C interne de PostgreSQL et la charger en tant que fonction définie par l'utilisateur

La raison pour laquelle le client psql vous demande si vous souhaitez vous reconnecter est que le backend est en erreur de segmentation, comme indiqué dans les commentaires.

Il serait possible de collecter un vidage mémoire d'un tel plantage et de l'examiner avec un débogueur (par exemple gdb) pour savoir exactement où il plante. Cependant, ma meilleure hypothèse est qu'il plante parce que vous avez pris un gros fichier écrit pour être un composant central de postgresql, l'avez compilé séparément et avez tenté de le charger en tant que module d'extension.

Le fichier numeric.c contient un grand nombre de fonctions, de variables statiques et de structures de données, dont vous essayez de dupliquer une seule. Toutes ces fonctions, variables, etc. existent déjà dans le système postgresql en cours d'exécution. Lorsque vous compilez votre version de numeric.c et que vous la chargez, la nouvelle fonction que vous ajoutez référencera les fonctions et les variables de votre bibliothèque au lieu d'utiliser celles du programme principal postgresql. Il fait probablement référence à des structures de données qui ne sont pas correctement initialisées, ce qui le fait planter.

Je vous recommande de commencer avec un fichier vierge et de ne copier que la fonction int2_avg_accum de numeric.c (renommée comme vous l'avez fait). Si cette fonction appelle d'autres fonctions dans postgresql ou fait référence à des variables, elle utilisera les fonctions et les variables du binaire postgresql principal, ce que vous voulez. Vous pouvez #inclure le numeric.h d'origine pour obtenir les déclarations de toutes les fonctions externes.

Il existe d'autres différences entre la façon dont la fonction est définie en tant que fonction interne et la façon dont elle doit être définie lorsqu'elle est chargée en tant que module chargé dynamiquement :

  • Vous deviez spécifier que vous utilisez la convention d'appel V1 en ajoutant la macro :

    PG_FUNCTION_INFO_V1(int2_avg_accum2);

    S'il est manquant, cela entraînera également des erreurs de segmentation car postgresql assumera les conventions d'appel de la version 0, ce qui ne correspond pas à la définition de la fonction !

  • Comme vous l'avez indiqué, vous devez inclure le PG_MODOULE_MAGIC.

Le fichier complet, qui a fonctionné pour moi, est :

#include "postgres.h"
#include "fmgr.h"
#include "utils/array.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

typedef struct Int8TransTypeData
{
    int64       count;
    int64       sum;
} Int8TransTypeData;

PG_FUNCTION_INFO_V1(int2_avg_accum2);

Datum
int2_avg_accum2(PG_FUNCTION_ARGS)
{
    ArrayType  *transarray;
    int16       newval = PG_GETARG_INT16(1);
    Int8TransTypeData *transdata;

    /*
     * If we're invoked as an aggregate, we can cheat and modify our first
     * parameter in-place to reduce palloc overhead. Otherwise we need to make
     * a copy of it before scribbling on it.
     */
    if (AggCheckCallContext(fcinfo, NULL))
        transarray = PG_GETARG_ARRAYTYPE_P(0);
    else
        transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);

    if (ARR_HASNULL(transarray) ||
        ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
        elog(ERROR, "expected 2-element int8 array");

    transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
    transdata->count++;
    transdata->sum += newval;

    PG_RETURN_ARRAYTYPE_P(transarray);
}

Compilé avec :

gcc -I/usr/pgsql-9.2/include/server -fPIC -c my_avg_accum.c
gcc -shared -o my_avg_accum.so my_avg_accum.o

J'utilisais Postgresql 9.2 sur Centos 6. Vous devrez peut-être ajuster vos chemins en fonction de votre configuration.