diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgml index 9e895f2817b8479c54973a5e1b104ef395edc33c..29bcade195dca2c414f3e28c72a098059a2d03ad 100644 --- a/doc/src/sgml/ref/create_language.sgml +++ b/doc/src/sgml/ref/create_language.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.39 2005/01/04 00:39:53 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.40 2005/09/05 23:50:48 tgl Exp $ PostgreSQL documentation --> @@ -20,6 +20,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> +CREATE [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable> CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable> HANDLER <replaceable class="parameter">call_handler</replaceable> [ VALIDATOR <replaceable>valfunction</replaceable> ] </synopsis> @@ -46,9 +47,25 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</ </para> <para> - 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 <literal>template1</literal> database. + There are two forms of the <command>CREATE LANGUAGE</command> command. + In the first form, the user merely supplies the name of the desired + language, and the <productname>PostgreSQL</productname> 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 + <productname>PostgreSQL</productname> will replace the internal table + with a system catalog that can be extended to support additional + languages.) + </para> + + <para> + 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. </para> </refsect1> @@ -145,52 +162,68 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</ </listitem> </varlistentry> </variablelist> + + <para> + The <literal>TRUSTED</> option and the support function name(s) are + ignored if the server has information about the specified language + name in its internal table. + </para> </refsect1> <refsect1 id="sql-createlanguage-notes"> <title>Notes</title> <para> - This command normally should not be executed directly by users. - For the procedural languages supplied in the - <productname>PostgreSQL</productname> distribution, the <xref - linkend="app-createlang"> program should be used, which will also - install the correct call handler. (<command>createlang</command> - will call <command>CREATE LANGUAGE</command> internally.) + The <xref linkend="app-createlang"> program is a simple wrapper around + the <command>CREATE LANGUAGE</> command. It eases + installation of procedural languages from the shell command line. </para> <para> - In <productname>PostgreSQL</productname> versions before 7.3, it was - necessary to declare handler functions as returning the placeholder - type <type>opaque</>, rather than <type>language_handler</>. - To support loading - of old dump files, <command>CREATE LANGUAGE</> will accept a function - declared as returning <type>opaque</>, but it will issue a notice and - change the function's declared return type to <type>language_handler</>. + Use <xref linkend="sql-droplanguage" endterm="sql-droplanguage-title">, or better yet the <xref + linkend="app-droplang"> program, to drop procedural languages. </para> <para> - Use the <xref linkend="sql-createfunction" endterm="sql-createfunction-title"> command to create a new - function. + The system catalog <classname>pg_language</classname> (see <xref + linkend="catalog-pg-language">) records information about the + currently installed languages. Also, <command>createlang</command> + has an option to list the installed languages. </para> <para> - Use <xref linkend="sql-droplanguage" endterm="sql-droplanguage-title">, or better yet the <xref - linkend="app-droplang"> program, to drop procedural languages. + To create functions in a procedural language, a user must have the + <literal>USAGE</literal> privilege for the language. By default, + <literal>USAGE</> is granted to <literal>PUBLIC</> (i.e., everyone) + for trusted languages. This may be revoked if desired. </para> <para> - The system catalog <classname>pg_language</classname> (see <xref - linkend="catalog-pg-language">) records information about the - currently installed languages. Also <command>createlang</command> - has an option to list the installed languages. + Procedural languages are local to individual databases. + However, a language can be installed into the <literal>template1</literal> + database, which will cause it to be available automatically in + all subsequently-created databases. </para> <para> - To be able to use a procedural language, a user must be granted the - <literal>USAGE</literal> privilege. The - <command>createlang</command> 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 <command>CREATE LANGUAGE</> failing, if the + shared library that implements the language is not available in + the installation.) + </para> + + <para> + In <productname>PostgreSQL</productname> versions before 7.3, it was + necessary to declare handler functions as returning the placeholder + type <type>opaque</>, rather than <type>language_handler</>. + To support loading + of old dump files, <command>CREATE LANGUAGE</> will accept a function + declared as returning <type>opaque</>, but it will issue a notice and + change the function's declared return type to <type>language_handler</>. </para> </refsect1> @@ -198,8 +231,16 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</ <title>Examples</title> <para> - 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: +<programlisting> +CREATE LANGUAGE plpgsql; +</programlisting> + </para> + + <para> + For a language not known in the server's internal table, a sequence + such as this is needed: <programlisting> CREATE FUNCTION plsample_call_handler() RETURNS language_handler AS '$libdir/plsample' diff --git a/doc/src/sgml/ref/createlang.sgml b/doc/src/sgml/ref/createlang.sgml index 9829d845b0eca3ba50caabdce6bb397d00ff3535..f9f8dc1ea78fb5ec2d61762f50aa8d22477e6870 100644 --- a/doc/src/sgml/ref/createlang.sgml +++ b/doc/src/sgml/ref/createlang.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/createlang.sgml,v 1.35 2005/06/21 04:02:31 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/createlang.sgml,v 1.36 2005/09/05 23:50:48 tgl Exp $ PostgreSQL documentation --> @@ -42,13 +42,7 @@ PostgreSQL documentation programming language to a <productname>PostgreSQL</productname> database. <application>createlang</application> can handle all the languages supplied in the default <productname>PostgreSQL</> distribution, but - not languages provided by other parties. - </para> - <para> - Although backend programming languages can be added directly using - several <acronym>SQL</acronym> commands, it is recommended to use - <application>createlang</application> because it performs a number - of checks and is much easier to use. See + not languages provided by other parties. See <xref linkend="sql-createlanguage" endterm="sql-createlanguage-title"> for additional information. </para> @@ -66,8 +60,8 @@ PostgreSQL documentation <term><replaceable class="parameter">langname</replaceable></term> <listitem> <para> - Specifies the name of the procedural programming language to be - defined. + Specifies the name of the procedural programming language to be + defined. </para> </listitem> </varlistentry> @@ -77,7 +71,7 @@ PostgreSQL documentation <term><option><optional>--dbname</> <replaceable class="parameter">dbname</replaceable></></term> <listitem> <para> - 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. </para> @@ -104,17 +98,6 @@ PostgreSQL documentation </listitem> </varlistentry> - <varlistentry> - <term><option>-L <replaceable class="parameter">directory</replaceable></></term> - <listitem> - <para> - 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. - </para> - </listitem> - </varlistentry> - </variablelist> </para> @@ -128,10 +111,10 @@ PostgreSQL documentation <term><option>--host <replaceable class="parameter">host</replaceable></></term> <listitem> <para> - 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. </para> </listitem> </varlistentry> @@ -141,9 +124,9 @@ PostgreSQL documentation <term><option>--port <replaceable class="parameter">port</replaceable></></term> <listitem> <para> - 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. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/xplang.sgml b/doc/src/sgml/xplang.sgml index 76842017a6a1e515aaae3387ba54dde69e5852b0..7943138e62344e678b45442e8170b639f09403e5 100644 --- a/doc/src/sgml/xplang.sgml +++ b/doc/src/sgml/xplang.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.28 2004/12/30 21:45:37 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.29 2005/09/05 23:50:48 tgl Exp $ --> <chapter id="xplang"> @@ -59,17 +59,19 @@ $PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.28 2004/12/30 21:45:37 tgl Exp $ </para> <para> - For the languages supplied with the standard distribution, the - program <xref linkend="app-createlang"> 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 <command>CREATE LANGUAGE</> + <replaceable>language_name</> to install the language into the + current database. Alternatively, the program <xref + linkend="app-createlang"> may be used to do this from the shell + command line. For example, to install the language <application>PL/pgSQL</application> into the database <literal>template1</>, use <programlisting> createlang plpgsql template1 </programlisting> The manual procedure described below is only recommended for - installing custom languages that <command>createlang</command> + installing custom languages that <command>CREATE LANGUAGE</command> does not know about. </para> @@ -80,9 +82,10 @@ createlang plpgsql template1 <para> A procedural language is installed in a database in four steps, - which must be carried out by a database superuser. The - <command>createlang</command> program automates all but <xref - linkend="xplang-install-cr1">. + which must be carried out by a database superuser. (For languages + known to <command>CREATE LANGUAGE</>, the second and third steps + can be omitted, because they will be carried out automatically + if needed.) </para> <step performance="required" id="xplang-install-cr1"> @@ -202,10 +205,11 @@ CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql In a default <productname>PostgreSQL</productname> installation, the handler for the <application>PL/pgSQL</application> language is built and installed into the <quote>library</quote> - directory. If <application>Tcl</> support is configured in, the handlers for - <application>PL/Tcl</> and <application>PL/TclU</> are also built and installed in the same - location. Likewise, the <application>PL/Perl</> and <application>PL/PerlU</> handlers are built - and installed if Perl support is configured, and <application>PL/PythonU</> is + directory. If <application>Tcl</> support is configured in, the handlers + for <application>PL/Tcl</> and <application>PL/TclU</> are also built and + installed in the same location. Likewise, the <application>PL/Perl</> and + <application>PL/PerlU</> handlers are built and installed if Perl support + is configured, and the <application>PL/PythonU</> handler is installed if Python support is configured. </para> diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 3b90b4158be34cdf048f8fedb43afc6fe2dbbd3e..499523ae00758161131969a69067bc28a1ff7de2 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -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 <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_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), diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dda7723af72c7688ca671800b0a108bf950862fa..c2cdf58c2e90b090acc5ad8022b272789c023489 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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: diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 59aa3cd905833b775f367870d82e1bc5de11c75e..cc4a3eef4ba6bede8b8df0988959d8e916a502bb 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -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(); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 9d17a93fc552e49fe7fa7d37b50713c4ec31e7bc..1537fc3a396797932c9002d0d8e3d339064c3b54 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -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 */ diff --git a/src/bin/scripts/createlang.c b/src/bin/scripts/createlang.c index 082f348f9688f19bd4c6f7948012657b830d86e3..58e8a3703a647a96c82c1a809f362882061a6829 100644 --- a/src/bin/scripts/createlang.c +++ b/src/bin/scripts/createlang.c @@ -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")); diff --git a/src/test/regress/pg_regress.sh b/src/test/regress/pg_regress.sh index b68077fe9c60fe3f8d85d51298ea307e973d8da6..fd7fed095e69e5d8943ce19821b944d551cc7a15 100644 --- a/src/test/regress/pg_regress.sh +++ b/src/test/regress/pg_regress.sh @@ -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