vendredi 17 juillet 2015

How to handle errors in a select statement when attempting an insert or fail?

Is there a good way to handle errors in a select statement when attempting an insert or fail? Specifically, I want to insert elements into a table, but the select statement used to generate these elements is failing. I would like to have all the elements where the select statement succeeded to be inserted, but for the overall statement to fail. I thought that insert or fail would do this, but it does not. More specifically, imagine if we defined a new SQLite function "log"

#include <string>
#include <sqlite3ext.h>
#include <cmath>
SQLITE_EXTENSION_INIT1

extern "C" {
    int sqlite3_log_init(
        sqlite3 * db,
        char ** err,
        sqlite3_api_routines const * const api
    );
}

// Compute the log of the input
void mylog( 
    sqlite3_context *context,
    int argc,
    sqlite3_value **argv
){
    // Grab the number
    auto num = sqlite3_value_double(argv[0]);

    // If positive, take the log
    if(num > 0.) 
        sqlite3_result_double(context, log(num));

    // Otherwise, throw an error
    else {
        auto msg = std::string("Can't take the log of a nonpositive number");
        sqlite3_result_error(context,msg.c_str(),msg.size());
    }
}

// Initialize the functions
int sqlite3_log_init(
    sqlite3 *db,
    char **err,
    sqlite3_api_routines const * const api
){
    SQLITE_EXTENSION_INIT2(api)

    // Register the log function
    if( int ret = sqlite3_create_function(
        db, "log", 1, SQLITE_ANY, 0, mylog, 0, 0)
    ) {
        *err=sqlite3_mprintf("Error registering log: %s",
            sqlite3_errmsg(db));
        return ret;
    }

    // If we've made it this far, we should be ok
    return SQLITE_OK;
}

This can be compiled with

g++ -std=c++14 log.cpp -shared -o log.so -fPIC

Basically, the above function takes the log of its element. For example,

sqlite> select log(1);
0.0
sqlite> select log(0);
Error: Can't take the log of a nonpositve number

Now, consider the following sequence of SQL operations

sqlite> .load "./log.so"
sqlite> create table foo (num real);
sqlite> insert into foo values (2.), (1.), (0.);
sqlite> create table bar (num real);
sqlite> insert or fail into bar select log(num) from foo;
Error: Can't take the log of a nonpositve number
sqlite> select * from bar;
sqlite> 

Basically, the table bar is empty because the select statement failed on 0. What I want to have happen is for the table bar to contain the elements log(2.) and log(1.), but the error to still be thrown. Is there a way to have that happen?

Aucun commentaire:

Enregistrer un commentaire