diff options
author | Tom Lane | 2010-02-23 22:51:43 +0000 |
---|---|---|
committer | Tom Lane | 2010-02-23 22:51:43 +0000 |
commit | 11b58470581f1c8cfad670c57ac3ffe77d1f0cdd (patch) | |
tree | 8fc7b3c2995b08720d6cd6ff48aa11dfec6187b2 /src/backend/commands/proclang.c | |
parent | c4fc082a5ac11c1206ed6707e3644914db36c6c3 (diff) |
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.
Diffstat (limited to 'src/backend/commands/proclang.c')
-rw-r--r-- | src/backend/commands/proclang.c | 83 |
1 files changed, 59 insertions, 24 deletions
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index e51675ee11d..34f33670c3e 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; |