From 11b58470581f1c8cfad670c57ac3ffe77d1f0cdd Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 23 Feb 2010 22:51:43 +0000 Subject: [PATCH] Add an OR REPLACE option to CREATE LANGUAGE. This operates in the same way as other CREATE OR REPLACE commands, ie, it replaces everything but the ownership and ACL lists of an existing entry, and requires the caller to have owner privileges for that entry. While modifying an existing language has some use in development scenarios, in typical usage all the "replaced" values come from pg_pltemplate so there will be no actual change in the language definition. The reason for adding this is mainly to allow programs to ensure that a language exists without triggering an error if it already does exist. This commit just adds and documents the new option. A followon patch will use it to clean up some unpleasant cases in pg_dump and pg_regress. --- doc/src/sgml/ref/create_language.sgml | 32 ++++++++--- src/backend/commands/proclang.c | 83 +++++++++++++++++++-------- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 3 +- src/backend/parser/gram.y | 24 ++++---- src/include/nodes/parsenodes.h | 3 +- 6 files changed, 102 insertions(+), 46 deletions(-) diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgml index 457af69183..3f52578634 100644 --- a/doc/src/sgml/ref/create_language.sgml +++ b/doc/src/sgml/ref/create_language.sgml @@ -1,5 +1,5 @@ @@ -21,8 +21,8 @@ PostgreSQL documentation -CREATE [ PROCEDURAL ] LANGUAGE name -CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name +CREATE [ OR REPLACE ] [ PROCEDURAL ] LANGUAGE name +CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name HANDLER call_handler [ INLINE inline_handler ] [ VALIDATOR valfunction ] @@ -31,8 +31,7 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE nameDescription - Using CREATE LANGUAGE, a - PostgreSQL user can register a new + CREATE LANGUAGE registers a new procedural language with a PostgreSQL database. Subsequently, functions and trigger procedures can be defined in this new language. @@ -40,9 +39,9 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name CREATE LANGUAGE effectively associates the - language name with a call handler that is responsible for executing + language name with handler function(s) that are responsible for executing functions written in the language. Refer to - for more information about language call handlers. + for more information about language handlers. @@ -77,6 +76,23 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name + + + CREATE OR REPLACE LANGUAGE will either create a + new language, or replace an existing definition. If the language + already exists, its parameters are updated according to the values + specified or taken from pg_pltemplate, + but the language's ownership and permissions settings do not change, + and any existing functions written in the language are assumed to still + be valid. In addition to the normal privilege requirements for creating + a language, the user must be superuser or owner of the existing language. + The REPLACE case is mainly meant to be used to + ensure that the language exists. If the language has a + pg_pltemplate entry then REPLACE + will not actually change anything about an existing definition, except in + the unusual case where the pg_pltemplate entry + has been modified since the language was created. + @@ -88,7 +104,7 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE name - TRUSTED specifies that the call handler for + TRUSTED specifies that the language is safe, that is, it does not offer an unprivileged user any functionality to bypass access restrictions. If this key word is omitted when registering the diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index e51675ee11..34f33670c3 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.89 2010/02/14 18:42:14 rhaas Exp $ + * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.90 2010/02/23 22:51:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" -#include "catalog/pg_authid.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_pltemplate.h" @@ -49,7 +48,7 @@ typedef struct char *tmpllibrary; /* path of shared library */ } PLTemplate; -static void create_proc_lang(const char *languageName, +static void create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted); static PLTemplate *find_language_template(const char *languageName); @@ -73,16 +72,10 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) Oid funcargtypes[1]; /* - * Translate the language name and check that this language doesn't - * already exist + * Translate the language name to lower case */ languageName = case_translate_language_name(stmt->plname); - if (SearchSysCacheExists1(LANGNAME, PointerGetDatum(languageName))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("language \"%s\" already exists", languageName))); - /* * If we have template information for the language, ignore the supplied * parameters (if any) and use the template information. @@ -232,7 +225,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) valOid = InvalidOid; /* ok, create it */ - create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid, + create_proc_lang(languageName, stmt->replace, GetUserId(), + handlerOid, inlineOid, valOid, pltemplate->tmpltrusted); } else @@ -306,7 +300,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) valOid = InvalidOid; /* ok, create it */ - create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid, + create_proc_lang(languageName, stmt->replace, GetUserId(), + handlerOid, inlineOid, valOid, stmt->pltrusted); } } @@ -315,7 +310,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) * Guts of language creation. */ static void -create_proc_lang(const char *languageName, +create_proc_lang(const char *languageName, bool replace, Oid languageOwner, Oid handlerOid, Oid inlineOid, Oid valOid, bool trusted) { @@ -323,19 +318,21 @@ create_proc_lang(const char *languageName, TupleDesc tupDesc; Datum values[Natts_pg_language]; bool nulls[Natts_pg_language]; + bool replaces[Natts_pg_language]; NameData langname; + HeapTuple oldtup; HeapTuple tup; + bool is_update; ObjectAddress myself, referenced; - /* - * Insert the new language into pg_language - */ rel = heap_open(LanguageRelationId, RowExclusiveLock); - tupDesc = rel->rd_att; + tupDesc = RelationGetDescr(rel); + /* Prepare data to be inserted */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); + memset(replaces, true, sizeof(replaces)); namestrcpy(&langname, languageName); values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname); @@ -347,24 +344,62 @@ create_proc_lang(const char *languageName, values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid); nulls[Anum_pg_language_lanacl - 1] = true; - tup = heap_form_tuple(tupDesc, values, nulls); + /* Check for pre-existing definition */ + oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName)); - simple_heap_insert(rel, tup); + if (HeapTupleIsValid(oldtup)) + { + /* There is one; okay to replace it? */ + if (!replace) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("language \"%s\" already exists", languageName))); + if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, + languageName); + /* + * Do not change existing ownership or permissions. Note + * dependency-update code below has to agree with this decision. + */ + replaces[Anum_pg_language_lanowner - 1] = false; + replaces[Anum_pg_language_lanacl - 1] = false; + + /* Okay, do it... */ + tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(oldtup); + is_update = true; + } + else + { + /* Creating a new language */ + tup = heap_form_tuple(tupDesc, values, nulls); + simple_heap_insert(rel, tup); + is_update = false; + } + + /* Need to update indexes for either the insert or update case */ CatalogUpdateIndexes(rel, tup); /* - * Create dependencies for language + * Create dependencies for the new language. If we are updating an + * existing language, first delete any existing pg_depend entries. + * (However, since we are not changing ownership or permissions, the + * shared dependencies do *not* need to change, and we leave them alone.) */ myself.classId = LanguageRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; + if (is_update) + deleteDependencyRecordsFor(myself.classId, myself.objectId); + /* dependency on owner of language */ - referenced.classId = AuthIdRelationId; - referenced.objectId = languageOwner; - referenced.objectSubId = 0; - recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER); + if (!is_update) + recordDependencyOnOwner(myself.classId, myself.objectId, + languageOwner); /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 371d1f245e..22d24ef21a 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.462 2010/02/16 22:34:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.463 2010/02/23 22:51:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3237,6 +3237,7 @@ _copyCreatePLangStmt(CreatePLangStmt *from) { CreatePLangStmt *newnode = makeNode(CreatePLangStmt); + COPY_SCALAR_FIELD(replace); COPY_STRING_FIELD(plname); COPY_NODE_FIELD(plhandler); COPY_NODE_FIELD(plinline); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 7dfc1969f5..9a05bcc1c0 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.383 2010/02/16 22:34:43 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.384 2010/02/23 22:51:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1710,6 +1710,7 @@ _equalDropPropertyStmt(DropPropertyStmt *a, DropPropertyStmt *b) static bool _equalCreatePLangStmt(CreatePLangStmt *a, CreatePLangStmt *b) { + COMPARE_SCALAR_FIELD(replace); COMPARE_STRING_FIELD(plname); COMPARE_NODE_FIELD(plhandler); COMPARE_NODE_FIELD(plinline); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 94b28bf3d8..368586a95e 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.710 2010/02/17 04:19:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.711 2010/02/23 22:51:42 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2882,16 +2882,17 @@ NumericOnly: /***************************************************************************** * * QUERIES : - * CREATE PROCEDURAL LANGUAGE ... - * DROP PROCEDURAL LANGUAGE ... + * CREATE [OR REPLACE] [TRUSTED] [PROCEDURAL] LANGUAGE ... + * DROP [PROCEDURAL] LANGUAGE ... * *****************************************************************************/ CreatePLangStmt: - CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst + CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE ColId_or_Sconst { CreatePLangStmt *n = makeNode(CreatePLangStmt); - n->plname = $5; + n->replace = $2; + n->plname = $6; /* parameters are all to be supplied by system */ n->plhandler = NIL; n->plinline = NIL; @@ -2899,15 +2900,16 @@ CreatePLangStmt: n->pltrusted = false; $$ = (Node *)n; } - | CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst + | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE ColId_or_Sconst HANDLER handler_name opt_inline_handler opt_validator { CreatePLangStmt *n = makeNode(CreatePLangStmt); - n->plname = $5; - n->plhandler = $7; - n->plinline = $8; - n->plvalidator = $9; - n->pltrusted = $2; + n->replace = $2; + n->plname = $6; + n->plhandler = $8; + n->plinline = $9; + n->plvalidator = $10; + n->pltrusted = $3; $$ = (Node *)n; } ; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ca229c8e23..5a5c040c25 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.430 2010/02/16 22:34:57 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.431 2010/02/23 22:51:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1623,6 +1623,7 @@ typedef struct CreateTrigStmt typedef struct CreatePLangStmt { NodeTag type; + bool replace; /* T => replace if already exists */ char *plname; /* PL name */ List *plhandler; /* PL call handler function (qual. name) */ List *plinline; /* optional inline function (qual. name) */ -- 2.40.0