]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- document SQlite dotted name issue, fixes #3441
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 5 Jun 2015 20:11:13 +0000 (16:11 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 5 Jun 2015 20:11:48 +0000 (16:11 -0400)
(cherry picked from commit 4c90f355fd552e70009ffcdf3fdde9f3653e337e)

lib/sqlalchemy/dialects/sqlite/base.py

index ebc964a5978d4dce4a0945c35cfcef9d490c4ec0..8653d7bb82de2c7d7915f899810cc7d21a4571f7 100644 (file)
@@ -325,6 +325,107 @@ The index will be rendered at create time as::
 
 .. versionadded:: 0.9.9
 
+Dotted Column Names
+-------------------
+
+Using table or column names that explicitly have periods in them is
+**not recommended**.   While this is generally a bad idea for relational
+databases in general, as the dot is a syntactically significant character,
+the SQLite driver has a bug which requires that SQLAlchemy filter out these
+dots in result sets.
+
+The bug, entirely outside of SQLAlchemy, can be illustrated thusly::
+
+    import sqlite3
+
+    conn = sqlite3.connect(":memory:")
+    cursor = conn.cursor()
+
+    cursor.execute("create table x (a integer, b integer)")
+    cursor.execute("insert into x (a, b) values (1, 1)")
+    cursor.execute("insert into x (a, b) values (2, 2)")
+
+    cursor.execute("select x.a, x.b from x")
+    assert [c[0] for c in cursor.description] == ['a', 'b']
+
+    cursor.execute('''
+        select x.a, x.b from x where a=1
+        union
+        select x.a, x.b from x where a=2
+    ''')
+    assert [c[0] for c in cursor.description] == ['a', 'b'], \\
+        [c[0] for c in cursor.description]
+
+The second assertion fails::
+
+    Traceback (most recent call last):
+      File "test.py", line 19, in <module>
+        [c[0] for c in cursor.description]
+    AssertionError: ['x.a', 'x.b']
+
+Where above, the driver incorrectly reports the names of the columns
+including the name of the table, which is entirely inconsistent vs.
+when the UNION is not present.
+
+SQLAlchemy relies upon column names being predictable in how they match
+to the original statement, so the SQLAlchemy dialect has no choice but
+to filter these out::
+
+
+    from sqlalchemy import create_engine
+
+    eng = create_engine("sqlite://")
+    conn = eng.connect()
+
+    conn.execute("create table x (a integer, b integer)")
+    conn.execute("insert into x (a, b) values (1, 1)")
+    conn.execute("insert into x (a, b) values (2, 2)")
+
+    result = conn.execute("select x.a, x.b from x")
+    assert result.keys() == ["a", "b"]
+
+    result = conn.execute('''
+        select x.a, x.b from x where a=1
+        union
+        select x.a, x.b from x where a=2
+    ''')
+    assert result.keys() == ["a", "b"]
+
+Note that above, even though SQLAlchemy filters out the dots, *both
+names are still addressable*::
+
+    >>> row = result.first()
+    >>> row["a"]
+    1
+    >>> row["x.a"]
+    1
+    >>> row["b"]
+    1
+    >>> row["x.b"]
+    1
+
+Therefore, the workaround applied by SQLAlchemy only impacts
+:meth:`.ResultProxy.keys` and :meth:`.RowProxy.keys()` in the public API.
+In the very specific case where
+an application is forced to use column names that contain dots, and the
+functionality of :meth:`.ResultProxy.keys` and :meth:`.RowProxy.keys()`
+is required to return these dotted names unmodified, the ``sqlite_raw_colnames``
+execution option may be provided, either on a per-:class:`.Connection` basis::
+
+    result = conn.execution_options(sqlite_raw_colnames=True).execute('''
+        select x.a, x.b from x where a=1
+        union
+        select x.a, x.b from x where a=2
+    ''')
+    assert result.keys() == ["x.a", "x.b"]
+
+or on a per-:class:`.Engine` basis::
+
+    engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True})
+
+When using the per-:class:`.Engine` execution option, note that
+**Core and ORM queries that use UNION may not function properly**.
+
 """
 
 import datetime