Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  plpy_procedure.c

  Sprache: C
 

/*
 * Python procedure manipulation for plpython
 *
 * src/pl/plpython/plpy_procedure.c
 */


#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpy_util.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/syscache.h"

static HTAB *PLy_procedure_cache = NULL;

static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger);
static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
static char *PLy_procedure_munge_source(const char *name, const char *src);


void
init_procedure_caches(void)
{
 HASHCTL  hash_ctl;

 hash_ctl.keysize = sizeof(PLyProcedureKey);
 hash_ctl.entrysize = sizeof(PLyProcedureEntry);
 PLy_procedure_cache = hash_create("PL/Python procedures"32, &hash_ctl,
           HASH_ELEM | HASH_BLOBS);
}

/*
 * PLy_procedure_name: get the name of the specified procedure.
 *
 * NB: this returns the SQL name, not the internal Python procedure name
 */

char *
PLy_procedure_name(PLyProcedure *proc)
{
 if (proc == NULL)
  return "<unknown procedure>";
 return proc->proname;
}

/*
 * PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
 * returns a new PLyProcedure.
 *
 * fn_oid is the OID of the function requested
 * fn_rel is InvalidOid or the relation this function triggers on
 * is_trigger denotes whether the function is a trigger function
 *
 * The reason that both fn_rel and is_trigger need to be passed is that when
 * trigger functions get validated we don't know which relation(s) they'll
 * be used with, so no sensible fn_rel can be passed.
 */

PLyProcedure *
PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
{
 bool  use_cache = !(is_trigger && fn_rel == InvalidOid);
 HeapTuple procTup;
 PLyProcedureKey key;
 PLyProcedureEntry *volatile entry = NULL;
 PLyProcedure *volatile proc = NULL;
 bool  found = false;

 procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
 if (!HeapTupleIsValid(procTup))
  elog(ERROR, "cache lookup failed for function %u", fn_oid);

 /*
  * Look for the function in the cache, unless we don't have the necessary
  * information (e.g. during validation). In that case we just don't cache
  * anything.
 */

 if (use_cache)
 {
  key.fn_oid = fn_oid;
  key.fn_rel = fn_rel;
  entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
  proc = entry->proc;
 }

 PG_TRY();
 {
  if (!found)
  {
   /* Haven't found it, create a new procedure */
   proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
   if (use_cache)
    entry->proc = proc;
  }
  else if (!PLy_procedure_valid(proc, procTup))
  {
   /* Found it, but it's invalid, free and reuse the cache entry */
   entry->proc = NULL;
   if (proc)
    PLy_procedure_delete(proc);
   proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
   entry->proc = proc;
  }
  /* Found it and it's valid, it's fine to use it */
 }
 PG_CATCH();
 {
  /* Do not leave an uninitialized entry in the cache */
  if (use_cache)
   hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
  PG_RE_THROW();
 }
 PG_END_TRY();

 ReleaseSysCache(procTup);

 return proc;
}

/*
 * Create a new PLyProcedure structure
 */

