【OpenGauss源码学习(CopyOneRowTo)】

这篇具有很好参考价值的文章主要介绍了【OpenGauss源码学习(CopyOneRowTo)】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了 OpenGauss1.1.0 的开源代码

概述

  本文主要围绕列存储进行学习。

CopyOneRowTo函数

  CopyOneRowTo 函数的作用是将一个数据行(row)从一个源 ScalarVector 复制到目标 ScalarVector,以实现数据的拷贝。具体来说,它用于在处理批次数据时,从一个源列(ScalarVector)复制数据到另一个目标列(ScalarVector),以便在数据处理过程中进行转换修改等操作,CopyOneRowTo 函数帮助实现了批次数据的复制和转换。
  CopyOneRowTo函数源码如下:(src/gausskernel/optimizer/commands/copy.cpp

/*
 * Emit one row during CopyTo().
 */
static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum* values, const bool* nulls)
{
    bool need_delim = false;  // 标志是否需要添加分隔符
    FmgrInfo* out_functions = cstate->out_functions;  // 输出函数的信息
    MemoryContext oldcontext;  // 保存旧的内存上下文
    ListCell* cur = NULL;  // 遍历属性列表的指针
    char* string = NULL;  // 临时字符串

    // 重置行内存上下文,切换到行内存上下文
    MemoryContextReset(cstate->rowcontext);
    oldcontext = MemoryContextSwitchTo(cstate->rowcontext);

    if (IS_BINARY(cstate)) {
        // 对于二进制格式,发送元组的二进制头部信息
        CopySendInt16(cstate, list_length(cstate->attnumlist));
        // 如果需要,发送 OID
        if (cstate->oids) {
            // 假设 Oid 和 int32 大小相同
            CopySendInt32(cstate, sizeof(int32));
            CopySendInt32(cstate, tupleOid);
        }
    } else if (cstate->oids) {
        // 对于文本格式,如果需要,发送 OID
        // 假设数字不需要引用或编码转换
        string = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(tupleOid)));
        CopySendString(cstate, string);
        need_delim = true;
    }

	// 是否为固定列宽
    if (IS_FIXED(cstate))
        FixedRowOut(cstate, values, nulls);
    else {
        // 遍历属性列表
        foreach (cur, cstate->attnumlist) {
            int attnum = lfirst_int(cur);  // 属性序号
            Datum value = values[attnum - 1];  // 属性值
            bool isnull = nulls[attnum - 1];  // 是否为 NULL 值

            if (cstate->fileformat == FORMAT_CSV || cstate->fileformat == FORMAT_TEXT) {
                // 对于 CSV 或文本格式,添加分隔符
                if (need_delim)
                    CopySendString(cstate, cstate->delim);
                need_delim = true;
            }

            if (isnull) {
                // 处理 NULL 值
                switch (cstate->fileformat) {
                    case FORMAT_CSV:
                    case FORMAT_TEXT:
                        CopySendString(cstate, cstate->null_print_client);
                        break;
                    case FORMAT_BINARY:
                        CopySendInt32(cstate, -1);
                        break;
                    default:
                        ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Invalid file format")));
                }
            } else {
                if (!IS_BINARY(cstate)) {
                    // 非二进制格式,将值转换为字符串并处理
                    string = OutputFunctionCall(&out_functions[attnum - 1], value);
                    switch (cstate->fileformat) {
                        case FORMAT_CSV:
                            CopyAttributeOutCSV(cstate,
                                string,
                                cstate->force_quote_flags[attnum - 1],
                                list_length(cstate->attnumlist) == 1);
                            break;
                        case FORMAT_TEXT:
                            CopyAttributeOutText(cstate, string);
                            break;
                        default:
                            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Invalid file format")));
                    }
                } else {
                    // 二进制格式,调用输出函数并发送数据
                    bytea* outputbytes = NULL;
                    outputbytes = SendFunctionCall(&out_functions[attnum - 1], value);
                    CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
                    CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ);
                }
            }
        }
    }

    // 发送行数据,并切换回旧的内存上下文
    cstate->writelineFunc(cstate);
    (void)MemoryContextSwitchTo(oldcontext);
}

