/*
Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
#include "gfdb_sqlite3.h"
#include "gfdb_data_store.h"
#include "glusterfs/list.h"
#include "glusterfs/libglusterfs-messages.h"
/******************************************************************************
*
* Database Connection utils/internals
*
* ****************************************************************************/
/* GFDB Connection Node:
* ~~~~~~~~~~~~~~~~~~~~
* Represents the connection to the database while using libgfdb
* The connection node is not thread safe as far as fini_db is concerned.
* You can use a single connection node
* to do multithreaded db operations like insert/delete/find of records.
* But you need to wait for all the operating threads to complete i.e
* pthread_join() and then do fini_db() to kill the connection node.
* gfdb_conn_node_t is an opaque structure.
* */
struct gfdb_conn_node_t {
gfdb_connection_t gfdb_connection;
struct list_head conn_list;
};
/*
* db_conn_list is the circular linked list which
* will have all the database connections for the process
*
* */
static gfdb_conn_node_t *db_conn_list;
/*
* db_conn_mutex is the mutex for db_conn_list
*
* */
static pthread_mutex_t db_conn_mutex = PTHREAD_MUTEX_INITIALIZER;
/*Checks the sanity of the connection node*/
#define CHECK_CONN_NODE(_conn_node) \
do { \
GF_ASSERT(_conn_node); \
GF_ASSERT(_conn_node->gfdb_connection.gf_db_connection); \
} while (0)
/* Checks the sanity of the connection node and goto */
#define CHECK_CONN_NODE_GOTO(_conn_node, label) \
do { \
if (!_conn_node) { \
goto label; \
}; \
if (!_conn_node->gfdb_connection.gf_db_connection) { \
goto label; \
}; \
} while (0)
/*Check if the conn node is first in the list*/
#define IS_FIRST_NODE(db_conn_list, _conn_node) \
((_conn_node == db_conn_list) ? _gf_true : _gf_false)
/*Check if the conn node is the only node in the list*/
#define IS_THE_ONLY_NODE(_conn_node) \
((_conn_node->conn_list.next == _conn_node->conn_list.prev) ? _gf_true \
: _gf_false)
/*Internal Function: Adds connection node to the end of
* the db connection list.*/
static int
add_connection_node(gfdb_conn_node_t *_conn_node)
{
int ret = -1;
GF_ASSERT(_conn_node);
/*Lock the list*/
ret = pthread_mutex_lock(&db_conn_mutex);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ret, LG_MSG_LOCK_LIST_FAILED,
"Failed lock db connection "
"list %s",
strerror(ret));
ret = -1;
goto out;
}
if (db_conn_list == NULL) {
db_conn_list = _conn_node;
} else {
list_add_tail(&_conn_node->conn_list, &db_conn_list->conn_list);
}
/*unlock the list*/
ret = pthread_mutex_unlock(&db_conn_mutex);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ret, LG_MSG_UNLOCK_LIST_FAILED,
"Failed unlock db "
"connection list %s",
strerror(ret));
ret = -1;
goto out;
/*TODO What if the unlock fails.
* Will it lead to deadlock?
* Most of the gluster code
* no check for unlock or destroy of mutex!*/
}
ret = 0;
out:
return ret;
}
/*Internal Function:
* Delete connection node from the list*/
static int
delete_conn_node(gfdb_conn_node_t *_conn_node)
{
int ret = -1;
GF_ASSERT(_conn_node);
/*Lock of the list*/
ret = pthread_mutex_lock(&db_conn_mutex);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ret, LG_MSG_LOCK_LIST_FAILED,
"Failed lock on db connection"
" list %s",
strerror(ret));
goto out;
}
/*Remove the connection object from list*/
if (IS_THE_ONLY_NODE(_conn_node)) {
db_conn_list = NULL;
GF_FREE(_conn_node);
} else {
if (IS_FIRST_NODE(db_conn_list, _conn_node)) {
db_conn_list = list_entry(db_conn_list->conn_list.next,
gfdb_conn_node_t, conn_list);
}
list_del(&_conn_node->conn_list);
GF_FREE(_conn_node);
}
/*Release the list lock*/
ret = pthread_mutex_unlock(&db_conn_mutex);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_WARNING, ret, LG_MSG_UNLOCK_LIST_FAILED,
"Failed unlock on db "
"connection list %s",
strerror(ret));
/*TODO What if the unlock fails.
* Will it lead to deadlock?
* Most of the gluster code
* no check for unlock or destroy of mutex!*/
ret = -1;
goto out;
}
ret = 0;
out:
return ret;
}
/*Internal function: Used initialize/map db operation of
* specified type of db plugin*/
static int
init_db_operations(gfdb_db_type_t gfdb_db_type,
gfdb_db_operations_t *gfdb_db_operations)
{
int ret = -1;
GF_ASSERT(gfdb_db_operations);
/*Clear the gfdb_db_operations*/
gfdb_db_operations = memset(gfdb_db_operations, 0,
sizeof(*gfdb_db_operations));
switch (gfdb_db_type) {
case GFDB_SQLITE3:
gf_sqlite3_fill_db_operations(gfdb_db_operations);
ret = 0;
break;
case GFDB_HYPERDEX:
case GFDB_HASH_FILE_STORE:
case GFDB_ROCKS_DB:
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_UNSUPPORTED_PLUGIN,
"Plugin not supported");
break;
case GFDB_INVALID_DB:
case GFDB_DB_END:
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_INVALID_DB_TYPE,
"Invalid DB Type");
break;
}
return ret;
}
/******************************************************************************
*
* LIBGFDB API Functions
*
* ****************************************************************************/
/*Libgfdb API Function: Used to initialize a db connection
* (Constructor function for db connection object)
* Arguments:
* args : Dictionary containing database specific parameters
* eg: For sqlite3, pagesize, cachesize, db name, db path
etc
* gfdb_db_type : Type of data base used i.e sqlite or hyperdex etc
* Returns : if successful return the GFDB Connection node to the caller or
* NULL in case of failure*/
gfdb_conn_node_t *
init_db(dict_t *args, gfdb_db_type_t gfdb_db_type)
{
int ret = -1;
gfdb_conn_node_t *_conn_node = NULL;
gfdb_db_operations_t *db_operations_t = NULL;
/*Create data base connection object*/
_conn_node = GF_CALLOC(1, sizeof(gfdb_conn_node_t), gf_mt_db_conn_node_t);
if (!_conn_node) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
"Failed mem alloc for "
"gfdb_conn_node_t");
goto alloc_failed;
}
/*Init the list component of db connection object*/
INIT_LIST_HEAD(&_conn_node->conn_list);
/*Add created connection node to the list*/
ret = add_connection_node(_conn_node);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_ADD_TO_LIST_FAILED,
"Failed to add connection "
"node to list");
goto _conn_failed;
}
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
/*init the db ops object of db connection object*/
ret = init_db_operations(gfdb_db_type, db_operations_t);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_INIT_DB_FAILED,
"Failed initializing database "
"operation failed.");
goto init_db_failed;
}
/*Calling the init_db_op of the respected db type*/
GF_ASSERT(db_operations_t->init_db_op);
ret = db_operations_t->init_db_op(
args, &_conn_node->gfdb_connection.gf_db_connection);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_INIT_DB_FAILED,
"Failed initializing database");
goto init_db_failed;
}
_conn_node->gfdb_connection.gfdb_db_type = gfdb_db_type;
return _conn_node;
/*****Error Handling********/
/* If init_db_operations or init_db of plugin failed delete
* conn node from the list.
* connection node will be free by delete_conn_node*/
init_db_failed:
ret = delete_conn_node(_conn_node);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DELETE_FROM_LIST_FAILED,
"Failed deleting "
"connection node from list");
}
return NULL;
/*if adding to the list failed free connection node*/
_conn_failed:
GF_FREE(_conn_node);
/*if allocation failed*/
alloc_failed:
return NULL;
/*****Error Handling********/
}
/*Libgfdb API Function: Used to terminate/de-initialize db connection
* (Destructor function for db connection object)
* Arguments:
* _conn_node : GFDB Connection node
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
fini_db(gfdb_conn_node_t *_conn_node)
{
int ret = -1;
gfdb_db_operations_t *db_operations_t = NULL;
CHECK_CONN_NODE_GOTO(_conn_node, empty);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
GF_ASSERT(db_operations_t->fini_db_op);
ret = db_operations_t->fini_db_op(
&_conn_node->gfdb_connection.gf_db_connection);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_CLOSE_CONNECTION_FAILED,
"Failed close the db "
"connection");
goto out;
}
ret = delete_conn_node(_conn_node);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DELETE_FROM_LIST_FAILED,
"Failed deleting "
"connection node from list");
}
empty:
ret = 0;
out:
return ret;
}
/*Libgfdb API Function: Used to insert/update records in the database
* NOTE: In current gfdb_sqlite plugin we use that
* same function to delete the record. Set the
* gfdb_fop_path to GFDB_FOP_UNDEL to delete the
* link of inode from GF_FLINK_TB and
* GFDB_FOP_UNDEL_ALL to delete all the records from
* GF_FLINK_TB and GF_FILE_TB.
* TODO: Should separate this function into the
* delete_record function
* Refer CTR Xlator features/changetimerecorder for usage
* Arguments:
* _conn_node : GFDB Connection node
* gfdb_db_record : Record to be inserted/updated
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
insert_record(gfdb_conn_node_t *_conn_node, gfdb_db_record_t *gfdb_db_record)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->insert_record_op) {
ret = db_operations_t->insert_record_op(gf_db_connection,
gfdb_db_record);
if (ret) {
gf_msg(GFDB_DATA_STORE,
_gfdb_log_level(GF_LOG_ERROR, gfdb_db_record->ignore_errors),
0, LG_MSG_INSERT_OR_UPDATE_FAILED,
"Insert/Update"
" operation failed");
}
}
return ret;
}
/*Libgfdb API Function: Used to delete record from the database
* NOTE: In the current gfdb_sqlite3 plugin
* implementation this function is dummy.
* Use the insert_record function.
* Refer CTR Xlator features/changetimerecorder for usage
* Arguments:
* _conn_node : GFDB Connection node
* gfdb_db_record : Record to be deleted
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
delete_record(gfdb_conn_node_t *_conn_node, gfdb_db_record_t *gfdb_db_record)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->delete_record_op) {
ret = db_operations_t->delete_record_op(gf_db_connection,
gfdb_db_record);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DELETE_FAILED,
"Delete operation "
"failed");
}
}
return ret;
}
/*Libgfdb API Function: Compact the database.
*
* Arguments:
* _conn_node : GFDB Connection node
* _compact_active : Is compaction currently on?
* _compact_mode_switched : Was the compaction switch flipped?
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
compact_db(gfdb_conn_node_t *_conn_node, gf_boolean_t _compact_active,
gf_boolean_t _compact_mode_switched)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->compact_db_op) {
ret = db_operations_t->compact_db_op(gf_db_connection, _compact_active,
_compact_mode_switched);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_COMPACT_FAILED,
"Compaction operation "
"failed");
}
}
return ret;
}
/*Libgfdb API Function: Query all the records from the database
* Arguments:
* _conn_node : GFDB Connection node
* query_callback : Call back function that will be called
* for every record found
* _query_cbk_args : Custom argument passed for the call back
* function query_callback
* query_limit : number to limit number of rows returned by the query
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
find_all(gfdb_conn_node_t *_conn_node, gf_query_callback_t query_callback,
void *_query_cbk_args, int query_limit)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->find_all_op) {
ret = db_operations_t->find_all_op(gf_db_connection, query_callback,
_query_cbk_args, query_limit);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Find all operation "
"failed");
}
}
return ret;
}
/*Libgfdb API Function: Query records/files that have not changed/accessed
* from a time in past to current time
* Arguments:
* _conn_node : GFDB Connection node
* query_callback : Call back function that will be called
* for every record found
* _query_cbk_args : Custom argument passed for the call back
* function query_callback
* for_time : Time from where the file/s are not
* changed/accessed
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
find_unchanged_for_time(gfdb_conn_node_t *_conn_node,
gf_query_callback_t query_callback,
void *_query_cbk_args, gfdb_time_t *for_time)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->find_unchanged_for_time_op) {
ret = db_operations_t->find_unchanged_for_time_op(
gf_db_connection, query_callback, _query_cbk_args, for_time);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Find unchanged "
"operation failed");
}
}
return ret;
}
/*Libgfdb API Function: Query records/files that have changed/accessed from a
* time in past to current time
* Arguments:
* _conn_node : GFDB Connection node
* query_callback : Call back function that will be called
* for every record found
* _query_cbk_args : Custom argument passed for the call back
* function query_callback
* for_time : Time from where the file/s are
* changed/accessed
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
find_recently_changed_files(gfdb_conn_node_t *_conn_node,
gf_query_callback_t query_callback,
void *_query_cbk_args, gfdb_time_t *from_time)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->find_recently_changed_files_op) {
ret = db_operations_t->find_recently_changed_files_op(
gf_db_connection, query_callback, _query_cbk_args, from_time);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Find changed operation failed");
}
}
return ret;
}
/*Libgfdb API Function: Query records/files that have not changed/accessed
* from a time in past to current time, with
* a desired frequency
* Arguments:
* _conn_node : GFDB Connection node
* query_callback : Call back function that will be called
* for every record found
* _query_cbk_args : Custom argument passed for the call back
* function query_callback
* for_time : Time from where the file/s are not
* changed/accessed
* write_freq_thresold : Desired Write Frequency lower limit
* read_freq_thresold : Desired Read Frequency lower limit
* _clear_counters : If true, Clears all the frequency counters of
* all files.
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
find_unchanged_for_time_freq(gfdb_conn_node_t *_conn_node,
gf_query_callback_t query_callback,
void *_query_cbk_args, gfdb_time_t *for_time,
int write_freq_thresold, int read_freq_thresold,
gf_boolean_t _clear_counters)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->find_unchanged_for_time_freq_op) {
ret = db_operations_t->find_unchanged_for_time_freq_op(
gf_db_connection, query_callback, _query_cbk_args, for_time,
write_freq_thresold, read_freq_thresold, _clear_counters);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Find unchanged with freq operation failed");
}
}
return ret;
}
/*Libgfdb API Function: Query records/files that have changed/accessed from a
* time in past to current time, with
* a desired frequency
* Arguments:
* _conn_node : GFDB Connection node
* query_callback : Call back function that will be called
* for every record found
* _query_cbk_args : Custom argument passed for the call back
* function query_callback
* for_time : Time from where the file/s are
* changed/accessed
* write_freq_thresold : Desired Write Frequency lower limit
* read_freq_thresold : Desired Read Frequency lower limit
* _clear_counters : If true, Clears all the frequency counters of
* all files.
* Returns : if successful return 0 or
* -ve value in case of failure*/
int
find_recently_changed_files_freq(gfdb_conn_node_t *_conn_node,
gf_query_callback_t query_callback,
void *_query_cbk_args, gfdb_time_t *from_time,
int write_freq_thresold,
int read_freq_thresold,
gf_boolean_t _clear_counters)
{
int ret = 0;
gfdb_db_operations_t *db_operations_t = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(_conn_node);
db_operations_t = &_conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = _conn_node->gfdb_connection.gf_db_connection;
if (db_operations_t->find_recently_changed_files_freq_op) {
ret = db_operations_t->find_recently_changed_files_freq_op(
gf_db_connection, query_callback, _query_cbk_args, from_time,
write_freq_thresold, read_freq_thresold, _clear_counters);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Find changed with freq operation failed");
}
}
return ret;
}
/*Libgfdb API Function: Clear the heat for all the files
*
* Arguments:
* conn_node : GFDB Connection node
*
* Returns : if successful return 0 or
* -ve value in case of failure
**/
int
clear_files_heat(gfdb_conn_node_t *conn_node)
{
int ret = 0;
gfdb_db_operations_t *db_operations = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(conn_node);
db_operations = &conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = conn_node->gfdb_connection.gf_db_connection;
if (db_operations->clear_files_heat_op) {
ret = db_operations->clear_files_heat_op(gf_db_connection);
if (ret) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0,
LG_MSG_INSERT_OR_UPDATE_FAILED,
"Clear files heat operation failed");
}
}
return ret;
}
/* Libgfdb API Function: Function to extract version of the db
* Input:
* gfdb_conn_node_t *conn_node : GFDB Connection node
* char **version : the version is extracted as a string and will be stored in
* this variable. The freeing of the memory should be done by
* the caller.
* Return:
* On success return the length of the version string that is
* extracted.
* On failure return -1
* */
int
get_db_version(gfdb_conn_node_t *conn_node, char **version)
{
int ret = 0;
gfdb_db_operations_t *db_operations = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(conn_node);
db_operations = &conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = conn_node->gfdb_connection.gf_db_connection;
if (db_operations->get_db_version) {
ret = db_operations->get_db_version(gf_db_connection, version);
if (ret < 0) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Get version failed");
}
}
return ret;
}
int
get_db_params(gfdb_conn_node_t *conn_node, char *param_key, char **param_value)
{
int ret = -1;
gfdb_db_operations_t *db_operations = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(conn_node);
db_operations = &conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = conn_node->gfdb_connection.gf_db_connection;
if (db_operations->get_db_params) {
ret = db_operations->get_db_params(gf_db_connection, param_key,
param_value);
if (ret < 0) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_FIND_OP_FAILED,
"Get setting failed");
}
}
return ret;
}
int
set_db_params(gfdb_conn_node_t *conn_node, char *param_key, char *param_value)
{
int ret = -1;
gfdb_db_operations_t *db_operations = NULL;
void *gf_db_connection = NULL;
CHECK_CONN_NODE(conn_node);
db_operations = &conn_node->gfdb_connection.gfdb_db_operations;
gf_db_connection = conn_node->gfdb_connection.gf_db_connection;
if (db_operations->set_db_params) {
ret = db_operations->set_db_params(gf_db_connection, param_key,
param_value);
if (ret < 0) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0,
LG_MSG_INSERT_OR_UPDATE_FAILED,
"Failed to set database setting");
}
}
return ret;
}
static const char *
get_db_path_key()
{
return GFDB_SQL_PARAM_DBPATH;
}
void
get_gfdb_methods(gfdb_methods_t *methods)
{
methods->init_db = init_db;
methods->fini_db = fini_db;
methods->find_all = find_all;
methods->find_unchanged_for_time = find_unchanged_for_time;
methods->find_recently_changed_files = find_recently_changed_files;
methods->find_unchanged_for_time_freq = find_unchanged_for_time_freq;
methods
->find_recently_changed_files_freq = find_recently_changed_files_freq;
methods->clear_files_heat = clear_files_heat;
methods->get_db_version = get_db_version;
methods->get_db_params = get_db_params;
methods->set_db_params = set_db_params;
methods->get_db_path_key = get_db_path_key;
/* Query Record related functions */
methods->gfdb_query_record_new = gfdb_query_record_new;
methods->gfdb_query_record_free = gfdb_query_record_free;
methods->gfdb_add_link_to_query_record = gfdb_add_link_to_query_record;
methods->gfdb_write_query_record = gfdb_write_query_record;
methods->gfdb_read_query_record = gfdb_read_query_record;
/* Link info related functions */
methods->gfdb_link_info_new = gfdb_link_info_new;
methods->gfdb_link_info_free = gfdb_link_info_free;
/* Compaction related functions */
methods->compact_db = compact_db;
}