Как скомпилировать функцию определенной пользователем в SQLite
-
08-10-2019 - |
Вопрос
У меня есть пример функции C, которую я хотел бы прикрепить к базе данных temp.sqlite
(Это от книги O'Reilly, поэтому я знаю, что это работает). Я прочитал раздел в книге и SQLite.org, но они придумывают, что я знаю, как и где и где компилировать эту вещь с правильными настройками. Я на i Mac (с Xcode) или Ubuntu.
Я знаю достаточно C, чтобы изменить код, чтобы сделать то, что я хочу, но я понятия не имею, что делать это из моей базы данных temp.sqlite
.
Спасибо за вашу помощь! Я провалил на это!
ОБНОВЛЕНИЕ: еще несколько часов, и я сбрасывался вместе достаточно данных из заброшенных веб-страниц, чтобы создать команду компиляции и генерировать ошибку:
richard$ gcc -o wtavg wtavg.c -Wall -W -O2 -L/usr/local/lib -lsqlite3
wtavg.c: In function ‘wtavg_init’:
wtavg.c:63: warning: unused parameter ‘error’
Undefined symbols:
"_main", referenced from:
start in crt1.10.6.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
FWIW, вот wtavg.c, который прямой с сайта O'Reilly, предусмотренный в моей книге:
/* wtavg.c */
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1;
#include <stdlib.h>
typedef struct wt_avg_state_s {
double total_data; /* sum of (data * weight) values */
double total_wt; /* sum of weight values */
} wt_avg_state;
static void wt_avg_step( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
double row_wt = 1.0;
int type;
wt_avg_state *st = (wt_avg_state*)sqlite3_aggregate_context( ctx,
sizeof( wt_avg_state ) );
if ( st == NULL ) {
sqlite3_result_error_nomem( ctx );
return;
}
/* Extract weight, if we have a weight and it looks like a number */
if ( num_values == 2 ) {
type = sqlite3_value_numeric_type( values[1] );
if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
row_wt = sqlite3_value_double( values[1] );
}
}
/* Extract data, if we were given something that looks like a number. */
type = sqlite3_value_numeric_type( values[0] );
if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
st->total_data += row_wt * sqlite3_value_double( values[0] );
st->total_wt += row_wt;
}
}
static void wt_avg_final( sqlite3_context *ctx )
{
double result = 0.0;
wt_avg_state *st = (wt_avg_state*)sqlite3_aggregate_context( ctx,
sizeof( wt_avg_state ) );
if ( st == NULL ) {
sqlite3_result_error_nomem( ctx );
return;
}
if ( st->total_wt != 0.0 ) {
result = st->total_data / st->total_wt;
}
sqlite3_result_double( ctx, result );
}
int wtavg_init( sqlite3 *db, char **error, const sqlite3_api_routines *api )
{
SQLITE_EXTENSION_INIT2(api);
sqlite3_create_function( db, "wtavg", 1, SQLITE_UTF8,
NULL, NULL, wt_avg_step, wt_avg_final );
sqlite3_create_function( db, "wtavg", 2, SQLITE_UTF8,
NULL, NULL, wt_avg_step, wt_avg_final );
return SQLITE_OK;
}
Решение
Функция определенной пользователем должна быть скомпилирована в виде общего файла библиотеки:
gcc -shared -fPIC -o wtavg.so wtavg.c -lsqlite3
Эта общая библиотека может быть загружена с помощью оператора SQLite:
SELECT load_extension('/path/to/wt_avg.so', 'wtavg_init');
К сожалению, версия SQLite3, предоставленной Apple, не поддерживает погрузку общих библиотек. Вместо этого вы можете использовать SQLite от MacPorts. MacPorts Программы, связывающие против SQLite, также должны иметь возможность нагрузки пользовательских функций таким образом.
При использовании SQLite внутри другой программы, однако, механизм загрузки расширения может быть отключен для соображений безопасности. В Python, например, вы должны позвонить con.enable_load_extension(True)
Чтобы включить это.