如何在SQLite中处理Blob时处理类型错误

How to handle type errors when working with blobs in SQLite?

本文关键字:处理 Blob 类型 错误 SQLite      更新时间:2023-10-16

在SQLite中使用Blob时,是否有一种处理类型错误的好方法?例如,以下代码注册了两个函数create_vectordisplay_vector。基本上,create_vectorstd::vector存储为blob,display_vector将该blob转换为文本,这样我们就可以看到它:

/* In order to use
sqlite> .load "./blob.so" 
sqlite> select display_vector(create_vector());
[ 1.200000, 3.400000, 5.600000, 7.800000, 9.100000 ]
*/
#include <string>
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1
extern "C" {
    int sqlite3_blob_init(
        sqlite3 * db,
        char ** err,
        sqlite3_api_routines const * const api
    );
}
// Cleanup handler that deletes an array 
template <typename T>
void array_cleanup(void * v) {
   delete [] static_cast <T *> (v);
}
// Creates and returns a std::vector as a blob
static void create_vector(
    sqlite3_context *context,
    int argc,
    sqlite3_value **argv
){
    // Create a dummy vector
    auto * v = new double[5] {1.2,3.4,5.6,7.8,9.10};
    // Either cleanup works
    sqlite3_result_blob(context,v,sizeof(double[5]),array_cleanup <double>);
}
// Converts a std::vector into text
static void display_vector(
    sqlite3_context *context,
    int argc,
    sqlite3_value **argv
){
    // Grab the vector.  Note, if this is not a vector, then sqlite will
    // almost certainly segfault.
    auto const * const v =static_cast <double const * const> (
        sqlite3_value_blob(argv[0]));
    // Assuming we have a vector, convert it into a string
    auto s = std::string("[ ");
    for(unsigned i=0;i<5;i++) {
        // If we're not on the first element, add a comma
        if(i>0) s += ", ";
        // Add the number
        s += std::to_string(v[i]);
    }
    s += " ]";
    // Return the text
    sqlite3_result_text(
        context,sqlite3_mprintf("%s",s.c_str()),s.size(),sqlite3_free);
}
// Register our blob functions
int sqlite3_blob_init(
    sqlite3 *db,
    char **err,
    sqlite3_api_routines const * const api
){
    SQLITE_EXTENSION_INIT2(api)
    // Register the create_vector function
    if( int ret = sqlite3_create_function(
        db, "create_vector", 0, SQLITE_ANY, 0, create_vector, 0, 0)
    ) {
        *err=sqlite3_mprintf("Error registering create_vector: %s",
            sqlite3_errmsg(db));
        return ret;
    }
    // Register the display_vector function
    if( int ret = sqlite3_create_function(
        db, "display_vector", 1, SQLITE_ANY, 0, display_vector, 0, 0)
    ) {
        *err=sqlite3_mprintf("Error registering display_vector: %s",
            sqlite3_errmsg(db));
        return ret;
    }
    // If we've made it this far, we should be ok
    return SQLITE_OK;
}

我们可以用编译

$ make
g++ -g -std=c++14 blob.cpp -shared -o blob.so -fPIC

现在,如果我们像广告中所说的那样使用这些功能,一切都会很好:

sqlite> .load "./blob.so"
sqlite> select display_vector(create_vector());
[ 1.200000, 3.400000, 5.600000, 7.800000, 9.100000 ]

然而,如果我们试图在非向量上使用display_vector,我们会segfault:

sqlite> .load "./blob.so"
sqlite> select display_vector(NULL);
Segmentation fault

实际上,问题是display_vector向量中的static_cast是不正确的。在任何情况下,有没有一个好的方法来检查blob的类型,甚至保证我们有一个blob?当一个新的扩展需要某种类型的输入时,有没有一个好的方法来防止segfault?

blob只是一堆字节,并不是每个值都是blob。

函数应该使用sqlite3_value_type()检查值的类型,并使用sqlite_3_value_bytes()检查长度。