Created
December 11, 2010 17:09
-
-
Save alexeyr/737473 to your computer and use it in GitHub Desktop.
sqlite3_drv.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
F:\MyProgramming\erlang-sqlite3\c_src>cl -D__WIN32__ -If:/PROGLA~1/erl/lib/erl_interface-3.7.2/inclu | |
de -If:/PROGLA~1/erl/erts-5.8.2/include -IF:\MyProgramming\sqlite-amalgamation\ *.c | |
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86 | |
Copyright (C) Microsoft Corporation. All rights reserved. | |
sqlite3_drv.c | |
sqlite3_drv.c(40) : error C2059: syntax error : 'type' | |
sqlite3_drv.c(57) : error C2143: syntax error : missing ';' before 'const' | |
sqlite3_drv.c(58) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(61) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(61) : warning C4047: '=' : 'int' differs in levels of indirection from 'char [11]' | |
sqlite3_drv.c(63) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(67) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(67) : warning C4047: 'function' : 'const char *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(67) : warning C4024: 'sqlite3_open' : different types for formal and actual parameter 1 | |
sqlite3_drv.c(72) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(74) : error C2065: 'db_name' : undeclared identifier | |
sqlite3_drv.c(289) : warning C4047: 'function' : 'void (__cdecl *)(void *)' differs in levels of indirection from 'TWinDynDriverdriver_free (__cdecl **)' | |
sqlite3_drv.c(289) : warning C4024: 'sqlite3_bind_text' : different types for formal and actual parameter 5 | |
sqlite3_drv.c(295) : warning C4047: 'function' : 'void (__cdecl *)(void *)' differs in levels of indirection from 'TWinDynDriverdriver_free (__cdecl **)' | |
sqlite3_drv.c(295) : warning C4024: 'sqlite3_bind_text' : different types for formal and actual parameter 5 | |
sqlite3_drv.c(308) : warning C4047: 'function' : 'void (__cdecl *)(void *)' differs in levels of indirection from 'TWinDynDriverdriver_free (__cdecl **)' | |
sqlite3_drv.c(308) : warning C4024: 'sqlite3_bind_blob' : different types for formal and actual parameter 5 | |
sqlite3_drv.c(457) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(458) : error C2065: 'command' : undeclared identifier | |
sqlite3_drv.c(458) : warning C4022: 'ei_decode_binary' : pointer mismatch for actual parameter 3 | |
sqlite3_drv.c(460) : error C2065: 'command' : undeclared identifier | |
sqlite3_drv.c(460) : warning C4047: 'function' : 'const char *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(460) : warning C4024: 'sqlite3_prepare_v2' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(462) : error C2065: 'command' : undeclared identifier | |
sqlite3_drv.c(462) : warning C4022: 'function through pointer' : pointer mismatch for actual parameter 1 | |
sqlite3_drv.c(483) : warning C4047: 'function' : 'void (__cdecl *)(void *)' differs in levels of indirection from 'TWinDynDriverdriver_free (__cdecl **)' | |
sqlite3_drv.c(483) : warning C4024: 'free_ptr_list' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(529) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(531) : error C2065: 'base_term_count' : undeclared identifier | |
sqlite3_drv.c(537) : error C2065: 'base_term_count' : undeclared identifier | |
sqlite3_drv.c(538) : error C2065: 'base_term_count' : undeclared identifier | |
sqlite3_drv.c(540) : error C2065: 'base_term_count' : undeclared identifier | |
sqlite3_drv.c(541) : error C2065: 'base_term_count' : undeclared identifier | |
sqlite3_drv.c(931) : error C2275: 'ErlDrvTermData' : illegal use of this type as an expression | |
f:/PROGLA~1/erl/erts-5.8.2/include\erl_driver.h(185) : see declaration of 'ErlDrvTermData' | |
sqlite3_drv.c(931) : error C2146: syntax error : missing ';' before identifier 'spec' | |
sqlite3_drv.c(931) : error C2065: 'spec' : undeclared identifier | |
sqlite3_drv.c(931) : error C2059: syntax error : ']' | |
sqlite3_drv.c(936) : error C2065: 'spec' : undeclared identifier | |
sqlite3_drv.c(936) : warning C4047: 'function' : 'ErlDrvTermData *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(936) : warning C4024: 'function through pointer' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(936) : error C2065: 'spec' : undeclared identifier | |
sqlite3_drv.c(936) : error C2065: 'spec' : undeclared identifier | |
sqlite3_drv.c(936) : error C2109: subscript requires array or pointer type | |
sqlite3_drv.c(936) : error C2198: 'function through pointer' : too few arguments for call | |
sqlite3_drv.c(953) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(955) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(960) : error C2275: 'sqlite3_stmt' : illegal use of this type as an expression | |
F:\MyProgramming\sqlite-amalgamation\sqlite3.h(2427) : see declaration of 'sqlite3_stmt' | |
sqlite3_drv.c(960) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(960) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(962) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(962) : warning C4047: 'function' : 'sqlite3_stmt *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(962) : warning C4024: 'bind_parameters' : different types for formal and actual parameter 5 | |
sqlite3_drv.c(981) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(983) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(988) : error C2275: 'sqlite3_stmt' : illegal use of this type as an expression | |
F:\MyProgramming\sqlite-amalgamation\sqlite3.h(2427) : see declaration of 'sqlite3_stmt' | |
sqlite3_drv.c(988) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(988) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(989) : error C2275: 'ErlDrvTermData' : illegal use of this type as an expression | |
f:/PROGLA~1/erl/erts-5.8.2/include\erl_driver.h(185) : see declaration of 'ErlDrvTermData' | |
sqlite3_drv.c(989) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(994) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(994) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(994) : warning C4022: 'function through pointer' : pointer mismatch for actual parameter 1 | |
sqlite3_drv.c(994) : warning C4047: '=' : 'int' differs in levels of indirection from 'void *' | |
sqlite3_drv.c(996) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(996) : error C2109: subscript requires array or pointer type | |
sqlite3_drv.c(997) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(997) : error C2109: subscript requires array or pointer type | |
sqlite3_drv.c(999) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(1002) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1002) : warning C4047: 'function' : 'sqlite3_stmt *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(1002) : warning C4024: 'get_columns' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(1002) : error C2065: 'column_count' : undeclared identifier | |
sqlite3_drv.c(1002) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(1002) : warning C4047: 'function' : 'ErlDrvTermData **' differs in levels of indirection from 'int *' | |
sqlite3_drv.c(1002) : warning C4024: 'get_columns' : different types for formal and actual parameter 7 | |
sqlite3_drv.c(1004) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(1004) : error C2109: subscript requires array or pointer type | |
sqlite3_drv.c(1005) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(1005) : error C2109: subscript requires array or pointer type | |
sqlite3_drv.c(1007) : error C2065: 'dataset' : undeclared identifier | |
sqlite3_drv.c(1007) : warning C4047: 'function' : 'ErlDrvTermData *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(1007) : warning C4024: 'function through pointer' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(1023) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(1025) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1030) : error C2275: 'sqlite3_stmt' : illegal use of this type as an expression | |
F:\MyProgramming\sqlite-amalgamation\sqlite3.h(2427) : see declaration of 'sqlite3_stmt' | |
sqlite3_drv.c(1030) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1030) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1031) : error C2275: 'async_sqlite3_command' : illegal use of this type as an expression | |
f:\myprogramming\erlang-sqlite3\c_src\sqlite3_drv.h(71) : see declaration of 'async_sqlite3_command' | |
sqlite3_drv.c(1031) : error C2065: 'async_command' : undeclared identifier | |
sqlite3_drv.c(1031) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1031) : warning C4047: 'function' : 'sqlite3_stmt *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(1031) : warning C4024: 'make_async_command' : different types for formal and actual parameter 2 | |
sqlite3_drv.c(1035) : error C2065: 'async_command' : undeclared identifier | |
sqlite3_drv.c(1035) : warning C4022: 'function through pointer' : pointer mismatch for actual parameter 4 | |
sqlite3_drv.c(1037) : error C2065: 'async_command' : undeclared identifier | |
sqlite3_drv.c(1037) : warning C4022: 'sql_exec_async' : pointer mismatch for actual parameter 1 | |
sqlite3_drv.c(1038) : error C2065: 'async_command' : undeclared identifier | |
sqlite3_drv.c(1054) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(1056) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1062) : error C2275: 'sqlite3_stmt' : illegal use of this type as an expression | |
F:\MyProgramming\sqlite-amalgamation\sqlite3.h(2427) : see declaration of 'sqlite3_stmt' | |
sqlite3_drv.c(1062) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1062) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1063) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1063) : warning C4047: 'function' : 'sqlite3_stmt *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(1063) : warning C4024: 'sqlite3_reset' : different types for formal and actual parameter 1 | |
sqlite3_drv.c(1078) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(1080) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1085) : error C2275: 'sqlite3_stmt' : illegal use of this type as an expression | |
F:\MyProgramming\sqlite-amalgamation\sqlite3.h(2427) : see declaration of 'sqlite3_stmt' | |
sqlite3_drv.c(1085) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1085) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1086) : error C2065: 'statement' : undeclared identifier | |
sqlite3_drv.c(1086) : warning C4047: 'function' : 'sqlite3_stmt *' differs in levels of indirection from 'int' | |
sqlite3_drv.c(1086) : warning C4024: 'sqlite3_clear_bindings' : different types for formal and actual parameter 1 | |
sqlite3_drv.c(1101) : error C2143: syntax error : missing ';' before 'type' | |
sqlite3_drv.c(1103) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1109) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1110) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1114) : error C2065: 'prepared_index' : undeclared identifier | |
sqlite3_drv.c(1155) : error C2059: syntax error : 'type' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "sqlite3_drv.h" | |
// MSVC needs "__inline" instead of "inline" in C-source files. | |
#if defined(_MSC_VER) | |
# define inline __inline | |
#endif | |
static ErlDrvEntry basic_driver_entry = { | |
NULL, /* init */ | |
start, /* startup (defined below) */ | |
stop, /* shutdown (defined below) */ | |
NULL, /* output */ | |
NULL, /* ready_input */ | |
NULL, /* ready_output */ | |
"sqlite3_drv", /* the name of the driver */ | |
NULL, /* finish */ | |
NULL, /* handle */ | |
control, /* control */ | |
NULL, /* timeout */ | |
NULL, /* outputv */ | |
ready_async, /* ready_async (defined below) */ | |
NULL, /* flush */ | |
NULL, /* call */ | |
NULL, /* event */ | |
ERL_DRV_EXTENDED_MARKER, /* ERL_DRV_EXTENDED_MARKER */ | |
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */ | |
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */ | |
ERL_DRV_FLAG_USE_PORT_LOCKING, /* ERL_DRV_FLAGs */ | |
NULL /* handle2 */, | |
NULL /* process_exit */, | |
NULL /* stop_select */ | |
}; | |
DRIVER_INIT(basic_driver) { | |
return &basic_driver_entry; | |
} | |
static inline ptr_list *add_to_ptr_list(ptr_list *list, void *value_ptr); | |
static inline void free_ptr_list(ptr_list *list, void(* free_head)(void *)); | |
static inline int max(int a, int b); | |
static inline int sql_is_insert(const char *sql); | |
// Driver Start | |
static ErlDrvData start(ErlDrvPort port, char* cmd) { | |
sqlite3_drv_t* retval = (sqlite3_drv_t*) driver_alloc(sizeof(sqlite3_drv_t)); | |
struct sqlite3 *db = NULL; | |
int status = 0; | |
retval->log = fopen(LOG_PATH, "a+"); | |
if (!retval->log) { | |
fprintf(stderr, "Can't create log file\n"); | |
} | |
fprintf(retval->log, | |
"--- Start erlang-sqlite3 driver\nCommand line: [%s]\n", cmd); | |
const char *db_name = strstr(cmd, " "); | |
if (!db_name) { | |
fprintf(retval->log, | |
"ERROR: DB name should be passed at command line\n"); | |
db_name = DB_PATH; | |
} else { | |
++db_name; // move to first character after ' ' | |
} | |
// Create and open the database | |
sqlite3_open(db_name, &db); | |
status = sqlite3_errcode(db); | |
if (status != SQLITE_OK) { | |
fprintf(retval->log, "ERROR: Unable to open file: %s because %s\n\n", | |
db_name, sqlite3_errmsg(db)); | |
} else { | |
fprintf(retval->log, "Opened file %s\n", db_name); | |
} | |
// Set the state for the driver | |
retval->port = port; | |
retval->db = db; | |
retval->key = 42; | |
// FIXME Any way to get canonical path to the DB? | |
// We need to ensure equal keys for different paths to the same file | |
retval->async_handle = 0; | |
retval->prepared_stmts = NULL; | |
retval->prepared_count = 0; | |
retval->prepared_alloc = 0; | |
retval->atom_blob = driver_mk_atom("blob"); | |
retval->atom_error = driver_mk_atom("error"); | |
retval->atom_columns = driver_mk_atom("columns"); | |
retval->atom_rows = driver_mk_atom("rows"); | |
retval->atom_null = driver_mk_atom("null"); | |
retval->atom_rowid = driver_mk_atom("rowid"); | |
retval->atom_ok = driver_mk_atom("ok"); | |
retval->atom_done = driver_mk_atom("done"); | |
retval->atom_unknown_cmd = driver_mk_atom("unknown_command"); | |
fflush(retval->log); | |
return (ErlDrvData) retval; | |
} | |
// Driver Stop | |
static void stop(ErlDrvData handle) { | |
sqlite3_drv_t* driver_data = (sqlite3_drv_t*) handle; | |
unsigned int i; | |
if (driver_data->prepared_stmts) { | |
for (i = 0; i < driver_data->prepared_count; i++) { | |
sqlite3_finalize(driver_data->prepared_stmts[i]); | |
} | |
driver_free(driver_data->prepared_stmts); | |
} | |
sqlite3_close(driver_data->db); | |
fclose(driver_data->log); | |
driver_data->log = NULL; | |
driver_free(driver_data); | |
} | |
// Handle input from Erlang VM | |
static int control( | |
ErlDrvData drv_data, unsigned int command, char *buf, | |
int len, char **rbuf, int rlen) { | |
sqlite3_drv_t* driver_data = (sqlite3_drv_t*) drv_data; | |
int i; | |
switch (command) { | |
case CMD_SQL_EXEC: | |
sql_exec(driver_data, buf, len); | |
break; | |
case CMD_SQL_BIND_AND_EXEC: | |
sql_bind_and_exec(driver_data, buf, len); | |
break; | |
case CMD_PREPARE: | |
prepare(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_BIND: | |
prepared_bind(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_STEP: | |
prepared_step(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_RESET: | |
prepared_reset(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_CLEAR_BINDINGS: | |
prepared_clear_bindings(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_FINALIZE: | |
prepared_finalize(driver_data, buf, len); | |
break; | |
case CMD_PREPARED_COLUMNS: | |
prepared_columns(driver_data, buf, len); | |
break; | |
default: | |
unknown(driver_data, buf, len); | |
} | |
return 0; | |
} | |
static inline int return_error( | |
sqlite3_drv_t *drv, int error_code, const char *error, | |
ErlDrvTermData **spec, int *term_count) { | |
*spec = (ErlDrvTermData *) driver_alloc(11 * sizeof(ErlDrvTermData)); | |
(*spec)[0] = ERL_DRV_PORT; | |
(*spec)[1] = driver_mk_port(drv->port); | |
(*spec)[2] = ERL_DRV_ATOM; | |
(*spec)[3] = drv->atom_error; | |
(*spec)[4] = ERL_DRV_INT; | |
(*spec)[5] = error_code; | |
(*spec)[6] = ERL_DRV_STRING; | |
(*spec)[7] = (ErlDrvTermData) error; | |
(*spec)[8] = strlen(error); | |
(*spec)[9] = ERL_DRV_TUPLE; | |
(*spec)[10] = 4; | |
*term_count = 11; | |
return 0; | |
} | |
static inline int output_error( | |
sqlite3_drv_t *drv, int error_code, const char *error) { | |
ErlDrvTermData *dataset; | |
int term_count; | |
return_error(drv, error_code, error, &dataset, &term_count); | |
driver_output_term(drv->port, dataset, term_count); | |
return 0; | |
} | |
static inline int output_db_error(sqlite3_drv_t *drv) { | |
return output_error(drv, sqlite3_errcode(drv->db), sqlite3_errmsg(drv->db)); | |
} | |
static inline int output_ok(sqlite3_drv_t *drv) { | |
// Return {Port, ok} | |
ErlDrvTermData spec[] = { | |
ERL_DRV_PORT, driver_mk_port(drv->port), | |
ERL_DRV_ATOM, drv->atom_ok, | |
ERL_DRV_TUPLE, 2 | |
}; | |
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0])); | |
} | |
static inline async_sqlite3_command *make_async_command( | |
sqlite3_drv_t *drv, sqlite3_stmt *statement) { | |
async_sqlite3_command *result = | |
(async_sqlite3_command *) driver_alloc(sizeof(async_sqlite3_command)); | |
memset(result, 0, sizeof(async_sqlite3_command)); | |
result->driver_data = drv; | |
result->statement = statement; | |
return result; | |
} | |
static inline int sql_exec_statement( | |
sqlite3_drv_t *drv, sqlite3_stmt *statement) { | |
async_sqlite3_command *async_command = make_async_command(drv, statement); | |
#ifdef DEBUG | |
fprintf(drv->log, "Driver async: %d %p\n", SQLITE_VERSION_NUMBER, async_command->statement); | |
fflush(drv->log); | |
#endif | |
if (sqlite3_threadsafe()) { | |
drv->async_handle = driver_async(drv->port, &drv->key, sql_exec_async, | |
async_command, sql_free_async); | |
} else { | |
sql_exec_async(async_command); | |
ready_async((ErlDrvData) drv, (ErlDrvThreadData) async_command); | |
} | |
return 0; | |
} | |
static int sql_exec(sqlite3_drv_t *drv, char *command, int command_size) { | |
int result; | |
char *rest = NULL; | |
sqlite3_stmt *statement; | |
#ifdef DEBUG | |
fprintf(drv->log, "Preexec: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
result = sqlite3_prepare_v2(drv->db, command, command_size, &statement, | |
(const char **) &rest); | |
if (result != SQLITE_OK) { | |
return output_db_error(drv); | |
} | |
return sql_exec_statement(drv, statement); | |
} | |
static inline int decode_and_bind_param( | |
sqlite3_drv_t *drv, char *buffer, int *p_index, | |
sqlite3_stmt *statement, int param_index, int *p_type, int *p_size) { | |
int result; | |
sqlite3_int64 int64_val; | |
double double_val; | |
char* char_buf_val; | |
long bin_size; | |
ei_get_type(buffer, p_index, p_type, p_size); | |
switch (*p_type) { | |
case ERL_SMALL_INTEGER_EXT: | |
case ERL_INTEGER_EXT: | |
case ERL_SMALL_BIG_EXT: | |
case ERL_LARGE_BIG_EXT: | |
ei_decode_longlong(buffer, p_index, &int64_val); | |
result = sqlite3_bind_int64(statement, param_index, int64_val); | |
break; | |
case ERL_FLOAT_EXT: | |
case NEW_FLOAT_EXT: // what's the difference? | |
ei_decode_double(buffer, p_index, &double_val); | |
result = sqlite3_bind_double(statement, param_index, double_val); | |
break; | |
case ERL_ATOM_EXT: | |
// include space for null separator | |
char_buf_val = driver_alloc((*p_size + 1) * sizeof(char)); | |
ei_decode_atom(buffer, p_index, char_buf_val); | |
if (strncmp(char_buf_val, "null", 5) == 0) { | |
result = sqlite3_bind_null(statement, param_index); | |
} | |
else { | |
output_error(drv, SQLITE_MISUSE, "Non-null atom as parameter"); | |
return 1; | |
} | |
break; | |
case ERL_STRING_EXT: | |
// include space for null separator | |
char_buf_val = driver_alloc((*p_size + 1) * sizeof(char)); | |
ei_decode_string(buffer, p_index, char_buf_val); | |
result = sqlite3_bind_text(statement, param_index, char_buf_val, *p_size, &driver_free); | |
break; | |
case ERL_BINARY_EXT: | |
char_buf_val = driver_alloc(*p_size * sizeof(char)); | |
ei_decode_binary(buffer, p_index, char_buf_val, &bin_size); | |
// assert(bin_size == *p_size) | |
result = sqlite3_bind_text(statement, param_index, char_buf_val, *p_size, &driver_free); | |
break; | |
case ERL_SMALL_TUPLE_EXT: | |
// assume this is {blob, Blob} | |
ei_get_type(buffer, p_index, p_type, p_size); | |
ei_decode_tuple_header(buffer, p_index, p_size); | |
assert (*p_size == 2); | |
ei_skip_term(buffer, p_index); // skipped the atom 'blob' | |
ei_get_type(buffer, p_index, p_type, p_size); | |
assert (*p_type == ERL_BINARY_EXT); | |
char_buf_val = driver_alloc(*p_size * sizeof(char)); | |
ei_decode_binary(buffer, p_index, char_buf_val, &bin_size); | |
// assert(bin_size == *p_size) | |
result = sqlite3_bind_blob(statement, param_index, char_buf_val, *p_size, &driver_free); | |
break; | |
default: | |
output_error(drv, SQLITE_MISUSE, "bad parameter type"); | |
return 1; | |
} | |
if (result != SQLITE_OK) { | |
output_db_error(drv); | |
return result; | |
} | |
return SQLITE_OK; | |
} | |
static int bind_parameters( | |
sqlite3_drv_t *drv, char *buffer, int buffer_size, int *p_index, | |
sqlite3_stmt *statement, int *p_type, int *p_size) { | |
// decoding parameters | |
int i, cur_list_size = -1, param_index = 1, param_indices_are_explicit = 0, result; | |
long param_index_long; | |
char param_name[MAXATOMLEN + 1]; // parameter names shouldn't be longer than 256! | |
while (*p_index < buffer_size) { | |
ei_decode_list_header(buffer, p_index, &cur_list_size); | |
for (i = 0; i < cur_list_size; i++) { | |
ei_get_type(buffer, p_index, p_type, p_size); | |
if (*p_type == ERL_SMALL_TUPLE_EXT) { | |
int old_index = *p_index; | |
// param with name or explicit index | |
param_indices_are_explicit = 1; | |
if (*p_size != 2) { | |
return output_error(drv, SQLITE_MISUSE, | |
"tuple should contain index or name, and value"); | |
} | |
ei_decode_tuple_header(buffer, p_index, p_size); | |
ei_get_type(buffer, p_index, p_type, p_size); | |
// first element of tuple is int (index), atom, or string (name) | |
switch (*p_type) { | |
case ERL_SMALL_INTEGER_EXT: | |
case ERL_INTEGER_EXT: | |
ei_decode_long(buffer, p_index, ¶m_index_long); | |
param_index = param_index_long; | |
break; | |
case ERL_ATOM_EXT: | |
ei_decode_atom(buffer, p_index, param_name); | |
// insert zero terminator | |
param_name[*p_size] = '\0'; | |
if (strncmp(param_name, "blob", 5) == 0) { | |
// this isn't really a parameter name! | |
*p_index = old_index; | |
param_indices_are_explicit = 0; | |
goto IMPLICIT_INDEX; // yuck | |
} | |
else { | |
param_index = sqlite3_bind_parameter_index(statement, param_name); | |
} | |
break; | |
case ERL_STRING_EXT: | |
if (*p_size >= MAXATOMLEN) { | |
return output_error(drv, SQLITE_TOOBIG, "parameter name too long"); | |
} | |
ei_decode_string(buffer, p_index, param_name); | |
// insert zero terminator | |
param_name[*p_size] = '\0'; | |
param_index = sqlite3_bind_parameter_index(statement, param_name); | |
break; | |
default: | |
return output_error( | |
drv, SQLITE_MISMATCH, | |
"parameter index must be given as integer, atom, or string"); | |
} | |
result = decode_and_bind_param( | |
drv, buffer, p_index, statement, param_index, p_type, p_size); | |
if (result != SQLITE_OK) { | |
return result; // error has already been output | |
} | |
} | |
else { | |
IMPLICIT_INDEX: | |
if (param_indices_are_explicit) { | |
return output_error( | |
drv, SQLITE_MISUSE, | |
"parameters without indices shouldn't follow indexed or named parameters"); | |
} | |
result = decode_and_bind_param( | |
drv, buffer, p_index, statement, param_index, p_type, p_size); | |
if (result != SQLITE_OK) { | |
return result; // error has already been output | |
} | |
++param_index; | |
} | |
} | |
} | |
return result; | |
} | |
static void get_columns( | |
sqlite3_drv_t *drv, sqlite3_stmt *statement, int column_count, int base, | |
int *p_term_count, int *p_term_allocated, ErlDrvTermData **p_dataset) { | |
int i; | |
*p_term_count += column_count * 3 + 3; | |
if (*p_term_count > *p_term_allocated) { | |
*p_term_allocated = max(*p_term_count, (*p_term_allocated)*2); | |
*p_dataset = driver_realloc(*p_dataset, sizeof(ErlDrvTermData) * *p_term_allocated); | |
} | |
for (i = 0; i < column_count; i++) { | |
char *column_name = (char *) sqlite3_column_name(statement, i); | |
#ifdef DEBUG | |
fprintf(drv->log, "Column: %s\n", column_name); | |
fflush(drv->log); | |
#endif | |
(*p_dataset)[base + (i * 3)] = ERL_DRV_STRING; | |
(*p_dataset)[base + (i * 3) + 1] = (ErlDrvTermData) column_name; | |
(*p_dataset)[base + (i * 3) + 2] = strlen(column_name); | |
} | |
(*p_dataset)[base + column_count * 3 + 0] = ERL_DRV_NIL; | |
(*p_dataset)[base + column_count * 3 + 1] = ERL_DRV_LIST; | |
(*p_dataset)[base + column_count * 3 + 2] = column_count + 1; | |
} | |
static int sql_bind_and_exec(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
int result; | |
int index = 0; | |
int type, size; | |
char *rest = NULL; | |
sqlite3_stmt *statement; | |
long bin_size; | |
#ifdef DEBUG | |
fprintf(drv->log, "Preexec: %.*s\n", buffer_size, buffer); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
result = ei_decode_tuple_header(buffer, &index, &size); | |
if (size != 2) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Expected a tuple of SQL command and params"); | |
} | |
// decode SQL statement | |
ei_get_type(buffer, &index, &type, &size); | |
// TODO support any iolists | |
if (type != ERL_BINARY_EXT) { | |
return output_error(drv, SQLITE_MISUSE, | |
"SQL should be sent as an Erlang binary"); | |
} | |
char *command = driver_alloc(size * sizeof(char)); | |
ei_decode_binary(buffer, &index, command, &bin_size); | |
// assert(bin_size == size) | |
result = sqlite3_prepare_v2(drv->db, command, size, &statement, | |
(const char **) &rest); | |
driver_free(command); | |
if (result != SQLITE_OK) { | |
return output_db_error(drv); | |
} | |
result = bind_parameters(drv, buffer, buffer_size, &index, statement, &type, &size); | |
if (result == SQLITE_OK) { | |
return sql_exec_statement(drv, statement); | |
} else { | |
return result; // error has already been output | |
} | |
} | |
static void sql_free_async(void *_async_command) { | |
async_sqlite3_command *async_command = | |
(async_sqlite3_command *) _async_command; | |
driver_free(async_command->dataset); | |
async_command->driver_data->async_handle = 0; | |
free_ptr_list(async_command->ptrs, &driver_free); | |
free_ptr_list(async_command->binaries, | |
(void (*)(void *)) &driver_free_binary); | |
if (async_command->finalize_statement_on_free && async_command->statement) { | |
sqlite3_finalize(async_command->statement); | |
async_command->statement = NULL; | |
} | |
driver_free(async_command); | |
} | |
static void sql_exec_async(void *_async_command) { | |
async_sqlite3_command *async_command = | |
(async_sqlite3_command *) _async_command; | |
int term_count = 0; | |
int term_allocated = 0; | |
ErlDrvTermData *dataset = NULL; | |
int row_count = 0; | |
sqlite3_drv_t *drv = async_command->driver_data; | |
int next_row, column_count; | |
sqlite3_stmt *statement = async_command->statement; | |
ptr_list *ptrs = NULL; | |
ptr_list *binaries = NULL; | |
int i; | |
column_count = sqlite3_column_count(statement); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_PORT; | |
dataset[term_count - 1] = driver_mk_port(drv->port); | |
if (column_count > 0) { | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_ATOM; | |
dataset[term_count - 1] = drv->atom_columns; | |
int base_term_count = term_count; | |
get_columns( | |
drv, statement, column_count, base_term_count, &term_count, &term_allocated, &dataset); | |
term_count += 4; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[base_term_count + column_count * 3 + 3] = ERL_DRV_TUPLE; | |
dataset[base_term_count + column_count * 3 + 4] = 2; | |
dataset[base_term_count + column_count * 3 + 5] = ERL_DRV_ATOM; | |
dataset[base_term_count + column_count * 3 + 6] = drv->atom_rows; | |
} | |
#ifdef DEBUG | |
fprintf(drv->log, "Exec: %s\n", sqlite3_sql(statement)); | |
fflush(drv->log); | |
#endif | |
while ((next_row = sqlite3_step(statement)) == SQLITE_ROW) { | |
for (i = 0; i < column_count; i++) { | |
#ifdef DEBUG | |
fprintf(drv->log, "Column %d type: %d\n", i, sqlite3_column_type(statement, i)); | |
fflush(drv->log); | |
#endif | |
switch (sqlite3_column_type(statement, i)) { | |
case SQLITE_INTEGER: { | |
ErlDrvSInt64 *int64_ptr = driver_alloc(sizeof(ErlDrvSInt64)); | |
*int64_ptr = (ErlDrvSInt64) sqlite3_column_int64(statement, i); | |
ptrs = add_to_ptr_list(ptrs, int64_ptr); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_INT64; | |
dataset[term_count - 1] = (ErlDrvTermData) int64_ptr; | |
break; | |
} | |
case SQLITE_FLOAT: { | |
double *float_ptr = driver_alloc(sizeof(double)); | |
*float_ptr = sqlite3_column_double(statement, i); | |
ptrs = add_to_ptr_list(ptrs, float_ptr); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_FLOAT; | |
dataset[term_count - 1] = (ErlDrvTermData) float_ptr; | |
break; | |
} | |
case SQLITE_BLOB: { | |
int bytes = sqlite3_column_bytes(statement, i); | |
ErlDrvBinary* binary = driver_alloc_binary(bytes); | |
binary->orig_size = bytes; | |
memcpy(binary->orig_bytes, | |
sqlite3_column_blob(statement, i), bytes); | |
binaries = add_to_ptr_list(binaries, binary); | |
term_count += 8; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 8] = ERL_DRV_ATOM; | |
dataset[term_count - 7] = drv->atom_blob; | |
dataset[term_count - 6] = ERL_DRV_BINARY; | |
dataset[term_count - 5] = (ErlDrvTermData) binary; | |
dataset[term_count - 4] = bytes; | |
dataset[term_count - 3] = 0; | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
break; | |
} | |
case SQLITE_TEXT: { | |
int bytes = sqlite3_column_bytes(statement, i); | |
ErlDrvBinary* binary = driver_alloc_binary(bytes); | |
binary->orig_size = bytes; | |
memcpy(binary->orig_bytes, | |
sqlite3_column_blob(statement, i), bytes); | |
binaries = add_to_ptr_list(binaries, binary); | |
term_count += 4; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 4] = ERL_DRV_BINARY; | |
dataset[term_count - 3] = (ErlDrvTermData) binary; | |
dataset[term_count - 2] = bytes; | |
dataset[term_count - 1] = 0; | |
break; | |
} | |
case SQLITE_NULL: { | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_ATOM; | |
dataset[term_count - 1] = drv->atom_null; | |
break; | |
} | |
} | |
} | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = column_count; | |
row_count++; | |
} | |
async_command->finalize_statement_on_free = 1; | |
async_command->row_count = row_count; | |
async_command->ptrs = ptrs; | |
async_command->binaries = binaries; | |
if (next_row == SQLITE_BUSY) { | |
return_error(drv, SQLITE_BUSY, "SQLite3 database is busy", | |
&async_command->dataset, &async_command->term_count); | |
return; | |
} | |
if (next_row != SQLITE_DONE) { | |
return_error(drv, next_row, sqlite3_errmsg(drv->db), | |
&async_command->dataset, &async_command->term_count); | |
return; | |
} | |
if (column_count > 0) { | |
term_count += 3+2+3; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 8] = ERL_DRV_NIL; | |
dataset[term_count - 7] = ERL_DRV_LIST; | |
dataset[term_count - 6] = row_count + 1; | |
dataset[term_count - 5] = ERL_DRV_TUPLE; | |
dataset[term_count - 4] = 2; | |
dataset[term_count - 3] = ERL_DRV_NIL; | |
dataset[term_count - 2] = ERL_DRV_LIST; | |
dataset[term_count - 1] = 3; | |
} else if (sql_is_insert(sqlite3_sql(statement))) { | |
sqlite3_int64 rowid = sqlite3_last_insert_rowid(drv->db); | |
term_count += 6; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 6] = ERL_DRV_ATOM; | |
dataset[term_count - 5] = drv->atom_rowid; | |
dataset[term_count - 4] = ERL_DRV_INT; | |
dataset[term_count - 3] = rowid; | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
} else { | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_ATOM; | |
dataset[term_count - 1] = drv->atom_ok; | |
} | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
async_command->dataset = dataset; | |
async_command->term_count = term_count; | |
#ifdef DEBUG | |
fprintf(drv->log, "Total term count: %p %d, rows count: %dx%d\n", statement, term_count, column_count, row_count); | |
fflush(drv->log); | |
#endif | |
} | |
static void sql_step_async(void *_async_command) { | |
async_sqlite3_command *async_command = | |
(async_sqlite3_command *) _async_command; | |
int term_count = 0; | |
int term_allocated = 0; | |
ErlDrvTermData *dataset = NULL; | |
sqlite3_drv_t *drv = async_command->driver_data; | |
int column_count; | |
sqlite3_stmt *statement = async_command->statement; | |
ptr_list *ptrs = NULL; | |
ptr_list *binaries = NULL; | |
int i; | |
int result; | |
switch(result = sqlite3_step(statement)) { | |
case SQLITE_ROW: | |
column_count = sqlite3_column_count(statement); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_PORT; | |
dataset[term_count - 1] = driver_mk_port(drv->port); | |
for (i = 0; i < column_count; i++) { | |
#ifdef DEBUG | |
fprintf(drv->log, "Column %d type: %d\n", i, sqlite3_column_type(statement, i)); | |
fflush(drv->log); | |
#endif | |
switch (sqlite3_column_type(statement, i)) { | |
case SQLITE_INTEGER: { | |
ErlDrvSInt64 *int64_ptr = driver_alloc(sizeof(ErlDrvSInt64)); | |
*int64_ptr = (ErlDrvSInt64) sqlite3_column_int64(statement, i); | |
ptrs = add_to_ptr_list(ptrs, int64_ptr); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_INT64; | |
dataset[term_count - 1] = (ErlDrvTermData) int64_ptr; | |
break; | |
} | |
case SQLITE_FLOAT: { | |
double *float_ptr = driver_alloc(sizeof(double)); | |
*float_ptr = sqlite3_column_double(statement, i); | |
ptrs = add_to_ptr_list(ptrs, float_ptr); | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_FLOAT; | |
dataset[term_count - 1] = (ErlDrvTermData) float_ptr; | |
break; | |
} | |
case SQLITE_BLOB: { | |
int bytes = sqlite3_column_bytes(statement, i); | |
ErlDrvBinary* binary = driver_alloc_binary(bytes); | |
binary->orig_size = bytes; | |
memcpy(binary->orig_bytes, | |
sqlite3_column_blob(statement, i), bytes); | |
binaries = add_to_ptr_list(binaries, binary); | |
term_count += 8; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 8] = ERL_DRV_ATOM; | |
dataset[term_count - 7] = drv->atom_blob; | |
dataset[term_count - 6] = ERL_DRV_BINARY; | |
dataset[term_count - 5] = (ErlDrvTermData) binary; | |
dataset[term_count - 4] = bytes; | |
dataset[term_count - 3] = 0; | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
break; | |
} | |
case SQLITE_TEXT: { | |
int bytes = sqlite3_column_bytes(statement, i); | |
ErlDrvBinary* binary = driver_alloc_binary(bytes); | |
binary->orig_size = bytes; | |
memcpy(binary->orig_bytes, | |
sqlite3_column_blob(statement, i), bytes); | |
binaries = add_to_ptr_list(binaries, binary); | |
term_count += 4; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 4] = ERL_DRV_BINARY; | |
dataset[term_count - 3] = (ErlDrvTermData) binary; | |
dataset[term_count - 2] = bytes; | |
dataset[term_count - 1] = 0; | |
break; | |
} | |
case SQLITE_NULL: { | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_ATOM; | |
dataset[term_count - 1] = drv->atom_null; | |
break; | |
} | |
} | |
} | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = column_count; | |
async_command->ptrs = ptrs; | |
async_command->binaries = binaries; | |
break; | |
case SQLITE_DONE: | |
term_count += 4; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 4] = ERL_DRV_PORT; | |
dataset[term_count - 3] = driver_mk_port(drv->port); | |
dataset[term_count - 2] = ERL_DRV_ATOM; | |
dataset[term_count - 1] = drv->atom_done; | |
sqlite3_reset(statement); | |
break; | |
case SQLITE_BUSY: | |
return_error(drv, SQLITE_BUSY, "SQLite3 database is busy", | |
&dataset, &term_count); | |
sqlite3_reset(statement); | |
goto POPULATE_COMMAND; | |
break; | |
default: | |
return_error(drv, result, sqlite3_errmsg(drv->db), | |
&dataset, &term_count); | |
sqlite3_reset(statement); | |
goto POPULATE_COMMAND; | |
} | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
POPULATE_COMMAND: | |
async_command->dataset = dataset; | |
async_command->term_count = term_count; | |
async_command->ptrs = ptrs; | |
async_command->binaries = binaries; | |
async_command->row_count = 1; | |
#ifdef DEBUG | |
fprintf(drv->log, "Total term count: %p %d, rows count: %dx%d\n", statement, term_count, column_count, row_count); | |
fflush(drv->log); | |
#endif | |
} | |
static void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data) { | |
async_sqlite3_command *async_command = | |
(async_sqlite3_command *) thread_data; | |
sqlite3_drv_t *drv = async_command->driver_data; | |
int res = driver_output_term(drv->port, | |
async_command->dataset, | |
async_command->term_count); | |
(void) res; // suppress unused warning | |
#ifdef DEBUG | |
fprintf(drv->log, "Total term count: %p %d, rows count: %d (%d)\n", async_command->statement, async_command->term_count, async_command->row_count, res); | |
fflush(drv->log); | |
#endif | |
sql_free_async(async_command); | |
} | |
static int prepare(sqlite3_drv_t *drv, char *command, int command_size) { | |
int result; | |
char *rest = NULL; | |
sqlite3_stmt *statement; | |
#ifdef DEBUG | |
fprintf(drv->log, "Preparing statement: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
result = sqlite3_prepare_v2(drv->db, command, command_size, &statement, | |
(const char **) &rest); | |
if (result != SQLITE_OK) { | |
return output_db_error(drv); | |
} | |
if (drv->prepared_count >= drv->prepared_alloc) { | |
drv->prepared_alloc = | |
(drv->prepared_alloc != 0) ? 2*drv->prepared_alloc : 4; | |
drv->prepared_stmts = | |
driver_realloc(drv->prepared_stmts, | |
drv->prepared_alloc * sizeof(sqlite3_stmt *)); | |
} | |
drv->prepared_stmts[drv->prepared_count] = statement; | |
drv->prepared_count++; | |
ErlDrvTermData spec[] = { | |
ERL_DRV_PORT, driver_mk_port(drv->port), | |
ERL_DRV_UINT, drv->prepared_count - 1, | |
ERL_DRV_TUPLE, 2 | |
}; | |
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0])); | |
} | |
static int prepared_bind(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
int result; | |
long long_prepared_index; | |
int index = 0, type, size; | |
#ifdef DEBUG | |
fprintf(drv->log, "Finalizing prepared statement: %.*s\n", buffer_size, buffer); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_tuple_header(buffer, &index, &size); | |
// assert(size == 2); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to bind non-existent prepared statement"); | |
} | |
sqlite3_stmt *statement = drv->prepared_stmts[prepared_index]; | |
result = | |
bind_parameters(drv, buffer, buffer_size, &index, statement, &type, &size); | |
if (result == SQLITE_OK) { | |
return output_ok(drv); | |
} else { | |
return result; // error has already been output | |
} | |
} | |
static int prepared_columns(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
long long_prepared_index; | |
int index = 0, term_count = 0, term_allocated = 0; | |
#ifdef DEBUG | |
fprintf(drv->log, "Finalizing prepared statement: %.*s\n", buffer_size, buffer); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to reset non-existent prepared statement"); | |
} | |
sqlite3_stmt *statement = drv->prepared_stmts[prepared_index]; | |
ErlDrvTermData *dataset = NULL; | |
term_count += 2; | |
if (term_count > term_allocated) { | |
term_allocated = max(term_count, term_allocated*2); | |
dataset = driver_realloc(dataset, sizeof(ErlDrvTermData) * term_allocated); | |
} | |
dataset[term_count - 2] = ERL_DRV_PORT; | |
dataset[term_count - 1] = driver_mk_port(drv->port); | |
int column_count = sqlite3_column_count(statement); | |
get_columns( | |
drv, statement, column_count, 2, &term_count, &term_allocated, &dataset); | |
term_count += 2; | |
dataset[term_count - 2] = ERL_DRV_TUPLE; | |
dataset[term_count - 1] = 2; | |
return driver_output_term(drv->port, dataset, term_count); | |
} | |
static int prepared_step(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
int result; | |
long long_prepared_index; | |
int index = 0; | |
char *rest = NULL; | |
#ifdef DEBUG | |
fprintf(drv->log, "Evaluating prepared statement: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to evaluate non-existent prepared statement"); | |
} | |
sqlite3_stmt *statement = drv->prepared_stmts[prepared_index]; | |
async_sqlite3_command *async_command = make_async_command(drv, statement); | |
if (sqlite3_threadsafe()) { | |
drv->async_handle = driver_async(drv->port, &drv->key, sql_step_async, | |
async_command, sql_free_async); | |
} else { | |
sql_exec_async(async_command); | |
ready_async((ErlDrvData) drv, (ErlDrvThreadData) async_command); | |
} | |
return 0; | |
} | |
static int prepared_reset(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
long long_prepared_index; | |
int index = 0; | |
#ifdef DEBUG | |
fprintf(drv->log, "Finalizing prepared statement: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to reset non-existent prepared statement"); | |
} | |
// don't bother about error code, any errors should already be shown by step | |
sqlite3_stmt *statement = drv->prepared_stmts[prepared_index]; | |
sqlite3_reset(statement); | |
return output_ok(drv); | |
} | |
static int prepared_clear_bindings(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
long long_prepared_index; | |
int index = 0; | |
#ifdef DEBUG | |
fprintf(drv->log, "Finalizing prepared statement: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to clear bindings of non-existent prepared statement"); | |
} | |
sqlite3_stmt *statement = drv->prepared_stmts[prepared_index]; | |
sqlite3_clear_bindings(statement); | |
return output_ok(drv); | |
} | |
static int prepared_finalize(sqlite3_drv_t *drv, char *buffer, int buffer_size) { | |
long long_prepared_index; | |
int index = 0; | |
#ifdef DEBUG | |
fprintf(drv->log, "Finalizing prepared statement: %.*s\n", command_size, command); | |
fflush(drv->log); | |
#endif | |
ei_decode_version(buffer, &index, NULL); | |
ei_decode_long(buffer, &index, &long_prepared_index); | |
unsigned int prepared_index = (unsigned int) long_prepared_index; | |
if (prepared_index >= drv->prepared_count) { | |
return output_error(drv, SQLITE_MISUSE, | |
"Trying to finalize non-existent prepared statement"); | |
} | |
// finalize the statement and make sure it isn't accidentally executed again | |
sqlite3_finalize(drv->prepared_stmts[prepared_index]); | |
drv->prepared_stmts[prepared_index] = NULL; | |
// if the statement is at the end of the array, space can be reused; | |
// otherwise don't bother | |
if (prepared_index == drv->prepared_count - 1) { | |
drv->prepared_count--; | |
} | |
return output_ok(drv); | |
} | |
// Unknown Command | |
static int unknown(sqlite3_drv_t *drv, char *command, int command_size) { | |
// Return {Port, error, unknown_command} | |
ErlDrvTermData spec[] = { | |
ERL_DRV_PORT, driver_mk_port(drv->port), | |
ERL_DRV_ATOM, drv->atom_error, | |
ERL_DRV_INT, -1, | |
ERL_DRV_ATOM, drv->atom_unknown_cmd, | |
ERL_DRV_TUPLE, 4 | |
}; | |
return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0])); | |
} | |
static inline ptr_list *add_to_ptr_list(ptr_list *list, void *value_ptr) { | |
ptr_list* new_node = driver_alloc(sizeof(ptr_list)); | |
new_node->head = value_ptr; | |
new_node->tail = NULL; | |
if (list) { | |
list->tail = new_node; | |
return list; | |
} else { | |
return new_node; | |
} | |
} | |
static inline void free_ptr_list(ptr_list *list, void(* free_head)(void *)) { | |
ptr_list* tail; | |
while (list) { | |
tail = list->tail; | |
(*free_head)(list->head); | |
driver_free(list); | |
list = tail; | |
} | |
} | |
static inline int max(int a, int b) { | |
return a >= b ? a : b; | |
} | |
static inline int sql_is_insert(const char *sql) { | |
// neither strcasestr nor strnicmp are portable, so have to do this | |
int i; | |
char *insert = "insert"; | |
for (i = 0; i < 6; i++) { | |
if ((tolower(sql[i]) != insert[i]) && (sql[i] != ' ')) | |
return 0; | |
} | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment