golang实现mysql udf
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的更多相关文章
- MySql UDF 调用外部程序和系统命令
1.mysql利用mysqludf的一个mysql插件可以实现调用外部程序和系统命令 下载lib_mysqludf_sys程序:https://github.com/mysqludf/lib_mysq ...
- Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步
[TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...
- [转帖]golang操作mysql使用总结
golang操作mysql使用总结 https://www.cnblogs.com/hanyouchun/ 讲解的很详细~ 前言 Golang 提供了database/sql包用于对SQL数据库的访问 ...
- golang 的 mysql 操作
goLang的mysql操作,大致可分为三个步骤: 1.下载mysql驱动:go get github.com/go-sql-driver/mysql 2.建立连接:sql.Open("my ...
- 【API】Mysql UDF BackDoor
1.MySQL UDF是什么 UDF是Mysql提供给用户实现自己功能的一个接口,为了使UDF机制起作用,函数必须用C或C ++编写,并且操作系统必须支持动态加载.这篇文章主要介绍UDF开发和利用的方 ...
- golang操作mysql数据库
golang操作mysql数据库 代码: mysql的增.删.改.查 package main import ( "database/sql" "fmt" &q ...
- mysql udf提权实战测试
根据前天对大牛们的资料学习,进行一次mysql udf提权测试. 测试环境: 受害者系统:centos 7.7 ,docker部署mysql5.6.46, IP:192.168.226.128 攻击者 ...
- Golang操作MySQL的正确姿势
封装原因: 查看了很多网上提供的ORM类型的数据库操作,觉得比较麻烦,需要提前配置很多的表结构体,然后才能使用,对于数据表很多的项目就配置起来就比较麻烦,所以对golang的mysql包进行了外层包装 ...
- 使用golang理解mysql的两阶段提交
使用golang理解mysql的两阶段提交 文章源于一个问题:如果我们现在有两个mysql实例,在我们要尽量简单地完成分布式事务,怎么处理? 场景重现 比如我们现在有两个数据库,mysql3306和m ...
随机推荐
- 一篇搞定Java集合类原理
Java集合类实现原理 1.Iterable接口 定义了迭代集合的迭代方法 iterator() forEach() 对1.8的Lambda表达式提供了支持 2. Collection接口 定义了集合 ...
- [Luogu P4147] 玉蟾宫 (网格DP)
题面 传送门:https://www.luogu.org/problemnew/show/P4147 Solution 裸的求极大子矩阵 感谢wzj dalao的教学 首先,有一个很显然但很重要的结论 ...
- python开发基础(二)运算符以及数据类型之list(列表)
# encoding: utf-8 # module builtins # from (built-in) # by generator 1.147 """ Built- ...
- [游记] 2020ZJOI 爆零记
DAY 0 凌晨一点打完CF,身心俱疲,由于头痛和困意原本可做的E没做出来,懊悔地睡下,竟然又过了一个多小时才睡着? 早上醒来,感觉身体轻飘飘的,("我真的睡过觉了吗")--听说打 ...
- C# 8: 可变结构体中的只读实例成员
在之前的文章中我们介绍了 C# 中的 只读结构体(readonly struct)[1] 和与其紧密相关的 in 参数[2]. 今天我们来讨论一下从 C# 8 开始引入的一个特性:可变结构体中的只读实 ...
- 回顾MySql的一些基本的增删改查
---恢复内容开始--- 回顾数据库的一些简单的增删查改的操作语法与注意点,来自菜鸟教程https://www.runoob.com/mysql/mysql-tutorial.html 关于数据库的操 ...
- Android基础——项目的文件结构(一)
Android基础--项目的文件结构(一) Android视图与Project视图对比 [注]此项目文件结构仅限于Android Studio下的Android项目!!! 在Android Studi ...
- cmd的基本命令
使用方法Windows键+R键输入cmd回车 Windows 系统常用小工具 工具名称 cmd 命令 含义 计算器 calc 与 "工具名称" 相同 记事本 notepad 与 & ...
- 八位“Booth二位乘算法”乘法器
目录 八位"Booth二位乘算法"乘法器 原理 补码乘法器 Booth一位乘 Booth二位乘 设计思路 减法变加法 vivado特性 设计文件 综合电路 测试文件 仿真波形 八位 ...
- fork系统调用关于如何让子进程先运行{wait(),waitpid(),sleep()}
在父进程里面调用wait()和waitpid()可以确保子进程先运行,因为当子进程运行完后会变成僵尸进程,此时会发送一个信号给父进程,父进程接受到信号才会运行. 有人或许会问如果在父进程调用wait之 ...