📄 functioncmds.c
字号:
char *languageName; Oid languageOid; Oid languageValidator; char *funcname; Oid namespaceId; AclResult aclresult; oidvector *parameterTypes; ArrayType *allParameterTypes; ArrayType *parameterModes; ArrayType *parameterNames; Oid requiredResultType; bool isStrict, security; char volatility; HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, &funcname); /* Check we have creation rights in target namespace */ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); /* default attributes */ isStrict = false; security = false; volatility = PROVOLATILE_VOLATILE; /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &volatility, &isStrict, &security); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); /* Look up the language and validate permissions */ languageTuple = SearchSysCache(LANGNAME, PointerGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(languageTuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName), (PLTemplateExists(languageName) ? errhint("Use CREATE LANGUAGE to load the language into the database.") : 0))); languageOid = HeapTupleGetOid(languageTuple); languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); if (languageStruct->lanpltrusted) { /* if trusted language, need USAGE privilege */ AclResult aclresult; aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_LANGUAGE, NameStr(languageStruct->lanname)); } else { /* if untrusted language, must be superuser */ if (!superuser()) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE, NameStr(languageStruct->lanname)); } languageValidator = languageStruct->lanvalidator; ReleaseSysCache(languageTuple); /* * Convert remaining parameters of CREATE to form wanted by * ProcedureCreate. */ examine_parameter_list(stmt->parameters, languageOid, ¶meterTypes, &allParameterTypes, ¶meterModes, ¶meterNames, &requiredResultType); if (stmt->returnType) { /* explicit RETURNS clause */ compute_return_type(stmt->returnType, languageOid, &prorettype, &returnsSet); if (OidIsValid(requiredResultType) && prorettype != requiredResultType) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("function result type must be %s because of OUT parameters", format_type_be(requiredResultType)))); } else if (OidIsValid(requiredResultType)) { /* default RETURNS clause from OUT parameters */ prorettype = requiredResultType; returnsSet = false; } else { ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("function result type must be specified"))); /* Alternative possibility: default to RETURNS VOID */ prorettype = VOIDOID; returnsSet = false; } compute_attributes_with_style(stmt->withClause, &isStrict, &volatility); interpret_AS_clause(languageOid, languageName, as_clause, &prosrc_str, &probin_str); if (languageOid == INTERNALlanguageId) { /* * In PostgreSQL versions before 6.5, the SQL name of the created * function could not be different from the internal name, and * "prosrc" wasn't used. So there is code out there that does CREATE * FUNCTION xyz AS '' LANGUAGE 'internal'. To preserve some modicum of * backwards compatibility, accept an empty "prosrc" value as meaning * the supplied SQL function name. */ if (strlen(prosrc_str) == 0) prosrc_str = funcname; } if (languageOid == ClanguageId) { /* If link symbol is specified as "-", substitute procedure name */ if (strcmp(prosrc_str, "-") == 0) prosrc_str = funcname; } /* * And now that we have all the parameters, and know we're permitted to do * so, go ahead and create the function. */ ProcedureCreate(funcname, namespaceId, stmt->replace, returnsSet, prorettype, languageOid, languageValidator, prosrc_str, /* converted to text later */ probin_str, /* converted to text later */ false, /* not an aggregate */ security, isStrict, volatility, parameterTypes, PointerGetDatum(allParameterTypes), PointerGetDatum(parameterModes), PointerGetDatum(parameterNames));}/* * RemoveFunction * Deletes a function. */voidRemoveFunction(RemoveFuncStmt *stmt){ List *functionName = stmt->funcname; List *argTypes = stmt->args; /* list of TypeName nodes */ Oid funcOid; HeapTuple tup; ObjectAddress object; /* * Find the function, do permissions and validity checks */ funcOid = LookupFuncNameTypeNames(functionName, argTypes, false); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", funcOid); /* Permission check: must own func or its namespace */ if (!pg_proc_ownercheck(funcOid, GetUserId()) && !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(functionName)); if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", NameListToString(functionName)), errhint("Use DROP AGGREGATE to drop aggregate functions."))); if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId) { /* "Helpful" NOTICE when removing a builtin function ... */ ereport(NOTICE, (errcode(ERRCODE_WARNING), errmsg("removing built-in function \"%s\"", NameListToString(functionName)))); } ReleaseSysCache(tup); /* * Do the deletion */ object.classId = ProcedureRelationId; object.objectId = funcOid; object.objectSubId = 0; performDeletion(&object, stmt->behavior);}/* * Guts of function deletion. * * Note: this is also used for aggregate deletion, since the OIDs of * both functions and aggregates point to pg_proc. */voidRemoveFunctionById(Oid funcOid){ Relation relation; HeapTuple tup; bool isagg; /* * Delete the pg_proc tuple. */ relation = heap_open(ProcedureRelationId, RowExclusiveLock); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", funcOid); isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg; simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); /* * If there's a pg_aggregate tuple, delete that too. */ if (isagg) { relation = heap_open(AggregateRelationId, RowExclusiveLock); tup = SearchSysCache(AGGFNOID, ObjectIdGetDatum(funcOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid); simple_heap_delete(relation, &tup->t_self); ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); }}/* * Rename function */voidRenameFunction(List *name, List *argtypes, const char *newname){ Oid procOid; Oid namespaceOid; HeapTuple tup; Form_pg_proc procForm; Relation rel; AclResult aclresult; rel = heap_open(ProcedureRelationId, RowExclusiveLock); procOid = LookupFuncNameTypeNames(name, argtypes, false); tup = SearchSysCacheCopy(PROCOID, ObjectIdGetDatum(procOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); procForm = (Form_pg_proc) GETSTRUCT(tup); if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", NameListToString(name)), errhint("Use ALTER AGGREGATE to rename aggregate functions."))); namespaceOid = procForm->pronamespace; /* make sure the new name doesn't exist */ if (SearchSysCacheExists(PROCNAMEARGSNSP, CStringGetDatum(newname), PointerGetDatum(&procForm->proargtypes), ObjectIdGetDatum(namespaceOid), 0)) { ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function %s already exists in schema \"%s\"", funcname_signature_string(newname, procForm->pronargs, procForm->proargtypes.values), get_namespace_name(namespaceOid)))); } /* must be owner */ if (!pg_proc_ownercheck(procOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name)); /* must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ namestrcpy(&(procForm->proname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup);}/* * Change function owner */voidAlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId){ Oid procOid; HeapTuple tup; Form_pg_proc procForm; Relation rel; AclResult aclresult; rel = heap_open(ProcedureRelationId, RowExclusiveLock); procOid = LookupFuncNameTypeNames(name, argtypes, false); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(procOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", procOid); procForm = (Form_pg_proc) GETSTRUCT(tup); if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", NameListToString(name)), errhint("Use ALTER AGGREGATE to change owner of aggregate functions."))); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (procForm->proowner != newOwnerId) { Datum repl_val[Natts_pg_proc]; char repl_null[Natts_pg_proc]; char repl_repl[Natts_pg_proc]; Acl *newAcl; Datum aclDatum; bool isNull; HeapTuple newtuple; /* Superusers can always do it */ if (!superuser()) { /* Otherwise, must be owner of the existing object */ if (!pg_proc_ownercheck(procOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(procForm->pronamespace, newOwnerId, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(procForm->pronamespace)); } memset(repl_null, ' ', sizeof(repl_null)); memset(repl_repl, ' ', sizeof(repl_repl)); repl_repl[Anum_pg_proc_proowner - 1] = 'r'; repl_val[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only * necessary when the ACL is non-null. */ aclDatum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proacl, &isNull); if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), procForm->proowner, newOwnerId); repl_repl[Anum_pg_proc_proacl - 1] = 'r'; repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl); } newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); heap_freetuple(newtuple); /* Update owner dependency reference */ changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId); } ReleaseSysCache(tup); heap_close(rel, NoLock);}/* * Implements the ALTER FUNCTION utility command (except for the * RENAME and OWNER clauses, which are handled as part of the generic * ALTER framework). */voidAlterFunction(AlterFunctionStmt *stmt){ HeapTuple tup; Oid funcOid; Form_pg_proc procForm; Relation rel; ListCell *l; DefElem *volatility_item = NULL; DefElem *strict_item = NULL; DefElem *security_def_item = NULL; rel = heap_open(ProcedureRelationId, RowExclusiveLock); funcOid = LookupFuncNameTypeNames(stmt->func->funcname, stmt->func->funcargs, false); tup = SearchSysCacheCopy(PROCOID, ObjectIdGetDatum(funcOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for function %u", funcOid); procForm = (Form_pg_proc) GETSTRUCT(tup); /* Permission check: must own function */ if (!pg_proc_ownercheck(funcOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->func->funcname)); if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is an aggregate function", NameListToString(stmt->func->funcname)))); /* Examine requested actions. */ foreach(l, stmt->actions) { DefElem *defel = (DefElem *) lfirst(l); if (compute_common_attribute(defel, &volatility_item, &strict_item, &security_def_item) == false) elog(ERROR, "option \"%s\" not recognized", defel->defname); } if (volatility_item) procForm->provolatile = interpret_func_volatility(volatility_item); if (strict_item) procForm->proisstrict = intVal(strict_item->arg); if (security_def_item) procForm->prosecdef = intVal(security_def_item->arg); /* Do the update */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -