313 lines
13 KiB
C++
313 lines
13 KiB
C++
/* -------------------------------------------------------------------------
|
||
*
|
||
* aggregatecmds.cpp
|
||
*
|
||
* Routines for aggregate-manipulation commands
|
||
*
|
||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||
* Portions Copyright (c) 1994, Regents of the University of California
|
||
*
|
||
*
|
||
* IDENTIFICATION
|
||
* src/gausskernel/optimizer/commands/aggregatecmds.cpp
|
||
*
|
||
* DESCRIPTION
|
||
* The "DefineFoo" routines take the parse tree and pick out the
|
||
* appropriate arguments/flags, passing the results to the
|
||
* corresponding "FooDefine" routines (in src/catalog) that do
|
||
* the actual catalog-munging. These routines also verify permission
|
||
* of the user to execute the command.
|
||
*
|
||
* -------------------------------------------------------------------------
|
||
*/
|
||
#include "postgres.h"
|
||
#include "knl/knl_variable.h"
|
||
|
||
#include "access/heapam.h"
|
||
#include "access/tableam.h"
|
||
#include "catalog/dependency.h"
|
||
#include "catalog/indexing.h"
|
||
#include "catalog/pg_aggregate.h"
|
||
#include "catalog/pg_proc.h"
|
||
#include "catalog/pg_type.h"
|
||
#include "commands/defrem.h"
|
||
#include "miscadmin.h"
|
||
#include "parser/parse_func.h"
|
||
#include "parser/parse_type.h"
|
||
#include "utils/acl.h"
|
||
#include "utils/builtins.h"
|
||
#include "utils/guc.h"
|
||
#include "utils/lsyscache.h"
|
||
#include "utils/syscache.h"
|
||
#include "catalog/pg_proc_fn.h"
|
||
|
||
/*
|
||
DefineAggregate
|
||
"oldstyle" 表示旧式聚合函数定义风格,
|
||
其中聚合函数的输入类型由参数中的 BASETYPE 元素指定。
|
||
否则,"args" 定义了输入类型
|
||
*/
|
||
void DefineAggregate(List* name, List* args, bool oldstyle, List* parameters)
|
||
{
|
||
char* aggName = NULL;//字符型指针,用于存储聚合函数的名称
|
||
Oid aggNamespace;//对象标识符类型,表示聚合函数所属的命名空间
|
||
AclResult aclresult;//aclresult 是一个枚举值,表示访问控制的结果
|
||
List* transfuncName = NIL;//链表类型,用于存储聚合函数的过渡函数的名称
|
||
List* finalfuncName = NIL;//用于存储聚合函数的最终函数的名称
|
||
List* sortoperatorName = NIL;//用于存储排序操作符的名称
|
||
TypeName* baseType = NULL;//一个指向 TypeName 结构的指针,表示聚合函数的基础类型
|
||
TypeName* transType = NULL;//也是TypeName结构的指针,表示聚合函数的过渡类型
|
||
char* initval = NULL;//用于存储聚合函数的初始值
|
||
#ifdef PGXC
|
||
List* collectfuncName = NIL;
|
||
char* initcollect = NULL;
|
||
#endif
|
||
//定义了用于收集数据的函数名称和初始值
|
||
Oid* aggArgTypes = NULL;//表示聚合函数的参数类型
|
||
int numArgs;//表示聚合函数的参数数量
|
||
Oid transTypeId;//表示聚合函数的过渡类型的标识符
|
||
ListCell* pl = NULL;//循环中间变量
|
||
|
||
//有序集合聚合函数的属性或特征
|
||
char aggKind = AGGKIND_NORMAL;
|
||
|
||
//将一组名称列表转换为名称和命名空间
|
||
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
|
||
|
||
//检查是否具有在目标命名空间中创建的权限
|
||
aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
|
||
if (aclresult != ACLCHECK_OK)
|
||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
|
||
if (u_sess->attr.attr_sql.enforce_a_behavior) {
|
||
Oid proowner = InvalidOid;
|
||
/*
|
||
如果 isalter 为真,则将对象的所有者更改为命名空间的所有者,
|
||
但前提是命名空间的所有者与命名空间本身具有相同的名称
|
||
*/
|
||
bool isalter = false;
|
||
proowner = GetUserIdFromNspId(aggNamespace);
|
||
|
||
if (!OidIsValid(proowner))
|
||
proowner = GetUserId();
|
||
else if (proowner != GetUserId())
|
||
isalter = true;
|
||
|
||
if (isalter) {
|
||
aclresult = pg_namespace_aclcheck(aggNamespace, proowner, ACL_CREATE);
|
||
if (aclresult != ACLCHECK_OK)
|
||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(aggNamespace));
|
||
}
|
||
}
|
||
foreach (pl, parameters) {
|
||
DefElem* defel = (DefElem*)lfirst(pl);
|
||
|
||
//sfunc1、stype1 和 initcond1 被认为 是sfunc、stype 和 initcond 的过时拼写方式
|
||
|
||
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
|
||
transfuncName = defGetQualifiedName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
|
||
transfuncName = defGetQualifiedName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
|
||
finalfuncName = defGetQualifiedName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "sortop") == 0)
|
||
sortoperatorName = defGetQualifiedName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "basetype") == 0)
|
||
baseType = defGetTypeName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "stype") == 0)
|
||
transType = defGetTypeName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "stype1") == 0)
|
||
transType = defGetTypeName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "initcond") == 0)
|
||
initval = defGetString(defel);
|
||
else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
|
||
initval = defGetString(defel);
|
||
/*
|
||
上述使用一个循环遍历 parameters 列表中的每个元素(DefElem 结构),
|
||
然后根据元素的 defname 字段(参数名称)的值执行不同的操作
|
||
*/
|
||
#ifdef PGXC
|
||
else if (pg_strcasecmp(defel->defname, "cfunc") == 0)
|
||
collectfuncName = defGetQualifiedName(defel);
|
||
else if (pg_strcasecmp(defel->defname, "initcollect") == 0)
|
||
initcollect = defGetString(defel);
|
||
#endif
|
||
else
|
||
ereport(WARNING,
|
||
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("aggregate attribute \"%s\" not recognized", defel->defname)));
|
||
}
|
||
|
||
|
||
//确保我们有所需的定义变量
|
||
|
||
if (transType == NULL)
|
||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate stype must be specified")));
|
||
if (transfuncName == NIL)
|
||
ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate sfunc must be specified")));
|
||
|
||
if (oldstyle) {
|
||
/*
|
||
Old style支持零个或一个输入的聚合函数,其中输入类型 ANY 表示零个输入
|
||
以往我们允许命令看起来像 basetype = 'ANY',因此我们必须对名称 ANY 进行不区分大小写的比较
|
||
*/
|
||
if (baseType == NULL)
|
||
ereport(ERROR,
|
||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate input type must be specified")));
|
||
//baseType参数的值不能为空,聚合函数的输入类型必须指定
|
||
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0) {
|
||
numArgs = 0;
|
||
aggArgTypes = NULL;
|
||
} else {
|
||
numArgs = 1;
|
||
aggArgTypes = (Oid*)palloc(sizeof(Oid));
|
||
aggArgTypes[0] = typenameTypeId(NULL, baseType);
|
||
}
|
||
} else {
|
||
ListCell* lc = NULL;
|
||
int i = 0;
|
||
|
||
if (baseType != NULL)
|
||
ereport(ERROR,
|
||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||
errmsg("basetype is redundant with aggregate input type specification")));
|
||
|
||
/* 在使用没有直接参数的有序集合聚合函数时,aggr_args 变量会在 gram.y 文件中进行修改。
|
||
因此,解析 aggr_args 的过程应该进行相应的更改。
|
||
*/
|
||
numArgs = list_length((List*)linitial(args));
|
||
// 获取传递给聚合函数的参数数量
|
||
aggArgTypes = (Oid*)palloc(sizeof(Oid) * numArgs);
|
||
// 为聚合函数的参数类型标识符分配内存
|
||
foreach (lc, (List*)linitial(args))
|
||
{//// 遍历传递给聚合函数的参数列表
|
||
TypeName* curTypeName = (TypeName*)lfirst(lc);
|
||
|
||
aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
|
||
// 获取当前参数的类型标识符并存储到数组中
|
||
}
|
||
|
||
if (intVal(lsecond(args)) == 0) {
|
||
aggKind = AGGKIND_ORDERED_SET;
|
||
}
|
||
}
|
||
|
||
/*
|
||
下面查找聚合函数的 transtype
|
||
transtype 不能是伪类型,因为我们需要能够存储 transtype 的值。
|
||
然而,在某些情况下,我们可以允许多态的 transtype(AggregateCreate 函数将会检查这一点)。
|
||
此外,我们允许使用 "internal",用于那些希望传递指向私有数据结构的指针的函数
|
||
*/
|
||
transTypeId = typenameTypeId(NULL, transType);
|
||
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && !IsPolymorphicType(transTypeId)
|
||
&& (transTypeId != INTERNALOID || !superuser())) {
|
||
ereport(ERROR,
|
||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||
errmsg("aggregate transition data type cannot be %s", format_type_be(transTypeId))));
|
||
}
|
||
|
||
//大部分的参数检查都在 AggregateCreate 函数内部完成
|
||
AggregateCreate(aggName, /* 聚合函数的名称 */
|
||
aggNamespace, /* 命名空间 */
|
||
aggKind, /* 聚合种类 */
|
||
aggArgTypes, /* 输入类型种类 */
|
||
numArgs, /* 输入参数数量 */
|
||
transfuncName, /* 过渡函数名称 */
|
||
#ifdef PGXC
|
||
collectfuncName, /* 收集函数名称 */
|
||
#endif
|
||
finalfuncName, /* 最终函数名称 */
|
||
sortoperatorName, /* 排序操作符名称 */
|
||
transTypeId, /* 过渡数据类型 */
|
||
#ifdef PGXC
|
||
initval, /* 初始条件 */
|
||
initcollect); /* 收集函数的初始条件 */
|
||
#else
|
||
initval); /* 初始条件 */
|
||
#endif
|
||
}
|
||
|
||
void RenameAggregate(List* name, List* args, const char* newname)
|
||
{
|
||
Oid procOid; /* 聚合函数的 Oid */
|
||
Oid namespaceOid; /* 命名空间的 Oid */
|
||
HeapTuple tup; /* HeapTuple 结构,用于存储元组 */
|
||
Form_pg_proc procForm; /* pg_proc 表中的元组结构 */
|
||
Relation rel; /* pg_proc 表的 Relation 对象 */
|
||
AclResult aclresult; /* AclResult 枚举,用于存储访问控制的结果 */
|
||
bool isNull = false; /* 布尔变量,表示是否为 NULL */
|
||
rel = heap_open(ProcedureRelationId, RowExclusiveLock); /* 打开 pg_proc 表 */
|
||
|
||
/* 查询函数并确保他是聚合的*/
|
||
procOid = LookupAggNameTypeNames(name, args, false);
|
||
|
||
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
|
||
if (!HeapTupleIsValid(tup)) /* 如果运行正常这是不会出现的 */
|
||
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for function %u", procOid)));
|
||
procForm = (Form_pg_proc)GETSTRUCT(tup);
|
||
|
||
namespaceOid = procForm->pronamespace;
|
||
|
||
oidvector* proargs = ProcedureGetArgTypes(tup);
|
||
Datum packageoid = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_packageid, &isNull);
|
||
if (isNull) {
|
||
packageoid = DatumGetObjectId(InvalidOid);
|
||
}
|
||
|
||
#ifndef ENABLE_MULTIPLE_NODES
|
||
Datum allargtypes = ProcedureGetAllArgTypes(tup, &isNull);
|
||
Datum argmodes = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proargmodes, &isNull);
|
||
// 在系统缓存中搜索具有相同参数的函数
|
||
if (SearchSysCacheForProcAllArgs(
|
||
CStringGetDatum(newname),
|
||
allargtypes,
|
||
ObjectIdGetDatum(namespaceOid),
|
||
ObjectIdGetDatum(packageoid),
|
||
argmodes))
|
||
// 如果找到相同参数的函数,报告错误
|
||
ereport(ERROR,
|
||
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
||
errmsg("function %s already exists in schema \"%s\"",
|
||
funcname_signature_string(newname, procForm->pronargs, NIL, proargs->values),
|
||
get_namespace_name(namespaceOid))));
|
||
#else
|
||
if (SearchSysCacheExists3(PROCNAMEARGSNSP,
|
||
CStringGetDatum(newname),
|
||
PointerGetDatum(&procForm->proargtypes),
|
||
ObjectIdGetDatum(namespaceOid)))
|
||
ereport(ERROR,
|
||
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
||
errmsg("function %s already exists in schema \"%s\"",
|
||
funcname_signature_string(newname, procForm->pronargs, NIL, proargs->values),
|
||
get_namespace_name(namespaceOid))));
|
||
#endif
|
||
// 检查当前用户是否是聚合函数的所有者,如果不是,则报告错误
|
||
if (!pg_proc_ownercheck(procOid, GetUserId()))
|
||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(name));
|
||
|
||
// 检查当前用户是否有在命名空间中创建对象的权限
|
||
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
|
||
if (aclresult != ACLCHECK_OK)
|
||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid));
|
||
|
||
//重命名
|
||
(void)namestrcpy(&(((Form_pg_proc)GETSTRUCT(tup))->proname), newname);
|
||
simple_heap_update(rel, &tup->t_self, tup);
|
||
CatalogUpdateIndexes(rel, tup);
|
||
|
||
heap_close(rel, NoLock);
|
||
tableam_tops_free_tuple(tup);
|
||
}
|
||
|
||
/*
|
||
下面函数用来更改聚合函数的所有者
|
||
*/
|
||
void AlterAggregateOwner(List* name, List* args, Oid newOwnerId)
|
||
{
|
||
Oid procOid;
|
||
|
||
/* 查找函数并确保它是一个聚合函数。 */
|
||
procOid = LookupAggNameTypeNames(name, args, false);
|
||
|
||
/* 其余部分与普通函数类似 */
|
||
AlterFunctionOwner_oid(procOid, newOwnerId);
|
||
} |