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

SQLite Virtual table for querying lists of custom datatypes

本文关键字:SQLite 虚拟 列表 查询 定义数据类型 用于      更新时间:2023-10-16

考虑下面列出的类:

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. SQlite VTable文档https://www.sqlite.org/vtab.html建议sqlite3_vtab结构需要在自定义VTable实现中进行子类化
  2. 所有回调都直接或通过sqlite3_vtab_cursor接收sqlite3_vtab

因此,我从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 modulen");
        //return SQLITE_ERROR;
    }
    // Create it. 
    rc = sqlite3_exec(m_pDatabase, "CREATE VIRTUAL TABLE FOR CustDataType", NULL, NULL, &msg);
    if(rc != SQLITE_OK)
    { 
        printf("ERROR: %sn", 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);
}

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

  1. 这种方法正确吗?我使用这种方法是为了将单个模块实现扩展到多个数据类型
  2. 如果正确,我做错了什么来获得访问违规异常

如果方法错误,请建议另一种方法。

我们做了更多的挖掘并找到了解决方案。基本方法很好,但需要进行一些微调。我们没有直接从sqlite3_vtab派生类层次结构,而是创建了一个从sqlite_3_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 modulen");
        //return SQLITE_ERROR;
    }
    // Create it. 
    rc = sqlite3_exec(m_pDatabase, "CREATE VIRTUAL TABLE FOR CustDataType", NULL, NULL, &msg);
    if(rc != SQLITE_OK)
    { 
        printf("ERROR: %sn", 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;
}

我对sqlite3虚拟表和模块做了很多工作。

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

举个例子(只是回调逻辑),我实现了一个sqlite3虚拟表模块,该模块与libpng 接口

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

去掉很多多余的东西。。回电看起来像这样:

外部"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问题总是让我感兴趣,但有时我不清楚所问的问题。