#include "proxysql.h" #include "sqlite3.h" #include "cpp.h" //#include "SpookyV2.h" #include #include #include #include #ifdef DEBUG //#define VALGRIND_ENABLE_ERROR_REPORTING //#define VALGRIND_DISABLE_ERROR_REPORTING #include "valgrind.h" #else #define VALGRIND_ENABLE_ERROR_REPORTING #define VALGRIND_DISABLE_ERROR_REPORTING #endif // DEBUG #define USLEEP_SQLITE_LOCKED 100 /** * @brief Constructor for the SQLite3_column class. * * @param a The datatype of the column. * @param b The name of the column. */ SQLite3_column::SQLite3_column(int a, const char *b) { datatype=a; if (b) { name=strdup(b); } else { name=strdup((char *)""); } } /** * @brief Destructor for the SQLite3_column class. */ SQLite3_column::~SQLite3_column() { free(name); } /** * @brief Constructor for the SQLite3_row class. * * @param c The number of columns in the row. */ SQLite3_row::SQLite3_row(int c) { sizes=(int *)malloc(sizeof(int)*c); fields=(char **)malloc(sizeof(char *)*c); memset(fields,0,sizeof(char *)*c); cnt=c; data=NULL; ds=0; } /** * @brief Calculates the total size of the SQLite3_row object. * * @return The size of the SQLite3_row object. */ unsigned long long SQLite3_row::get_size() { unsigned long long s = sizeof(SQLite3_row); s += cnt * sizeof(int); s += cnt * sizeof(char *); s += ds; return s; } /** * @brief Destructor for the SQLite3_row class. */ SQLite3_row::~SQLite3_row() { free(fields); free(sizes); if (data) { free(data); } } /** * @brief Adds fields to the SQLite3_row object based on data from a SQLite statement. * * @param stmt The SQLite statement. */ void SQLite3_row::add_fields(sqlite3_stmt *stmt) { int i; int t; int data_size=0; int data_ptr=0; // compute the length for (i=0;i SQLite3DB::prepare_v2(const char* query) { int rc { 0 }; sqlite3_stmt* stmt { nullptr }; do { rc = (*proxy_sqlite3_prepare_v2)(db, query, -1, &stmt, nullptr); if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) { struct timespec ts { .tv_sec = 0, .tv_nsec = USLEEP_SQLITE_LOCKED * 1000 }; nanosleep(&ts, nullptr); } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); return { rc, stmt_unique_ptr(stmt) }; } /** * @brief Executes a SQL statement and returns the result set. * * @param str The SQL statement to execute. * @param _error Pointer to a variable to store the error message. * @param _cols Pointer to a variable to store the number of columns. * @param _affected_rows Pointer to a variable to store the number of affected rows. * @return A pointer to the SQLite3_result object representing the result set. */ SQLite3_result* SQLite3DB::execute_statement(const char *str, char **_error, int *_cols, int *_affected_rows) { SQLite3_result* resultset; char *myerror; char **error = (_error == NULL ? &myerror : _error); int mycols; int *cols = (_cols == NULL ? &mycols : _cols); int my_affected_rows; int *affected_rows = (_affected_rows == NULL ? &my_affected_rows : _affected_rows); if (execute_statement(str, error, cols, affected_rows, &resultset)) return resultset; return NULL; } /** * @brief Executes a SQL statement and returns the result set. * * @param str The SQL statement to execute. * @param error Pointer to a variable to store the error message. * @param cols Pointer to a variable to store the number of columns. * @param affected_rows Pointer to a variable to store the number of affected rows. * @param resultset Pointer to a pointer to a SQLite3_result object representing the result set. * @return True if the execution was successful, false otherwise. */ bool SQLite3DB::execute_statement(const char *str, char **error, int *cols, int *affected_rows, SQLite3_result **resultset) { int rc; sqlite3_stmt *statement=NULL; *error=NULL; bool ret=false; VALGRIND_DISABLE_ERROR_REPORTING; do { rc = (*proxy_sqlite3_prepare_v2)(db, str, -1, &statement, 0); if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) { // the execution of the prepared statement failed because locked if ((*proxy_sqlite3_get_autocommit)(db)==0) { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } usleep(USLEEP_SQLITE_LOCKED); } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); if (rc == SQLITE_OK) { } else { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } VALGRIND_ENABLE_ERROR_REPORTING; *cols = (*proxy_sqlite3_column_count)(statement); if (*cols==0) { // not a SELECT *resultset=NULL; // Get total changes before executing the statement long long total_changes_before = (*proxy_sqlite3_total_changes64)(db); do { rc=(*proxy_sqlite3_step)(statement); if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) { // the execution of the prepared statement failed because locked if ((*proxy_sqlite3_get_autocommit)(db)==0) { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } usleep(USLEEP_SQLITE_LOCKED); } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); if (rc==SQLITE_DONE) { // Calculate affected rows as the difference in total changes long long total_changes_after = (*proxy_sqlite3_total_changes64)(db); *affected_rows = (int)(total_changes_after - total_changes_before); ret=true; } else { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } } else { *affected_rows=0; *resultset=new SQLite3_result(statement); ret=true; } __exit_execute_statement: (*proxy_sqlite3_reset)(statement); (*proxy_sqlite3_finalize)(statement); return ret; } /** * @brief Executes a SQL statement and returns the result set without parsing it. * * @param str The SQL statement to execute. * @param error Pointer to a variable to store the error message. * @param cols Pointer to a variable to store the number of columns. * @param affected_rows Pointer to a variable to store the number of affected rows. * @param statement Pointer to a pointer to a SQLite statement object. * @return True if the execution was successful, false otherwise. */ bool SQLite3DB::execute_statement_raw(const char *str, char **error, int *cols, int *affected_rows, sqlite3_stmt **statement) { int rc; //sqlite3_stmt *statement=NULL; *error=NULL; bool ret=false; VALGRIND_DISABLE_ERROR_REPORTING; if((*proxy_sqlite3_prepare_v2)(db, str, -1, statement, 0) != SQLITE_OK) { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } VALGRIND_ENABLE_ERROR_REPORTING; *cols = (*proxy_sqlite3_column_count)(*statement); if (*cols==0) { // not a SELECT //*resultset=NULL; // Get total changes before executing the statement long long total_changes_before = (*proxy_sqlite3_total_changes64)(db); do { rc=(*proxy_sqlite3_step)(*statement); if (rc==SQLITE_LOCKED || rc==SQLITE_BUSY) { // the execution of the prepared statement failed because locked usleep(USLEEP_SQLITE_LOCKED); } } while (rc==SQLITE_LOCKED || rc==SQLITE_BUSY); if (rc==SQLITE_DONE) { // Calculate affected rows as the difference in total changes long long total_changes_after = (*proxy_sqlite3_total_changes64)(db); *affected_rows = (int)(total_changes_after - total_changes_before); ret=true; } else { *error=strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_statement; } } else { *affected_rows=0; //*resultset=new SQLite3_result(statement); ret=true; } __exit_execute_statement: // NOTE: the caller MUST call (*proxy_sqlite3_finalize)() //(*proxy_sqlite3_finalize)(statement); return ret; } /** * @brief Executes a prepared SQL statement and returns the result set. * * @param statement The prepared SQL statement to execute. * @param _error Pointer to a variable to store the error message. * @param _cols Pointer to a variable to store the number of columns. * @param _affected_rows Pointer to a variable to store the number of affected rows. * @return A pointer to the SQLite3_result object representing the result set. */ SQLite3_result* SQLite3DB::execute_prepared(sqlite3_stmt* statement, char** error, int* cols, int* affected_rows) { SQLite3_result* resultset; char* myerror; char** _error = (error == NULL ? &myerror : error); int mycols; int* _cols = (cols == NULL ? &mycols : cols); int my_affected_rows; int* _affected_rows = (affected_rows == NULL ? &my_affected_rows : affected_rows); if (execute_prepared(statement, _error, _cols, _affected_rows, &resultset)) return resultset; return NULL; } /** * @brief Executes a prepared SQL statement and returns the result set. * * @param statement The prepared SQLite statement to execute. * @param error Pointer to a variable to store the error message. * @param cols Pointer to a variable to store the number of columns. * @param affected_rows Pointer to a variable to store the number of affected rows. * @param resultset Pointer to a pointer to a SQLite3_result object representing the result set. * @return True if the execution was successful, false otherwise. */ bool SQLite3DB::execute_prepared(sqlite3_stmt* statement, char** error, int* cols, int* affected_rows, SQLite3_result** resultset) { int rc; *error = NULL; bool ret = false; *cols = (*proxy_sqlite3_column_count)(statement); if (*cols == 0) { // not a SELECT *resultset = NULL; // Get total changes before executing the statement long long total_changes_before = (*proxy_sqlite3_total_changes64)(db); do { rc = (*proxy_sqlite3_step)(statement); if (rc == SQLITE_LOCKED || rc == SQLITE_BUSY) { // the execution of the prepared statement failed because locked if ((*proxy_sqlite3_get_autocommit)(db) == 0) { *error = strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_prepared; } usleep(USLEEP_SQLITE_LOCKED); } } while (rc == SQLITE_LOCKED || rc == SQLITE_BUSY); if (rc == SQLITE_DONE) { // Calculate affected rows as the difference in total changes long long total_changes_after = (*proxy_sqlite3_total_changes64)(db); *affected_rows = (int)(total_changes_after - total_changes_before); ret = true; } else { *error = strdup((*proxy_sqlite3_errmsg)(db)); goto __exit_execute_prepared; } } else { *affected_rows = 0; *resultset = new SQLite3_result(statement); ret = true; } __exit_execute_prepared: //(*proxy_sqlite3_reset)(statement); return ret; } /** * @brief Executes a SQL statement and returns a single integer result. * * @param str The SQL statement to execute. * @return The integer result of the execution. */ int SQLite3DB::return_one_int(const char *str) { char *error=NULL; int cols=0; int affected_rows=0; int ret=0; SQLite3_result *resultset=NULL; execute_statement(str, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", str, error); free(error); } else { for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; ret=atoi(r->fields[0]); break; } } if (resultset) delete resultset; return ret; } /** * @brief Checks the structure of a table in the database. * * @param table_name The name of the table. * @param table_def The definition of the table. * @return The number of tables matching the structure. */ int SQLite3DB::check_table_structure(char *table_name, char *table_def) { const char *q1="SELECT COUNT(*) FROM sqlite_master WHERE type=\"table\" AND name=\"%s\" AND sql=\"%s\""; int count=0; int l=strlen(q1)+strlen(table_name)+strlen(table_def)+1; sqlite3_stmt *statement; char *buff=(char *)calloc(1,l); sprintf(buff, q1, table_name , table_def); if((*proxy_sqlite3_prepare_v2)(db, buff, -1, &statement, 0) != SQLITE_OK) { proxy_debug(PROXY_DEBUG_SQLITE, 1, "SQLITE: Error on (*proxy_sqlite3_prepare_v2)() running query \"%s\" : %s\n", buff, (*proxy_sqlite3_errmsg)(db)); (*proxy_sqlite3_finalize)(statement); free(buff); assert(0); } int result=0; while ((result=(*proxy_sqlite3_step)(statement))==SQLITE_ROW) { count+=(*proxy_sqlite3_column_int)(statement,0); } (*proxy_sqlite3_finalize)(statement); free(buff); return count; } /** * @brief Builds a table in the database. * * @param table_name The name of the table. * @param table_def The definition of the table. * @param dropit Flag to indicate whether to drop existing table before creating. * @return True if the table creation was successful, false otherwise. */ bool SQLite3DB::build_table(char *table_name, char *table_def, bool dropit) { bool rc; if (dropit) { const char *q2="DROP TABLE IF EXISTS %s"; int l=strlen(q2)+strlen(table_name)+1; char *buff=(char *)calloc(1,l); sprintf(buff,q2,table_name); proxy_debug(PROXY_DEBUG_SQLITE, 5, "SQLITE: dropping table: %s\n", buff); rc=execute(buff); free(buff); if (rc==false) return rc; } proxy_debug(PROXY_DEBUG_SQLITE, 5, "SQLITE: creating table: %s\n", table_def); rc=execute(table_def); return rc; } /** * @brief Checks and builds a table in the database if it doesn't exist. * * This function checks whether a table exists in the database. If the table does not exist, * it builds the table using the provided table name and definition. * * @param table_name The name of the table. * @param table_def The definition of the table. * @return True if the table already exists or was successfully created, false otherwise. */ bool SQLite3DB::check_and_build_table(char *table_name, char *table_def) { int rci; bool rcb; rci=check_table_structure(table_name,table_def); if (rci) return true; rcb=build_table(table_name,table_def,true); return rcb; } /** * @brief Acquires a read lock on the database. * * This function acquires a read lock on the SQLite database to prevent concurrent write access. */ void SQLite3DB::rdlock() { pthread_rwlock_rdlock(&rwlock); } /** * @brief Releases the read lock on the database. * * This function releases the read lock acquired by rdlock() on the SQLite database. */ void SQLite3DB::rdunlock() { pthread_rwlock_unlock(&rwlock); } /** * @brief Acquires a write lock on the database. * * This function acquires a write lock on the SQLite database to prevent concurrent read and write access. */ void SQLite3DB::wrlock() { pthread_rwlock_wrlock(&rwlock); } /** * @brief Releases the write lock on the database. * * This function releases the write lock acquired by wrlock() on the SQLite database. */ void SQLite3DB::wrunlock() { pthread_rwlock_unlock(&rwlock); } /** * @brief Calculates the raw checksum of the SQLite3_result object. * * This function calculates the raw checksum of the SQLite3_result object, excluding metadata. * * @return The raw checksum of the result. */ uint64_t SQLite3_result::raw_checksum() { if (this->rows_count==0) return 0; uint64_t hash1, hash2; SpookyHash myhash; myhash.Init(19,3); for (std::vector::iterator it=rows.begin() ; it!=rows.end(); ++it) { SQLite3_row *r=*it; for (int i=0; ifields[i]) { myhash.Update(r->fields[i],r->sizes[i]); } else { myhash.Update("",0); } } } myhash.Final(&hash1, &hash2); return hash1; } /** * @brief Calculates the checksum of the SQLite3_result object. * * This function calculates the checksum of the SQLite3_result object, including metadata. * * @return The checksum of the result. */ char *SQLite3_result::checksum() { uint64_t hash1=raw_checksum(); char buf[128]; memset(buf,'0',128); uint32_t d32[2]; memcpy(&d32,&hash1,sizeof(hash1)); sprintf(buf,"0x%X%X", d32[0], d32[1]); return strdup(buf); } /** * @brief Dumps the content of the SQLite3_result object to the standard error stream. * * This function prints the content of the SQLite3_result object to the standard error stream. * It is useful for debugging purposes to inspect the content of the result object. * * @note This function is intended for debugging purposes and should not be used in production code. */ void SQLite3_result::dump_to_stderr() { if (columns == 0) return; size_t *columns_lengths = (size_t *)malloc(sizeof(size_t)*columns); int i = 0; for (i = 0; i::iterator it=column_definition.begin() ; it!=column_definition.end(); ++it) { SQLite3_column *r=*it; size_t len = strlen(r->name); if (len > columns_lengths[i]) { columns_lengths[i] = len; i++; } } for (std::vector::iterator it=rows.begin() ; it!=rows.end(); ++it) { SQLite3_row *r=*it; for (int i=0; ifields[i]) { if ((unsigned int)r->sizes[i] > columns_lengths[i]) { columns_lengths[i] = r->sizes[i]; } } else { if (columns_lengths[i] < 4) { columns_lengths[i] = 4; // NULL } } } } string s; s = "+"; for (i=0; i::iterator it=column_definition.begin() ; it!=column_definition.end(); ++it) { SQLite3_column *r=*it; size_t len = strlen(r->name); s.append(" "); s.append(r->name); unsigned int j; for (j=0; j < columns_lengths[i] - len + 1 ; j++) { s.append(" "); } s.append("|"); i++; } fprintf(stderr,"%s\n",s.c_str()); s = "+"; for (i=0; i::iterator it=rows.begin() ; it!=rows.end(); ++it) { SQLite3_row *r=*it; s = "|"; i = 0; for (int i=0; ifields[i]) { len = r->sizes[i]; s.append(r->fields[i]); } else { len = 4; s.append("NULL"); } unsigned int j; for (j=0; j < columns_lengths[i] - len + 1 ; j++) { s.append(" "); } s.append("|"); } fprintf(stderr,"%s\n",s.c_str()); } s = "+"; for (i=0; icolumns; if (src->enabled_mutex) { pthread_mutex_init(&m,NULL); enabled_mutex = true; } else { enabled_mutex = false; } for (std::vector::iterator it = src->column_definition.begin() ; it != src->column_definition.end(); ++it) { SQLite3_column *r=*it; add_column_definition(SQLITE_TEXT,r->name); } for (std::vector::iterator it = src->rows.begin() ; it != src->rows.end(); ++it) { SQLite3_row *r=*it; add_row(r); } } /** * @brief Retrieves the size of the SQLite3_result object. * * This function returns the current size (number of rows) of the SQLite3_result object. * * @return The size (number of rows) of the result object. */ unsigned long long SQLite3_result::get_size() { unsigned long long s = sizeof(SQLite3_result); s += column_definition.size() * sizeof(SQLite3_column *); s += rows.size() * sizeof(SQLite3_row *); for (std::vector::iterator it = column_definition.begin() ; it != column_definition.end(); ++it) { SQLite3_column *r=*it; s+= sizeof(SQLite3_column) + strlen(r->name); } for (std::vector::iterator it = rows.begin() ; it != rows.end(); ++it) { SQLite3_row *r=*it; s += r->get_size(); } return s; } /** * @brief Adds a column definition to the SQLite3_result object. * * This function creates a new SQLite3_column object with the specified name and type * and adds it to the column definitions vector of the SQLite3_result object. * * @param[in] a The name of the column to add. * @param[in] b The type of the column to add. */ void SQLite3_result::add_column_definition(int a, const char *b) { SQLite3_column *cf=new SQLite3_column(a,b); column_definition.push_back(cf); } /** * @brief Adds a row to the SQLite3_result object. * * This function adds a row to the SQLite3_result object either from the provided * SQLite statement or from the specified fields array. If the `skip` parameter is set * to false, a new row is created and added to the rows vector of the SQLite3_result object. * * @param[in] stmt The SQLite statement from which to fetch the row data. * @param[in] skip A boolean indicating whether to skip adding the row (default is false). * @return An integer representing the result of the operation (SQLITE_ROW on success). */ int SQLite3_result::add_row(sqlite3_stmt *stmt, bool skip) { int rc = 0; do { rc = (*proxy_sqlite3_step)(stmt); if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { usleep(USLEEP_SQLITE_LOCKED); } } while (rc == SQLITE_BUSY || rc == SQLITE_LOCKED); if (rc!=SQLITE_ROW) return rc; if (skip==false) { SQLite3_row *row=new SQLite3_row(columns); row->add_fields(stmt); rows.push_back(row); rows_count++; } return SQLITE_ROW; } /** * @brief Adds a row to the SQLite3_result object. * * This function adds a row to the SQLite3_result object from the provided fields array. * A new row is created and added to the rows vector of the SQLite3_result object. * * @param[in] _fields The array of fields representing the row data. * @return An integer representing the result of the operation (SQLITE_ROW on success). */ int SQLite3_result::add_row(char **_fields) { SQLite3_row *row=new SQLite3_row(columns); row->add_fields(_fields); if (enabled_mutex) { pthread_mutex_lock(&m); } rows.push_back(row); rows_count++; if (enabled_mutex) { pthread_mutex_unlock(&m); } return SQLITE_ROW; } /** * @brief Adds a new row to the resulset using a NULL terminated variadic list of * 'const char*' as argument. * * NOTE: This function is unsafe, the final 'NULL' element should be supplied, * otherwise a segfault is likely to happen. * * @param _field Initial element of variadic arguments supplied. * @param ... Variadic NULL terminated list of 'const char*' holding the rest * of fields to add. * * @return SQLITE_ROW */ int SQLite3_result::add_row(const char* _field, ...) { va_list ap; va_start(ap, _field); vector fields {}; while (_field) { fields.push_back(_field); _field = va_arg(ap, const char *); } va_end(ap); return this->add_row(const_cast(&fields[0])); } /** * @brief Adds a row to the SQLite3_result object based on an existing row. * * This function creates a new row in the SQLite3_result object and copies the fields * from the provided existing row to the new row. * * @param[in] old_row Pointer to the existing SQLite3_row object from which to copy fields. * @return int Returns SQLITE_ROW to indicate successful addition of the row. */ int SQLite3_result::add_row(SQLite3_row *old_row) { SQLite3_row *row=new SQLite3_row(columns); row->add_fields(old_row->fields); rows.push_back(row); rows_count++; return SQLITE_ROW; } /** * @brief Constructs a SQLite3_result object based on the result of a SQLite3 statement. * * This constructor initializes a SQLite3_result object using the result of a SQLite3 statement. * It retrieves the column count and column names/types from the statement and adds them as column definitions. * It then iterates through the result rows, adding each row to the SQLite3_result object. * * @param[in] stmt Pointer to the SQLite3 statement. */ SQLite3_result::SQLite3_result(sqlite3_stmt *stmt) { enabled_mutex = false; // default rows_count=0; columns=(*proxy_sqlite3_column_count)(stmt); for (int i=0; i 0 || limit > 0) { while (offset > 0 && rc==SQLITE_ROW) { rc = add_row(stmt, true); if (rc == SQLITE_ROW) fr++; offset--; } while (limit > 0 && rc==SQLITE_ROW) { rc = add_row(stmt, false); if (rc == SQLITE_ROW) fr++; limit--; } } else { while (rc == SQLITE_ROW) { rc=add_row(stmt, false); if (rc == SQLITE_ROW) fr++; } } while (rc == SQLITE_ROW) { rc=add_row(stmt, true); if (rc == SQLITE_ROW) fr++; } *found_rows = fr; } /** * @brief Constructs a SQLite3_result object with a specified number of columns and mutex enablement. * * This constructor initializes a SQLite3_result object with a specified number of columns and enables/disables mutex based on the provided flag. * * @param[in] num_columns Number of columns. * @param[in] en_mutex Flag to enable/disable mutex. */ SQLite3_result::SQLite3_result(int num_columns, bool en_mutex) { rows_count=0; columns=num_columns; if (en_mutex) { pthread_mutex_init(&m,NULL); enabled_mutex = true; } else { enabled_mutex = false; } } /** * @brief Destructor for the SQLite3_result object. * * This destructor cleans up memory by deleting allocated column definitions and rows. */ SQLite3_result::~SQLite3_result() { for (std::vector::iterator it = column_definition.begin() ; it != column_definition.end(); ++it) { SQLite3_column *c=*it; delete c; } for (std::vector::iterator it = rows.begin() ; it != rows.end(); ++it) { SQLite3_row *r=*it; delete r; } } /** * @brief Default constructor for the SQLite3_result object. * * This constructor initializes a SQLite3_result object with default values. */ SQLite3_result::SQLite3_result() { enabled_mutex = false; // default columns=0; } /** * @brief Loads a SQLite3 plugin. * * This function loads a SQLite3 plugin specified by the given plugin_name. * It initializes function pointers to SQLite3 API functions provided by the plugin. * If the plugin_name is NULL, it loads the built-in SQLite3 library and initializes function pointers to its API functions. * * @param[in] plugin_name The name of the SQLite3 plugin library to load. */ void SQLite3DB::LoadPlugin(const char *plugin_name) { const bool allow_load_plugin = false; // TODO: Revisit plugin loading safety mechanism proxy_sqlite3_config = NULL; proxy_sqlite3_bind_double = NULL; proxy_sqlite3_bind_int = NULL; proxy_sqlite3_bind_int64 = NULL; proxy_sqlite3_bind_null = NULL; proxy_sqlite3_bind_text = NULL; proxy_sqlite3_bind_blob = NULL; proxy_sqlite3_column_name = NULL; proxy_sqlite3_column_text = NULL; proxy_sqlite3_column_bytes = NULL; proxy_sqlite3_column_type = NULL; proxy_sqlite3_column_count = NULL; proxy_sqlite3_column_int = NULL; proxy_sqlite3_column_int64 = NULL; proxy_sqlite3_column_double = NULL; proxy_sqlite3_last_insert_rowid = NULL; proxy_sqlite3_errstr = NULL; proxy_sqlite3_db_handle = NULL; proxy_sqlite3_enable_load_extension = NULL; proxy_sqlite3_auto_extension = NULL; proxy_sqlite3_errmsg = NULL; proxy_sqlite3_finalize = NULL; proxy_sqlite3_reset = NULL; proxy_sqlite3_clear_bindings = NULL; proxy_sqlite3_close_v2 = NULL; proxy_sqlite3_get_autocommit = NULL; proxy_sqlite3_free = NULL; proxy_sqlite3_status = NULL; proxy_sqlite3_status64 = NULL; proxy_sqlite3_changes = NULL; proxy_sqlite3_total_changes64 = NULL; proxy_sqlite3_step = NULL; proxy_sqlite3_shutdown = NULL; proxy_sqlite3_prepare_v2 = NULL; proxy_sqlite3_open_v2 = NULL; proxy_sqlite3_exec = NULL; if (plugin_name && allow_load_plugin == true) { int fd = -1; fd = ::open(plugin_name, O_RDONLY); char binary_sha1_sqlite3[SHA_DIGEST_LENGTH*2+1]; if(fd >= 0) { struct stat statbuf; if(fstat(fd, &statbuf) == 0) { unsigned char *fb = (unsigned char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); if (fb != MAP_FAILED) { unsigned char temp[SHA_DIGEST_LENGTH]; SHA1(fb, statbuf.st_size, temp); memset(binary_sha1_sqlite3, 0, SHA_DIGEST_LENGTH*2+1); char buf[SHA_DIGEST_LENGTH*2]; for (int i=0; i < SHA_DIGEST_LENGTH - 1; i++) { sprintf((char*)&(buf[i*2]), "%02x", temp[i]); } memcpy(binary_sha1_sqlite3, buf, SHA_DIGEST_LENGTH*2); munmap(fb,statbuf.st_size); } else { proxy_error("Unable to mmap %s: %s\n", plugin_name, strerror(errno)); } } else { proxy_error("Unable to fstat %s: %s\n", plugin_name, strerror(errno)); } } else { proxy_error("Unable to open %s: %s\n", plugin_name, strerror(errno)); } close(fd); proxy_info("SQLite3 Plugin SHA1 checksum: %s\n", binary_sha1_sqlite3); dlerror(); char * dlsym_error = NULL; dlerror(); dlsym_error=NULL; void * handle_sqlite3_plugin = dlopen(plugin_name, RTLD_NOW); if (!handle_sqlite3_plugin) { cerr << "Cannot load SQLite3 plugin from library: " << dlerror() << '\n'; exit(EXIT_FAILURE); } else { dlerror(); int (*proxy_load_sqlite3)(); proxy_load_sqlite3 = (int (*)()) dlsym(handle_sqlite3_plugin, "proxy_load_sqlite3"); dlsym_error = dlerror(); if (dlsym_error!=NULL) { cerr << "Cannot load symbol proxy_load_sqlite3: " << dlsym_error << '\n'; exit(EXIT_FAILURE); } (*proxy_load_sqlite3)(); } if (handle_sqlite3_plugin==NULL || dlsym_error) { proxy_error("Unable to load SQLite3 plugin from %s\n", plugin_name); exit(EXIT_FAILURE); } proxy_info("Loaded SQLite3 from plugin\n"); } else { proxy_sqlite3_config = sqlite3_config; proxy_sqlite3_bind_double = sqlite3_bind_double; proxy_sqlite3_bind_int = sqlite3_bind_int; proxy_sqlite3_bind_int64 = sqlite3_bind_int64; proxy_sqlite3_bind_null = sqlite3_bind_null; proxy_sqlite3_bind_text = sqlite3_bind_text; proxy_sqlite3_bind_blob = sqlite3_bind_blob; proxy_sqlite3_column_name = sqlite3_column_name; proxy_sqlite3_column_text = sqlite3_column_text; proxy_sqlite3_column_bytes = sqlite3_column_bytes; proxy_sqlite3_column_type = sqlite3_column_type; /* signature matches */ proxy_sqlite3_column_count = sqlite3_column_count; proxy_sqlite3_column_int = sqlite3_column_int; proxy_sqlite3_column_int64 = sqlite3_column_int64; proxy_sqlite3_column_double = sqlite3_column_double; proxy_sqlite3_last_insert_rowid = sqlite3_last_insert_rowid; proxy_sqlite3_errstr = sqlite3_errstr; proxy_sqlite3_db_handle = sqlite3_db_handle; proxy_sqlite3_enable_load_extension = sqlite3_enable_load_extension; proxy_sqlite3_auto_extension = sqlite3_auto_extension; proxy_sqlite3_errmsg = sqlite3_errmsg; proxy_sqlite3_finalize = sqlite3_finalize; proxy_sqlite3_reset = sqlite3_reset; proxy_sqlite3_clear_bindings = sqlite3_clear_bindings; proxy_sqlite3_close_v2 = sqlite3_close_v2; proxy_sqlite3_get_autocommit = sqlite3_get_autocommit; proxy_sqlite3_free = sqlite3_free; proxy_sqlite3_status = sqlite3_status; proxy_sqlite3_status64 = sqlite3_status64; proxy_sqlite3_changes = sqlite3_changes; proxy_sqlite3_total_changes64 = sqlite3_total_changes64; proxy_sqlite3_step = sqlite3_step; proxy_sqlite3_shutdown = sqlite3_shutdown; proxy_sqlite3_prepare_v2 = sqlite3_prepare_v2; proxy_sqlite3_open_v2 = sqlite3_open_v2; proxy_sqlite3_exec = sqlite3_exec; proxy_info("Loaded built-in SQLite3\n"); } assert(proxy_sqlite3_config); assert(proxy_sqlite3_bind_double); assert(proxy_sqlite3_bind_int); assert(proxy_sqlite3_bind_int64); assert(proxy_sqlite3_bind_null); assert(proxy_sqlite3_bind_text); assert(proxy_sqlite3_bind_blob); assert(proxy_sqlite3_column_name); assert(proxy_sqlite3_column_text); assert(proxy_sqlite3_column_bytes); assert(proxy_sqlite3_column_type); assert(proxy_sqlite3_column_count); assert(proxy_sqlite3_column_int); assert(proxy_sqlite3_column_int64); assert(proxy_sqlite3_column_double); assert(proxy_sqlite3_last_insert_rowid); assert(proxy_sqlite3_errstr); assert(proxy_sqlite3_db_handle); assert(proxy_sqlite3_enable_load_extension); assert(proxy_sqlite3_auto_extension); assert(proxy_sqlite3_errmsg); assert(proxy_sqlite3_finalize); assert(proxy_sqlite3_reset); assert(proxy_sqlite3_clear_bindings); assert(proxy_sqlite3_close_v2); assert(proxy_sqlite3_get_autocommit); assert(proxy_sqlite3_free); assert(proxy_sqlite3_status); assert(proxy_sqlite3_status64); assert(proxy_sqlite3_changes); assert(proxy_sqlite3_total_changes64); assert(proxy_sqlite3_step); assert(proxy_sqlite3_shutdown); assert(proxy_sqlite3_prepare_v2); assert(proxy_sqlite3_open_v2); assert(proxy_sqlite3_exec); { /* moved here, so if needed by multiple modules it applies to all of them */ int i=(*proxy_sqlite3_config)(SQLITE_CONFIG_URI, 1); if (i!=SQLITE_OK) { fprintf(stderr,"SQLITE: Error on (*proxy_sqlite3_config)(SQLITE_CONFIG_URI,1)\n"); assert(i==SQLITE_OK); exit(EXIT_FAILURE); } } }