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)
더 다양한 명렁에 대한 정리는 다음 블로그를 참조하면 된다.
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 메모리 관리 방식은 따로 공부를 해봐야겠다.
만약 255 이상의 데이터를 보내고 싶다면 어떻게 해야 할까? UDF_INIT * initid 인자를 이용해야 한다.
UDF_INIT 구조체는 result에 대한 정보를 정의할 수가 있다.
만약 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이 나뉜다고 생각하면 쉽다.
'IT > BackEnd' 카테고리의 다른 글
Crawling을 위한 Selenium, Chrome Driver 설치 (0) | 2018.07.15 |
---|---|
SSL 인증서 발급 및 Nginx 적용. 그리고 체인 이슈 해결하기 (1) | 2018.02.28 |
Atom Remote-FTP package 소개 (0) | 2017.11.02 |
JWT (0) | 2017.09.27 |