ZetCode

Python sqlite3.Connection.enable_load_extension Method

Last modified April 15, 2025

This comprehensive guide explores Python's enable_load_extension method for SQLite databases. It allows loading SQLite extensions to add functionality.

Basic Definitions

The enable_load_extension method enables or disables the ability to load SQLite extensions. By default, this feature is disabled for security reasons.

SQLite extensions can add new functions, collations, virtual tables, and more. They are typically compiled as shared libraries (.so, .dll, or .dylib files).

Enabling Extension Loading

This basic example shows how to enable extension loading for a connection.

enable_extension.py
import sqlite3

with sqlite3.connect(':memory:') as conn:
    # Enable extension loading
    conn.enable_load_extension(True)
    
    # Verify extension loading is enabled
    cursor = conn.cursor()
    cursor.execute("PRAGMA compile_options")
    options = [row[0] for row in cursor.fetchall()]
    print("Extension loading enabled:", "ENABLE_LOAD_EXTENSION" in options)

This example creates an in-memory database and enables extension loading. It then verifies the feature is active by checking SQLite's compile options.

The enable_load_extension(True) call must come before any attempt to load extensions. The setting is per-connection.

Loading a Simple Extension

This example demonstrates loading a simple extension that adds math functions.

load_extension.py
import sqlite3
import os

# Path to SQLite math extension (adjust for your system)
extension_path = os.path.join('extensions', 'math.so')

with sqlite3.connect(':memory:') as conn:
    conn.enable_load_extension(True)
    
    try:
        # Load the extension
        conn.load_extension(extension_path)
        
        # Use the extension's functions
        cursor = conn.cursor()
        cursor.execute("SELECT sqrt(25), power(2, 8)")
        print(cursor.fetchone())  # (5.0, 256.0)
    except sqlite3.OperationalError as e:
        print(f"Failed to load extension: {e}")

This attempts to load a math extension and use its functions. The exact path to the extension will vary by system and SQLite installation.

Extensions must be compiled for your specific SQLite version and platform. Loading fails if the extension is incompatible or not found.

Loading Multiple Extensions

Multiple extensions can be loaded into a single database connection.

multiple_extensions.py
import sqlite3
import os

extensions = [
    os.path.join('extensions', 'math.so'),
    os.path.join('extensions', 'regex.so'),
    os.path.join('extensions', 'stats.so')
]

with sqlite3.connect(':memory:') as conn:
    conn.enable_load_extension(True)
    
    for ext in extensions:
        try:
            conn.load_extension(ext)
            print(f"Loaded {os.path.basename(ext)}")
        except sqlite3.OperationalError:
            print(f"Failed to load {os.path.basename(ext)}")
    
    # Verify loaded extensions
    cursor = conn.cursor()
    cursor.execute("SELECT 'regex' REGEXP '^r.*x$'")
    print("Regex test:", cursor.fetchone()[0])  # 1 (true)

This example attempts to load three different extensions. Each extension adds its own functionality to the SQLite environment.

Extensions are loaded in order and may depend on each other. Load fundamental extensions first if there are dependencies.

Extension Loading with Error Handling

Proper error handling is essential when working with extensions.

error_handling.py
import sqlite3
import os

def safe_load_extension(conn, path):
    try:
        conn.load_extension(path)
        return True
    except sqlite3.OperationalError as e:
        print(f"Error loading {os.path.basename(path)}: {e}")
        return False

with sqlite3.connect(':memory:') as conn:
    conn.enable_load_extension(True)
    
    # Attempt to load extensions with error handling
    extensions = ['math.so', 'nonexistent.so', 'invalid_ext.so']
    loaded = 0
    
    for ext in extensions:
        if safe_load_extension(conn, ext):
            loaded += 1
    
    print(f"Successfully loaded {loaded}/{len(extensions)} extensions")

This example shows robust error handling when loading extensions. It continues even if some extensions fail to load.

The safe_load_extension helper function encapsulates the error handling logic for cleaner code.

Disabling Extension Loading

Extension loading can be disabled after use for security.

disable_extension.py
import sqlite3
import os

with sqlite3.connect(':memory:') as conn:
    # Enable and use extensions
    conn.enable_load_extension(True)
    conn.load_extension(os.path.join('extensions', 'math.so'))
    
    # Perform operations with extensions
    cursor = conn.cursor()
    cursor.execute("SELECT sqrt(144)")
    print("Square root:", cursor.fetchone()[0])  # 12.0
    
    # Disable extension loading
    conn.enable_load_extension(False)
    
    try:
        # This will now fail
        conn.load_extension(os.path.join('extensions', 'regex.so'))
    except sqlite3.OperationalError:
        print("Extension loading correctly disabled")

This example enables extension loading, uses an extension, then disables it. Attempting to load another extension after disabling fails as expected.

Disabling extension loading when done is a security best practice, especially in web applications or untrusted environments.

Checking Extension Loading Status

You can check whether extension loading is currently enabled.

check_status.py
import sqlite3

def is_extension_loading_enabled(conn):
    cursor = conn.cursor()
    cursor.execute("PRAGMA compile_options")
    options = [row[0] for row in cursor.fetchall()]
    return "ENABLE_LOAD_EXTENSION" in options

with sqlite3.connect(':memory:') as conn:
    # Initial state (default is disabled)
    print("Default state:", is_extension_loading_enabled(conn))  # False
    
    # Enable and verify
    conn.enable_load_extension(True)
    print("After enabling:", is_extension_loading_enabled(conn))  # True
    
    # Disable and verify
    conn.enable_load_extension(False)
    print("After disabling:", is_extension_loading_enabled(conn))  # False

This example demonstrates how to check the current extension loading status. The status is per-connection and can change during the connection's lifetime.

The check uses SQLite's PRAGMA compile_options to inspect the current configuration. This is more reliable than tracking the state manually.

Loading Extensions with Entry Points

Some extensions require specific entry points when loading.

entry_point.py
import sqlite3
import os

with sqlite3.connect(':memory:') as conn:
    conn.enable_load_extension(True)
    
    # Load extension with specific entry point
    extension_path = os.path.join('extensions', 'complex.so')
    entry_point = 'sqlite3_complex_init'
    
    try:
        conn.load_extension(extension_path, entry_point)
        
        # Use extension functionality
        cursor = conn.cursor()
        cursor.execute("SELECT complex_add(3, 4i, 2, 5i)")
        result = cursor.fetchone()[0]
        print("Complex addition result:", result)  # 5+9i
    except sqlite3.OperationalError as e:
        print(f"Failed to load extension: {e}")

This example shows loading an extension with a specific initialization function. Some extensions require this when they don't use the default entry point name.

The entry point is typically specified in the extension's documentation. Using the wrong entry point will cause the load to fail.

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.