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. Mybatis---05Mybatis配置文件浅析(三)

    1.objectFactory:(对象工厂)MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成. 默认的对象工厂需要做的仅仅是实例化目标类,要么 ...

  2. MongoDB Java连接---MongoDB基础用法(四)

    MongoDB 连接 标准 URI 连接语法: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN ...

  3. 【CF1443F】Identify the Operations 题解

    原题链接 题意简介 建议去原题看.这题意我表达不清楚. 大概就是给你一个 n 的排列,现在要求你进行 m 次操作. 每次操作,你会在现有的排列中删去一个数,然后选择其左边或右边的一个与之相邻的数加入 ...

  4. XJOI 夏令营501-511NOIP训练14 砍树(2)

    小A是小B家的园丁.小B的家里有n棵树,第i棵树的横坐标为i.一天,小B交给小A一个任务,让他降低自己家中的某些树木的高度.这个任务对小A来说十分简单,因为他有一把极其锋利的斧头和一门独门砍树秘籍,能 ...

  5. 7. 组合你的UI

    1. UI布局关键概念 一个组合应用UI的根节点被称作Shell,一般只有一个Shell.Shell作为应用的主页,包含一个或者多个域.域是内容占位符,可以包含一个或者多个View.有很多控件可以作为 ...

  6. ansible快速部署cassandra3集群

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. python菜鸟教程学习3:基础语法

    菜鸟教程对应网址:https://www.runoob.com/python3/python3-basic-syntax.html 编码:python3用UTF-8编码,所有字符串都是unicode字 ...

  8. gdb 符号表 &信息 &工具

    查看二进制文件的编译器版本 strings  info.o |grep GCCGCC: (crosstool-NG linaro-1.13.1-2012.02-20120222 - Linaro GC ...

  9. nginx&http 第二章 ngx 事件event配置等初始化

    event事件模块,配置分为两层:ngx_events_module 事件模块 和 ngx_event_core_module 事件核心模块.ngx_events_module:模块类型NGX_COR ...

  10. ceph-deploy 部署加密osd异常的问题

    问题解析 问题 journal encryption with dmcrypt (Reno Rainz) 问题原文: I'm trying to setup a cluster with encryp ...