Proof-of-concept patch for CREATE LANGUAGE template idea - Mailing list pgsql-patches

From Tom Lane
Subject Proof-of-concept patch for CREATE LANGUAGE template idea
Date
Msg-id 1905.1125764587@sss.pgh.pa.us
Whole thread Raw
List pgsql-patches
Attached is a preliminary patch for the "PL template" facility I
proposed a couple days ago.  This is a stripped-down implementation
that just hardwires a lookup table in proclang.c, instead of putting
the data into a system catalog as should happen in 8.2.  It's not
ready to apply yet because I haven't touched pg_dump or the
documentation, but I thought I'd put it up for comment.

Note that the patch disables createlang's -L option.  I can't see that
that option has any use anymore; it used to be needed by pg_regress
for testing temporary installations, but since we put in the
auto-package-relocation support, $libdir works fine.  Anyone still
see a need for it?

One small annoyance is that since createlang no longer knows what
languages are supported, it gives a fairly unhelpful error message
if you misspell:

$ createlang plprl
createlang: language installation failed: ERROR:  no handler specified for procedural language

This could of course be fixed properly if the pltemplate system catalog
existed, but in the meantime it seems we need a kluge.  I hate to
duplicate the list of known PLs in both the backend and createlang
... anyone have a better idea?

            regards, tom lane

*** src/backend/commands/proclang.c.orig    Thu Apr 14 16:03:24 2005
--- src/backend/commands/proclang.c    Fri Sep  2 19:51:06 2005
***************
*** 13,37 ****
   */
  #include "postgres.h"

- #include <ctype.h>
-
  #include "access/heapam.h"
  #include "catalog/dependency.h"
  #include "catalog/indexing.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_language.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "commands/proclang.h"
  #include "commands/defrem.h"
  #include "fmgr.h"
  #include "miscadmin.h"
  #include "parser/parse_func.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"


  /* ---------------------------------------------------------------------
   * CREATE PROCEDURAL LANGUAGE
   * ---------------------------------------------------------------------
--- 13,52 ----
   */
  #include "postgres.h"

  #include "access/heapam.h"
  #include "catalog/dependency.h"
  #include "catalog/indexing.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "commands/proclang.h"
  #include "commands/defrem.h"
  #include "fmgr.h"
  #include "miscadmin.h"
+ #include "parser/gramparse.h"
  #include "parser/parse_func.h"
  #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"


+ typedef struct
+ {
+     char       *lanname;        /* PL name */
+     bool        lantrusted;        /* trusted? */
+     char       *lanhandler;        /* name of handler function */
+     char       *lanvalidator;    /* name of validator function, or NULL */
+     char       *lanlibrary;        /* path of shared library */
+ } PLTemplate;
+
+ static void create_proc_lang(const char *languageName,
+                              Oid handlerOid, Oid valOid, bool trusted);
+ static PLTemplate *find_language_template(const char *languageName);
+
+
  /* ---------------------------------------------------------------------
   * CREATE PROCEDURAL LANGUAGE
   * ---------------------------------------------------------------------
***************
*** 40,58 ****
  CreateProceduralLanguage(CreatePLangStmt *stmt)
  {
      char       *languageName;
!     Oid            procOid,
!                 valProcOid;
      Oid            funcrettype;
      Oid            funcargtypes[1];
-     NameData    langname;
-     char        nulls[Natts_pg_language];
-     Datum        values[Natts_pg_language];
-     Relation    rel;
-     HeapTuple    tup;
-     TupleDesc    tupDesc;
-     int            i;
-     ObjectAddress myself,
-                 referenced;

      /*
       * Check permission
--- 55,65 ----
  CreateProceduralLanguage(CreatePLangStmt *stmt)
  {
      char       *languageName;
!     PLTemplate *pltemplate;
!     Oid            handlerOid,
!                 valOid;
      Oid            funcrettype;
      Oid            funcargtypes[1];

      /*
       * Check permission
***************
*** 76,139 ****
                   errmsg("language \"%s\" already exists", languageName)));

      /*
!      * Lookup the PL handler function and check that it is of the expected
!      * return type
       */