static PLyProcedure *
PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
{
 char  procName[NAMEDATALEN + 256];
 Form_pg_proc procStruct;
 PLyProcedure *volatile proc;
 MemoryContext cxt;
 MemoryContext oldcxt;
 int   rv;
 char    *ptr;

 procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 rv = snprintf(procName, sizeof(procName),
      "__plpython_procedure_%s_%u",
      NameStr(procStruct->proname),
      fn_oid);
 if (rv >= sizeof(procName) || rv < 0)
  elog(ERROR, "procedure name would overrun buffer");

 /* Replace any not-legal-in-Python-names characters with '_' */
 for (ptr = procName; *ptr; ptr++)
 {
  if (!((*ptr >= 'A' && *ptr <= 'Z') ||
     (*ptr >= 'a' && *ptr <= 'z') ||
     (*ptr >= '0' && *ptr <= '9')))
   *ptr = '_';
 }

 /* Create long-lived context that all procedure info will live in */
 cxt = AllocSetContextCreate(TopMemoryContext,
        "PL/Python function",
        ALLOCSET_DEFAULT_SIZES);

 oldcxt = MemoryContextSwitchTo(cxt);

 proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
 proc->mcxt = cxt;

 PG_TRY();
 {
  Datum  protrftypes_datum;
  Datum  prosrcdatum;
  bool  isnull;
  char    *procSource;
  int   i;

  proc->proname = pstrdup(NameStr(procStruct->proname));
  MemoryContextSetIdentifier(cxt, proc->proname);
  proc->pyname = pstrdup(procName);
  proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
  proc->fn_tid = procTup->t_self;
  proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
  proc->is_setof = procStruct->proretset;
  proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
  proc->is_trigger = is_trigger;
  proc->src = NULL;
  proc->argnames = NULL;
  proc->args = NULL;
  proc->nargs = 0;
  proc->langid = procStruct->prolang;
  protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
           Anum_pg_proc_protrftypes,
           &isnull);
  proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
  proc->code = NULL;
  proc->statics = NULL;
  proc->globals = NULL;
  proc->calldepth = 0;
  proc->argstack = NULL;

  /*
   * get information required for output conversion of the return value,
   * but only if this isn't a trigger.
 */

  if (!is_trigger)
  {
   Oid   rettype = procStruct->prorettype;
   HeapTuple rvTypeTup;
   Form_pg_type rvTypeStruct;

   rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
   if (!HeapTupleIsValid(rvTypeTup))
    elog(ERROR, "cache lookup failed for type %u", rettype);
   rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);

   /* Disallow pseudotype result, except for void or record */
   if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
   {
    if (rettype == VOIDOID ||
     rettype == RECORDOID)
      /* okay */ ;
    else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
     ereport(ERROR,
       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
        errmsg("trigger functions can only be called as triggers")));
    else
     ereport(ERROR,
       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
        errmsg("PL/Python functions cannot return type %s",
         format_type_be(rettype))));
   }

   /* set up output function for procedure result */
   PLy_output_setup_func(&proc->result, proc->mcxt,
          rettype, -1, proc);

   ReleaseSysCache(rvTypeTup);
  }
  else
  {
   /*
    * In a trigger function, we use proc->result and proc->result_in
    * for converting tuples, but we don't yet have enough info to set
    * them up.  PLy_exec_trigger will deal with it.
 */

   proc->result.typoid = InvalidOid;
   proc->result_in.typoid = InvalidOid;
  }

  /*
   * Now get information required for input conversion of the
   * procedure's arguments.  Note that we ignore output arguments here.
   * If the function returns record, those I/O functions will be set up
   * when the function is first called.
 */

  if (procStruct->pronargs)
  {
   Oid     *types;
   char   **names,
        *modes;
   int   pos,
      total;

   /* extract argument type info from the pg_proc tuple */
   total = get_func_arg_info(procTup, &types, &names, &modes);

   /* count number of in+inout args into proc->nargs */
   if (modes == NULL)
    proc->nargs = total;
   else
   {
    /* proc->nargs was initialized to 0 above */
    for (i = 0; i < total; i++)
    {
     if (modes[i] != PROARGMODE_OUT &&
      modes[i] != PROARGMODE_TABLE)
      (proc->nargs)++;
    }
   }

   /* Allocate arrays for per-input-argument data */
   proc->argnames = (char **) palloc0(sizeof(char *) * proc->nargs);
   proc->args = (PLyDatumToOb *) palloc0(sizeof(PLyDatumToOb) * proc->nargs);

   for (i = pos = 0; i < total; i++)
   {
    HeapTuple argTypeTup;
    Form_pg_type argTypeStruct;

    if (modes &&
     (modes[i] == PROARGMODE_OUT ||
      modes[i] == PROARGMODE_TABLE))
     continue/* skip OUT arguments */

    Assert(types[i] == procStruct->proargtypes.values[pos]);

    argTypeTup = SearchSysCache1(TYPEOID,
            ObjectIdGetDatum(types[i]));
    if (!HeapTupleIsValid(argTypeTup))
     elog(ERROR, "cache lookup failed for type %u", types[i]);
    argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);

    /* disallow pseudotype arguments */
    if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
     ereport(ERROR,
       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
        errmsg("PL/Python functions cannot accept type %s",
         format_type_be(types[i]))));

    /* set up I/O function info */
    PLy_input_setup_func(&proc->args[pos], proc->mcxt,
          types[i], -1/* typmod not known */
          proc);

    /* get argument name */
    proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;

    ReleaseSysCache(argTypeTup);

    pos++;
   }
  }

  /*
   * get the text of the function.
 */

  prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
            Anum_pg_proc_prosrc);
  procSource = TextDatumGetCString(prosrcdatum);

  PLy_procedure_compile(proc, procSource);

  pfree(procSource);
 }
 PG_CATCH();
 {
  MemoryContextSwitchTo(oldcxt);
  PLy_procedure_delete(proc);
  PG_RE_THROW();
 }
 PG_END_TRY();

 MemoryContextSwitchTo(oldcxt);
 return proc;
}

