UDF(user-defined function)

当mysql提供的内置函数(count,min,max等)无法满足需求时,udf用于扩展自定义函数,满足特定查询需求。

在这里,假定一种db应用场景,有一个 t_quest_db的table,有2个字段 roleid(INT), data(blob)。data为自己编码的二进制数据字段,当想要通过select查询出肉眼可见的内容时,就需要用到UDF了。

主要代码实现

示例代码用到cgo,附上完整示例代码:https://files.cnblogs.com/files/fitness/udftest.zip

// 本文件实现t_quest_db的data字段的序列化和反序列化

package udfdll

import (
"bytes"
"encoding/gob"
) type PlayerDoingQuestDbData struct {
TaskId int
TaskDbId int64
AcceptTime int32
ProgressMap map[int]string
IsProgressFinish bool
} type PlayerDoneQuestDbData struct {
TaskId int
LastDoneTimestamp int32 // 上次完成任务的时间戳
PeriodDoneTimes int // 任务周期内完成的次数(天/周)
} // 这个结构存db, 对应t_quest_db的data字段
type PlayerAllQuestDbData struct {
DoneTaskMap map[int]*PlayerDoneQuestDbData
DoingTaskMap map[int64]*PlayerDoingQuestDbData
} // 把this编码为二进制,存入data字段
func (this *PlayerAllQuestDbData) ToDbBlob() ([]byte, error) {
buf := bytes.NewBuffer([]byte{})
enc := gob.NewEncoder(buf)
if err := enc.Encode(this); err != nil {
return nil, err
} return buf.Bytes(), nil
} // 解析data字段
func (this *PlayerAllQuestDbData) FromDbBlob(data []byte) error {
if len(data) > 0 {
dec := gob.NewDecoder(bytes.NewBuffer(data))
if err := dec.Decode(this); err != nil {
return err
}
} if this.DoneTaskMap == nil {
this.DoneTaskMap = make(map[int]*PlayerDoneQuestDbData)
} if this.DoingTaskMap == nil {
this.DoingTaskMap = make(map[int64]*PlayerDoingQuestDbData)
} return nil
}
// 本文件实现UDF扩展

package main

/*
#cgo CFLAGS: -Iinclude
#include <mysql.h>
#include <string.h>
#include <stdlib.h>
*/
import "C"
import (
"bytes"
_ "encoding/gob"
"encoding/json"
"fmt"
"strings"
"unsafe"
) func main() {} func getUintPointerValue(pointer *uint32, offset int) *C.uint {
return (*C.uint)(unsafe.Pointer(uintptr(unsafe.Pointer(pointer)) + uintptr(offset)*uintptr(unsafe.Sizeof(C.uint(0)))))
} func getCharPointerValue(pointer **C.char, offset int) **C.char {
var c C.char
return (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(pointer)) + uintptr(offset)*uintptr(unsafe.Sizeof(&c))))
} func getUlongPointerValue(pointer *C.ulong, offset int) *C.ulong {
return (*C.ulong)(unsafe.Pointer(uintptr(unsafe.Pointer(pointer)) + uintptr(offset)*uintptr(unsafe.Sizeof(C.ulong(0)))))
} func getBytePointerValue(pointer *C.char, offset int) *C.char {
return (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(pointer)) + uintptr(offset)*uintptr(unsafe.Sizeof(C.char(0)))))
} //export questblob_init
func questblob_init(init *C.UDF_INIT, args *C.UDF_ARGS, msg *C.char) C.my_bool {
init.maybe_null = 1
if args.arg_count < 2 {
s := C.CString("UnExpected err: args count len < 1")
C.strcpy(msg, s)
C.free(unsafe.Pointer(s))
return 1
}
*getUintPointerValue(args.arg_type, 0) = C.STRING_RESULT
*getUintPointerValue(args.arg_type, 1) = C.STRING_RESULT
return 0
} //export questblob_deinit
func questblob_deinit(init *C.UDF_INIT) {
C.free(unsafe.Pointer(init.ptr))
} //export questblob
func questblob(init *C.UDF_INIT, args *C.UDF_ARGS, result *C.char, length *C.ulong, is_null *C.char, error *C.char) *C.char {
*is_null = 1
chars := getCharPointerValue(args.args, 0)
arglen := getUlongPointerValue(args.lengths, 0)
names := getCharPointerValue(args.args, 1)
namelen := getUlongPointerValue(args.lengths, 1)
rb := C.GoBytes(unsafe.Pointer(*chars), C.int(*arglen))
name := C.GoStringN(*names, C.int(*namelen))
nameInfos := strings.Split(name, ".") _ = nameInfos[0] // 数据库表名
_ = nameInfos[1] // 数据库字段名,实际情况中可根据这两个string,决定构造那个对象
errStringPrefix := fmt.Sprintf("[Error]table:'%s' column:'%s' ", nameInfos[0], nameInfos[1]) data := &PlayerAllQuestDbData{}
err := data.FromDbBlob(bytes.NewBuffer(rb).Bytes())
if err != nil {
*is_null = 0
errString := errStringPrefix + err.Error()
*length = C.ulong(len([]byte(errString)))
errS := C.CString(errString)
init.ptr = errS
return errS
} rb, err = json.Marshal(data)
if err != nil {
*is_null = 0
errString := errStringPrefix + " Marshal error: " + err.Error()
*length = C.ulong(len([]byte(errString)))
errS := C.CString(errString)
init.ptr = errS
return errS
} *is_null = 0
*length = C.ulong(len(rb))
s := C.CString(string(rb))
init.ptr = s
return s
}

生成dll

windows运行build.bat,生成libquestblob.dll, 拷贝到mysql的plugin目录下。

使用

CREATE FUNCTION questblob RETURNS STRING SONAME 'libquestblob.dll'; #此语句执行一次即可
SELECT roleid, cast(questblob(t_quest_db.data, 't_quest_db.data') AS CHAR) FROM t_quest_db;

最新文章

  1. python gutter area / 设置断点、行号右边代码左边的空白栏
  2. html页面清除缓存
  3. C#读取Word模板替换相应的字符串(标签)生成新的Word
  4. Linux环境下安装Redis步骤即问题解决
  5. Angularjs快速入门(二)
  6. 忽略node.js服务中favicon.icon的请求
  7. java 流输出的一些问题
  8. [USACO 07NOV]Cow Relays
  9. python正则表达式写[强口令检测]
  10. jquery的show()和hide()方法
  11. 【Android】LMK 工作机制
  12. Spring中四种实例化bean的方式
  13. spring与mybatis四种整合方法
  14. Codeforces 600 E - Lomsat gelral
  15. SQL中的where条件,在数据库中提取与应用浅析
  16. 如何获取当前应用程序所用的OpenGL ES的版本
  17. 单项目实现vendor分离编译,增加编译效率(vue-cli)
  18. CentOS和Ubuntu安装软件命令对比(区别)
  19. 微信小程序-js为object添加属性
  20. java中内存泄露和内存溢出

热门文章

  1. Zeal(文档)安装使用
  2. 调度《Taint(污点) 和 Toleration(容忍)》
  3. [Luogu P3953] 逛公园 (最短路+拓扑排序+DP)
  4. js &amp;&amp;与||的妙用
  5. JavaScript的原型对象prototype、原型属性__proto__、原型链和constructor
  6. !!vue-style-loader!css-loader?
  7. sed1
  8. linux 命令之file
  9. 面试题:了解MySQL的Flush-List吗?顺便说一下脏页的落盘机制!(文末送书)
  10. git , repo out off memory