Skip to content

[sqlite3] Converter Not Called with Aggregate Functions #131002

Closed

Description

@DLu

Bug report

Bug description:

The converter functions are correctly called when using the raw field name in a query, but when you use an aggregate function like MIN(fieldname) the value is returned as a string.

Minimal Example

import sqlite3


sqlite3.register_converter('faketype', lambda s: int(s.rstrip(b';')))
con = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.execute('CREATE TABLE awesome(f faketype)')
cur.execute('INSERT INTO awesome(f) VALUES("5;")')

queries = ['SELECT f FROM awesome', 'SELECT MIN(f) FROM awesome']
for query in queries:
    cur.execute(query)
    result = cur.fetchone()[0]
    print(f'{query}\n{type(result)} {result}\n')

Expected Output

SELECT f FROM awesome
<class 'int'> 5

SELECT MIN(f) FROM awesome
<class 'int'> 5

Both queries return an integer

Actual Output

SELECT f FROM awesome
<class 'int'> 5

SELECT MIN(f) FROM awesome
<class 'str'> 5;

The query with just the fieldname f returns the proper type (int). The query using the aggregate function MIN(f) returns a string, indicating the converter function has not been called.

Additional information

A slightly expanded example is available as gist

Python 3.12.3 (main, Feb 4 2025, 14:48:35) [GCC 13.3.0] on linux / Ubuntu 24.04

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Mar 9, 2025
  2. picnixz commented on Mar 9, 2025

    @picnixz
    Member

    Is this the same in pure sqlite3 or not?

  3. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    Our sqlite3 library has no control over what happens inside the underlying SQLite library. Similarly, the underlying SQLite library knows nothing at all about Python; it has no concepts of adapters and converters.

  4. added
    pendingThe issue will be closed if no feedback is provided
    on Mar 9, 2025
  5. picnixz commented on Mar 9, 2025

    @picnixz
    Member

    Oh! I thought that converters were something from sqlite3 as well. My bad

  6. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    Converters and adapters are a feature specific to the sqlite3 stdlib module. It has nothing to do with SQLite. We implement it by looking up the declared type (which can be any string; see SQlite's flexible typing) or by bisecting the custom pysqlite "column name" format.

    Quoting the sqlite3.connect docs for the detect_types parameter:

    Types cannot be detected for generated fields (for example max(data)), even when the detect_types parameter is set; str will be returned instead.

  7. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    One solution for the query in the OP could be to use PARSE_COLNAMES and enforce "faketype" in the query:

    sqlite3.register_converter('faketype', lambda s: int(s.rstrip(b';')))
    con = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES)
    cur = con.execute('CREATE TABLE awesome(f faketype)')
    cur.execute('INSERT INTO awesome(f) VALUES("5;")')
    
    queries = [
        'SELECT f FROM awesome',
        'SELECT MIN(f) as "f [faketype]" FROM awesome',
    ]
    for query in queries:
        cur.execute(query)
        result = cur.fetchone()[0]
        print(f'{query}\n{type(result)} {result}\n')
  8. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    Quoting the sqlite3.connect docs for the detect_types parameter:

    Types cannot be detected for generated fields (for example max(data)), even when the detect_types parameter is set; str will be returned instead.

    Perhaps we could adjust this particular sentence in the docs. First, I think we should move this sentence to the sqlite3.PARSE_DECLTYPES documentation. Second, I think we should suggest using sqlite3.PARSE_COLNAMES for these cases.

    Re-opening and rebranding as a docs issue.

  9. 1 remaining item

  10. removed
    pendingThe issue will be closed if no feedback is provided
    on Mar 9, 2025
  11. added a commit that references this issue on Mar 9, 2025
  12. DLu commented on Mar 9, 2025

    @DLu
    Author

    I'm glad to see that this was already covered in the documentation. I missed it partially because I was using the terminology "aggregate", so the referred to bit of docs didn't pop up in my search. My apologies for that.

    Would it be possible to recast this as a feature request for some future version? Throw an extra detect_types=sqlite3.PARSE_AGGREGATES flag in and add the parsing to pysqlite_build_row_cast_map

  13. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    Unfortunately, I don't think it is possible to implement such a feature with the SQLite C API :( I'll have an extra look, though, just to be sure.

  14. erlend-aasland commented on Mar 9, 2025

    @erlend-aasland
    Contributor

    I'll have an extra look, though, just to be sure.

    I wrote that comment too hastily :) A generated expression cannot have a declared type; it does not make sense. The SQLite C API that deals with declared types all return NULL for expressions and sub-queries.

  15. DLu commented on Mar 10, 2025

    @DLu
    Author

    Just to be clear, you mean that sqlite3_column_name always returns NULL for expressions/etc?

  16. erlend-aasland commented on Mar 10, 2025

    @erlend-aasland
    Contributor

    Just to be clear, you mean that sqlite3_column_name always returns NULL for expressions/etc?

    No, this is not the API you want; it only gives you the column query name. That can be anything (SELECT a as "anything" FROM t). sqlite3_column_decltype provides the declared type:

    If the Nth column of the result set is an expression or subquery, then a NULL pointer is returned.

  17. added a commit that references this issue on Mar 17, 2025
  18. added 2 commits that reference this issue on Mar 17, 2025
  19. added 2 commits that reference this issue on Mar 18, 2025
  20. added a commit that references this issue on Mar 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docsDocumentation in the Doc dirtopic-sqlite3type-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions