Last active
April 20, 2017 19:37
-
-
Save gjlondon/e28cb1f707b6637421137c0c4bf3c282 to your computer and use it in GitHub Desktop.
reproduction of apparent bug with .label() and not_() in SQLAlchemy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
from sqlalchemy import create_engine | |
from sqlalchemy.schema import MetaData, Table, Index, Column | |
from sqlalchemy.sql import and_, or_, not_, select, text | |
from sqlalchemy.types import Text, Integer, Float, String | |
engine = create_engine('sqlite:///:memory:', echo=False) | |
metadata = MetaData() | |
user = Table('user', metadata, | |
Column('user_id', Integer, primary_key=True), | |
Column('user_name', String(16), nullable=False), | |
Column('email_address', String(60)), | |
Column('password', String(20), nullable=False) | |
) | |
user.create(engine) | |
with engine.connect() as conn: | |
for name in ('jack', 'james', 'jenny'): | |
email = '{name}@gmail.com'.format(name=name) | |
ins = user.insert().values(user_name=name, email_address=email, password="bad_pass") | |
result = conn.execute(ins) | |
with engine.connect() as conn: | |
query = select([user]) | |
found = conn.execute(query).fetchall() | |
print("Full results: {results}".format(results=found)) | |
query_with_disjunction = query.where(or_(user.c.user_name == "jack", user.c.user_name == "james")) | |
print("SQL: {}".format(str(query_with_disjunction))) | |
found = conn.execute(query_with_disjunction).fetchall() | |
print("\nReturns: {results}.\nThat makes sense.\n".format(results=found)) | |
disjunction = or_(user.c.user_name == "jack", user.c.user_name == "james") | |
query_with_negated_disjunction = query.where(not_(disjunction)) | |
print("SQL: {}".format(str(query_with_negated_disjunction))) | |
found = conn.execute(query_with_negated_disjunction).fetchall() | |
print("\nReturns: {results}\nThat makes sense.\n".format(results=found)) | |
disjunction_with_label = disjunction.label("labelled_disjunction") | |
query_with_negated_disjunction_with_label = query.where(not_(disjunction_with_label)) | |
print(("Produces the SQL: {}\n\nNotice that the 'not' does " | |
"NOT put parentheses around the disjunction".format(str(query_with_negated_disjunction_with_label)))) | |
found = conn.execute(query_with_negated_disjunction_with_label).fetchall() | |
print(("\nReturns: {results}\n\nThat doesn't make sense for 2 reasons: " | |
"1) we'd expect the same result as the unlabelled disjunction, i.e. just jenny and " | |
"2) even if it's correct to apply the 'not' only to the first clause, " | |
"the result should be james and jenny, not jack and jenny.\n".format(results=found))) | |
literal_query = str(query_with_negated_disjunction_with_label.compile( | |
compile_kwargs={"literal_binds": True})) | |
print("If we pre-bind the variables and run directly, we get james and jenny:") | |
res = conn.execute(literal_query).fetchall() | |
print(str(res)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment