Implement a preliminary 'template' facility for procedural languages,
authorTom Lane
Mon, 5 Sep 2005 23:50:49 +0000 (23:50 +0000)
committerTom Lane
Mon, 5 Sep 2005 23:50:49 +0000 (23:50 +0000)
as per my recent proposal.  For now the template data is hard-wired in
proclang.c --- this should be replaced later by a new shared system
catalog, but we don't want to force initdb during 8.1 beta.  This change
lets us cleanly load existing dump files even if they contain outright
wrong information about a PL's support functions, such as a wrong path
to the shared library or a missing validator function.  Also, we can
revert the recent kluges to make pg_dump dump PL support functions that
are stored in pg_catalog.
While at it, I removed the code in pg_regress that replaced $libdir
with a hardcoded path for temporary installations.  This is no longer
needed given our support for relocatable installations.

doc/src/sgml/ref/create_language.sgml
doc/src/sgml/ref/createlang.sgml
doc/src/sgml/xplang.sgml
src/backend/commands/proclang.c
src/backend/parser/gram.y
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/scripts/createlang.c
src/test/regress/pg_regress.sh

index 9e895f2817b8479c54973a5e1b104ef395edc33c..29bcade195dca2c414f3e28c72a098059a2d03ad 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -20,6 +20,7 @@ PostgreSQL documentation
 
  
 
+CREATE [ PROCEDURAL ] LANGUAGE name
 CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name
     HANDLER call_handler [ VALIDATOR valfunction ]
 
@@ -46,9 +47,25 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name
   
 
   
-   Note that procedural languages are local to individual databases.
-   To make a language available in all databases by default, it should
-   be installed into the template1 database.
+   There are two forms of the CREATE LANGUAGE command.
+   In the first form, the user merely supplies the name of the desired
+   language, and the PostgreSQL server consults
+   an internal table to determine the correct parameters.  In
+   the second form, the user supplies the language parameters along with
+   the language name.  The second form must be used to create a language
+   that is not present in the internal table, but this form is considered
+   obsolescent.  (It is expected that future releases of
+   PostgreSQL will replace the internal table
+   with a system catalog that can be extended to support additional
+   languages.)
+  
+
+  
+   When the server finds an entry in its internal table for the given
+   language name, it will use the table data even if the given command
+   includes language parameters.  This behavior simplifies loading of
+   old dump files, which are likely to contain out-of-date information
+   about language support functions.
   
  
 
@@ -145,52 +162,68 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name
      
     
    
+
+  
+   The TRUSTED option and the support function name(s) are
+   ignored if the server has information about the specified language
+   name in its internal table.
+  
  
 
  
   Notes
 
   
-   This command normally should not be executed directly by users.
-   For the procedural languages supplied in the
-   PostgreSQL distribution, the 
-   linkend="app-createlang"> program should be used, which will also
-   install the correct call handler.  (createlang
-   will call CREATE LANGUAGE internally.)
+   The  program is a simple wrapper around
+   the CREATE LANGUAGE command.  It eases
+   installation of procedural languages from the shell command line.
   
 
   
-   In PostgreSQL versions before 7.3, it was
-   necessary to declare handler functions as returning the placeholder
-   type opaque, rather than language_handler.
-   To support loading 
-   of old dump files, CREATE LANGUAGE will accept a function
-   declared as returning opaque, but it will issue a notice and
-   change the function's declared return type to language_handler.
+   Use , or better yet the 
+   linkend="app-droplang"> program, to drop procedural languages.
   
 
   
-   Use the  command to create a new
-   function.
+   The system catalog pg_language (see 
+   linkend="catalog-pg-language">) records information about the
+   currently installed languages.  Also, createlang
+   has an option to list the installed languages.
   
 
   
-   Use , or better yet the 
-   linkend="app-droplang"> program, to drop procedural languages.
+   To create functions in a procedural language, a user must have the
+   USAGE privilege for the language.  By default,
+   USAGE is granted to PUBLIC (i.e., everyone)
+   for trusted languages.  This may be revoked if desired.
   
 
   
