jeudi 7 janvier 2016

SQLite Virtual table for querying lists of custom datatypes

Consider the class listed below:

typedef enum{
    TYPE_ERROR = 0,
    TYPE_WARNING = 1,
    TYPE_INFO = 2,
    TYPE_MAX
}TYPE_E;

typedef enum{
    STATE_INITIALIZED = 0,
    STATE_RUNNING = 1,
    STATE_EXPIRED = 2,
    STATE_UNKNOWN
}STATE_E;


class CustDataType
{
public :

    ~CustDataType(void);
    CustDataType(int p_ID, TYPE_E p_Type, STATE_E stateIn);

    int GetID(void) const;
    string GetName(void) const;
    TYPE_E GetType(void) const;
    STATE_E GetState(void) const;

    void DisplayDetails(void);
    static void CreatesList(vector<CustDataType*> &p_vecsIn);
    static void DestroysList(vector<CustDataType*> &p_vecsIn);
    static void DisplaysList(vector<CustDataType*> &p_vecsIn);

private :

    int m_nID;
    string m_strName;
    TYPE_E m_nType;
    STATE_E m_nState;
};

The usecase is to implement a search filter using SQL like sysntax. SQLite virtual tables came in handy and was implemented. The issue I face now is how I can extend the implementation to other types of classes in my project which are similar to the class listed above but have different properties. SQLite VTable module requires CSTyle static function pointers. Hence I cannot pass my class's methods. So I need a mechanism to redirect calls to my objects based on the type that has been created from the module callbacks. I noticed 2 things:

  1. SQlite VTable documentation at http://ift.tt/1ornQib suggests that the sqlite3_vtab structure needs to be subclassed in custom VTable implementations.
  2. All callbacks receive the sqlite3_vtab either directly or via sqlite3_vtab_cursor.

Accordingly I derived a class from sqlite3_vtab

class CustDataFilter : public sqlite3_vtab
{
    public:
        int iVersion;
        int CreateVirtualTable(sqlite3*, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char**);
        int ConnectVirtualTable(sqlite3*, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char**);
        int BestIndexVirtualTable(sqlite3_vtab *pVTab, sqlite3_index_info*);
        int DisconnectVirtualTable(sqlite3_vtab *pVTab);
        int DestroyVirtualTable(sqlite3_vtab *pVTab);
        int OpenCursor(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
        int CloseCursor(sqlite3_vtab_cursor*);
        int FilterVirtualTable(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, int argc, sqlite3_value **argv);
        int NextRow(sqlite3_vtab_cursor*);
        int Eof(sqlite3_vtab_cursor*);
        int GetCurrentColumnValue(sqlite3_vtab_cursor*, sqlite3_context*, int);
        int GetRow(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid);

        int FindFunctionVirtualTable(sqlite3_vtab *pVtab, int nArg, const char *zName,
                             void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                             void **ppArg);
        void MatchFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv);
        int DestructorVirtualTable(sqlite3_vtab *pVtab);
        int CallbackResultSet(void *data, int argc, char **argv, char **azColName);

    private: 
        vector<CustDataType> m_DataList;
}

Created the object and passed it as a void * to the sqlite3_create_module call.

int main()
{    
    // Register Alarm Module
    if(registerModule(m_pDatabase) != SQLITE_OK)
    {
        fprintf(stderr, "Failed to register alarm module\n");
        //return SQLITE_ERROR;
    }

    // Create it. 
    rc = sqlite3_exec(m_pDatabase, "CREATE VIRTUAL TABLE FOR CustDataType", NULL, NULL, &msg);
    if(rc != SQLITE_OK)
    { 
        printf("ERROR: %s\n", msg);
    }
}
int registerModule(sqlite3 *db, CustDataFilter *filterIn)
{
    return sqlite3_create_module(db, "ALARM_MODULE", &alarm_module, (void *)filterIn);
}

Finally in the xcreate callback I receive my object which I passed to sqlite as below:

int MyModule::CreateVirtualTable( sqlite3 *db,
                      void *p_aux,
                      int argc, const char *const*argv,
                      sqlite3_vtab **pp_vt,
                      char **pzErr )                      
{
    *pp_vt = (sqlit3_vtab *) (p_aux);
}

In the other call backs I want to recast the sqlite3_vtab object received to my CustDataFilter type and call its relevant methods. I see that I receive an object with the same address that was created. However when I cast this using C-style cast or dynamic_cast or reinterpret_cast I get an access violation exception when trying to call the object's methods. So my question is

  1. Is the approach correct? I am using this approach so I can extend a single module implementation to multiple data types.
  2. If correct what am I doing wrong to get the access violation exception?

If the approach is wrong please suggest an alternate one.

Aucun commentaire:

Enregistrer un commentaire