介绍
主要对存放与解析节点相关的代码的文件parse_node.cpp,对里面的函数进行逐一介绍,该文件的目的是实现创建和处理这些查询树节点的相关逻辑。解释整段代码时会将一部分单独介绍,这一部分将用灰色背景来标记以方便区分
文件路径
src/common/backend/parser/parse_node.cpp
函数 make_parsestate
这个函数的目的是提供一个规范的方式来创建和初始化 ParseState 结构体的实例,以便在 SQL 查询解析过程中有效地跟踪和管理状态。这个函数允许指定一个父级 ParseState,并将一些默认值设置为新实例的成员变量。如果存在父级 ParseState,则还会复制一些父级的信息到新实例中。以下是整段代码与部分代码解释
ParseState* make_parsestate(ParseState* parentParseState){
ParseState* pstate = NULL;
pstate = (ParseState*)palloc0(sizeof(ParseState));
pstate->parentParseState = parentParseState;
pstate->isAliasReplace = true;
/* Fill in fields that don't start at null/false/zero */
pstate->p_next_resno = 1;
pstate->p_star_start = NIL;
pstate->p_star_end = NIL;
pstate->p_star_only = NIL;
pstate->p_resolve_unknowns = true;
pstate->ignoreplus = false;
pstate->p_plusjoin_rte_info = NULL;
pstate->p_rawdefaultlist = NIL;
pstate->p_has_ignore = false;
pstate->p_indexhintLists = NIL;
pstate->p_is_flt_frame = false;
if (parentParseState != NULL) {
pstate->p_sourcetext = parentParseState->p_sourcetext;
/* all hooks are copied from parent */
pstate->p_pre_columnref_hook = parentParseState->p_pre_columnref_hook;
pstate->p_post_columnref_hook = parentParseState->p_post_columnref_hook;
pstate->p_paramref_hook = parentParseState->p_paramref_hook;
pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook;
pstate->p_ref_hook_state = parentParseState->p_ref_hook_state;
pstate->p_create_proc_operator_hook = parentParseState->p_create_proc_operator_hook;
pstate->p_create_proc_insert_hook = parentParseState->p_create_proc_insert_hook;
pstate->p_cl_hook_state = parentParseState->p_cl_hook_state;
pstate->p_bind_variable_columnref_hook = parentParseState->p_bind_variable_columnref_hook;
pstate->p_bind_hook_state = parentParseState->p_bind_hook_state;
pstate->p_bind_describe_hook = parentParseState->p_bind_describe_hook;
pstate->p_describeco_hook_state = parentParseState->p_describeco_hook_state;
}
return pstate;
}
ParseState* make_parsestate(ParseState* parentParseState);
首先表明了make_parsestate 函数返回一个指向 ParseState 结构体的指针,并且接受一个 ParseState* 类型的参数 parentParseState。
ParseState* pstate = NULL;
pstate = (ParseState*)palloc0(sizeof(ParseState));
声明了一个指向 ParseState 结构体的指针 pstate,并将其初始化为 NULL。
使用 palloc0 函数分配足够大小的内存以存储一个 ParseState 结构体,并将分配的内存初始化为零。这里使用 palloc0 保证了分配的内存中的每个字节都被设置为零。
pstate->parentParseState = parentParseState;
pstate->isAliasReplace = true;
// ...
pstate->parentParseState = parentParseState;:将新创建的 ParseState 实例的 parentParseState 成员设置为传入的 parentParseState 参数。
pstate->isAliasReplace = true;:设置 isAliasReplace 成员为 true。
后续的一系列操作都是对新创建的 ParseState 实例的各个成员进行初始化,确保它们具有合适的初始值。
if (parentParseState != NULL) {
pstate->p_sourcetext = parentParseState->p_sourcetext;
// ...(一系列复制父级信息的操作)
}
return pstate;
如果传入的 parentParseState 不为 NULL,则将父级 ParseState 的一些信息复制到新创建的实例中。这些信息包括源文本、钩子函数等。最后,返回指向新创建的 ParseState 实例的指针。
函数free_parsestate
这个函数的主要目的是确保在释放 ParseState 结构体之前,相关的资源都被正确地释放。这包括关闭与目标关系(p_target_relation)相关的数据库关系资源,并检查结果列的编号是否在合法范围内。
/*
free_parsestate
Release a ParseState and any subsidiary resources.
/
*void free_parsestate(ParseState* pstate)
{
Assert(pstate != NULL);
/*
Check that we did not produce too many resnos; at the very least we
cannot allow more than 2^16, since that would exceed the range of a
AttrNumber. It seems safest to use MaxTupleAttributeNumber.
*/
if (pstate->p_next_resno - 1 > MaxTupleAttributeNumber) {
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("target lists can have at most %d entries", MaxTupleAttributeNumber)));
}
foreach_cell (l, pstate->p_target_relation) {
Relation r = (Relation)lfirst(l);
heap_close(r, NoLock);
}
pfree_ext(pstate);
}
void free_parsestate(ParseState* pstate)
{
Assert(pstate != NULL);
//使用 Assert 宏确保传入的 pstate 不为 NULL。
if (pstate->p_next_resno - 1 > MaxTupleAttributeNumber) {
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("target lists can have at most %d entries", MaxTupleAttributeNumber)));
}
//检查 p_next_resno 的值是否合法。p_next_resno 表示下一个要分配的结果列的编号。如果超过了 MaxTupleAttributeNumber,就报错。这是为了确保结果列的编号不超过 AttrNumber 的范围。
foreach_cell (l, pstate->p_target_relation) {
Relation r = (Relation)lfirst(l);
heap_close(r, NoLock);
}
//使用 foreach_cell 宏循环遍历 p_target_relation 列表,关闭每个关系(使用 heap_close 函数)。这是确保关系资源被正确释放的步骤。
pfree_ext(pstate);
最后,使用 pfree_ext 函数释放 pstate 占用的内存。这是一个扩展的 pfree 宏,用于释放动态分配的内存,并将指针设置为 NULL。
函数parser_errposition
这个函数用于在解析阶段的错误报告中提供源文本中的位置信息。如果位置信息不可用或未提供,函数返回 0。否则,它将计算得到的位置信息传递给错误报告机制。
int parser_errposition(ParseState* pstate, int location)
{
int pos;
/* No-op if location was not provided /
if (location < 0) {
return 0;
}
* /* Can't do anything if source text is not available /
if (pstate == NULL || pstate->p_sourcetext == NULL) {
return 0;
}
* /* Convert offset to character number /
pos = pg_mbstrlen_with_len(pstate->p_sourcetext, location) + 1;
* /* And pass it to the ereport mechanism */
return errposition(pos);
}
int parser_errposition(ParseState* pstate, int location)
表明 parser_errposition 函数返回一个整数,并接受两个参数:pstate 是一个指向 ParseState 结构体的指针,location 是一个整数表示的源文本中的偏移量。
/* No-op if location was not provided */
if (location < 0) {
return 0;}
如果未提供位置信息(location 小于 0),则返回 0。这是一个空操作(No-op),表示无法提供有效的位置信息。
if (pstate == NULL || pstate->p_sourcetext == NULL) {
return 0;}
如果 pstate 为 NULL 或者 p_sourcetext 为 NULL,同样返回 0。表示无法提供有效的位置信息。
pos = pg_mbstrlen_with_len(pstate->p_sourcetext, location) + 1;
return errposition(pos);
使用 pg_mbstrlen_with_len 函数将源文本中的偏移量(location)转换为字符数,并将结果存储在 pos 变量中。+ 1 是因为位置是从 1 开始计数的,而不是从 0 开始。将计算得到的字符数传递给 errposition 函数,该函数将位置信息报告给错误报告机制,并返回结果。
函数setup_parser_errposition_callback
这个函数的主要目的是设置解析错误位置的回调函数,以便在发生错误时能够追踪和报告相关的信息。在解析错误发生时,pcb_error_callback 函数将被调用,该函数负责在错误报告中提供解析错误的位置信息。
void setup_parser_errposition_callback(ParseCallbackState* pcbstate, ParseState* pstate, int location)
{
/* Setup error traceback support for ereport() /
pcbstate->pstate = pstate;
pcbstate->location = location;
pcbstate->errcontext.callback = pcb_error_callback;
* pcbstate->errcontext.arg = (void*)pcbstate;
pcbstate->errcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &pcbstate->errcontext;
}
void setup_parser_errposition_callback(ParseCallbackState* pcbstate, ParseState* pstate, int location)
表明 setup_parser_errposition_callback 函数不返回任何值(void),接受三个参数:pcbstate 是一个指向 ParseCallbackState 结构体的指针,pstate 是一个指向 ParseState 结构体的指针,location 是一个整数表示的源文本中的偏移量。
/* Setup error traceback support for ereport() */
pcbstate->pstate = pstate;
pcbstate->location = location;
pcbstate->errcontext.callback = pcb_error_callback;
pcbstate->errcontext.arg = (void*)pcbstate;
pcbstate->errcontext.previous = t_thrd.log_cxt.error_context_stack;
t_thrd.log_cxt.error_context_stack = &pcbstate->errcontext;
}
pcbstate->pstate = pstate;:将传入的 pstate 参数设置为 pcbstate 结构体的 pstate 成员,表示要跟踪的解析状态。
pcbstate->location = location;:将传入的 location 参数设置为 pcbstate 结构体的 location 成员,表示要跟踪的错误位置。
pcbstate->errcontext.callback = pcb_error_callback;:将错误回调函数 pcb_error_callback 设置为 pcbstate 结构体的 errcontext.callback 成员,表示当发生错误时应该执行的回调函数。
pcbstate->errcontext.arg = (void*)pcbstate;:将 pcbstate 结构体的指针设置为 errcontext.arg,这样在回调函数执行时可以访问到 pcbstate 结构体的信息。
pcbstate->errcontext.previous = t_thrd.log_cxt.error_context_stack;:将当前线程的错误上下文栈的栈顶指针保存到 errcontext.previous,以便在后续发生错误时可以恢复先前的错误上下文。
t_thrd.log_cxt.error_context_stack = &pcbstate->errcontext;:将 pcbstate 结构体的错误上下文添加到当前线程的错误上下文栈中,成为新的栈顶。
函数 cancel_parser_errposition_callback
这个函数的主要目的是取消先前设置的解析错误位置回调函数,使错误报告机制不再跟踪和报告解析错误的位置信息。在解析完成或退出解析上下文时,可以调用这个函数来清理相关的设置。
void cancel_parser_errposition_callback(ParseCallbackState* pcbstate)
{
/* Pop the error context stack */
t_thrd.log_cxt.error_context_stack = pcbstate->errcontext.previous;
}
void cancel_parser_errposition_callback(ParseCallbackState* pcbstate)
表明 cancel_parser_errposition_callback 函数不返回任何值(void),接受一个参数 pcbstate,是一个指向 ParseCallbackState 结构体的指针。
t_thrd.log_cxt.error_context_stack = pcbstate->errcontext.previous;
将当前线程的错误上下文栈的栈顶指针设置为 pcbstate 结构体的 errcontext.previous,即回退到之前保存的错误上下文。这样就取消了之前设置的错误回调函数和位置信息。
函数 pcb_error_callback
这个函数的主要目的是在发生错误时,检查错误是否由查询被取消引起。如果不是被取消的查询错误,那么通过 parser_errposition 函数报告解析错误的位置信息。
static void pcb_error_callback(void* arg)
{
ParseCallbackState* pcbstate = (ParseCallbackState*)arg;
if (geterrcode() != ERRCODE_QUERY_CANCELED) {
(void)parser_errposition(pcbstate->pstate, pcbstate->location);
}
}
static void pcb_error_callback(void* arg)
表明 pcb_error_callback 函数不返回任何值(void),接受一个参数 arg,是一个指向未知类型的指针。
ParseCallbackState* pcbstate = (ParseCallbackState*)arg;
将传入的 arg 参数强制转换为 ParseCallbackState* 类型的指针,以便在函数中使用。这里假设 arg 参数是一个指向 ParseCallbackState 结构体的指针。
if (geterrcode() != ERRCODE_QUERY_CANCELED) {
(void)parser_errposition(pcbstate->pstate, pcbstate->location); }
geterrcode() 函数用于获取当前的错误代码。如果当前错误代码不是 ERRCODE_QUERY_CANCELED,表示不是由于查询被取消而引起的错误,那么调用 parser_errposition 函数,传递 pcbstate->pstate 和 pcbstate->location 作为参数。这是为了报告解析错误的位置信息。
函数ts_make_var
这个函数用于在解析过程中创建表示查询中变量的 Var 结点,并初始化其属性,包括类型信息和在源文本中的位置。
Var* ts_make_var(ParseState* pstate, RangeTblEntry* rte, int attrno, int location)
{
Oid var_type_id;
int32 type_mod;
Oid var_collid;
int kv_type = ATT_KV_UNDEFINED;
get_rte_attribute_type(rte, attrno, &var_type_id, &type_mod, &var_collid, &kv_type);
if (kv_type == ATT_KV_HIDE) {
return NULL;
}
Var* result = NULL;
int vnum, sublevels_up;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
result = makeVar(vnum, attrno, var_type_id, type_mod, var_collid, sublevels_up);
result->location = location;
return result;
}
Var* ts_make_var(ParseState* pstate, RangeTblEntry* rte, int attrno, int location)
表明 ts_make_var 函数返回一个指向 Var 结点的指针,接受四个参数:pstate 是一个指向 ParseState 结构体的指针,rte 是一个指向 RangeTblEntry 结构体的指针,attrno 是变量的属性编号,location 是变量在源文本中的位置。
Oid var_type_id;
int32 type_mod;
Oid var_collid;
int kv_type = ATT_KV_UNDEFINED;
get_rte_attribute_type(rte, attrno, &var_type_id, &type_mod, &var_collid, &kv_type);
调用 get_rte_attribute_type 函数获取变量的类型信息,并存储在对应的变量中。get_rte_attribute_type 函数通常用于从 RangeTblEntry 中获取变量的类型、类型修饰符和关联的类型集合。
if (kv_type == ATT_KV_HIDE) {
return NULL;}
如果 kv_type 的值为 ATT_KV_HIDE,表示该变量应该被隐藏,直接返回 NULL。这可能是某些特殊用途的标记,用于控制变量是否应该被隐藏。
Var* result = NULL;
int vnum, sublevels_up;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
result = makeVar(vnum, attrno, var_type_id, type_mod, var_collid, sublevels_up);
result->location = location;
return result;
调用 RTERangeTablePosn 函数获取变量在范围表中的位置,并存储在 vnum 和 sublevels_up 变量中。调用 makeVar 函数创建一个 Var 结点,并将相关信息传递给该函数,包括范围表位置、属性编号、类型信息等。将变量的位置信息设置为传入的 location 参数。返回创建的 Var 结点的指针。
函数make_var
这个函数用于在解析过程中创建表示查询中变量的 Var 结点,并初始化其属性,包括类型信息和在源文本中的位置。此函数与前面提到的 ts_make_var 函数非常相似,可能是在不同的上下文中使用的变种。
Var* make_var(ParseState* pstate, RangeTblEntry* rte, int attrno, int location)
{
Var* result = NULL;
int vnum, sublevels_up;
Oid vartypeid;
int32 type_mod;
Oid varcollid;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
result->location = location;
return result;
}
Var* make_var(ParseState* pstate, RangeTblEntry* rte, int attrno, int location)
表明 make_var 函数返回一个指向 Var 结点的指针,接受四个参数:pstate 是一个指向 ParseState 结构体的指针,rte 是一个指向 RangeTblEntry 结构体的指针,attrno 是变量的属性编号,location 是变量在源文本中的位置。
Var* result = NULL;
int vnum, sublevels_up;
Oid vartypeid;
int32 type_mod;
Oid varcollid;
声明变量 result,vnum,sublevels_up,vartypeid,type_mod,varcollid,用于存储变量的相关信息。
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
调用 RTERangeTablePosn 函数获取变量在范围表中的位置,并存储在 vnum 和 sublevels_up 变量中。
get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
调用 get_rte_attribute_type 函数获取变量的类型、类型修饰符和关联的类型集合,并存储在对应的变量中。
result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
result->location = location;
调用 makeVar 函数创建一个 Var 结点,并将相关信息传递给该函数,包括范围表位置、属性编号、类型信息等。将变量的位置信息设置为传入的 location 参数。
函数transformArrayType
这个函数的主要作用是将数组类型转换为其基础元素类型,并返回该元素类型的 Oid。在转换过程中,还进行了一系列的错误检查,确保转换过程是合法的。
Oid transformArrayType(Oid* arrayType, int32* arrayTypmod)
{
Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
/*
* If the input is a domain, smash to base type, and extract the actual
* typmod to be applied to the base type. Subscripting a domain is an
* operation that necessarily works on the base array type, not the domain
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
*arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
/* Get the type tuple for the array */
type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
if (!HeapTupleIsValid(type_tuple_array)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", *arrayType)));
}
type_struct_array = (Form_pg_type)GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
if (elementType == InvalidOid) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot subscript type %s because it is not an array", format_type_be(origArrayType))));
}
ReleaseSysCache(type_tuple_array);
return elementType;
}
Oid transformArrayType(Oid* arrayType, int32* arrayTypmod)
这是函数的声明,表明 transformArrayType 函数返回一个 Oid 类型的值,接受两个参数:arrayType 是一个指向 Oid 类型的指针,arrayTypmod 是一个指向 int32 类型的指针。
Oid origArrayType = *arrayType;
将输入的数组类型保存在 origArrayType 变量中。
*arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
调用 getBaseTypeAndTypmod 函数,将数组类型转换为其基础类型,同时提取出要应用于基础类型的类型修饰符。这是为了处理数组类型为域的情况,因为对域进行下标操作时必须使用基础数组类型。
type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
使用 SearchSysCache1 函数根据数组类型的 Oid 从系统缓存中获取类型元组(Type Tuple)。
if (!HeapTupleIsValid(type_tuple_array)) {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for type %u", *arrayType))); }
如果缓存查找失败,即未找到相关类型元组,抛出错误。
type_struct_array = (Form_pg_type)GETSTRUCT(type_tuple_array);
将获取到的类型元组转换为结构体,以便后续访问其字段。
elementType = type_struct_array->typelem;
从类型元组的结构体中获取数组元素类型的 Oid。
if (elementType == InvalidOid) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot subscript type %s because it is not an array", format_type_be(origArrayType)))); }
如果数组元素类型为 InvalidOid,表示数组类型未定义,抛出错误。
ReleaseSysCache(type_tuple_array);
释放获取到的系统缓存。
函数transformArraySubscripts
其主要功能是将数组下标表达式转换为 ArrayRef 节点,表示对数组的索引或切片操作。
ArrayRef* transformArraySubscripts(ParseState* pstate, Node* arrayBase, Oid arrayType, Oid elementType,
int32 arrayTypMod, List* indirection, Node* assignFrom)
{
bool isSlice = false;
List* upperIndexpr = NIL;
List* lowerIndexpr = NIL;
ListCell* idx = NULL;
ArrayRef* aref = NULL;
bool isIndexByVarchar = false;
/*
* Caller may or may not have bothered to determine elementType. Note
* that if the caller did do so, arrayType/arrayTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType)) {
elementType = transformArrayType(&arrayType, &arrayTypMod);
}
/*
* A list containing only single subscripts refers to a single array
* element. If any of the items are double subscripts (lower:upper), then
* the subscript expression means an array slice operation. In this case,
* we supply a default lower bound of 1 for any items that contain only a
* single subscript. We have to prescan the indirection list to see if
* there are any double subscripts.
*/
foreach (idx, indirection) {
A_Indices* ai = (A_Indices*)lfirst(idx);
if (ai->lidx != NULL) {
isSlice = true;
break;
}
}
/*
* Transform the subscript expressions.
*/
int i = 0;
foreach (idx, indirection) {
A_Indices* ai = (A_Indices*)lfirst(idx);
Node* subexpr = NULL;
AssertEreport(IsA(ai, A_Indices), MOD_OPT, "");
if (isSlice) {
if (ai->lidx) {
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(
pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
if (subexpr == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->lidx))));
}
} else {
/* Make a constant 1 */
subexpr = (Node*)makeConst(
INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(1), false, true); /* pass by value */
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
}
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
if (get_typecategory(arrayType) == TYPCATEGORY_TABLEOF_VARCHAR) {
isIndexByVarchar = true;
}
if ((nodeTag(arrayBase) == T_Param && list_length(((Param*)arrayBase)->tableOfIndexTypeList) > i
&& list_nth_oid(((Param*)arrayBase)->tableOfIndexTypeList, i) == VARCHAROID)
|| isIndexByVarchar) {
/* subcript type is varchar */
subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr),
list_nth_oid(((Param*)arrayBase)->tableOfIndexTypeList, i),
-1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
} else {
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(
pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
}
if (subexpr == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->uidx))));
}
upperIndexpr = lappend(upperIndexpr, subexpr);
i++;
}
/*
* If doing an array store, coerce the source value to the right type.
* (This should agree with the coercion done by transformAssignedExpr.)
*/
if (assignFrom != NULL) {
Oid typesource = exprType(assignFrom);
Oid typeneeded = isSlice ? arrayType : elementType;
Node* newFrom = NULL;
newFrom = coerce_to_target_type(
pstate, assignFrom, typesource, typeneeded, arrayTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
if (newFrom == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array assignment requires type %s"
" but expression is of type %s",
format_type_be(typeneeded),
format_type_be(typesource)),
errhint("You will need to rewrite or cast the expression."),
parser_errposition(pstate, exprLocation(assignFrom))));
}
assignFrom = newFrom;
}
/*
* Ready to build the ArrayRef node.
*/
aref = makeNode(ArrayRef);
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod;
/* refcollid will be set by parse_collate.c */
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr*)arrayBase;
aref->refassgnexpr = (Expr*)assignFrom;
return aref;
}
ArrayRef* transformArraySubscripts(ParseState* pstate, Node* arrayBase, Oid arrayType, Oid elementType,
int32 arrayTypMod, List* indirection, Node* assignFrom)
这是函数的声明,表明 transformArraySubscripts 函数返回一个指向 ArrayRef 结点的指针,接受七个参数,包括解析状态 pstate、数组基本表达式 arrayBase、数组类型 arrayType、元素类型 elementType、数组类型修饰符 arrayTypMod、下标信息链表 indirection 和可能的赋值表达式 assignFrom。
bool isSlice = false;
List* upperIndexpr = NIL;
List* lowerIndexpr = NIL;
ListCell* idx = NULL;
ArrayRef* aref = NULL;
bool isIndexByVarchar = false;
初始化一些辅助变量,如表示是否为切片操作的 isSlice,上下限下标表达式链表 upperIndexpr 和 lowerIndexpr,循环变量 idx,以及 ArrayRef 结点的指针 aref。
if (!OidIsValid(elementType)) {
elementType = transformArrayType(&arrayType, &arrayTypMod); }
如果未提供元素类型,通过调用 transformArrayType 函数获取,并修改数组类型和修饰符为其基础类型的值。
foreach (idx, indirection) {
A_Indices* ai = (A_Indices*)lfirst(idx);
if (ai->lidx != NULL) {
isSlice = true;
break; } }
循环遍历下标信息链表,如果存在包含下限的下标表达式,则将 isSlice 置为 true,表示进行切片操作。
foreach (idx, indirection) {
A_Indices* ai = (A_Indices*)lfirst(idx);
Node* subexpr = NULL;
AssertEreport(IsA(ai, A_Indices), MOD_OPT, "");
if (isSlice) {
// 处理下限下标表达式
if (ai->lidx) {
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
// 如果不是 int4,尝试进行隐式类型转换
subexpr = coerce_to_target_type(
pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
if (subexpr == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->lidx)))); }
} else {
// 创建常量值 1 作为下限
subexpr = (Node*)makeConst(
INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(1), false, true); /* pass by value */ }
lowerIndexpr = lappend(lowerIndexpr, subexpr); }
// 处理上限下标表达式
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
if (get_typecategory(arrayType) == TYPCATEGORY_TABLEOF_VARCHAR) {
isIndexByVarchar = true; }
if ((nodeTag(arrayBase) == T_Param && list_length(((Param*)arrayBase)->tableOfIndexTypeList) > i
&& list_nth_oid(((Param*)arrayBase)->tableOfIndexTypeList, i) == VARCHAROID)
|| isIndexByVarchar) {
// 如果下标类型为 varchar,进行隐式类型转换
subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr),
list_nth_oid(((Param*)arrayBase)->tableOfIndexTypeList, i),
-1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
} else {
// 如果不是 int4,尝试进行隐式类型转换
subexpr = coerce_to_target_type(
pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); }
if (subexpr == NULL) {
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->uidx)))); }
upperIndexpr = lappend(upperIndexpr, subexpr);
i++; }
在这个步骤中,首先通过循环遍历下标信息链表 indirection,检查是否存在下限表达式,以确定是否执行切片操作。然后,再次循环遍历下标信息链表,对每一个下标表达式进行转换处理。对于下限表达式,如果存在,则将其转换为 INT4OID 类型,如果不存在,则创建一个常量值 1 作为下限。对于上限表达式,根据数组类型的特殊情况(是否为 TYPCATEGORY_TABLEOF_VARCHAR 类型或者是否由 VARCHAROID 等特殊类型组成),进行类型转换为 INT4OID 类型,然后将其添加到 upperIndexpr 链表中。如果在转换过程中发生错误,将抛出相应的异常。
aref = makeNode(ArrayRef);
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod;
/* refcollid will be set by parse_collate.c */
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr*)arrayBase;
aref->refassgnexpr = (Expr*)assignFrom;
return aref; }
在这一步,通过调用 makeNode(ArrayRef) 创建了一个新的 ArrayRef 结点。然后,将数组的类型、元素类型、类型修饰符以及上下限下标表达式等信息设置到 ArrayRef 结点的相应字段中。最后,返回构建好的 ArrayRef 结点。这个结点包含了对数组下标操作的所有信息,可以在后续的语法树中使用。
函数make_const
这个函数的目的是根据传入的值创建一个 Const 结点,表示一个常量。根据值的不同类型,进行相应的处理,并设置合适的数据类型、长度、是否按值传递等属性。
Const* make_const(ParseState* pstate, Value* value, int location)
{
Const* con = NULL;
Datum val;
int64 val64;
Oid typid;
Oid collid = InvalidOid;
int typelen;
bool typebyval = false;
ParseCallbackState pcbstate;
switch (nodeTag(value)) {
case T_Integer:
val = Int32GetDatum(intVal(value));
typid = INT4OID;
typelen = sizeof(int32);
typebyval = true;
break;
case T_Float:
/* could be an oversize integer as well as a float ... /
if (scanint8(strVal(value), true, &val64)) {
/
* It might actually fit in int32. Probably only INT_MIN can
* occur, but we'll code the test generally just to be sure.
*/
int32 val32 = (int32)val64;
if (val64 == (int64)val32) {
val = Int32GetDatum(val32);
typid = INT4OID;
typelen = sizeof(int32);
typebyval = true;
} else {
val = Int64GetDatum(val64);
typid = INT8OID;
typelen = sizeof(int64);
typebyval = FLOAT8PASSBYVAL; /* int8 and float8 alike /
}
} else {
* /* arrange to report location if numeric_in() fails */
setup_parser_errposition_callback(&pcbstate, pstate, location);
val = DirectFunctionCall3(
numeric_in, CStringGetDatum(strVal(value)), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1));
cancel_parser_errposition_callback(&pcbstate);
typid = NUMERICOID;
typelen = -1; /* variable len */
typebyval = false;
}
break;
case T_String:
/*
* We assume here that UNKNOWN's internal representation is the
* same as CSTRING
*/
val = CStringGetDatum(strVal(value));
typid = UNKNOWNOID; /* will be coerced later /
* typelen = -2; /* cstring-style varwidth type */
typebyval = false;
if (OidIsValid(GetCollationConnection())) {
collid = GetCollationConnection();
}
break;
case T_BitString:
/* arrange to report location if bit_in() fails */
setup_parser_errposition_callback(&pcbstate, pstate, location);
val = DirectFunctionCall3(
bit_in, CStringGetDatum(strVal(value)), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1));
cancel_parser_errposition_callback(&pcbstate);
typid = BITOID;
typelen = -1;
typebyval = false;
break;
case T_Null:
/* return a null const */
con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false);
con->location = location;
return con;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d", (int)nodeTag(value))));
return NULL; /* keep compiler quiet */
}
con = makeConst(typid,
-1, /* typmod -1 is OK for all cases /
* collid, /* all cases are uncollatable types */
typelen,
val,
false,
typebyval);
con->location = location;
return con;
}
Const* make_const(ParseState* pstate, Value* value, int location){
Const* con = NULL;
Datum val;
int64 val64;
Oid typid;
Oid collid = InvalidOid;
int typelen;
bool typebyval = false;
ParseCallbackState pcbstate; }
在函数开始部分,声明了函数返回的 Const 结点指针 con,以及一些用于构建 Const 结点所需的临时变量。其中,val 用于存储常量值的 Datum 表示,typid 用于存储常量值的数据类型的 OID,collid 用于存储常量值的排序规则的 OID,typelen 用于存储数据类型的长度,typebyval 用于表示数据类型是否为按值传递。
switch (nodeTag(value)) {
case T_Integer:
// 处理整数类型
break;
case T_Float:
// 处理浮点数类型
break;
case T_String:
// 处理字符串类型
break;
case T_BitString:
// 处理位字符串类型
break;
case T_Null:
// 处理空值
break;
default:
// 处理未识别的节点类型
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d", (int)nodeTag(value))));
return NULL; /* keep compiler quiet */ }
根据传入的 value 参数的节点类型,使用 switch 语句分别处理整数、浮点数、字符串、位字符串、以及空值的情况。
case T_Integer:
val = Int32GetDatum(intVal(value));
typid = INT4OID;
typelen = sizeof(int32);
typebyval = true;
break;
如果 value 是整数类型,将整数值转换为 Datum,并设置数据类型为 INT4OID,数据类型长度为 sizeof(int32),数据类型是否按值传递为 true。
case T_Float:
如果 value 是浮点数类型,根据浮点数字符串的格式进行处理。如果浮点数可以被解析为 int64,则尝试将其转换为 INT4OID 类型。否则,将其转换为 INT8OID 类型。
case T_String:
如果 value 是字符串类型,将字符串值转换为 CString,并设置数据类型为 UNKNOWNOID(稍后会进行类型强制转换),数据类型长度为 -2(表示 cstring-style 变宽类型),数据类型是否按值传递为 false。如果当前会话设置了排序规则,将其设置为排序规则的 OID。
case T_BitString:
如果 value 是位字符串类型,将位字符串值转换为 Datum,并设置数据类型为 BITOID,数据类型长度为 -1(表示变宽类型),数据类型是否按值传递为 false。
case T_Null:
// 返回一个表示空值的 Const 结点
con = makeConst(UNKNOWNOID, -1, InvalidOid, -2, (Datum)0, true, false);
con->location = location;
return con;
如果 value 是空值类型,直接返回一个表示空值的 Const 结点。
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized node type: %d", (int)nodeTag(value))));
return NULL; /* keep compiler quiet */
如果 value 是其他未识别的节点类型,则抛出错误。
con = makeConst(typid,
-1, /* typmod -1 is OK for all cases */
collid, /* all cases are uncollatable types */
typelen,
val,
false,
typebyval);
con->location = location;
return con; }
最后,根据之前设置的类型信息和常量值创建 Const 结点,并将位置信息设置到 Const 结点中,然后返回该结点。