/*
 * Insert the procedure into the Python interpreter
 */

void
PLy_procedure_compile(PLyProcedure *proc, const char *src)
{
 PyObject   *crv = NULL;
 char    *msrc;
 PyObject   *code0;

 proc->globals = PyDict_Copy(PLy_interp_globals);

 /*
  * SD is private preserved data between calls. GD is global data shared by
  * all functions
 */

 proc->statics = PyDict_New();
 if (!proc->statics)
  PLy_elog(ERROR, NULL);
 PyDict_SetItemString(proc->globals, "SD", proc->statics);

 /*
  * insert the function code into the interpreter
 */

 msrc = PLy_procedure_munge_source(proc->pyname, src);
 /* Save the mangled source for later inclusion in tracebacks */
 proc->src = MemoryContextStrdup(proc->mcxt, msrc);
 code0 = Py_CompileString(msrc, "<string>", Py_file_input);
 if (code0)
  crv = PyEval_EvalCode(code0, proc->globals, NULL);
 pfree(msrc);

 if (crv != NULL)
 {
  int   clen;
  char  call[NAMEDATALEN + 256];

  Py_DECREF(crv);

  /*
   * compile a call to the function
 */

  clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
  if (clen < 0 || clen >= sizeof(call))
   elog(ERROR, "string would overflow buffer");
  proc->code = Py_CompileString(call, "<string>", Py_eval_input);
  if (proc->code != NULL)
   return;
 }

 if (proc->proname)
  PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
     proc->proname);
 else
  PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
}

void
PLy_procedure_delete(PLyProcedure *proc)
{
 Py_XDECREF(proc->code);
 Py_XDECREF(proc->statics);
 Py_XDECREF(proc->globals);
 MemoryContextDelete(proc->mcxt);
}

/*
 * Decide whether a cached PLyProcedure struct is still valid
 */

static bool
PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
{
 if (proc == NULL)
  return false;

 /* If the pg_proc tuple has changed, it's not valid */
 if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
    ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
  return false;

 return true;
}

static char *
PLy_procedure_munge_source(const char *name, const char *src)
{
 char    *mrc,
      *mp;
 const char *sp;
 size_t  mlen;
 int   plen;

 /*
  * room for function source and the def statement
 */

 mlen = (strlen(src) * 2) + strlen(name) + 16;

 mrc = palloc(mlen);
 plen = snprintf(mrc, mlen, "def %s():\n\t", name);
 Assert(plen >= 0 && plen < mlen);

 sp = src;
 mp = mrc + plen;

 while (*sp != '\0')
 {
  if (*sp == '\r' && *(sp + 1) == '\n')
   sp++;

  if (*sp == '\n' || *sp == '\r')
  {
   *mp++ = '\n';
   *mp++ = '\t';
   sp++;
  }
  else
   *mp++ = *sp++;
 }
 *mp++ = '\n';
 *mp++ = '\n';
 *mp = '\0';

 if (mp > (mrc + mlen))
  elog(FATAL, "buffer overrun in PLy_procedure_munge_source");

 return mrc;
}

Messung V0.5 in Prozent
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142