-   The system catalog pg_language (see 
-   linkend="catalog-pg-language">) records information about the
-   currently installed languages.  Also createlang
-   has an option to list the installed languages.
+   Procedural languages are local to individual databases.
+   However, a language can be installed into the template1
+   database, which will cause it to be available automatically in
+   all subsequently-created databases.
   
 
   
-   To be able to use a procedural language, a user must be granted the
-   USAGE privilege.  The
-   createlang program automatically grants
-   permissions to everyone if the language is known to be trusted.
+   The call handler function and the validator function (if any)
+   must already exist if the server does not have information about
+   the language in its internal table.  But when there is an entry
+   in the internal table, the functions need not already exist;
+   they will be automatically defined if not present in the database.
+   (This can result in CREATE LANGUAGE failing, if the
+   shared library that implements the language is not available in
+   the installation.)
+  
+
+  
+   In PostgreSQL versions before 7.3, it was
+   necessary to declare handler functions as returning the placeholder
+   type opaque, rather than language_handler.
+   To support loading 
+   of old dump files, CREATE LANGUAGE will accept a function
+   declared as returning opaque, but it will issue a notice and
+   change the function's declared return type to language_handler.
   
  
 
@@ -198,8 +231,16 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name
   Examples
 
   
-   The following two commands executed in sequence will register a new
-   procedural language and the associated call handler.
+   The preferred way of creating any of the standard procedural languages
+   is just:
+
+CREATE LANGUAGE plpgsql;
+
+  
+
+  
+   For a language not known in the server's internal table, a sequence
+   such as this is needed:
 
 CREATE FUNCTION plsample_call_handler() RETURNS language_handler
     AS '$libdir/plsample'
index 9829d845b0eca3ba50caabdce6bb397d00ff3535..f9f8dc1ea78fb5ec2d61762f50aa8d22477e6870 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -42,13 +42,7 @@ PostgreSQL documentation
    programming language to a PostgreSQL database.
    createlang can handle all the languages
    supplied in the default PostgreSQL distribution, but
-   not languages provided by other parties.
-  
-  
-   Although backend programming languages can be added directly using
-   several SQL commands, it is recommended to use
-   createlang because it performs a number
-   of checks and is much easier to use. See
+   not languages provided by other parties.  See
    
    for additional information.
   
@@ -66,8 +60,8 @@ PostgreSQL documentation
       langname
       
        
-   Specifies the name of the procedural programming language to be
-   defined.
+        Specifies the name of the procedural programming language to be
+        defined.
        
       
      
@@ -77,7 +71,7 @@ PostgreSQL documentation
       
       
        
-   Specifies to which database the language should be added.
+        Specifies to which database the language should be added.
         The default is to use the database with the same name as the
         current system user.
        
@@ -104,17 +98,6 @@ PostgreSQL documentation
       
      
 
-     
-      
-      
-       
-   Specifies the directory in which the language interpreter is
-        to be found.  The directory is normally found automatically; this
-        option is primarily for debugging purposes.
-       
-      
-     
-
     
    
 
@@ -128,10 +111,10 @@ PostgreSQL documentation
       
       
        
-   Specifies the host name of the machine on which the 
-   server
-   is running.  If the value begins with a slash, it is used 
-   as the directory for the Unix domain socket.
+        Specifies the host name of the machine on which the 
+        server
+        is running.  If the value begins with a slash, it is used 
+        as the directory for the Unix domain socket.
        
       
      
@@ -141,9 +124,9 @@ PostgreSQL documentation
       
       
        
-   Specifies the TCP port or local Unix domain socket file 
-   extension on which the server
-   is listening for connections.
+        Specifies the TCP port or local Unix domain socket file 
+        extension on which the server
+        is listening for connections.
        
       
      
