python / python/cpython

SQLite3 threadsafety property should return 2 for serialized

Open
#123,873 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

topic-sqlite3 type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

If the underlying SQLite library sqlite3_threadsafe() function returns 1 (i.e. SERIALIZED) the sqlite3.threadsafety property returns 3, meaning that cursor can be shared between threads.
The sqlite3 module cannot really share cursors between threads, though.
Sometimes it ends up in check_cursor_locked(), more often it segfaults.
Given the dubious value of sharing cursors between threads, introducing the locks needed to support level 3 seems unnecessary.
Until #118172 is fixed, sqlite3.threadsafety should return 1 (share module only).

Reproducible with a test program similar to the one in #118172

import sqlite3
import threading

numthreads=10
KB = None
threadcursor = None
print(f"sqlite3.threadsafety: {sqlite3.threadsafety}")

def execute_query():
    threadcursor.execute("SELECT * FROM test_table")
    result = threadcursor.fetchall()
    assert result == [(1, 'test1'), (2, 'test2'), (3, 'test3')], str(result)
    return result

def run_threads():
    global threadcursor
    threadcursor = KB.cursor()
    threads = []
    for i in range(numthreads + 1):
        thread = threading.Thread(target=execute_query)
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

def test_multithreading():
    global KB
 
    KB = sqlite3.connect(
        "example.db", check_same_thread=False
    )
    cursor = KB.cursor()

    cursor.execute(
        """CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, value TEXT)"""
    )
    KB.commit()

    cursor.execute("""DELETE FROM test_table""")
    KB.commit()

    cursor.execute("""INSERT INTO test_table (value) VALUES ('test1')""")
    cursor.execute("""INSERT INTO test_table (value) VALUES ('test2')""")
    cursor.execute("""INSERT INTO test_table (value) VALUES ('test3')""")
    KB.commit()

    run_threads()
    KB.close()


if __name__ == "__main__":
    test_multithreading()
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-124316

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the sqlite3.threadsafety property and the sqlite3_threadsafe() mapping described in the report, then run the supplied multithreading reproducer. Review linked PR gh-124316 and its tests; done means the reported threadsafety value and cursor-sharing behavior match the agreed resolution without the demonstrated failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.