/* * Change language owner */ void AlterLanguageOwner(const char *name, Oid newOwnerId) { HeapTuple tup; Relation rel; /* Translate name for consistency with CREATE */ name = case_translate_language_name(name); rel = heap_open(LanguageRelationId, RowExclusiveLock); tup = SearchSysCache(LANGNAME, CStringGetDatum(name), 0, 0, 0); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", name))); AlterLanguageOwner_internal(tup, rel, newOwnerId); ReleaseSysCache(tup); heap_close(rel, RowExclusiveLock); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; HeapTuple langTup; ObjectAddress object; /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); langTup = SearchSysCache(LANGNAME, CStringGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { if (!stmt->missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName))); else ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } /* * Check permission */ if (!pg_language_ownercheck(HeapTupleGetOid(langTup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, languageName); object.classId = LanguageRelationId; object.objectId = HeapTupleGetOid(langTup); object.objectSubId = 0; ReleaseSysCache(langTup); /* * Do the deletion */ performDeletion(&object, stmt->behavior); if (Gp_role == GP_ROLE_DISPATCH) { CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NIL, NULL); } }
/* * Rename language */ void 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, CStringGetDatum(oldname), 0, 0, 0); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ if (SearchSysCacheExists(LANGNAME, CStringGetDatum(newname), 0, 0, 0)) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); } /* must be owner of PL */ if (!pg_language_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, oldname); /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
/* * Rename language */ void 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, CStringGetDatum(oldname), 0, 0, 0); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ if (SearchSysCacheExists(LANGNAME, CStringGetDatum(newname), 0, 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); /* must be superuser, since we do not have owners for PLs */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to rename procedural language"))); /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; HeapTuple langTup; ObjectAddress object; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop procedural language"))); /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); langTup = SearchSysCache(LANGNAME, CStringGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { if (!stmt->missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName))); else ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } object.classId = LanguageRelationId; object.objectId = HeapTupleGetOid(langTup); object.objectSubId = 0; ReleaseSysCache(langTup); /* * Do the deletion */ performDeletion(&object, stmt->behavior); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; Oid oid; ObjectAddress object; /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); oid = get_language_oid(languageName, stmt->missing_ok); if (!OidIsValid(oid)) { ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } /* * Check permission */ if (!pg_language_ownercheck(oid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, languageName); object.classId = LanguageRelationId; object.objectId = oid; object.objectSubId = 0; /* * Do the deletion */ performDeletion(&object, stmt->behavior); }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; PLTemplate *pltemplate; Oid handlerOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language"))); /* * Translate the language name and check that this language doesn't * already exist */ languageName = case_translate_language_name(stmt->plname); if (SearchSysCacheExists(LANGNAME, PointerGetDatum(languageName), 0, 0, 0)) 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. */ if ((pltemplate = find_language_template(languageName)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters"))); /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(funcname)))); } else { handlerOid = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL)); } /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { valOid = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, pltemplate->tmplvalidator, pltemplate->tmpllibrary, 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->tmpltrusted); } else { /* * 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. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", languageName), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * 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); } }
/* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; PLTemplate *pltemplate; Oid handlerOid, inlineOid, valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Translate the language name and check that this language doesn't * already exist */ languageName = case_translate_language_name(stmt->plname); if (SearchSysCacheExists(LANGNAME, PointerGetDatum(languageName), 0, 0, 0)) { /* * MPP-7563: special case plpgsql to omit a notice if it already exists * rather than an error. This allows us to install plpgsql by default * while allowing it to be dropped and not create issues for * dump/restore. This should be phased out in a later releases if/when * plpgsql becomes a true internal language that can not be dropped. * * Note: hardcoding this on the name is semi-safe since we would ignore * any handler functions anyways since plpgsql exists in pg_pltemplate. * Alternatively this logic could be extended to apply to all languages * in pg_pltemplate. */ if (strcmp(languageName, "plpgsql") == 0) { ereport(NOTICE, (errmsg("language \"plpgsql\" already exists, skipping"))); return; } else { 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. */ if ((pltemplate = find_language_template(languageName)) != NULL) { List *funcname; /* * Give a notice if we are ignoring supplied parameters. */ if (stmt->plhandler) if (Gp_role != GP_ROLE_EXECUTE) ereport(NOTICE, (errmsg("using pg_pltemplate information instead of " "CREATE LANGUAGE parameters"))); /* * Check permission */ if (!superuser()) { if (!pltemplate->tmpldbacreate) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create procedural language \"%s\"", languageName))); if (!pg_database_ownercheck(MyDatabaseId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); } /* * Find or create the handler function, which we force to be in the * pg_catalog schema. If already present, it must have the correct * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s must return type \"language_handler\"", NameListToString(funcname)))); } else { handlerOid = ProcedureCreate(pltemplate->tmplhandler, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ LANGUAGE_HANDLEROID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplhandler, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ false, /* isStrict */ PROVOLATILE_VOLATILE, buildoidvector(funcargtypes, 0), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plhandlerOid); } /* * Likewise for the anonymous block handler, if required; but we don't * care about its return type. */ if (pltemplate->tmplinline) { funcname = SystemFuncName(pltemplate->tmplinline); funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(inlineOid)) { inlineOid = ProcedureCreate(pltemplate->tmplinline, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplinline, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ true, /* isStrict */ PROVOLATILE_IMMUTABLE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plinlineOid); } } else inlineOid = InvalidOid; /* * Likewise for the validator, if required; but we don't care about * its return type. */ if (pltemplate->tmplvalidator) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; valOid = LookupFuncName(funcname, 1, funcargtypes, true); if (!OidIsValid(valOid)) { valOid = ProcedureCreate(pltemplate->tmplvalidator, PG_CATALOG_NAMESPACE, false, /* replace */ false, /* returnsSet */ VOIDOID, ClanguageId, F_FMGR_C_VALIDATOR, InvalidOid, /* describeFuncOid */ pltemplate->tmplvalidator, pltemplate->tmpllibrary, false, /* isAgg */ false, /* isWin */ false, /* security_definer */ true, /* isStrict */ PROVOLATILE_IMMUTABLE, buildoidvector(funcargtypes, 1), PointerGetDatum(NULL), PointerGetDatum(NULL), PointerGetDatum(NULL), NIL, PointerGetDatum(NULL), 1, 0, PRODATAACCESS_NONE, stmt->plvalidatorOid); } } else valOid = InvalidOid; /* ok, create it */ create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid, valOid, pltemplate->tmpltrusted, &(stmt->plangOid)); } else { /* * 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. */ if (!stmt->plhandler) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unsupported language \"%s\"", languageName), errhint("The supported languages are listed in the pg_pltemplate system catalog."))); /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create custom procedural language"))); /* * Lookup the PL handler function and check that it is of the expected * return type */ handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { /* * We allow OPAQUE just so we can load old dump files. When we * see a handler function declared OPAQUE, change it to * LANGUAGE_HANDLER. (This is probably obsolete and removable?) */ if (funcrettype == OPAQUEOID) { if (Gp_role != GP_ROLE_EXECUTE) 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 inline function */ if (stmt->plinline) { funcargtypes[0] = INTERNALOID; inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false); /* return value is ignored, so we don't check the type */ } else inlineOid = InvalidOid; /* 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, GetUserId(), handlerOid, inlineOid, valOid, stmt->pltrusted, &(stmt->plangOid)); } if (Gp_role == GP_ROLE_DISPATCH) { stmt->plhandlerOid = handlerOid; stmt->plinlineOid = inlineOid; stmt->plvalidatorOid = valOid; CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NULL); } }