index 76842017a6a1e515aaae3387ba54dde69e5852b0..7943138e62344e678b45442e8170b639f09403e5 100644 (file)
@@ -1,5 +1,5 @@
 
 
  
@@ -59,17 +59,19 @@ $PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.28 2004/12/30 21:45:37 tgl Exp $
    
 
    
-    For the languages supplied with the standard distribution, the
-    program  may be used to install the
-    language instead of carrying out the details by hand.  For
-    example, to install the language
+    For the languages supplied with the standard distribution, it is
+    only necessary to execute CREATE LANGUAGE
+    language_name to install the language into the
+    current database.  Alternatively, the program 
+    linkend="app-createlang"> may be used to do this from the shell
+    command line.  For example, to install the language
     PL/pgSQL into the database
     template1, use
 
 createlang plpgsql template1
 
     The manual procedure described below is only recommended for
-    installing custom languages that createlang
+    installing custom languages that CREATE LANGUAGE
     does not know about.
    
 
@@ -80,9 +82,10 @@ createlang plpgsql template1
 
     
      A procedural language is installed in a database in four steps,
-     which must be carried out by a database superuser.  The
-     createlang program automates all but 
-     linkend="xplang-install-cr1">.
+     which must be carried out by a database superuser.  (For languages
+     known to CREATE LANGUAGE, the second and third steps
+     can be omitted, because they will be carried out automatically
+     if needed.)
     
 
     
@@ -202,10 +205,11 @@ CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql
     In a default PostgreSQL installation,
     the handler for the PL/pgSQL language
     is built and installed into the library
-    directory. If Tcl support is configured in, the handlers for
-    PL/Tcl and PL/TclU are also built and installed in the same
-    location.  Likewise, the PL/Perl and PL/PerlU handlers are built
-    and installed if Perl support is configured, and PL/PythonU is
+    directory. If Tcl support is configured in, the handlers
+    for PL/Tcl and PL/TclU are also built and
+    installed in the same location.  Likewise, the PL/Perl and
+    PL/PerlU handlers are built and installed if Perl support
+    is configured, and the PL/PythonU handler is
     installed if Python support is configured.
    
 
index 3b90b4158be34cdf048f8fedb43afc6fe2dbbd3e..499523ae00758161131969a69067bc28a1ff7de2 100644 (file)
@@ -7,31 +7,46 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.60 2005/04/14 20:03:24 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.61 2005/09/05 23:50:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include 
-
 #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,19 +55,11 @@ void
 CreateProceduralLanguage(CreatePLangStmt *stmt)
 {
    char       *languageName;
-   Oid         procOid,
-               valProcOid;
+   PLTemplate *pltemplate;
+   Oid         handlerOid,
+               valOid;
    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
@@ -76,64 +83,181 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                 errmsg("language \"%s\" already exists", languageName)));
 
    /*
-    * Lookup the PL handler function and check that it is of the expected
-    * return type
+    * If we have template information for the language, ignore the supplied
+    * parameters (if any) and use the template information.
     */
-   procOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
-   funcrettype = get_func_rettype(procOid);
-   if (funcrettype != LANGUAGE_HANDLEROID)
+   if ((pltemplate = find_language_template(languageName)) != NULL)
    {
+       List    *funcname;
+
        /*
-        * We allow OPAQUE just so we can load old dump files.  When we
-        * see a handler function declared OPAQUE, change it to
-        * LANGUAGE_HANDLER.
+        * 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.
         */
-       if (funcrettype == OPAQUEOID)
+       funcname = SystemFuncName(pltemplate->lanhandler);
+       handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+       if (OidIsValid(handlerOid))
        {
-           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);
+           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
-           ereport(ERROR,
-                   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-             errmsg("function %s must return type \"language_handler\"",
-                    NameListToString(stmt->plhandler))));
-   }
+       {
+           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));
+       }
 