ScalarVector类

  ScalarVector 类是一种数据结构,用于存储单一数据列的向量化数据。在数据库系统中,数据通常以表格的形式存储,每个列都包含一组数据。ScalarVector 类的作用是为了优化这些列数据的处理,提高数据访问和计算的效率。
  ScalarVector 类的源码如下:(路径:src/include/vecexecutor/vectorbatch.h

// the core data structure for a column
class ScalarVector : public BaseObject {
    friend class VectorBatch;

public:
    // number of values.
    int m_rows;

    // type desciption information for this scalar value.
    ScalarDesc m_desc;

    // this value means that the value in the scalarvector is always the same
    bool m_const;

    // flags in the scalar value array.
    uint8* m_flag;

    // a company buffer for store the data if the data type is not plain.
    VarBuf* m_buf;

    // the value array.
    ScalarValue* m_vals;

public:
    // decode a variable length data.
    // null value judgement should be outside of this function.
    FORCE_INLINE
    static Datum Decode(ScalarValue val)
    {
        return val;
    }

    // convert a datum to scalar value
    static ScalarValue DatumToScalar(Datum datumVal, Oid datumType, bool isNull);

    template <Oid datumType>
    static ScalarValue DatumToScalarT(Datum datumVal, bool isNull);

public:
    // constructor/deconstructor.
    ScalarVector();
    ~ScalarVector();

    // init the ScalarVector.
    //
    void init(MemoryContext cxt, ScalarDesc desc);
    
    // used in tsdb. init with another ScalarVector object.
    //
    void init(MemoryContext cxt, ScalarVector* vec, const int batchSize);

    // serialize the Scalar vector
    //
    void Serialize(StringInfo buf);

    // serialize the Scalar vector of the particular index
    //
    void Serialize(StringInfo buf, int idx);

    // Deserialize the vector
    //
    char* Deserialize(char* msg, size_t len);

    // Add a variable length data
    // this var may be from
    // cstring, fixed length(> 8) data type, or pg traditional header-contain variable length
    Datum AddVar(Datum data, int index);

    // Add a header-contain variable
    Datum AddVarWithHeader(Datum data);

    // Add a variable without header on a special position. The original variable will be
    // transfered in together with the length of the content. And inside the funtion, the header
    // of the ScalarValue will be added before the actual content according to the data type.
    Datum AddBPCharWithoutHeader(const char* data, int maxLen, int len, int aindex);
    Datum AddVarCharWithoutHeader(const char* data, int len, int aindex);

    // Add a short decimal without header on a special position. The value of decimal
    // will be transfered in by int64 format together with the scale of it. And inside the function,
    // the header will be added and the value will be converted into PG format. Here we only support
    // short decimal which can be stored using int64.
    Datum AddShortNumericWithoutHeader(int64 value, uint8 scale, int aindex);
    Datum AddBigNumericWithoutHeader(int128 value, uint8 scale, int aindex);

    char* AddVars(const char* src, int length);

    // add a normal header-contain val
    Datum AddHeaderVar(Datum data, int index);

    // add a cstring type val
    Datum AddCStringVar(Datum data, int index);

    // add a fixed length val
    template <Size len>
    Datum AddFixLenVar(Datum data, int index);

    // copy a vector
    void copy(ScalarVector* vector, int start_idx, int endIdx);
    void copy(ScalarVector* vector);

    void copyDeep(ScalarVector* vector, int start_idx, int endIdx);
    void copyNth(ScalarVector* vector, int Nth);

    void copy(ScalarVector* vector, const bool* pSel);

    // convert a cstring to Scalar value.
    static Datum DatumCstringToScalar(Datum data, Size len);

    // convert a fixed len datatype to Scalar Value
    static Datum DatumFixLenToScalar(Datum data, Size len);

    FORCE_INLINE
    bool IsNull(int i)
    {
        Assert(i >= 0 && i < m_rows);
        return ((m_flag[i] & V_NULL_MASK) == V_NULL_MASK);
    }

    FORCE_INLINE
    void SetNull(int i)
    {
        Assert(i >= 0 && i < BatchMaxSize);
        m_flag[i] |= V_NULL_MASK;
    }

    FORCE_INLINE
    void SetAllNull()
    {
        for (int i = 0; i < m_rows; i++) {
            SetNull(i);
        }
    }

private:
    // init some function pointer.
    void BindingFp();

    Datum (ScalarVector::*m_addVar)(Datum data, int index);
};

CopySendString 函数

  CopySendString 函数,这个函数的目的是将字符串数据添加到 CopyState 结构中的前端消息缓冲区中,以便之后将这些数据发送给客户端。它使用 appendBinaryStringInfo 函数将字符串数据追加到消息缓冲区中。CopySendString 函数源码如下:(src/gausskernel/optimizer/commands/copy.cpp

// CopySendString does the same for null-terminated strings
void CopySendString(CopyState cstate, const char* str)
{
    appendBinaryStringInfo(cstate->fe_msgbuf, str, strlen(str));
}

  appendBinaryStringInfo 函数用于向StringInfo结构中追加任意二进制数据。首先,它会检查 StringInfo 结构是否为空。然后,根据需要分配更多空间以容纳要追加的数据。接下来,使用 memcpy_s 函数将数据追加到 StringInfo 结构的末尾,并更新长度信息。最后,会在字符串的末尾添加一个 null 字符,即使对于二进制数据来说,这个 null 字符可能没有实际用处。appendBinaryStringInfo 函数源码如下:(src/common/backend/lib/stringinfo.cpp

/*
 * appendBinaryStringInfo
 *
 * 向StringInfo结构追加任意二进制数据,如果需要的话会分配更多空间。
 */
void appendBinaryStringInfo(StringInfo str, const char* data, int datalen)
{
    Assert(str != NULL);  // 断言:确保StringInfo结构非空

    /* 如果需要的话分配更多空间 */
    enlargeStringInfo(str, datalen);

    /* 追加数据 */
    errno_t rc = memcpy_s(str->data + str->len, (size_t)(str->maxlen - str->len), data, (size_t)datalen);
    securec_check(rc, "\0", "\0");
    str->len += datalen;

    /*
     * 保持末尾的null,尽管对于二进制数据来说可能没有用处。
     * (一些调用者处理的是文本数据,但是因为输入没有以null结尾,所以调用了这个函数。)
     */
    str->data[str->len] = '\0';
}

  DatumGetCString 是一个宏,作用是将 Datum 类型的数据转换为C字符串。源码如下:(路径:src/include/postgres.h

/*
 * DatumGetCString
 *		Returns C string (null-terminated string) value of a datum.
 *
 * Note: C string is not a full-fledged Postgres type at present,
 * but type input functions use this conversion for their inputs.
 */

#define DatumGetCString(X) ((char*)DatumGetPointer(X))

FixedRowOut 函数

  FixedRowOut 函数是在固定列宽格式下将一行数据输出。它首先根据格式信息扩展输出缓冲区,然后遍历每个字段进行处理,根据字段的值和是否为null,调用相应的处理函数输出数据或null值。源码如下:(路径:src/gausskernel/optimizer/commands/formatter.cpp

// 固定列宽格式下输出一行数据
void FixedRowOut(CopyState cstate, Datum* values, const bool* nulls)
{
    // 获取输出函数信息和固定列宽格式信息
    FmgrInfo* out_functions = cstate->out_functions; // 输出函数信息
    FixFormatter* formatter = (FixFormatter*)cstate->formatter; // 固定列宽格式信息
    FieldDesc* descs = formatter->fieldDesc; // 字段描述
    char* string = NULL; // 临时字符串指针

    // 根据行大小扩展输出缓冲区
    enlargeStringInfo(cstate->fe_msgbuf, formatter->lineSize);

    // 遍历每个字段进行处理
    for (int i = 0; i < formatter->nfield; i++) {
        // 获取当前字段的属性序号和对应的值
        int attnum = formatter->fieldDesc[i].attnum; // 当前字段属性序号
        Datum value = values[attnum - 1]; // 当前字段值
        bool isnull = nulls[attnum - 1]; // 当前字段是否为null

        // 根据是否为null进行处理
        if (isnull) {
            // 调用AttributeOutFixed函数输出null值
            AttributeOutFixed<false>(cstate, descs[i].nullString, descs + i);
        } else {
            // 对非null值,调用输出函数并输出
            string = OutputFunctionCall(&out_functions[attnum - 1], value);
            Assert(string != NULL);
            AttributeOutFixed<false>(cstate, string, descs + i);
        }
    }
}

CopySendInt32 函数

  CopySendInt32 函数用于将一个 int32 类型的值以网络字节序发送出去。它首先将传入的 int32 值转换为网络字节序,并将结果存储在 buf 中,然后通过调用 CopySendData 函数将 buf 中的数据发送出去。函数源码如下:(路径:src/gausskernel/optimizer/commands/copy.cpp

/*
 * 这些函数会进行一些数据转换
 */

/*
 * CopySendInt32 以网络字节序发送 int32 类型的值
 */
static void CopySendInt32(CopyState cstate, int32 val)
{
    uint32 buf;

    // 将 int32 类型的值转换为网络字节序,并存储在 buf 中
    buf = htonl((uint32)val);

    // 调用 CopySendData 函数将 buf 中的数据发送出去,发送的字节数为 sizeof(buf)
    CopySendData(cstate, &buf, sizeof(buf));
}

CopySendData 函数

  这段代码定义了一系列发送数据的函数,这些函数会将指定的数据追加到 cstate->fe_msgbuf 中,其中 cstateCopyState 结构体的指针,表示数据拷贝的状态。这些函数分别用于发送二进制数据、以 null 结尾的字符串、单个字符以及在每行数据末尾执行适当的操作。这些函数并不会对数据进行任何转换,只是简单地将数据追加到消息缓冲区中。

/* ----------
 * CopySendData 将输出数据发送到目标(文件或前端)
 * CopySendString 对以 null 结尾的字符串执行相同操作
 * CopySendChar 对单个字符执行相同操作
 * CopySendEndOfRow 在每行数据末尾执行适当的操作
 *  (实际上只有在 CopySendEndOfRow 时才会刷新数据,其他函数不会刷新数据)
 *
 * 注意:这些函数不会对数据进行任何转换
 * ----------
 */
static void CopySendData(CopyState cstate, const void* databuf, int datasize)
{
    // 调用 appendBinaryStringInfo 函数将指定大小的数据追加到 cstate->fe_msgbuf 中
    appendBinaryStringInfo(cstate->fe_msgbuf, (const char*)databuf, datasize);
}

appendBinaryStringInfo 函数

  appendBinaryStringInfo 函数接受一个 StringInfo 结构体指针 str,一个 const char* 类型的数据指针 data,以及一个整数 datalen,表示数据的长度。函数会首先确保 str 不为空,然后根据需要分配更多空间,将指定长度的数据复制到 str 的数据缓冲区中,然后更新已追加数据的长度,并在数据末尾添加一个 null 字符,以保证字符串的正确终止。这个函数通常用于将二进制数据添加到 StringInfo 结构体中,StringInfo 是一个动态字符串结构体,它的大小可以根据需要自动增长。源码如下:(路径:src/common/backend/lib/stringinfo.cpp文章来源地址https://www.toymoban.com/news/detail-663500.html

/*
 * appendBinaryStringInfo
 *
 * 将任意的二进制数据追加到 StringInfo 中,如果需要的话会分配更多的空间。
 */
void appendBinaryStringInfo(StringInfo str, const char* data, int datalen)
{
    Assert(str != NULL);  // 断言确保 str 不为空

    /* 如果需要的话分配更多空间 */
    enlargeStringInfo(str, datalen);

    /* 将数据追加到 str 中 */
    errno_t rc = memcpy_s(str->data + str->len, (size_t)(str->maxlen - str->len), data, (size_t)datalen);
    securec_check(rc, "\0", "\0");
    str->len += datalen;  // 更新已追加数据的长度

    /*
     * 保持末尾的 null 字符,即使对于二进制数据它可能没有用处。
     * (一些调用者处理文本,但调用这个函数是因为输入可能没有以 null 结尾。)
     */
    str->data[str->len] = '\0';
}

到了这里,关于【OpenGauss源码学习(CopyOneRowTo)】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 开源数据库 OpenGauss 的 SQL 解析源码分析

    openGauss 是关系型数据库,采用客户端/服务器,单进程多线程架构;支持单机和一主多备部署方式,同时支持备机可读、双机高可用等特性。 从代码结构体系结构的角度来说,oepnGauss 的第一个组成部分是通信管理。 openGauss 查询响应是使用“单个用户对应一个服务器线程”的

    2024年03月17日
    浏览(50)
  • Nacos-2.2.2源码修改集成高斯数据库GaussDB,postresql

    一 ,下载代码 Release 2.2.2 (Apr 11, 2023) · alibaba/nacos · GitHub 二, 执行打包 mvn -Prelease-nacos -Dmaven.test.skip=true -Drat.skip=true clean install -U 或 mvn -Prelease-nacos ‘-Dmaven.test.skip=true’ ‘-Drat.skip=true’ clean install -U 注意:请不要把源码放在中文路径下,会报各种意想不到的错误。 打包的

    2024年02月05日
    浏览(39)
  • GaussDB与openGauss有什么相同和不同?

    众所周知, GaussDB是华为自主创新研发的分布式关系型数据库,为企业提供功能全面、稳定可靠、扩展性强、性能优越的企业级数据库服务, openGauss是开源数据库,两者之间又是什么样的关系,有什么相同和不同,让我们一一展开来探讨。 一款支持SQL2003标准语法,支持主备

    2024年01月23日
    浏览(33)
  • openGauss学习笔记-09 openGauss 简单数据管理-创建数据库

    数据库安装完成后,默认生成名称为postgres的数据库。您需要自己创建一个新的数据库。 9.1 语法格式 创建数据库 查看数据库 使用“l”用于查看已经存在的数据库。 使用 “c + 数据库名” 进入已存在数据库。 修改数据库 删除数据库 9.2 参数说明 database_name 要创建、修改或

    2024年02月16日
    浏览(55)
  • openGauss学习笔记-60 openGauss 数据库管理-逻辑存储结构

    openGauss的数据库节点负责存储数据,其存储介质也是磁盘,本节主要从逻辑视角介绍数据库节点都有哪些对象,以及这些对象之间的关系。数据库逻辑结构如 图1 。 图 1 数据库逻辑结构图 说明: Tablespace,即表空间,是一个目录,可以存在多个,里面存储的是它所包含的数据

    2024年02月09日
    浏览(50)
  • openGauss学习笔记-74 openGauss 数据库管理-创建和管理视图

    74.1 背景信息 当用户对数据库中的一张或者多张表的某些字段的组合感兴趣,而又不想每次键入这些查询时,用户就可以定义一个视图,以便解决这个问题。 视图与基本表不同,不是物理上实际存在的,是一个虚表。数据库中仅存放视图的定义,而不存放视图对应的数据,这

    2024年02月07日
    浏览(53)
  • openGauss学习笔记-184 openGauss 数据库运维-升级-升级验证

    本章介绍升级完成后的验证操作。给出验证的用例和详细操作步骤。 184.1 验证项目的检查表 表 1 验证项目的检查表 序号 验证项目 检查标准 检查结果 1 版本查询 查询升级后版本是否正确 - 2 健康检查 使用gs_checkos工具完成操作系统状态检查。 - 3 数据库状态 使用gs_om工具完成

    2024年02月03日
    浏览(49)
  • openGauss学习笔记-55 openGauss 高级特性-全密态数据库

    全密态数据库意在解决数据全生命周期的隐私保护问题,使得系统无论在何种业务场景和环境下,数据在传输、运算以及存储的各个环节始终都处于密文状态。当数据拥有者在客户端完成数据加密并发送给服务端后,在攻击者借助系统脆弱点窃取用户数据的状态下仍然无法获

    2024年02月10日
    浏览(43)
  • GaussDB 实验篇+openGauss的4种1级分区案例

    ✔ 范围分区/range分区 ✔ 间隔分区/interval分区 ✔ 哈希分区/hash分区 ✔ 列表分区/list分区 ※ 如果您觉得文章写的还不错, 别忘了在文末给作者点个赞哦 ~

    2024年02月12日
    浏览(33)
  • openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户

    使用CREATE USER和ALTER USER可以创建和管理数据库用户。openGauss包含一个或多个已命名数据库。用户和角色在整个openGauss范围内是共享的,但是其数据并不共享。即用户可以连接任何数据库,但当连接成功后,任何用户都只能访问连接请求里声明的那个数据库。 非 三权分立 下,

    2024年02月08日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包