!     procOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
!     funcrettype = get_func_rettype(procOid);
!     if (funcrettype != LANGUAGE_HANDLEROID)
      {
          /*
!          * We allow OPAQUE just so we can load old dump files.    When we
!          * see a handler function declared OPAQUE, change it to
!          * LANGUAGE_HANDLER.
           */
!         if (funcrettype == OPAQUEOID)
          {
!             ereport(WARNING,
!                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                      errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"",
!                             NameListToString(stmt->plhandler))));
!             SetFunctionReturnType(procOid, LANGUAGE_HANDLEROID);
          }
          else
!             ereport(ERROR,
!                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!               errmsg("function %s must return type \"language_handler\"",
!                      NameListToString(stmt->plhandler))));
!     }

!     /* validate the validator function */
!     if (stmt->plvalidator)
!     {
!         funcargtypes[0] = OIDOID;
!         valProcOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
!         /* return value is ignored, so we don't check the type */
      }
      else
!         valProcOid = InvalidOid;

      /*
       * Insert the new language into pg_language
       */
!     for (i = 0; i < Natts_pg_language; i++)
!     {
!         nulls[i] = ' ';
!         values[i] = (Datum) NULL;
!     }

!     i = 0;
!     namestrcpy(&langname, languageName);
!     values[i++] = NameGetDatum(&langname);        /* lanname */
!     values[i++] = BoolGetDatum(true);    /* lanispl */
!     values[i++] = BoolGetDatum(stmt->pltrusted);        /* lanpltrusted */
!     values[i++] = ObjectIdGetDatum(procOid);    /* lanplcallfoid */
!     values[i++] = ObjectIdGetDatum(valProcOid); /* lanvalidator */
!     nulls[i] = 'n';                /* lanacl */

!     rel = heap_open(LanguageRelationId, RowExclusiveLock);

-     tupDesc = rel->rd_att;
      tup = heap_formtuple(tupDesc, values, nulls);

      simple_heap_insert(rel, tup);
--- 83,256 ----
                   errmsg("language \"%s\" already exists", languageName)));

      /*
!      * If we have template information for the language, ignore the supplied
!      * parameters (if any) and use the template information.
       */
!     if ((pltemplate = find_language_template(languageName)) != NULL)
      {
+         List    *funcname;
+
          /*
!          * Find or create the handler function, which we force to be in
!          * the pg_catalog schema.  If already present, it must have the
!          * correct return type.
           */
!         funcname = SystemFuncName(pltemplate->lanhandler);
!         handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
!         if (OidIsValid(handlerOid))
          {
!             funcrettype = get_func_rettype(handlerOid);
!             if (funcrettype != LANGUAGE_HANDLEROID)
!                 ereport(ERROR,
!                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                          errmsg("function %s must return type \"language_handler\"",
!                                 NameListToString(funcname))));
          }
          else
!         {
!             handlerOid = ProcedureCreate(pltemplate->lanhandler,
!                                          PG_CATALOG_NAMESPACE,
!                                          false,        /* replace */
!                                          false,        /* returnsSet */
!                                          LANGUAGE_HANDLEROID,
!                                          ClanguageId,
!                                          F_FMGR_C_VALIDATOR,
!                                          pltemplate->lanhandler,
!                                          pltemplate->lanlibrary,
!                                          false,        /* isAgg */
!                                          false,        /* security_definer */
!                                          false,        /* isStrict */
!                                          PROVOLATILE_VOLATILE,
!                                          buildoidvector(funcargtypes, 0),
!                                          PointerGetDatum(NULL),
!                                          PointerGetDatum(NULL),
!                                          PointerGetDatum(NULL));
!         }