-   /* 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 */
+       /*
+        * 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
-       valProcOid = InvalidOid;
+   {
+       /*
+        * No template, so use the provided information.  If there's
+        * no handler clause, the user is trying to rely on a template
+        * that we don't have, so complain accordingly.
+        *
+        * XXX In 8.2, replace the detail message with a hint to look in
+        * pg_pltemplate.
+        */
+       if (!stmt->plhandler)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                    errmsg("unsupported language \"%s\"",
+                           languageName),
+                    errdetail("Supported languages are plpgsql, pltcl, pltclu, "
+                              "plperl, plperlu, and plpythonu.")));
+
+       /*
+        * 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
     */
-   for (i = 0; i < Natts_pg_language; i++)
-   {
-       nulls[i] = ' ';
-       values[i] = (Datum) NULL;
-   }
+   rel = heap_open(LanguageRelationId, RowExclusiveLock);
+   tupDesc = rel->rd_att;
 
-   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 */
+   memset(values, 0, sizeof(values));
+   memset(nulls, ' ', sizeof(nulls));
 
-   rel = heap_open(LanguageRelationId, RowExclusiveLock);
+   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';
 
-   tupDesc = rel->rd_att;
    tup = heap_formtuple(tupDesc, values, nulls);
 
    simple_heap_insert(rel, tup);
@@ -149,15 +273,15 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 
    /* dependency on the PL handler function */
    referenced.classId = ProcedureRelationId;
-   referenced.objectId = procOid;
+   referenced.objectId = handlerOid;
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
    /* dependency on the validator function, if any */
-   if (OidIsValid(valProcOid))
+   if (OidIsValid(valOid))
    {
        referenced.classId = ProcedureRelationId;
-       referenced.objectId = valProcOid;
+       referenced.objectId = valOid;
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    }
@@ -165,6 +289,45 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
    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.
+ *
+ * XXX if you add languages to this list, add them also to the errdetail
+ * message above and the list in functioncmds.c.  Those hard-wired lists
+ * should go away in 8.2, also.
+ */
+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,8 +349,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
               errmsg("must be superuser to drop procedural language")));
 
    /*
-    * Translate the language name, check that this language exist and is
-    * a PL
+    * Translate the language name, check that the language exists
     */
    languageName = case_translate_language_name(stmt->plname);
 
@@ -244,6 +406,10 @@ RenameLanguage(const char *oldname, const char *newname)
    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,7 +428,7 @@ RenameLanguage(const char *oldname, const char *newname)
                (errcode(ERRCODE_DUPLICATE_OBJECT),
                 errmsg("language \"%s\" already exists", newname)));
 
-   /* must be superuser */
+   /* must be superuser, since we do not have owners for PLs */
    if (!superuser())
        ereport(ERROR,
                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
index dda7723af72c7688ca671800b0a108bf950862fa..c2cdf58c2e90b090acc5ad8022b272789c023489 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.509 2005/08/24 19:34:12 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.510 2005/09/05 23:50:48 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -2303,13 +2303,24 @@ IntegerOnly: SignedIconst                           { $$ = makeInteger($1); };
 
 CreatePLangStmt:
            CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
-           HANDLER handler_name opt_validator opt_lancompiler
+           {
+               CreatePLangStmt *n = makeNode(CreatePLangStmt);
+               n->plname = $5;
+               /* parameters are all to be supplied by system */
+               n->plhandler = NIL;
+               n->plvalidator = NIL;
+               n->pltrusted = false;
+               $$ = (Node *)n;
+           }
+           | 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;
+               /* LANCOMPILER is now ignored entirely */
                $$ = (Node *)n;
            }
        ;
@@ -2328,14 +2339,14 @@ handler_name:
            | name attrs                { $$ = lcons(makeString($1), $2); }
        ;
 
-opt_lancompiler:
-           LANCOMPILER Sconst                      { $$ = $2; }
-           | /*EMPTY*/                             { $$ = ""; }
+opt_validator:
+           VALIDATOR handler_name                  { $$ = $2; }
+           | /*EMPTY*/                             { $$ = NIL; }
        ;
 
