Mysql UDF 작성
1) 공유 라이브러리 파일 작성
윈도우는 lib, linux/unix 계열은 so 파일을 만들어야 한다.
CMake 를 이용하는게 더 간편하기에 자주 사용하는 명령어에 대해서만 간략하게 정리한다.
# cmake 최소 요구 버전 - 이하일시 에러 발생 CMAKE_MINIMUM_REQUIRED (VERSION 3.5) # CMAKE_PROJECT_NAME 설정 PROJECT(name) # project 이름을 콘솔에 출력 MESSAGE(${CMAKE_PROJECT_NAME}) # 전처리기 매크로 추가 ADD_DEFINITIONS(-fPIC) # 바이너리 생성 ADD_EXECUTABLE(app foo.c bar.c) # 라이브러리 생성 ADD_LIBRARY(app SHARED foo.c bar.c ) # 각 소스 파일에서 #include 구문으로 포함시킨 헤더 파일을 찾을 디렉토리 추가 INCLUDE_DIRECTORIES(include /usr/local/include) # 링크 과정에서 필요한 라이브러리 디렉토리 목록 추가 LINK_DIRECTORIES(lib /usr/local/lib) # Target에 포함된 소스 파일에서 #include 구문에 포함된 헤더 파일 찾을 디렉토리 추가 TARGET_INCLUDE_DIRECTORIES(include /usr/local/include)
더 다양한 명렁에 대한 정리는 다음 블로그를 참조하면 된다.
CMake 명령어 정리
CMake Tutorial
2) UDF 작성
위에서 CMAKE를 작성하는 법에 대해 알아보았다. 이제 공유 라이브러리를 만들기 위해 필요한 소스 파일을 작성해야 한다.
Mysql UDF 는 2가지 종류로 작성할 수 있다.
- xxx_init() -> xxx() -> xxx_deinit()
- xxx_init() -> xxx_clear() -> xxx_add() -> xxx() -> xxx_deinit()
xxx()는 본문 함수이고 UDF를 추가하기 위해서는 위의 형식을 지키며 함수를 추가해줘야 한다. 첫번째 형식이 가장 일반적인 경우이다.
두번째 형식은 Aggregate UDF라고 Group으로 xxx() 함수를 적용시킬 수 있는 경우이다.
ex) select sum(cost) from table group by dept;
Header 추가
먼저 각 인자들의 Type 을 위해 mysql 헤더를 추가한다.
#include <mysql.h>
init, deinit
기본적으로 init, deinit 과정이 있으며 각각 다음과 같은 역할을 한다.
init
- set default properties
- validate arguments
- allocate resources
- signal an error
deinit
- de-allocate any resources
my_bool xxx_init(UDF_INIT * initid, UDF_ARGS * args, char * message)
void xxx_deinit(UDF_INIT * initid);
본문 함수 작성
xxx() 는 본문함수이며 return 값에 따라 2가지 타입으로 선언할 수 있다.
// Integer Type long long xxx(UDF_INIT *initid, UDF_ARGS *args, my_bool *is_null, my_bool *error); // Real type double xxx(UDF_INIT *initid, UDF_ARGS *args, my_bool *is_null, my_bool *error); // String type, DECIMCAL type도 동일하게 작성 char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error);
string return value에 대해서만 더 자세하게 적도록 한다.
char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error) { // xxx(arg1, arg2) 입력 파라미터 값을 받을 수 있음. char arg1 = args->args[0]; char arg2 = args->args[1]; // 로직 작성 char *value = fn(); // 지역변수를 return하게 되면 안된다. result에 담아서 보내야 한다. strcpy(result, value); return result; }
다음 링크를 참고하면 Mysql에서는 xxx() 함수에 대해 result라는 버퍼를 제공하는데(최적화 이유) 255 크기로 제한이 되어 있다. -- 운영체제와 mysql 메모리 관리 방식은 따로 공부를 해봐야겠다.
mysql udf return values
만약 255 이상의 데이터를 보내고 싶다면 어떻게 해야 할까? UDF_INIT * initid 인자를 이용해야 한다.
UDF_INIT 구조체는 result에 대한 정보를 정의할 수가 있다.
mysql UDF_INIT Ref
만약 xxx_init()에서 mysql에서 제공하는 buffer(result) 를 이용하지 않고 새로 만들었다면 아래와 같이 사용가능하다.
xxx_init(..) { char * arr = malloc(1024); initid->ptr = arr; initid->max_length = 1024; } char * xxx(UDF_INIT *initid, UDF_ARGS * args, char * result, unsigned long length, my_bool *is_null, my_bool *error) { char * buffer = initid->ptr; return buffer; } xxx_deinit(..) { char * arr = initid->ptr; if(arr) { free(arr); } }
3) Mysql 에 UDF 추가
linux 를 기준으로 설명하면 /usr/local/mysql/lib/plugin 디렉토리가 존재한다.
생성한 so 파일을 해당 디렉토리에 넣어준다.
다음 명령을 통해 udf 를 추가할 수 있다.
create function func1 returns string soname 'lib_mysqludf.so'; create function func2 returns integer soname 'lib_mysqludf.so';
정리
CMake부터 시작하여 UDF를 작성하여 mysql에 추가해보는 시간을 가졌다. 실습을 해보면서 링크 및 컴파일에 대한 개념이 부족함을 알게 되었고, Mysql과 운영체제에서의 메모리 관리 방식에 대해서도 공부를 해봐야 겠다는 생각이 들었다.
Aggregate UDF 에 대해서는 다른 블로그를 참고하면서 직접 구현해보면 좋을 것 같다. clear를 기준으로 group이 나뉜다고 생각하면 쉽다.