Come compilare una funzione definita dall'utente in SQLite
-
08-10-2019 - |
Domanda
Ho un esempio di funzione C che vorrei collegare a un database di temp.sqlite
(è da un libro di O'Reilly, quindi so che funziona). Ho letto la sezione nel libro e sqlite.org, ma presumo via che io so come e dove compilare questa cosa con le impostazioni corrette. Sono su entrambi i Mac (con XCode) o Ubuntu.
Lo so abbastanza C per alterare il codice per fare quello che voglio, ma non ho idea di cosa fare chiamare dal mio temp.sqlite
database.
Grazie per il vostro aiuto! Sono stato a sfornare su questo!
Update: Ancora qualche ore in e ho scartato abbastanza roba insieme da pagine web abbandonate per creare un comando di compilazione e generare un errore:
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, di qui wtavg.c, che è direttamente dal sito di O'Reilly fornita nel mio libro:
/* 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;
}
Soluzione
La funzione definita dall'utente deve essere compilato come file di libreria condivisa:
gcc -shared -fPIC -o wtavg.so wtavg.c -lsqlite3
Questa libreria condivisa può essere caricata utilizzando una dichiarazione SQLite:
SELECT load_extension('/path/to/wt_avg.so', 'wtavg_init');
Purtroppo la versione di sqlite3 fornito da Apple non supporta il caricamento delle librerie condivise. È possibile utilizzare SQLite da MacPorts invece. MacPorts programmi che collegano contro SQLite dovrebbero anche avere la capacità di funzioni definite dall'utente carico in questo modo.
Quando si utilizza SQLite all'interno di un altro programma invece il meccanismo di estensione di carico può essere disattivato per motivi di sicurezza. In Python per esempio si deve chiamare con.enable_load_extension(True)
per attivarlo.