7

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.

5
  • couldn't you use a try except block, and raise the ZeroDivisionError on the except? Commented Aug 22, 2017 at 18:11
  • @KevinPasquarella it's just an example. I'd like to know about any exceptions that can appear. Commented Aug 22, 2017 at 18:13
  • Do you know in advance how you want to handle any arbitrary exception that might be raised? Can you post an actual Traceback? Commented Aug 22, 2017 at 18:35
  • Seems like some combination of the traceback, inspect and logging modules might help. There are an number of SO Q&A's that may be relevant; like - stackoverflow.com/q/1095601/2823755. Or stackoverflow.com/q/5067604/2823755 Commented Aug 22, 2017 at 18:48
  • @Yes, I'd like to tell about it to the user. It's complete traceback. I mean, trace stack contains only this call. Thanks for the advice, will try to find a solution. Commented Aug 22, 2017 at 18:57

1 Answer 1

9

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.