openGauss-server/src/gausskernel/optimizer/commands/aggregatecmds.cpp

313 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -------------------------------------------------------------------------
*
* 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 的值。
然而,在某些情况下,我们可以允许多态的 transtypeAggregateCreate 函数将会检查这一点)。
此外,我们允许使用 "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);
}