-opt_validator:
-           VALIDATOR handler_name { $$ = $2; }
-           | /*EMPTY*/ { $$ = NULL; }
+opt_lancompiler:
+           LANCOMPILER Sconst                      { $$ = $2; }
+           | /*EMPTY*/                             { $$ = NULL; }
        ;
 
 DropPLangStmt:
index 59aa3cd905833b775f367870d82e1bc5de11c75e..cc4a3eef4ba6bede8b8df0988959d8e916a502bb 100644 (file)
@@ -12,7 +12,7 @@
  * by PostgreSQL
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.419 2005/08/23 22:40:31 tgl Exp $
+ *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.420 2005/09/05 23:50:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2295,7 +2295,6 @@ getFuncs(int *numFuncs)
    int         i_proargtypes;
    int         i_prorettype;
    int         i_proacl;
-   int         i_is_pl_handler;
 
    /* Make sure we are in proper schema */
    selectSourceSchema("pg_catalog");
@@ -2304,34 +2303,16 @@ getFuncs(int *numFuncs)
 
    if (g_fout->remoteVersion >= 70300)
    {
-       /*
-        * For 7.3 and up, we consider it's user-defined if it's not in
-        * pg_catalog.  We also collect info on functions in pg_catalog, but
-        * only if they are call handlers or validators for PL languages.
-        */
        appendPQExpBuffer(query,
                          "SELECT tableoid, oid, proname, prolang, "
                          "pronargs, proargtypes, prorettype, proacl, "
                          "pronamespace, "
-                         "(%s proowner) as rolname, "
-                         "CASE WHEN oid IN "
-                         "  (select lanplcallfoid from pg_language "
-                         "   where lanispl) THEN true "
-                         " WHEN oid IN "
-                         "  (select lanvalidator from pg_language "
-                         "   where lanispl) THEN true "
-                         " ELSE false END AS is_pl_handler "
+                         "(%s proowner) as rolname "
                          "FROM pg_proc "
                          "WHERE NOT proisagg "
-                         "AND (pronamespace != "
-                         "    (select oid from pg_namespace "
-                         "     where nspname = 'pg_catalog')"
-                         "  OR oid IN "
-                         "    (select lanplcallfoid from pg_language "
-                         "     where lanispl) "
-                         "  OR oid IN "
-                         "    (select lanvalidator from pg_language "
-                         "     where lanispl))",
+                         "AND pronamespace != "
+                         "(select oid from pg_namespace"
+                         " where nspname = 'pg_catalog')",
                          username_subquery);
    }
    else if (g_fout->remoteVersion >= 70100)
@@ -2341,8 +2322,7 @@ getFuncs(int *numFuncs)
                          "pronargs, proargtypes, prorettype, "
                          "'{=X}' as proacl, "
                          "0::oid as pronamespace, "
-                         "(%s proowner) as rolname, "
-                         "false AS is_pl_handler "
+                         "(%s proowner) as rolname "
                          "FROM pg_proc "
                          "where pg_proc.oid > '%u'::oid",
                          username_subquery,
@@ -2358,8 +2338,7 @@ getFuncs(int *numFuncs)
                          "pronargs, proargtypes, prorettype, "
                          "'{=X}' as proacl, "
                          "0::oid as pronamespace, "
-                         "(%s proowner) as rolname, "
-                         "false AS is_pl_handler "
+                         "(%s proowner) as rolname "
                          "FROM pg_proc "
                          "where pg_proc.oid > '%u'::oid",
                          username_subquery,
@@ -2385,7 +2364,6 @@ getFuncs(int *numFuncs)
    i_proargtypes = PQfnumber(res, "proargtypes");
    i_prorettype = PQfnumber(res, "prorettype");
    i_proacl = PQfnumber(res, "proacl");
