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 ...
随机推荐
- 使用 Filebeat 对多行日志进行处理(multiline)
Filebeat 收集日志的过程中,默认是按行收取的,也就是每一行都会默认是一个单独的事件并添加时间戳.但是在收集一些特殊日志的时候,往往一个事件包含有多行,例如 Java 的堆栈跟踪日志: 20-0 ...
- js-同步和异步
js异步 学习js开发,无论是前端开发还是node.js,都避免不了要接触异步编程这个问题,就和其它大多数以多线程同步为主的编程语言不同,js的主要设计是单线程异步模型.正因为js天生的与众不同,才使 ...
- 浅析I/O模型-select、poll、epoll
I/O流 概念 (1)c++中将数据的输入输出称之为流(stream),在c++中,流被定义为类,成为流类(stream class),其定义的对象为流对象. (2)文件,套接字(socket),管道 ...
- xshell连接windows10子系统ubuntu
修改端口 cd /etc/ssh#备份sudo cp sshd_config sshd_config.baksudo vim sshd_config修改sshd_config Port 2233 #修 ...
- Antisymmetry
题意描述 Antisymmetry 求给定的字符串的子串集合中为"反对串"的个数. 反对串的定义为,将这个字符串 \(0\) 和 \(1\) 取反后,再将整个串反过来和原串一样,就 ...
- 模板——Fhq_treap
$Fhq$ $treap$ #include <bits/stdc++.h> using namespace std; const int MAXN=100100; int n,root, ...
- mysql千万级大数据SQL查询优化30条经验(Mysql索引优化注意)
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- 1.python的函数参数传递
1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun ...
- 剑指Offer-Python(6-10)
6.旋转数组的最小数字 class Solution: def minNumberInRotateArray(self, rotateArray): l = len(rotateArray) if l ...
- 自定义泛型方法, 三级排序, low版,待升级
package com.jd.dashboard.util; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken ...