这篇文章上次修改于 798 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

初识

最初是从朋友那里听说,他有一个很厉害的朋友在一个叫 matrixone的社区贡献代码,并且社区的前景很好,这便是我对matrixone对最初印象了.

参与

偶然间,看到了 good first issuelabels,发现我完全有能力去解决其中一些问题,于是在朋友的建议下,带着初次参与社区的好奇与热爱,第一次开始了参与开源社区并且为之贡献代码的精力.

开始coding

很快,我选择了一个比较简单的issue [Feature Request]: Mathematical Built-in function pi() #1821](https://github.com/matrixorigin/matrixone/issues/1821) ,为sql实现内置的pi()函数,这个功能从实现上来说没有任何难度,所以更方便我把重点放在学习如何把内置函数嵌入到sql查询中,并直观的显示返回给使用者

根据https://github.com/matrixorigin/matrixone/issues/1821#issue-1161068080提到的 开发文档开发内置函数,我便开始照猫画虎的去修改样例来实现我的功能.

首先是要求具备一定的golang语言能力,这个恰好在几个月前学过一阵子,虽然平时不怎么用,但是看代码还是能改改的,复习的话也很快就能回忆起来,这个不是难点

其次,文档介绍到

数据库中有两种函数,内置函数是数据库自带的函数,而自定义函数是用户自定义的。内置函数可以根据它们操作的数据类型进行分类,即字符串、日期和数字内置函数。

并且给出了执行开发内置函数的步骤

困难

第一步:注册功能

  1. 函数名注册

    这一部分简单,只需要在 pkg/builtin/types.go中添加函数名即可

  2. 声明函数参数类型和返回类型。

    开始有一点点复杂了,

    在 Golang 中,初始化包时会调用 init 函数。我们将 all 的功能包装ABS()在这个 init 函数中,因此我们不需要显式调用它。

    init中,我们要按照案例的写法,根据不同函数和参数返回值的情况,去注册不同的函数

    有个坑点是

    在该目录pkg/builtin/unary中,创建一个新的 go 文件abs.go

    目录下的函数unary只接受一个值作为输入。目录下的函数binary只接受两个值作为输入。其他形式的函数放在multi目录下。

    
    所以,在注册时,要根据输入值的不同数目,在不同的个目录下新建文件,并且将
    `extend.UnaryReturnTypes[builtin.Abs]`等改换成`extend.BinaryReturnTypes[builtin.xxx]`
    
    确实是个不容易注意到的地方呢.
    
  3. 函数调用准备

    重点到了,也是整个卡了我很久的地方

    overload.UnaryOps[builtin.Abs] = []*overload.UnaryOp{
        Typ:        types.T_float32,
         ReturnType: types.T_float32,
         Fn: func(origVec *vector.Vector, proc *process.Process, _ bool) (*vector.Vector, error) {
                    origVecCol := origVec.Col.([]float32)
                    resultVector, err := process.Get(proc, 4*int64(len(origVecCol)), types.Type{Oid: types.T_float32, Size: 4}) // get a new types.T_float32 vector to store the result vector
                    if err != nil {
                        return nil, err
                    }
                    results := encoding.DecodeFloat32Slice(resultVector.Data)
                    results = results[:len(origVecCol)]
                    resultVector.Col = results
                    nulls.Set(resultVector.Nsp, origVec.Nsp)                         // the new vector's nulls are the same as the original vector
                    vector.SetCol(resultVector, abs.AbsFloat32(origVecCol, results)) // set the vector col with the return value from abs.AbsFloat32 function
                    return resultVector, nil
                },
    }

    这个部分的文档写的非常晦涩,很难理解,于是我根据其他已经实现的内置函数的写法,推测这个部分的写法.

不过运气不是很好,因为几乎没有与我的功能相似的情况,所以没有现成的解决方案可以借鉴.

于是我开始放弃借鉴,直接点进去看上下文逻辑,看源码,然后修改,编译运行,其客户端去测试,看报错,看日志,总的来说,过程很复杂,很麻烦,但是离终点非常接近了,我可不想放弃

解决,不知道试了多少次,终于正确结果出来了,我很难忘记当时的激动和欣喜.

虽然不知道是否时最适合的写法,但是 ~能跑就行 (dog

其他步骤,编译运行什么的,都是再正常不过的逻辑了,不需要多费口舌,

merge

很快,提了pr,通过了review,终于merge进了主分支,期间我也感受到了社区的issue回复速度和pr的审查速度,大家都是很热情积极的关注并解决,整个社区看起来充满了活力.

最后,也和matrixone社区的运营人员交流了一下对社区和这次任务的感受,感觉还是很棒的氛围,以后有机会也会继续参与社区的.

image-20220314233457000

image-20220314233521009

开心~~~~