SQLite用于查询自定义数据类型列表的虚拟表

时间:2016-01-08 03:47:38

标签: c++ sqlite

考虑下面列出的类:

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;
};

用例是使用SQL之类的sysntax实现搜索过滤器。 SQLite虚拟表派上用场并实现了。我现在面临的问题是如何将实现扩展到我的项目中的其他类型的类,这些类与上面列出的类类似,但具有不同的属性。 SQLite VTable模块需要CSTyle静态函数指针。因此,我不能通过我班级的方法。所以我需要一种机制来根据从模块回调创建的类型将调用重定向到我的对象。我发现了两件事:

  1. https://www.sqlite.org/vtab.html处的SQlite VTable文档表明sqlite3_vtab结构需要在自定义VTable实现中进行子类化。
  2. 所有回调都直接或通过sqlite3_vtab_cursor接收sqlite3_vtab。
  3. 因此我从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;
    }
    

    创建对象并将其作为void *传递给sqlite3_create_module调用。

    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);
    }
    

    最后在xcreate回调中,我收到了我传递给sqlite的对象,如下所示:

    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);
    }
    

    在其他回调中,我想重新接收收到的CustDataFilter类型的sqlite3_vtab对象并调用其相关方法。 我看到我收到的对象与创建的地址相同。但是当我使用C风格的强制转换或dynamic_cast或reinterpret_cast进行转换时,在尝试调用对象的方法时会出现访问冲突异常。 所以我的问题是

    1. 方法是否正确?我正在使用这种方法,因此我可以将单个模块实现扩展到多种数据类型。
    2. 如果正确我做错了什么来获取访问冲突异常?
    3. 如果方法有误,请提出备用方法。

2 个答案:

答案 0 :(得分:0)

我们做了更多挖掘并找到了解决方案。基本方法很好但需要一些微调。 我们创建了一个从sqlite3_vtab派生的中间结构,而不是直接从sqlite3_vtab派生我们的类层次结构。这个新结构只有一个成员,它是一个指向我们对象的void *指针。剩下的唯一工作就是将这个void *指针强制转换为我们的对象类型,并将调用重定向到我们要处理的对象。新的实现现在看起来如下:

struct CustVTab : public sqlite3_vtab
{
    void *myVtabImpl;
}

int main()
{    
    // Register Alarm Module
    CustDataFilter *pCustFilter = new CustDataFilter();
    if(registerModule(db, pCustFilter) != 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)
{
    CustVTab *theVtab = new CustVTab();
    theVTab.myVTabImpl = (void *) filterIn;
    return sqlite3_create_module(db, "ALARM_MODULE", &alarm_module, (void *)theVtab);
}

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);
}

然后在回调中获取自定义过滤器实现,如下所示:

int CAlarm_module::GetRow(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid)
{
    CustVTab *theVTab = (CustVTab *) cur->pVtab;
    CustDataFilter *pFilter = (CustDataFilter *) (theVTab->myVtabImpl);
    pFilter->NextRow(cur);

    return SQLITE_OK;
}

答案 1 :(得分:0)

我已经使用sqlite3虚拟表和模块做了很多工作。

由于它与您在此处所做的相关,您是否考虑过使用sqlite3_exec的回调来处理结果?

作为示例(仅仅是回调逻辑),我实现了一个与libpng接口的sqlite3虚拟表模块

SELECT语句的查询结果包含图像文件中的数据。

剥离了很多额外的东西..回调看起来像这样:

extern“C”{

    /*!
    * SqlCore's callback function for sqlite3_exec(...)
    *
    */
    static int sqlite3_exec_cb(void* pUserData, int numColumns, 
                               char** pColumns, 
                               char** pColumnNames) {
      QUrl* image_url = (QUrl*)(pUserData);
      return SQLITE_OK;
    }

};

并且用这样的东西迷上了这个过程:

        rc = sqlite3_exec(_db,
                          qPrintable(query_str),
                          &sqlite3_exec_cb, &image_url, &err);
        if ( rc != SQLITE_OK ) {
          sqlite3_free(err);
          throw std::runtime_error("");
        }

**我为不完整性和未定义函数的引用道歉。 Sqlite3问题一直让我感兴趣 - 但有时我不清楚被问到的问题。