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;

golang实现mysql udf的更多相关文章

  1. MySql UDF 调用外部程序和系统命令

    1.mysql利用mysqludf的一个mysql插件可以实现调用外部程序和系统命令 下载lib_mysqludf_sys程序:https://github.com/mysqludf/lib_mysq ...

  2. Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步

    [TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...

  3. [转帖]golang操作mysql使用总结

    golang操作mysql使用总结 https://www.cnblogs.com/hanyouchun/ 讲解的很详细~ 前言 Golang 提供了database/sql包用于对SQL数据库的访问 ...

  4. golang 的 mysql 操作

    goLang的mysql操作,大致可分为三个步骤: 1.下载mysql驱动:go get github.com/go-sql-driver/mysql 2.建立连接:sql.Open("my ...

  5. 【API】Mysql UDF BackDoor

    1.MySQL UDF是什么 UDF是Mysql提供给用户实现自己功能的一个接口,为了使UDF机制起作用,函数必须用C或C ++编写,并且操作系统必须支持动态加载.这篇文章主要介绍UDF开发和利用的方 ...

  6. golang操作mysql数据库

    golang操作mysql数据库 代码: mysql的增.删.改.查 package main import ( "database/sql" "fmt" &q ...

  7. mysql udf提权实战测试

    根据前天对大牛们的资料学习,进行一次mysql udf提权测试. 测试环境: 受害者系统:centos 7.7 ,docker部署mysql5.6.46, IP:192.168.226.128 攻击者 ...

  8. Golang操作MySQL的正确姿势

    封装原因: 查看了很多网上提供的ORM类型的数据库操作,觉得比较麻烦,需要提前配置很多的表结构体,然后才能使用,对于数据表很多的项目就配置起来就比较麻烦,所以对golang的mysql包进行了外层包装 ...

  9. 使用golang理解mysql的两阶段提交

    使用golang理解mysql的两阶段提交 文章源于一个问题:如果我们现在有两个mysql实例,在我们要尽量简单地完成分布式事务,怎么处理? 场景重现 比如我们现在有两个数据库,mysql3306和m ...

随机推荐

  1. tp3.2,php5使用PHPExcel将数据导出至Excel

    安装类库 从GitHub上下载PHPExcel类库 地址:https://github.com/PHPOffice/PHPExcel public function checkexcel($id){ ...

  2. 11 Servlet_03 关键字总结 资源跳转:重定项 键值对 继承类 do-get do-post Request域 Context域 =空针解决方法

    知识点总结: 1.java里的关键字: byte short int long  数据类型 (正整数)char  数据类型(单字符型)boolesn ture false  布尔类型float dou ...

  3. DTU是什么 常见的DTU有哪些

    DTU也叫数据传输终端,它的主要功能是把远端设备的数据通过无线的方式传送回后台中心,想要完成数据的传输就需要建立一套完整的数据传输系统.DTU是一种现代物联网行业广泛使用的无线数据终端,利用公用运营商 ...

  4. PuTTY SSH 使用证书免密码登录

    1.用PuTTY SSH 密钥生成工具puttygen.exe生成密钥.生成的密钥类型和位数按照默认的就OK,SSH-2 RSA,1024位生成密钥时你需要在空白区域移动鼠标,以便产生随机数据点击保存 ...

  5. [Luogu P4124] [CQOI2016]手机号码 (数位DP)

    题面 传送门:洛咕 Solution 感谢神仙@lizbaka的教学 这题是数位DP的非常非常模板的题目,只是状态有点多 . 这题我使用记忆化搜索实现的 中国有句古话说的好,有多少个要求就设多少个状态 ...

  6. Dapr Golang HTTP 调用

    Dapr Golang HTTP 调用 版本介绍 Go 版本:1.15 Dapr Go SKD 版本:0.11.1 工程结构 从上图可知,新建 3 个 Go 启动项目,cmd 为启动项目目录,其中 c ...

  7. idea 远程debug springboot

    idea 远程debug springboot 1.新建一个springboot工程. 新建一个controller接口 @RestController @RequestMapping public ...

  8. The Product-Minded Software Engineer

    转自The Product-Minded Software Engineer Product-minded engineers are developers with lots of interest ...

  9. 3.2 表 ADT -3.3 Java Collection API 中的表

    3.2 表 ADT 处理形如 A0, A1, A2, --, AN-1 的一般的表.我们称这个表大小为N.将大小为0的特殊表称为空表 对于除空表以外的任何表,称 Ai-1 前驱 Ai,Ai 后继 Ai ...

  10. 如何让Visual Studio 2019更好用(VS2019配置指南)

    今天电脑没带,借用外面的电脑配环境来用.刚下载完的VS是这样的: UI挺好看的,但代码窗口看起来就和上个世纪的VC6没什么区别,快捷键用起来也不顺手.(2333) 接下来,我们将一步步优化编写环境,让 ...