ZetCode

Python sqlite3.Blob.seek Method

Last modified April 15, 2025

This comprehensive guide explores Python's sqlite3.Blob.seek method, used for positioning within BLOB data. We'll cover basic usage, parameters, positioning modes, and practical examples with database resources management.

Basic Definitions

The sqlite3.Blob.seek method changes the current position in a BLOB object. It works similarly to file seek operations but for database BLOB data.

Key characteristics: it accepts offset and origin parameters, supports absolute and relative positioning, and is essential for random access to BLOB contents. The method is part of Python's sqlite3 module for SQLite database interaction.

Basic BLOB Seek Operation

Here's the simplest usage of sqlite3.Blob.seek to position within a BLOB and read data from a specific location.

basic_seek.py
import sqlite3

with sqlite3.connect('blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS images (id INTEGER PRIMARY KEY, data BLOB)")
    
    # Insert sample BLOB data
    with open('sample.png', 'rb') as f:
        data = f.read()
    conn.execute("INSERT INTO images (data) VALUES (?)", (data,))
    conn.commit()
    
    # Open BLOB and seek
    blob = conn.blobopen('images', 'data', 1, 'main')
    blob.seek(100)  # Move to position 100
    chunk = blob.read(50)  # Read 50 bytes from position 100
    print(f"Read {len(chunk)} bytes from BLOB")
    blob.close()

This example shows basic BLOB handling: create table, insert BLOB data, open BLOB, seek to position, and read data. The seek(100) moves to byte 100.

Always remember to close BLOB objects with close when done to free database resources. The with statement ensures connection cleanup.

Seek with Different Origins

The seek method supports different origin points for positioning. This example demonstrates all three origin modes.

seek_origins.py
import sqlite3

with sqlite3.connect('blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS data (id INTEGER PRIMARY KEY, content BLOB)")
    conn.execute("INSERT INTO data (content) VALUES (?)", (b'ABCDEFGHIJKLMNOPQRSTUVWXYZ',))
    
    blob = conn.blobopen('data', 'content', 1, 'main')
    
    # Seek from start (default)
    blob.seek(10)
    print(blob.read(1).decode())  # K
    
    # Seek from current position
    blob.seek(5, 1)  # 1 means current position
    print(blob.read(1).decode())  # Q
    
    # Seek from end
    blob.seek(-3, 2)  # 2 means end
    print(blob.read(1).decode())  # X
    
    blob.close()

This example shows all three seek modes: 0 (start), 1 (current position), and 2 (end). The second parameter specifies the origin point for the offset.

Understanding these modes is crucial for navigating BLOB data efficiently, especially when working with large binary objects where random access is needed.

Seek and Partial Reads

This example demonstrates seeking to different positions and reading partial data from a BLOB, useful for processing large files in chunks.

partial_reads.py
import sqlite3

def create_large_blob(conn):
    conn.execute("CREATE TABLE IF NOT EXISTS large_data (id INTEGER PRIMARY KEY, payload BLOB)")
    # Insert 1MB of data
    conn.execute("INSERT INTO large_data (payload) VALUES (?)", (b'X' * 1024 * 1024,))
    conn.commit()

with sqlite3.connect(':memory:') as conn:
    create_large_blob(conn)
    
    with conn.blobopen('large_data', 'payload', 1, 'main') as blob:
        # Read first 100 bytes
        blob.seek(0)
        print(f"Start: {len(blob.read(100))} bytes")
        
        # Read middle 100 bytes
        blob.seek(512 * 1024)  # 512KB position
        print(f"Middle: {len(blob.read(100))} bytes")
        
        # Read last 100 bytes
        blob.seek(-100, 2)  # 100 bytes from end
        print(f"End: {len(blob.read(100))} bytes")

This example creates a large BLOB (1MB) and demonstrates reading from different positions. The seek calls position the read pointer appropriately.

For large BLOBs, this technique is essential to avoid loading entire contents into memory. It enables efficient processing of specific parts of the data.

Seek with Error Handling

This example shows proper error handling when seeking in BLOB objects, including boundary checks and invalid position handling.

error_handling.py
import sqlite3

with sqlite3.connect('blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, bin BLOB)")
    conn.execute("INSERT INTO test (bin) VALUES (?)", (b'1234567890',))
    
    try:
        with conn.blobopen('test', 'bin', 1, 'main') as blob:
            # Valid seek
            blob.seek(5)
            print(f"Position 5: {blob.read(1).decode()}")
            
            # Attempt invalid seek
            try:
                blob.seek(100)  # Beyond BLOB size
                data = blob.read(1)
                print(f"Position 100: {data.decode() if data else 'None'}")
            except sqlite3.OperationalError as e:
                print(f"Seek error: {e}")
                
            # Negative seek from start
            try:
                blob.seek(-1)
            except ValueError as e:
                print(f"Negative seek error: {e}")
    except sqlite3.Error as e:
        print(f"BLOB operation failed: {e}")

The example demonstrates proper error handling for BLOB seek operations. It shows how to catch both SQLite operational errors and Python value errors.

Boundary checking is important when working with BLOB positions to avoid errors and ensure robust application behavior with varying data sizes.

Seek and Write Operations

This example demonstrates seeking to different positions in a BLOB for both reading and writing operations.

seek_write.py
import sqlite3

with sqlite3.connect('rw_blobs.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS editable (id INTEGER PRIMARY KEY, data BLOB)")
    conn.execute("INSERT INTO editable (data) VALUES (?)", (b'Initial data-----',))
    conn.commit()
    
    # Open BLOB in read-write mode
    blob = conn.blobopen('editable', 'data', 1, 'main', True)
    
    # Overwrite part of the BLOB
    blob.seek(7)
    blob.write(b'content')
    
    # Read modified data
    blob.seek(0)
    print(f"Modified BLOB: {blob.read().decode()}")
    
    blob.close()
    conn.commit()

This example shows how to seek to specific positions and perform write operations on BLOB data. The True parameter enables write access.

When writing to BLOBs, proper positioning with seek is crucial to avoid corrupting data. Always commit changes after modifying BLOB contents.

Seek in Large Binary Files

This example demonstrates efficient processing of large binary files using seek to navigate to specific sections without loading the entire file.

large_files.py
import sqlite3

def process_large_blob(blob):
    # Read header (first 100 bytes)
    blob.seek(0)
    header = blob.read(100)
    print(f"Header length: {len(header)}")
    
    # Skip to data section (assumed at 1MB offset)
    blob.seek(1024 * 1024)
    data_chunk = blob.read(1024)
    print(f"Data chunk length: {len(data_chunk)}")
    
    # Check end marker (last 8 bytes)
    blob.seek(-8, 2)
    end_marker = blob.read(8)
    print(f"End marker: {end_marker}")

with sqlite3.connect('large_files.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS big_files (id INTEGER PRIMARY KEY, content BLOB)")
    
    # Simulate large file (in real use, this would be an actual large file)
    large_data = b'HEADER' + (b'X' * 1024 * 1024) + b'DATADATA' + (b'Y' * 1024 * 1024) + b'ENDMARKER'
    conn.execute("INSERT INTO big_files (content) VALUES (?)", (large_data,))
    conn.commit()
    
    with conn.blobopen('big_files', 'content', 1, 'main') as blob:
        process_large_blob(blob)

This example shows how to efficiently navigate large BLOBs by seeking to specific sections. It demonstrates reading headers, data sections, and end markers.

For multi-gigabyte files, this approach is essential as it avoids loading the entire contents into memory, instead accessing only needed portions.

Seek Performance Considerations

This example compares the performance of sequential access versus random access with seek operations in BLOB data.

performance.py
import sqlite3
import time

def time_operation(desc, operation):
    start = time.perf_counter()
    operation()
    elapsed = time.perf_counter() - start
    print(f"{desc}: {elapsed:.4f} seconds")

with sqlite3.connect('perf_test.db') as conn:
    conn.execute("CREATE TABLE IF NOT EXISTS perf (id INTEGER PRIMARY KEY, data BLOB)")
    
    # Insert 10MB of data
    data = b'A' * 10 * 1024 * 1024
    conn.execute("INSERT INTO perf (data) VALUES (?)", (data,))
    conn.commit()
    
    with conn.blobopen('perf', 'data', 1, 'main') as blob:
        # Sequential read
        def sequential_read():
            blob.seek(0)
            while blob.read(4096):
                pass
        
        # Random access read
        def random_read():
            import random
            for _ in range(1000):
                pos = random.randint(0, len(data) - 4096)
                blob.seek(pos)
                blob.read(4096)
        
        time_operation("Sequential read", sequential_read)
        time_operation("Random access read", random_read)

This example demonstrates the performance difference between sequential and random access patterns. Sequential access is generally faster due to read-ahead caching.

When designing applications using BLOB seek operations, consider access patterns and optimize for sequential reads when possible to maximize performance.

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.