-   i_is_pl_handler = PQfnumber(res,"is_pl_handler");
 
    for (i = 0; i < ntups; i++)
    {
@@ -2402,8 +2380,6 @@ getFuncs(int *numFuncs)
        finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
        finfo[i].proacl = strdup(PQgetvalue(res, i, i_proacl));
        finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
-       finfo[i].islanghandler = 
-           strcmp(PQgetvalue(res, i, i_is_pl_handler), "t") == 0;
        if (finfo[i].nargs == 0)
            finfo[i].argtypes = NULL;
        else
@@ -5131,7 +5107,9 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
 {
    PQExpBuffer defqry;
    PQExpBuffer delqry;
+   bool        useParams;
    char       *qlanname;
+   char       *lanschema;
    FuncInfo   *funcInfo;
    FuncInfo   *validatorInfo = NULL;
 
@@ -5139,31 +5117,33 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
        return;
 
    /*
-    * Find the support functions, complaining if not there.
+    * Try to find the support function(s).  It is not an error if we
+    * don't find them --- if the functions are in the pg_catalog schema,
+    * as is standard in 8.1 and up, then we won't have loaded them.
+    * (In this case we will emit a parameterless CREATE LANGUAGE command,
+    * which will require PL template knowledge in the backend to reload.)
     */
+
    funcInfo = findFuncByOid(plang->lanplcallfoid);
-   if (funcInfo == NULL)
-   {
-       write_msg(NULL, "WARNING: handler function for language \"%s\" not found\n",
-                 plang->dobj.name);
-       return;
-   }
+   if (funcInfo != NULL && !funcInfo->dobj.namespace->dump)
+       funcInfo = NULL;        /* treat not-dumped same as not-found */
 
    if (OidIsValid(plang->lanvalidator))
    {
        validatorInfo = findFuncByOid(plang->lanvalidator);
-       if (validatorInfo == NULL)
-       {
-           write_msg(NULL, "WARNING: validator function for language \"%s\" not found\n",
-                     plang->dobj.name);
-           return;
-       }
+       if (validatorInfo != NULL && !validatorInfo->dobj.namespace->dump)
+           validatorInfo = NULL;
    }
 
-   /* Dump if we should, or if both support functions are dumpable */
-   if (!shouldDumpProcLangs() &&
-       !(funcInfo->dobj.namespace->dump &&
-         (validatorInfo == NULL || validatorInfo->dobj.namespace->dump)))
+   /*
+    * If the functions are dumpable then emit a traditional CREATE LANGUAGE
+    * with parameters.  Otherwise, dump only if shouldDumpProcLangs() says
+    * to dump it.
+    */
+   useParams = (funcInfo != NULL &&
+                (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
+
+   if (!useParams && !shouldDumpProcLangs())
        return;
 
    defqry = createPQExpBuffer();
@@ -5171,34 +5151,42 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
 
    qlanname = strdup(fmtId(plang->dobj.name));
 
+   /*
+    * If dumping a HANDLER clause, treat the language as being in the
+    * handler function's schema; this avoids cluttering the HANDLER clause.
+    * Otherwise it doesn't really have a schema.
+    */
+   if (useParams)
+       lanschema = funcInfo->dobj.namespace->dobj.name;
+   else
+       lanschema = NULL;
+
    appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
                      qlanname);
 
    appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
-                     plang->lanpltrusted ? "TRUSTED " : "",
+                     (useParams && plang->lanpltrusted) ? "TRUSTED " : "",
                      qlanname);
-   appendPQExpBuffer(defqry, " HANDLER %s",
-                     fmtId(funcInfo->dobj.name));
-   if (OidIsValid(plang->lanvalidator))
+   if (useParams)
    {
-       appendPQExpBuffer(defqry, " VALIDATOR ");
-       /* Cope with possibility that validator is in different schema */
-       if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
-           appendPQExpBuffer(defqry, "%s.",
-                       fmtId(validatorInfo->dobj.namespace->dobj.name));
-       appendPQExpBuffer(defqry, "%s",
-                         fmtId(validatorInfo->dobj.name));
+       appendPQExpBuffer(defqry, " HANDLER %s",
+                         fmtId(funcInfo->dobj.name));
+       if (OidIsValid(plang->lanvalidator))
+       {
+           appendPQExpBuffer(defqry, " VALIDATOR ");
+           /* Cope with possibility that validator is in different schema */
+           if (validatorInfo->dobj.namespace != funcInfo->dobj.namespace)
+               appendPQExpBuffer(defqry, "%s.",
+                                 fmtId(validatorInfo->dobj.namespace->dobj.name));
+           appendPQExpBuffer(defqry, "%s",
+                             fmtId(validatorInfo->dobj.name));
+       }
    }
    appendPQExpBuffer(defqry, ";\n");
 
-   /*
-    * We mark the PL's archive entry as being in the call handler's
-    * namespace; this is what makes it OK to refer to the handler with
-    * an unqualified name above.
-    */
    ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
                 plang->dobj.name,
-                funcInfo->dobj.namespace->dobj.name, NULL, "",
+                lanschema, NULL, "",
                 false, "PROCEDURAL LANGUAGE",
                 defqry->data, delqry->data, NULL,
                 plang->dobj.dependencies, plang->dobj.nDeps,
@@ -5206,7 +5194,6 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
 
    /* Dump Proc Lang Comments */
    resetPQExpBuffer(defqry);
-
    appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
    dumpComment(fout, defqry->data,
                NULL, "",
@@ -5215,7 +5202,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
    if (plang->lanpltrusted)
        dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
                qlanname, plang->dobj.name,
-               funcInfo->dobj.namespace->dobj.name,
+               lanschema,
                NULL, plang->lanacl);
 
    free(qlanname);
@@ -5359,12 +5346,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
    char      **argmodes = NULL;
    char      **argnames = NULL;
 
-   if (dataOnly)
-       return;
-
-   /* Dump only funcs in dumpable namespaces, or needed language handlers */
-   if (!finfo->dobj.namespace->dump &&
-       (!finfo->islanghandler || !shouldDumpProcLangs()))
+   /* Dump only funcs in dumpable namespaces */
+   if (!finfo->dobj.namespace->dump || dataOnly)
        return;
 
    query = createPQExpBuffer();
index 9d17a93fc552e49fe7fa7d37b50713c4ec31e7bc..1537fc3a396797932c9002d0d8e3d339064c3b54 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.120 2005/08/23 22:40:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.121 2005/09/05 23:50:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -132,7 +132,6 @@ typedef struct _funcInfo
    Oid        *argtypes;
    Oid         prorettype;
    char       *proacl;
-   bool        islanghandler;
 } FuncInfo;
 
 /* AggInfo is a superset of FuncInfo */
index 082f348f9688f19bd4c6f7948012657b830d86e3..58e8a3703a647a96c82c1a809f362882061a6829 100644 (file)
@@ -5,12 +5,12 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.19 2005/08/15 21:02:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/scripts/createlang.c,v 1.20 2005/09/05 23:50:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-
 #include "postgres_fe.h"
+
 #include "common.h"
 #include "print.h"
 
@@ -28,7 +28,6 @@ main(int argc, char *argv[])
        {"username", required_argument, NULL, 'U'},
        {"password", no_argument, NULL, 'W'},
        {"dbname", required_argument, NULL, 'd'},
-       {"pglib", required_argument, NULL, 'L'},
        {"echo", no_argument, NULL, 'e'},
        {NULL, 0, NULL, 0}
    };