!         /*
!          * Likewise for the validator, if required; but we don't care about
!          * its return type.
!          */
!         if (pltemplate->lanvalidator)
!         {
!             funcname = SystemFuncName(pltemplate->lanvalidator);
!             funcargtypes[0] = OIDOID;
!             valOid = LookupFuncName(funcname, 1, funcargtypes, true);
!             if (!OidIsValid(valOid))
!             {
!                 valOid = ProcedureCreate(pltemplate->lanvalidator,
!                                          PG_CATALOG_NAMESPACE,
!                                          false,        /* replace */
!                                          false,        /* returnsSet */
!                                          VOIDOID,
!                                          ClanguageId,
!                                          F_FMGR_C_VALIDATOR,
!                                          pltemplate->lanvalidator,
!                                          pltemplate->lanlibrary,
!                                          false,        /* isAgg */
!                                          false,        /* security_definer */
!                                          false,        /* isStrict */
!                                          PROVOLATILE_VOLATILE,
!                                          buildoidvector(funcargtypes, 1),
!                                          PointerGetDatum(NULL),
!                                          PointerGetDatum(NULL),
!                                          PointerGetDatum(NULL));
!             }
!         }
!         else
!             valOid = InvalidOid;
!
!         /* ok, create it */
!         create_proc_lang(languageName, handlerOid, valOid,
!                          pltemplate->lantrusted);
      }
      else
!     {
!         /*
!          * No template, so use the provided information.  There MUST be
!          * a handler clause.
!          */
!         if (!stmt->plhandler)
!             ereport(ERROR,
!                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
!                      errmsg("no handler specified for procedural language")));
!
!         /*
!          * Lookup the PL handler function and check that it is of the expected
!          * return type
!          */
!         handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
!         funcrettype = get_func_rettype(handlerOid);
!         if (funcrettype != LANGUAGE_HANDLEROID)
!         {
!             /*
!              * We allow OPAQUE just so we can load old dump files.    When we
!              * see a handler function declared OPAQUE, change it to
!              * LANGUAGE_HANDLER.  (This is probably obsolete and removable?)
!              */
!             if (funcrettype == OPAQUEOID)
!             {
!                 ereport(WARNING,
!                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                          errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"",
!                                 NameListToString(stmt->plhandler))));
!                 SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
!             }
!             else
!                 ereport(ERROR,
!                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
!                          errmsg("function %s must return type \"language_handler\"",
!                                 NameListToString(stmt->plhandler))));
!         }
!
!         /* validate the validator function */
!         if (stmt->plvalidator)
!         {
!             funcargtypes[0] = OIDOID;
!             valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
!             /* return value is ignored, so we don't check the type */
!         }
!         else
!             valOid = InvalidOid;
!
!         /* ok, create it */
!         create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted);
!     }
! }
!
! /*
!  * Guts of language creation.
!  */
! static void
! create_proc_lang(const char *languageName,
!                  Oid handlerOid, Oid valOid, bool trusted)
! {
!     Relation    rel;
!     TupleDesc    tupDesc;
!     Datum        values[Natts_pg_language];
!     char        nulls[Natts_pg_language];
!     NameData    langname;
!     HeapTuple    tup;
!     ObjectAddress myself,
!                 referenced;

      /*
       * Insert the new language into pg_language
       */
!     rel = heap_open(LanguageRelationId, RowExclusiveLock);
!     tupDesc = rel->rd_att;

!     memset(values, 0, sizeof(values));
!     memset(nulls, ' ', sizeof(nulls));

!     namestrcpy(&langname, languageName);
!     values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
!     values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
!     values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
!     values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
!     values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
!     nulls[Anum_pg_language_lanacl - 1] = 'n';

      tup = heap_formtuple(tupDesc, values, nulls);

      simple_heap_insert(rel, tup);
***************
*** 149,163 ****

      /* dependency on the PL handler function */
      referenced.classId = ProcedureRelationId;
!     referenced.objectId = procOid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

      /* dependency on the validator function, if any */
!     if (OidIsValid(valProcOid))
      {
          referenced.classId = ProcedureRelationId;
!         referenced.objectId = valProcOid;
          referenced.objectSubId = 0;
          recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
      }
--- 266,280 ----

      /* dependency on the PL handler function */
      referenced.classId = ProcedureRelationId;
!     referenced.objectId = handlerOid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

      /* dependency on the validator function, if any */
!     if (OidIsValid(valOid))
      {
          referenced.classId = ProcedureRelationId;
!         referenced.objectId = valOid;
          referenced.objectSubId = 0;
          recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
      }
***************
*** 165,170 ****
--- 282,322 ----
      heap_close(rel, RowExclusiveLock);
  }

