wbeardall opened a new issue, #4143:
URL: https://github.com/apache/arrow-adbc/issues/4143

   ### What happened?
   
   The SQLite binaries shipped with `adbc-driver-sqlite` use a thinner set of 
compile options as compared to common system standard SQLite builds. For 
example, ENABLE_MATH_FUNCTIONS is not set, so common row-level mathematics 
functions are missing in ADBC's SQLite. This results in divergent behaviour 
between Python standard SQLite and ADBC's SQLite driver on the same machine. 
   
   ## Expected behaviour
   
   Official PyPI adbc-driver-sqlite binaries on a given platform should either:
   
   - Match common system/Python SQLite capabilities for core math / hash 
builtins that many apps rely on (MOD, COS, etc.), or
   
   - Document clearly that wheels use a minimal SQLite and list unsupported 
functions / required workarounds.
   
   Preference is (1) for least surprise.
   
   ### Stack Trace
   
   _No response_
   
   ### How can we reproduce the bug?
   
   
   The behaviour is investigated as follows: 
   
   ```
   
   from __future__ import annotations
   
   import importlib.resources
   import importlib.util
   import sqlite3
   import sys
   from collections.abc import Callable, Sequence
   from pathlib import Path
   
   import adbc_driver_sqlite
   import adbc_driver_sqlite.dbapi as adb
   
   DbConnect = Callable[[str], object]
   
   
   def _sqlite3_module_location() -> str:
       import _sqlite3
   
       path = getattr(_sqlite3, "__file__", None)
       if isinstance(path, str) and path:
           return str(Path(path).resolve())
       spec = importlib.util.find_spec("_sqlite3")
       if spec is not None and spec.origin:
           return str(Path(spec.origin).resolve())
       return "<unknown>"
   
   
   def _adbc_driver_library_path() -> str | None:
       root = importlib.resources.files("adbc_driver_sqlite")
       try:
           for entry in root.iterdir():
               if entry.is_file() and 
entry.name.startswith("libadbc_driver_sqlite"):
                   return str(entry)
       except (OSError, TypeError):
           pass
       return None
   
   
   def _fetchall(cursor, sql: str) -> tuple[str, object]:
       try:
           cursor.execute(sql)
           return "OK", cursor.fetchall()
       except Exception as e:  # noqa: BLE001 — diagnostic script
           return "FAIL", f"{type(e).__name__}: {e}"
   
   
   def _compile_options(cursor) -> list[str]:
       status, rows = _fetchall(cursor, "PRAGMA compile_options")
       if status != "OK" or not isinstance(rows, Sequence):
           return [f"<error: {rows}>"]
       return sorted(str(r[0]) for r in rows)
   
   
   def _investigate(label: str, connect: DbConnect, db: str) -> frozenset[str]:
       print("=" * 72)
       print(label)
       print("=" * 72)
       cx = connect(db)
       cur = cx.cursor()  # pyright: ignore[reportAttributeAccessIssue]
   
       for sql in (
           "SELECT sqlite_version()",
           "SELECT sqlite_source_id()",
       ):
           status, out = _fetchall(cur, sql)
           print(f"  {sql}")
           print(f"    -> {status}: {out}")
   
       opts = _compile_options(cur)
       opt_set = (
           frozenset(opts)
           if opts and not str(opts[0]).startswith("<error")
           else frozenset()
       )
       print(f"  PRAGMA compile_options ({len(opts)} lines)")
       for line in opts:
           print(f"    {line}")
   
       print("  Modulo / remainder probes")
       for sql in (
           "SELECT MOD(3, 2)",
           "SELECT mod(3, 2)",
           "SELECT 3 % 2",
       ):
           status, out = _fetchall(cur, sql)
           print(f"    {sql!r} -> {status}: {out}")
   
       print("  pragma_function_list (name contains 'mod')")
       status, out = _fetchall(
           cur,
           """
           SELECT name, builtin, type, narg
           FROM pragma_function_list
           WHERE lower(name) LIKE '%mod%'
           ORDER BY name, narg
           """,
       )
       if status == "OK" and isinstance(out, Sequence) and out:
           for row in out:
               print(f"    {row}")
       else:
           print(f"    -> {status}: {out}")
   
       print("  pragma_function_list (first 20 names, sorted)")
       status, out = _fetchall(
           cur,
           """
           SELECT name, builtin, type, narg
           FROM pragma_function_list
           ORDER BY name, narg
           LIMIT 20
           """,
       )
       if status == "OK" and isinstance(out, Sequence):
           for row in out:
               print(f"    {row}")
       else:
           print(f"    -> {status}: {out}")
   
       close = getattr(cx, "close", None)
       if callable(close):
           close()
       print()
       return opt_set
   
   
   def main() -> None:
       db = "/tmp/t_adbc_compare.db"
       sqlite3.connect(db).execute("CREATE TABLE IF NOT EXISTS t(id INTEGER);")
   
       print("Python:", sys.version.split()[0])
       print("Executable:", sys.executable)
       print("sqlite3.sqlite_version:", sqlite3.sqlite_version)
       print("adbc_driver_sqlite package:", adbc_driver_sqlite.__version__)  # 
pyright: ignore[reportPrivateImportUsage]
       print("stdlib _sqlite3:", _sqlite3_module_location())
       print(
           "adbc driver library:", _adbc_driver_library_path() or "<not found 
in package>"
       )
       print()
   
       opts_std = _investigate("sqlite3.connect", sqlite3.connect, db)
       opts_adb = _investigate("adbc_driver_sqlite.dbapi.connect", adb.connect, 
db)
   
       if opts_std and opts_adb:
           only_std = sorted(opts_std - opts_adb)
           only_adb = sorted(opts_adb - opts_std)
           print("=" * 72)
           print("PRAGMA compile_options — symmetric difference")
           print("=" * 72)
           print(f"  Only in stdlib ({len(only_std)}):")
           for x in only_std:
               print(f"    + {x}")
           print(f"  Only in ADBC ({len(only_adb)}):")
           for x in only_adb:
               print(f"    + {x}")
   
   
   if __name__ == "__main__":
       main()
   ```
   
   With sample trace
   
   ```
   Python: 3.13.11
   Executable: /path/to/.venv/bin/python3
   sqlite3.sqlite_version: 3.50.4
   adbc_driver_sqlite package: 1.10.0
   stdlib _sqlite3: /path/to/built-in
   adbc driver library: 
/path/to/site-packages/adbc_driver_sqlite/libadbc_driver_sqlite.so
   
   ========================================================================
   sqlite3.connect
   ========================================================================
     SELECT sqlite_version()
       -> OK: [('3.50.4',)]
     SELECT sqlite_source_id()
       -> OK: [('2025-07-30 19:33:53 
4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3',)]
     PRAGMA compile_options (45 lines)
       ATOMIC_INTRINSICS=1
       COMPILER=clang-21.1.4
       DEFAULT_AUTOVACUUM
       DEFAULT_CACHE_SIZE=-2000
       DEFAULT_FILE_FORMAT=4
       DEFAULT_JOURNAL_SIZE_LIMIT=-1
       DEFAULT_MMAP_SIZE=0
       DEFAULT_PAGE_SIZE=4096
       DEFAULT_PCACHE_INITSZ=20
       DEFAULT_RECURSIVE_TRIGGERS
       DEFAULT_SECTOR_SIZE=4096
       DEFAULT_SYNCHRONOUS=2
       DEFAULT_WAL_AUTOCHECKPOINT=1000
       DEFAULT_WAL_SYNCHRONOUS=2
       DEFAULT_WORKER_THREADS=0
       DIRECT_OVERFLOW_READ
       ENABLE_DBSTAT_VTAB
       ENABLE_FTS3
       ENABLE_FTS3_PARENTHESIS
       ENABLE_FTS4
       ENABLE_FTS5
       ENABLE_GEOPOLY
       ENABLE_MATH_FUNCTIONS
       ENABLE_RTREE
       MALLOC_SOFT_LIMIT=1024
       MAX_ATTACHED=10
       MAX_COLUMN=2000
       MAX_COMPOUND_SELECT=500
       MAX_DEFAULT_PAGE_SIZE=8192
       MAX_EXPR_DEPTH=1000
       MAX_FUNCTION_ARG=1000
       MAX_LENGTH=1000000000
       MAX_LIKE_PATTERN_LENGTH=50000
       MAX_MMAP_SIZE=0x7fff0000
       MAX_PAGE_COUNT=0xfffffffe
       MAX_PAGE_SIZE=65536
       MAX_SQL_LENGTH=1000000000
       MAX_TRIGGER_DEPTH=1000
       MAX_VARIABLE_NUMBER=32766
       MAX_VDBE_OP=250000000
       MAX_WORKER_THREADS=8
       MUTEX_PTHREADS
       SYSTEM_MALLOC
       TEMP_STORE=1
       THREADSAFE=1
     Modulo / remainder probes
       'SELECT MOD(3, 2)' -> OK: [(1.0,)]
       'SELECT mod(3, 2)' -> OK: [(1.0,)]
       'SELECT 3 % 2' -> OK: [(1,)]
     pragma_function_list (name contains 'mod')
       ('mod', 1, 's', 2)
     pragma_function_list (first 20 names, sorted)
       ('->', 1, 's', 2)
       ('->>', 1, 's', 2)
       ('abs', 1, 's', 1)
       ('acos', 1, 's', 1)
       ('acosh', 1, 's', 1)
       ('asin', 1, 's', 1)
       ('asinh', 1, 's', 1)
       ('atan', 1, 's', 1)
       ('atan2', 1, 's', 2)
       ('atanh', 1, 's', 1)
       ('avg', 1, 'w', 1)
       ('bm25', 0, 's', -1)
       ('ceil', 1, 's', 1)
       ('ceiling', 1, 's', 1)
       ('changes', 1, 's', 0)
       ('char', 1, 's', -1)
       ('coalesce', 1, 's', -4)
       ('concat', 1, 's', -3)
       ('concat_ws', 1, 's', -4)
       ('cos', 1, 's', 1)
   
   ========================================================================
   adbc_driver_sqlite.dbapi.connect
   ========================================================================
     SELECT sqlite_version()
       -> OK: [('3.50.4',)]
     SELECT sqlite_source_id()
       -> OK: [('2025-07-30 19:33:53 
4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3',)]
     PRAGMA compile_options (39 lines)
       ATOMIC_INTRINSICS=1
       COMPILER=clang-16.0.0
       DEFAULT_AUTOVACUUM
       DEFAULT_CACHE_SIZE=-2000
       DEFAULT_FILE_FORMAT=4
       DEFAULT_JOURNAL_SIZE_LIMIT=-1
       DEFAULT_MMAP_SIZE=0
       DEFAULT_PAGE_SIZE=4096
       DEFAULT_PCACHE_INITSZ=20
       DEFAULT_RECURSIVE_TRIGGERS
       DEFAULT_SECTOR_SIZE=4096
       DEFAULT_SYNCHRONOUS=2
       DEFAULT_WAL_AUTOCHECKPOINT=1000
       DEFAULT_WAL_SYNCHRONOUS=2
       DEFAULT_WORKER_THREADS=0
       DIRECT_OVERFLOW_READ
       ENABLE_COLUMN_METADATA
       ENABLE_UNLOCK_NOTIFY
       MALLOC_SOFT_LIMIT=1024
       MAX_ATTACHED=10
       MAX_COLUMN=2000
       MAX_COMPOUND_SELECT=500
       MAX_DEFAULT_PAGE_SIZE=8192
       MAX_EXPR_DEPTH=1000
       MAX_FUNCTION_ARG=1000
       MAX_LENGTH=1000000000
       MAX_LIKE_PATTERN_LENGTH=50000
       MAX_MMAP_SIZE=0x7fff0000
       MAX_PAGE_COUNT=0xfffffffe
       MAX_PAGE_SIZE=65536
       MAX_SQL_LENGTH=1000000000
       MAX_TRIGGER_DEPTH=1000
       MAX_VARIABLE_NUMBER=32766
       MAX_VDBE_OP=250000000
       MAX_WORKER_THREADS=8
       MUTEX_PTHREADS
       SYSTEM_MALLOC
       TEMP_STORE=1
       THREADSAFE=1
     Modulo / remainder probes
       'SELECT MOD(3, 2)' -> FAIL: ProgrammingError: INVALID_ARGUMENT: [SQLite] 
Failed to prepare query: no such function: MOD
   query: SELECT MOD(3, 2)
       'SELECT mod(3, 2)' -> FAIL: ProgrammingError: INVALID_ARGUMENT: [SQLite] 
Failed to prepare query: no such function: mod
   query: SELECT mod(3, 2)
       'SELECT 3 % 2' -> OK: [(1,)]
     pragma_function_list (name contains 'mod')
       -> OK: []
     pragma_function_list (first 20 names, sorted)
       ('->', 1, 's', 2)
       ('->>', 1, 's', 2)
       ('abs', 1, 's', 1)
       ('avg', 1, 'w', 1)
       ('changes', 1, 's', 0)
       ('char', 1, 's', -1)
       ('coalesce', 1, 's', -4)
       ('concat', 1, 's', -3)
       ('concat_ws', 1, 's', -4)
       ('count', 1, 'w', 0)
       ('count', 1, 'w', 1)
       ('cume_dist', 1, 'w', 0)
       ('current_date', 1, 's', 0)
       ('current_time', 1, 's', 0)
       ('current_timestamp', 1, 's', 0)
       ('date', 1, 's', -1)
       ('datetime', 1, 's', -1)
       ('dense_rank', 1, 'w', 0)
       ('first_value', 1, 'w', 1)
       ('format', 1, 's', -1)
   
   ========================================================================
   PRAGMA compile_options — symmetric difference
   ========================================================================
     Only in stdlib (9):
       + COMPILER=clang-21.1.4
       + ENABLE_DBSTAT_VTAB
       + ENABLE_FTS3
       + ENABLE_FTS3_PARENTHESIS
       + ENABLE_FTS4
       + ENABLE_FTS5
       + ENABLE_GEOPOLY
       + ENABLE_MATH_FUNCTIONS
       + ENABLE_RTREE
     Only in ADBC (3):
       + COMPILER=clang-16.0.0
       + ENABLE_COLUMN_METADATA
       + ENABLE_UNLOCK_NOTIFY
   ```
   
   ### Environment/Setup
   
   - MacOS 26.3.1 (M1)
   - ADBC installed from Pip wheels


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to