@@ -44,16 +43,9 @@ main(int argc, char *argv[])
    char       *username = NULL;
    bool        password = false;
    bool        echo = false;
-   char       *pglib = NULL;
    char       *langname = NULL;
 
    char       *p;
-   bool        handlerexists;
-   bool        validatorexists;
-   bool        trusted;
-   char       *handler;
-   char       *validator = NULL;
-   char       *object;
 
    PQExpBufferData sql;
 
@@ -65,7 +57,7 @@ main(int argc, char *argv[])
 
    handle_help_version_opts(argc, argv, "createlang", help);
 
-   while ((c = getopt_long(argc, argv, "lh:p:U:Wd:L:e", long_options, &optindex)) != -1)
+   while ((c = getopt_long(argc, argv, "lh:p:U:Wd:e", long_options, &optindex)) != -1)
    {
        switch (c)
        {
@@ -87,9 +79,6 @@ main(int argc, char *argv[])
            case 'd':
                dbname = optarg;
                break;
-           case 'L':
-               pglib = optarg;
-               break;
            case 'e':
                echo = true;
                break;
@@ -165,75 +154,17 @@ main(int argc, char *argv[])
        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';", 
+                     "SELECT oid FROM pg_catalog.pg_language WHERE lanname = '%s';", 
                      langname);
    result = executeQuery(conn, sql.data, progname, echo);
    if (PQntuples(result) > 0)
@@ -247,61 +178,7 @@ main(int argc, char *argv[])
    }
    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");