+ /*
+  * Look to see if we have template information for the given language name.
+  *
+  * XXX for PG 8.1, the template info is hard-wired.  This is to be replaced
+  * by a shared system catalog in 8.2.
+  */
+ static PLTemplate *
+ find_language_template(const char *languageName)
+ {
+     static PLTemplate templates[] = {
+         { "plpgsql", true, "plpgsql_call_handler", "plpgsql_validator",
+           "$libdir/plpgsql" },
+         { "pltcl", true, "pltcl_call_handler", NULL,
+           "$libdir/pltcl" },
+         { "pltclu", false, "pltclu_call_handler", NULL,
+           "$libdir/pltcl" },
+         { "plperl", true, "plperl_call_handler", "plperl_validator",
+           "$libdir/plperl" },
+         { "plperlu", false, "plperl_call_handler", "plperl_validator",
+           "$libdir/plperl" },
+         { "plpythonu", false, "plpython_call_handler", NULL,
+           "$libdir/plpython" },
+         { NULL, false, NULL, NULL, NULL }
+     };
+
+     PLTemplate *ptr;
+
+     for (ptr = templates; ptr->lanname != NULL; ptr++)
+     {
+         if (strcmp(languageName, ptr->lanname) == 0)
+             return ptr;
+     }
+     return NULL;
+ }
+

  /* ---------------------------------------------------------------------
   * DROP PROCEDURAL LANGUAGE
***************
*** 186,193 ****
                 errmsg("must be superuser to drop procedural language")));

      /*
!      * Translate the language name, check that this language exist and is
!      * a PL
       */
      languageName = case_translate_language_name(stmt->plname);

--- 338,344 ----
                 errmsg("must be superuser to drop procedural language")));

      /*
!      * Translate the language name, check that the language exists
       */
      languageName = case_translate_language_name(stmt->plname);

***************
*** 244,249 ****
--- 395,404 ----
      HeapTuple    tup;
      Relation    rel;

+     /* Translate both names for consistency with CREATE */
+     oldname = case_translate_language_name(oldname);
+     newname = case_translate_language_name(newname);
+
      rel = heap_open(LanguageRelationId, RowExclusiveLock);

      tup = SearchSysCacheCopy(LANGNAME,
***************
*** 262,268 ****
                  (errcode(ERRCODE_DUPLICATE_OBJECT),
                   errmsg("language \"%s\" already exists", newname)));

!     /* must be superuser */
      if (!superuser())
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 417,423 ----
                  (errcode(ERRCODE_DUPLICATE_OBJECT),
                   errmsg("language \"%s\" already exists", newname)));

