查询代码-node解析

介绍

主要对存放与解析节点相关的代码的文件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 结点中,然后返回该结点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值