+   printfPQExpBuffer(&sql, "CREATE LANGUAGE \"%s\";\n", langname);
 
    if (echo)
        printf("%s", sql.data);
@@ -330,7 +207,6 @@ help(const char *progname)
    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"));
index b68077fe9c60fe3f8d85d51298ea307e973d8da6..fd7fed095e69e5d8943ce19821b944d551cc7a15 100644 (file)
@@ -1,5 +1,5 @@
 #! /bin/sh
-# $PostgreSQL: pgsql/src/test/regress/pg_regress.sh,v 1.59 2005/07/17 18:28:45 tgl Exp $
+# $PostgreSQL: pgsql/src/test/regress/pg_regress.sh,v 1.60 2005/09/05 23:50:49 tgl Exp $
 
 me=`basename $0`
 : ${TMPDIR=/tmp}
@@ -84,7 +84,6 @@ fi
 : ${outputdir=.}
 
 libdir='@libdir@'
-pkglibdir='@pkglibdir@'
 bindir='@bindir@'
 datadir='@datadir@'
 host_platform='@host_tuple@'
@@ -322,16 +321,7 @@ LOGDIR=$outputdir/log
 if [ x"$temp_install" != x"" ]
 then
     if echo x"$temp_install" | grep -v '^x/' >/dev/null 2>&1; then
-        case $host_platform in
-          *-*-mingw32*)
-                pkglibdir="`pwd -W`/$temp_install/install/$pkglibdir"
-                temp_install="`pwd`/$temp_install"
-                ;;
-          *)
-                temp_install="`pwd`/$temp_install"
-                pkglibdir=$temp_install/install/$pkglibdir
-                ;;
-        esac
+        temp_install="`pwd`/$temp_install"
     fi
 
     bindir=$temp_install/install/$bindir
@@ -412,13 +402,6 @@ then
         (exit 2); exit
     fi
 
-    # fix conversion shared objs path
-    conv=$datadir/conversion_create.sql
-    backup=$conv.bak
-    mv $conv $backup
-    sed -e "s@\$libdir@$pkglibdir@g" $backup > $conv
-    rm $backup
-
     message "initializing database system"
     [ "$debug" = yes ] && initdb_options='--debug'
     "$bindir/initdb" -D "$PGDATA" -L "$datadir" --noclean $initdb_options >"$LOGDIR/initdb.log" 2>&1
@@ -586,7 +569,7 @@ if [ "$enable_shared" = yes ]; then
     for lang in xyzzy $load_langs ; do    
         if [ "$lang" != "xyzzy" ]; then
             message "installing $lang"
-            "$bindir/createlang" -L "$pkglibdir" $psql_options $lang $dbname
+            "$bindir/createlang" $psql_options $lang $dbname
             if [ $? -ne 0 ] && [ $? -ne 2 ]; then
                 echo "$me: createlang $lang failed"
                 (exit 2); exit