!     /* must be superuser, since we do not have owners for PLs */
      if (!superuser())
          ereport(ERROR,
                  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*** src/backend/parser/gram.y.orig    Wed Aug 24 15:34:33 2005
--- src/backend/parser/gram.y    Fri Sep  2 18:48:27 2005
***************
*** 194,200 ****
                  index_name name function_name file_name

  %type <list>    func_name handler_name qual_Op qual_all_Op subquery_Op
!                 opt_class opt_validator

  %type <range>    qualified_name OptConstrFromTable

--- 194,200 ----
                  index_name name function_name file_name

  %type <list>    func_name handler_name qual_Op qual_all_Op subquery_Op
!                 opt_class opt_handler opt_validator

  %type <range>    qualified_name OptConstrFromTable

***************
*** 2303,2315 ****

  CreatePLangStmt:
              CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
!             HANDLER handler_name opt_validator opt_lancompiler
              {
                  CreatePLangStmt *n = makeNode(CreatePLangStmt);
                  n->plname = $5;
!                 n->plhandler = $7;
!                 n->plvalidator = $8;
                  n->pltrusted = $2;
                  $$ = (Node *)n;
              }
          ;
--- 2303,2316 ----

  CreatePLangStmt:
              CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
!             opt_handler opt_validator opt_lancompiler
              {
                  CreatePLangStmt *n = makeNode(CreatePLangStmt);
                  n->plname = $5;
!                 n->plhandler = $6;
!                 n->plvalidator = $7;
                  n->pltrusted = $2;
+                 /* LANCOMPILER is now ignored entirely */
                  $$ = (Node *)n;
              }
          ;
***************
*** 2319,2324 ****
--- 2320,2335 ----
              | /*EMPTY*/                                { $$ = FALSE; }
          ;

+ opt_handler:
+             HANDLER handler_name                    { $$ = $2; }
+             | /*EMPTY*/                                { $$ = NIL; }
+         ;
+
+ opt_validator:
+             VALIDATOR handler_name                    { $$ = $2; }
+             | /*EMPTY*/                                { $$ = NIL; }
+         ;
+
  /* This ought to be just func_name, but that causes reduce/reduce conflicts
   * (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
   * Work around by using simple names, instead.
***************
*** 2330,2341 ****

  opt_lancompiler:
              LANCOMPILER Sconst                        { $$ = $2; }
!             | /*EMPTY*/                                { $$ = ""; }
!         ;
!
! opt_validator:
!             VALIDATOR handler_name { $$ = $2; }
!             | /*EMPTY*/ { $$ = NULL; }
          ;

  DropPLangStmt:
--- 2341,2347 ----

  opt_lancompiler:
              LANCOMPILER Sconst                        { $$ = $2; }
!             | /*EMPTY*/                                { $$ = NULL; }
          ;

  DropPLangStmt:
*** src/bin/scripts/createlang.c.orig    Mon Aug 15 17:02:26 2005
--- src/bin/scripts/createlang.c    Fri Sep  2 20:14:32 2005
***************
*** 9,16 ****
   *
   *-------------------------------------------------------------------------
   */
-
  #include "postgres_fe.h"
  #include "common.h"
  #include "print.h"

--- 9,16 ----
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres_fe.h"
+
  #include "common.h"
  #include "print.h"

***************
*** 48,59 ****
      char       *langname = NULL;

      char       *p;
-     bool        handlerexists;
-     bool        validatorexists;
-     bool        trusted;
-     char       *handler;
-     char       *validator = NULL;
-     char       *object;

      PQExpBufferData sql;

--- 48,53 ----
***************
*** 88,94 ****
                  dbname = optarg;
                  break;
              case 'L':
!                 pglib = optarg;
                  break;
              case 'e':
                  echo = true;
--- 82,88 ----
                  dbname = optarg;
                  break;
              case 'L':
!                 pglib = optarg;                /* obsolete, ignored */
                  break;
              case 'e':
                  echo = true;
***************
*** 165,239 ****
          exit(1);
      }

-     if (!pglib)
-         pglib = "$libdir";
-
      for (p = langname; *p; p++)
          if (*p >= 'A' && *p <= 'Z')
              *p += ('a' - 'A');

-     if (strcmp(langname, "plpgsql") == 0)
-     {
-         trusted = true;
-         handler = "plpgsql_call_handler";
-         validator = "plpgsql_validator";
-         object = "plpgsql";
-     }
-     else if (strcmp(langname, "pltcl") == 0)
-     {
-         trusted = true;
-         handler = "pltcl_call_handler";
-         object = "pltcl";
-     }
-     else if (strcmp(langname, "pltclu") == 0)
-     {
-         trusted = false;
-         handler = "pltclu_call_handler";
-         object = "pltcl";
-     }
-     else if (strcmp(langname, "plperl") == 0)
-     {
-         trusted = true;
-         handler = "plperl_call_handler";
-         validator = "plperl_validator";
-         object = "plperl";
-     }
-     else if (strcmp(langname, "plperlu") == 0)
-     {
-         trusted = false;
-         handler = "plperl_call_handler";
-         validator = "plperl_validator";
-         object = "plperl";
-     }
-     else if (strcmp(langname, "plpythonu") == 0)
-     {
-         trusted = false;
-         handler = "plpython_call_handler";
-         object = "plpython";
-     }
-     else
-     {
-         fprintf(stderr, _("%s: unsupported language \"%s\"\n"),
-                 progname, langname);
-         fprintf(stderr, _("Supported languages are plpgsql, pltcl, pltclu, "
-                           "plperl, plperlu, and plpythonu.\n"));
-         exit(1);
-     }
-
      conn = connectDatabase(dbname, host, port, username, password, progname);

      /*
-      * Force schema search path to be just pg_catalog, so that we don't
-      * have to be paranoid about search paths below.
-      */
-     executeCommand(conn, "SET search_path = pg_catalog;",
-                    progname, echo);
-
-     /*
       * Make sure the language isn't already installed
       */
      printfPQExpBuffer(&sql,
!                       "SELECT oid FROM pg_language WHERE lanname = '%s';",
                        langname);
      result = executeQuery(conn, sql.data, progname, echo);
      if (PQntuples(result) > 0)
