Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get an error kind from sqlite create_function?

Tags:

python

sqlite

I have database with a single table Person which has a name(str) and age(int) columns. So, I create simple validate function for my sqlite3.connection

def adult(age):
    return age > 18

And with following code, it works fine

connection = sqlite3.connect(r'C:\Dev\Garbage\database.db')
with connection:
    connection.create_function('adult', 1, adult)
    cursor = connection.cursor()
    persons = cursor.execute('select "p"."name", "p"."age" from "Person" "p" where adult("p"."age")').fetchall()
    for person in persons:
        print(person)

But if I change adult like this

def adult(age):
    return 1 / 0

I will get sqlite3.OperationalError: user-defined function raised exception. In my project, it might be a huge amount of functions and I'd like to know - is there any way to know which function raised an exception? Or get ZeroDivisionError: division by zero instead of this.

like image 240
sashaaero Avatar asked Mar 08 '23 06:03

sashaaero


1 Answers

Python's sqlite3 module throws away any error information from the exception, and replaces it with the constant message you've seen:

void _pysqlite_func_callback(sqlite3_context* context, int argc, sqlite3_value** argv)
{
    ...
    py_func = (PyObject*)sqlite3_user_data(context);

    args = _pysqlite_build_py_params(context, argc, argv);
    if (args) {
        py_retval = PyObject_CallObject(py_func, args);
        Py_DECREF(args);
    }

    ok = 0;
    if (py_retval) {
        ok = _pysqlite_set_result(context, py_retval) == 0;
        Py_DECREF(py_retval);
    }
    if (!ok) {
        if (_enable_callback_tracebacks) {
            PyErr_Print();
        } else {
            PyErr_Clear();
        }
        sqlite3_result_error(context, "user-defined function raised exception", -1);
    }
    ...
}

I don't know what prevents it from appending the exception message to the returned error message.

Anyway, it is possible to print out the inner stack trace by calling enable_callback_tracebacks:

import sqlite3

db = sqlite3.connect(':memory:')

def error():
    raise Exception('hello')

db.create_function('error', 0, error)

sqlite3.enable_callback_tracebacks(True)   # <-- !

db.execute('select error()')
Traceback (most recent call last):
  File "<stdin>", line 1, in error
Exception: hello
Traceback (most recent call last):
  File "<stdin>", line 1, in 
sqlite3.OperationalError: user-defined function raised exception
like image 139
CL. Avatar answered Mar 24 '23 06:03

CL.