Unreal에 SQLite 붙이기 – Extensions
개요
이전에 플러그인을 통해서 SQLite 를 잘 쓰고 있었습니다. 그러던도중 이번에는 SQL 을 작성하는데 특정 함수들이 없다는 에러가 떠서 해결해보고자합니다.
이 에러는 라이브러리에서 acos 및 기타 함수를 지원하지 않는다는 것을 의미하고, 이 말은 사용하려면 직접 넣어주어야한다는 뜻이죠. 찾아보니 SQL에서도 extension 을 통해 사용자가 직접 함수를 추가할 수 있게 구성이 되어있었습니다.
다만 문제점은 정적 라이브러리를 빌드해서 해당 파일을 읽어오는 식이었죠.
그래서 저는 함수 호출만으로도 extension 을 추가할 수 있게 수정을 하고 함수를 추가해주었습니다.
플러그인 업데이트
먼저 함수를 추가해주었습니다. 기존에 존재하던 sqlite3_load_extension 함수를 수정해서 동적 라이브러리 로드 부분을 지우고 바로 함수 호출 부분으로 이동하도록 구현했습니다.
/* sqlite3.c */
SQLITE_API int sqlite3_load_extension_manually(sqlite3 *db, sqlite3_loadext_entry fn, char **pzErrMsg)
{
void *handle;
char *zErrmsg = 0;
void **aHandle;
int rc;
rc = fn(db, &zErrmsg, &sqlite3Apis);
if( rc ){
if( rc==SQLITE_OK_LOAD_PERMANENTLY ) return SQLITE_OK;
if( pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
}
sqlite3_free(zErrmsg);
return SQLITE_ERROR;
}
/* Append the new shared library handle to the db->aExtension array. */
aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1));
if( aHandle==0 ){
return SQLITE_NOMEM_BKPT;
}
if( db->nExtension>0 ){
memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension);
}
sqlite3DbFree(db, db->aExtension);
db->aExtension = aHandle;
db->aExtension[db->nExtension++] = handle;
return SQLITE_OK;
}
이렇게 구성을 했지만 이 함수를 바로 제 프로젝트에서 불러올 순 없었습니다. SQLITE_API 와 플러그인의 CISQLITE3_API 가 달라서 외부에서 접근이 안됬죠. 그래서 USQLiteDatabase를 한번 거쳐 들어가도록 구현했습니다.
/* SQLiteDatabase.cpp */
FString USQLiteDatabase::RegisterExtension(const FString &Target, sqlite3_loadext_entry fn) {
if (!SQLite3Databases.Contains(Target)) {
return FString::Printf(TEXT("Unable to find Database By '%s'"), *Target);
}
auto Db = SQLite3Databases[Target];
char pzErrMsg[1024];
sqlite3_load_extension_manually(Db, fn, reinterpret_cast<char**>(&pzErrMsg));
return pzErrMsg;
}
이후 제 프로젝트 폴더에 각종 함수를 추가하는 c 파일을 추가하고 등록시켜 주니 문제 없이 SQL Command가 동작했습니다.
/* sql_trig.c */
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1;
#include <stdlib.h>
/* this bit is required to get M_PI out of MS headers */
#if defined( _WIN32 )
#define _USE_MATH_DEFINES
#endif /* _WIN32 */
#include <math.h>
#define RADIANS(d) (( d / 180.0 ) * M_PI)
static void sql_trig_sin( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
double a = RADIANS(sqlite3_value_double( values[0] ));
sqlite3_result_double( ctx, sin( a ) );
}
static void sql_trig_cos( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
double a = RADIANS(sqlite3_value_double( values[0] ));
sqlite3_result_double( ctx, cos( a ) );
}
static void sql_trig_acos( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
double a = sqlite3_value_double( values[0] );
sqlite3_result_double( ctx, acos( a ) );
}
static void sql_trig_radians( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
sqlite3_result_double( ctx, RADIANS(sqlite3_value_double( values[0] ) ));
}
NOVAAIR_API int sqlite3_extension_init( sqlite3 *db, char **error, const sqlite3_api_routines *api )
{
SQLITE_EXTENSION_INIT2(api);
sqlite3_create_function( db, "sin",1,
SQLITE_UTF8, NULL, &sql_trig_sin, NULL, NULL );
sqlite3_create_function( db, "cos",1,
SQLITE_UTF8, NULL, &sql_trig_cos, NULL, NULL );
sqlite3_create_function( db, "acos",1,
SQLITE_UTF8, NULL, &sql_trig_acos, NULL, NULL );
sqlite3_create_function( db, "radians",1,
SQLITE_UTF8, NULL, &sql_trig_radians, NULL, NULL );
return SQLITE_OK;
}
bool StorageManager::Initialize() {
...
auto Error = USQLiteDatabase::RegisterExtension(DB_NAME, sqlite3_extension_init);
if (!Error.IsEmpty()) {
UE_LOG(LogDatabase, Error, TEXT("Failed to register extension: %s"), *Error);
}
...
}
Leave a Reply
Want to join the discussion?Feel free to contribute!