--- 159,175 ----
          exit(1);
      }

      for (p = langname; *p; p++)
          if (*p >= 'A' && *p <= 'Z')
              *p += ('a' - 'A');

      conn = connectDatabase(dbname, host, port, username, password, progname);

      /*
       * Make sure the language isn't already installed
       */
      printfPQExpBuffer(&sql,
!                       "SELECT oid FROM pg_catalog.pg_language WHERE lanname = '%s';",
                        langname);
      result = executeQuery(conn, sql.data, progname, echo);
      if (PQntuples(result) > 0)
***************
*** 247,307 ****
      }
      PQclear(result);

!     /*
!      * Check whether the call handler exists
!      */
!     printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' "
!                       "AND pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') "
!                       "AND prorettype = 'language_handler'::regtype "
!                       "AND pronargs = 0;", handler);
!     result = executeQuery(conn, sql.data, progname, echo);
!     handlerexists = (PQntuples(result) > 0);
!     PQclear(result);
!
!     /*
!      * Check whether the validator exists
!      */
!     if (validator)
!     {
!         printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' "
!                           "AND pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') "
!                           "AND proargtypes[0] = 'oid'::regtype "
!                           "AND pronargs = 1;", validator);
!         result = executeQuery(conn, sql.data, progname, echo);
!         validatorexists = (PQntuples(result) > 0);
!         PQclear(result);
!     }
!     else
!         validatorexists = true; /* don't try to create it */
!
!     /*
!      * Create the function(s) and the language
!      *
!      * NOTE: the functions will be created in pg_catalog because
!      * of our previous "SET search_path".
!      */
!     resetPQExpBuffer(&sql);
!
!     if (!handlerexists)
!         appendPQExpBuffer(&sql,
!                           "CREATE FUNCTION \"%s\" () RETURNS language_handler "
!                           "AS '%s/%s' LANGUAGE C;\n",
!                           handler, pglib, object);
!
!     if (!validatorexists)
!         appendPQExpBuffer(&sql,
!                           "CREATE FUNCTION \"%s\" (oid) RETURNS void "
!                           "AS '%s/%s' LANGUAGE C;\n",
!                           validator, pglib, object);
!
!     appendPQExpBuffer(&sql,
!                       "CREATE %sLANGUAGE \"%s\" HANDLER \"%s\"",
!                       (trusted ? "TRUSTED " : ""), langname, handler);
!
!     if (validator)
!         appendPQExpBuffer(&sql, " VALIDATOR \"%s\"", validator);
!
!     appendPQExpBuffer(&sql, ";\n");

      if (echo)
          printf("%s", sql.data);
--- 183,189 ----
      }
      PQclear(result);

!     printfPQExpBuffer(&sql, "CREATE LANGUAGE \"%s\";\n", langname);

      if (echo)
          printf("%s", sql.data);
***************
*** 330,336 ****
      printf(_("  -d, --dbname=DBNAME       database to install language in\n"));
      printf(_("  -e, --echo                show the commands being sent to the server\n"));
      printf(_("  -l, --list                show a list of currently installed languages\n"));
-     printf(_("  -L, --pglib=DIRECTORY     find language interpreter file in DIRECTORY\n"));
      printf(_("  -h, --host=HOSTNAME       database server host or socket directory\n"));
      printf(_("  -p, --port=PORT           database server port\n"));
      printf(_("  -U, --username=USERNAME   user name to connect as\n"));
--- 212,217 ----

pgsql-patches by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: Updated french po files
Next
From: Andrew Dunstan
Date:
Subject: Re: Note that spaces between QUOTE and DELIMITER are included