From 45cec095b4904ba71425d2fe18c143982dd08f43 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 10 Jun 2009 21:18:24 +0000 Subject: - unit tests have been migrated from unittest to nose. See README.unittests for information on how to run the tests. [ticket:970] --- test/sql/_base.py | 2 +- test/sql/alltests.py | 38 - test/sql/case_statement.py | 137 ---- test/sql/columns.py | 60 -- test/sql/constraints.py | 337 --------- test/sql/defaults.py | 634 ---------------- test/sql/functions.py | 319 -------- test/sql/generative.py | 815 -------------------- test/sql/labels.py | 195 ----- test/sql/query.py | 1321 --------------------------------- test/sql/quote.py | 211 ------ test/sql/rowcount.py | 71 -- test/sql/select.py | 1552 --------------------------------------- test/sql/selectable.py | 526 ------------- test/sql/test_case_statement.py | 137 ++++ test/sql/test_columns.py | 58 ++ test/sql/test_constraints.py | 335 +++++++++ test/sql/test_defaults.py | 641 ++++++++++++++++ test/sql/test_functions.py | 317 ++++++++ test/sql/test_generative.py | 818 +++++++++++++++++++++ test/sql/test_labels.py | 195 +++++ test/sql/test_query.py | 1325 +++++++++++++++++++++++++++++++++ test/sql/test_quote.py | 210 ++++++ test/sql/test_rowcount.py | 70 ++ test/sql/test_select.py | 1550 ++++++++++++++++++++++++++++++++++++++ test/sql/test_selectable.py | 524 +++++++++++++ test/sql/test_types.py | 891 ++++++++++++++++++++++ test/sql/test_unicode.py | 138 ++++ test/sql/testtypes.py | 875 ---------------------- test/sql/unicode.py | 139 ---- 30 files changed, 7210 insertions(+), 7231 deletions(-) delete mode 100644 test/sql/alltests.py delete mode 100644 test/sql/case_statement.py delete mode 100644 test/sql/columns.py delete mode 100644 test/sql/constraints.py delete mode 100644 test/sql/defaults.py delete mode 100644 test/sql/functions.py delete mode 100644 test/sql/generative.py delete mode 100644 test/sql/labels.py delete mode 100644 test/sql/query.py delete mode 100644 test/sql/quote.py delete mode 100644 test/sql/rowcount.py delete mode 100644 test/sql/select.py delete mode 100755 test/sql/selectable.py create mode 100644 test/sql/test_case_statement.py create mode 100644 test/sql/test_columns.py create mode 100644 test/sql/test_constraints.py create mode 100644 test/sql/test_defaults.py create mode 100644 test/sql/test_functions.py create mode 100644 test/sql/test_generative.py create mode 100644 test/sql/test_labels.py create mode 100644 test/sql/test_query.py create mode 100644 test/sql/test_quote.py create mode 100644 test/sql/test_rowcount.py create mode 100644 test/sql/test_select.py create mode 100644 test/sql/test_selectable.py create mode 100644 test/sql/test_types.py create mode 100644 test/sql/test_unicode.py delete mode 100644 test/sql/testtypes.py delete mode 100644 test/sql/unicode.py (limited to 'test/sql') diff --git a/test/sql/_base.py b/test/sql/_base.py index c1a107eeb..48879ae7e 100644 --- a/test/sql/_base.py +++ b/test/sql/_base.py @@ -1,4 +1,4 @@ -from engine import _base as engine_base +from test.engine import _base as engine_base TablesTest = engine_base.TablesTest diff --git a/test/sql/alltests.py b/test/sql/alltests.py deleted file mode 100644 index f01b0e620..000000000 --- a/test/sql/alltests.py +++ /dev/null @@ -1,38 +0,0 @@ -import testenv; testenv.configure_for_tests() -from testlib import sa_unittest as unittest - - -def suite(): - modules_to_test = ( - 'sql.testtypes', - 'sql.columns', - 'sql.constraints', - - 'sql.generative', - - # SQL syntax - 'sql.select', - 'sql.selectable', - 'sql.case_statement', - 'sql.labels', - 'sql.unicode', - - # assorted round-trip tests - 'sql.functions', - 'sql.query', - 'sql.quote', - 'sql.rowcount', - - # defaults, sequences (postgres/oracle) - 'sql.defaults', - ) - alltests = unittest.TestSuite() - for name in modules_to_test: - mod = __import__(name) - for token in name.split('.')[1:]: - mod = getattr(mod, token) - alltests.addTest(unittest.findTestCases(mod, suiteClass=None)) - return alltests - -if __name__ == '__main__': - testenv.main(suite()) diff --git a/test/sql/case_statement.py b/test/sql/case_statement.py deleted file mode 100644 index 1d5383749..000000000 --- a/test/sql/case_statement.py +++ /dev/null @@ -1,137 +0,0 @@ -import testenv; testenv.configure_for_tests() -import sys -from sqlalchemy import * -from testlib import * -from sqlalchemy import util, exc -from sqlalchemy.sql import table, column - - -class CaseTest(TestBase, AssertsCompiledSQL): - - def setUpAll(self): - metadata = MetaData(testing.db) - global info_table - info_table = Table('infos', metadata, - Column('pk', Integer, primary_key=True), - Column('info', String(30))) - - info_table.create() - - info_table.insert().execute( - {'pk':1, 'info':'pk_1_data'}, - {'pk':2, 'info':'pk_2_data'}, - {'pk':3, 'info':'pk_3_data'}, - {'pk':4, 'info':'pk_4_data'}, - {'pk':5, 'info':'pk_5_data'}, - {'pk':6, 'info':'pk_6_data'}) - def tearDownAll(self): - info_table.drop() - - @testing.fails_on('firebird', 'FIXME: unknown') - @testing.fails_on('maxdb', 'FIXME: unknown') - @testing.requires.subqueries - def testcase(self): - inner = select([case([ - [info_table.c.pk < 3, - 'lessthan3'], - [and_(info_table.c.pk >= 3, info_table.c.pk < 7), - 'gt3']]).label('x'), - info_table.c.pk, info_table.c.info], - from_obj=[info_table]).alias('q_inner') - - inner_result = inner.execute().fetchall() - - # Outputs: - # lessthan3 1 pk_1_data - # lessthan3 2 pk_2_data - # gt3 3 pk_3_data - # gt3 4 pk_4_data - # gt3 5 pk_5_data - # gt3 6 pk_6_data - assert inner_result == [ - ('lessthan3', 1, 'pk_1_data'), - ('lessthan3', 2, 'pk_2_data'), - ('gt3', 3, 'pk_3_data'), - ('gt3', 4, 'pk_4_data'), - ('gt3', 5, 'pk_5_data'), - ('gt3', 6, 'pk_6_data') - ] - - outer = select([inner]) - - outer_result = outer.execute().fetchall() - - assert outer_result == [ - ('lessthan3', 1, 'pk_1_data'), - ('lessthan3', 2, 'pk_2_data'), - ('gt3', 3, 'pk_3_data'), - ('gt3', 4, 'pk_4_data'), - ('gt3', 5, 'pk_5_data'), - ('gt3', 6, 'pk_6_data') - ] - - w_else = select([case([ - [info_table.c.pk < 3, - 3], - [and_(info_table.c.pk >= 3, info_table.c.pk < 6), - 6]], - else_ = 0).label('x'), - info_table.c.pk, info_table.c.info], - from_obj=[info_table]).alias('q_inner') - - else_result = w_else.execute().fetchall() - - assert else_result == [ - (3, 1, 'pk_1_data'), - (3, 2, 'pk_2_data'), - (6, 3, 'pk_3_data'), - (6, 4, 'pk_4_data'), - (6, 5, 'pk_5_data'), - (0, 6, 'pk_6_data') - ] - - def test_literal_interpretation(self): - t = table('test', column('col1')) - - self.assertRaises(exc.ArgumentError, case, [("x", "y")]) - - self.assert_compile(case([("x", "y")], value=t.c.col1), "CASE test.col1 WHEN :param_1 THEN :param_2 END") - self.assert_compile(case([(t.c.col1==7, "y")], else_="z"), "CASE WHEN (test.col1 = :col1_1) THEN :param_1 ELSE :param_2 END") - - - @testing.fails_on('firebird', 'FIXME: unknown') - @testing.fails_on('maxdb', 'FIXME: unknown') - def testcase_with_dict(self): - query = select([case({ - info_table.c.pk < 3: 'lessthan3', - info_table.c.pk >= 3: 'gt3', - }, else_='other'), - info_table.c.pk, info_table.c.info - ], - from_obj=[info_table]) - assert query.execute().fetchall() == [ - ('lessthan3', 1, 'pk_1_data'), - ('lessthan3', 2, 'pk_2_data'), - ('gt3', 3, 'pk_3_data'), - ('gt3', 4, 'pk_4_data'), - ('gt3', 5, 'pk_5_data'), - ('gt3', 6, 'pk_6_data') - ] - - simple_query = select([case({ - 1: 'one', - 2: 'two', - }, value=info_table.c.pk, else_='other'), - info_table.c.pk - ], - whereclause=info_table.c.pk < 4, - from_obj=[info_table]) - - assert simple_query.execute().fetchall() == [ - ('one', 1), - ('two', 2), - ('other', 3), - ] - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/columns.py b/test/sql/columns.py deleted file mode 100644 index 661be891a..000000000 --- a/test/sql/columns.py +++ /dev/null @@ -1,60 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy import exc, sql -from testlib import * -from sqlalchemy import Table, Column # don't use testlib's wrappers - - -class ColumnDefinitionTest(TestBase): - """Test Column() construction.""" - - # flesh this out with explicit coverage... - - def columns(self): - return [ Column(), - Column('b'), - Column(Integer), - Column('d', Integer), - Column(name='e'), - Column(type_=Integer), - Column(Integer()), - Column('h', Integer()), - Column(type_=Integer()) ] - - def test_basic(self): - c = self.columns() - - for i, v in ((0, 'a'), (2, 'c'), (5, 'f'), (6, 'g'), (8, 'i')): - c[i].name = v - c[i].key = v - del i, v - - tbl = Table('table', MetaData(), *c) - - for i, col in enumerate(tbl.c): - assert col.name == c[i].name - - def test_incomplete(self): - c = self.columns() - - self.assertRaises(exc.ArgumentError, Table, 't', MetaData(), *c) - - def test_incomplete_key(self): - c = Column(Integer) - assert c.name is None - assert c.key is None - - c.name = 'named' - t = Table('t', MetaData(), c) - - assert c.name == 'named' - assert c.name == c.key - - - def test_bogus(self): - self.assertRaises(exc.ArgumentError, Column, 'foo', name='bar') - self.assertRaises(exc.ArgumentError, Column, 'foo', Integer, - type_=Integer()) - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/constraints.py b/test/sql/constraints.py deleted file mode 100644 index d019aa037..000000000 --- a/test/sql/constraints.py +++ /dev/null @@ -1,337 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy import exc -from testlib import * -from testlib import config, engines - -class ConstraintTest(TestBase, AssertsExecutionResults): - - def setUp(self): - global metadata - metadata = MetaData(testing.db) - - def tearDown(self): - metadata.drop_all() - - def test_constraint(self): - employees = Table('employees', metadata, - Column('id', Integer), - Column('soc', String(40)), - Column('name', String(30)), - PrimaryKeyConstraint('id', 'soc') - ) - elements = Table('elements', metadata, - Column('id', Integer), - Column('stuff', String(30)), - Column('emp_id', Integer), - Column('emp_soc', String(40)), - PrimaryKeyConstraint('id', name='elements_primkey'), - ForeignKeyConstraint(['emp_id', 'emp_soc'], ['employees.id', 'employees.soc']) - ) - metadata.create_all() - - def test_double_fk_usage_raises(self): - f = ForeignKey('b.id') - - self.assertRaises(exc.InvalidRequestError, Table, "a", metadata, - Column('x', Integer, f), - Column('y', Integer, f) - ) - - - def test_circular_constraint(self): - a = Table("a", metadata, - Column('id', Integer, primary_key=True), - Column('bid', Integer), - ForeignKeyConstraint(["bid"], ["b.id"], name="afk") - ) - b = Table("b", metadata, - Column('id', Integer, primary_key=True), - Column("aid", Integer), - ForeignKeyConstraint(["aid"], ["a.id"], use_alter=True, name="bfk") - ) - metadata.create_all() - - def test_circular_constraint_2(self): - a = Table("a", metadata, - Column('id', Integer, primary_key=True), - Column('bid', Integer, ForeignKey("b.id")), - ) - b = Table("b", metadata, - Column('id', Integer, primary_key=True), - Column("aid", Integer, ForeignKey("a.id", use_alter=True, name="bfk")), - ) - metadata.create_all() - - @testing.fails_on('mysql', 'FIXME: unknown') - def test_check_constraint(self): - foo = Table('foo', metadata, - Column('id', Integer, primary_key=True), - Column('x', Integer), - Column('y', Integer), - CheckConstraint('x>y')) - bar = Table('bar', metadata, - Column('id', Integer, primary_key=True), - Column('x', Integer, CheckConstraint('x>7')), - Column('z', Integer) - ) - - metadata.create_all() - foo.insert().execute(id=1,x=9,y=5) - try: - foo.insert().execute(id=2,x=5,y=9) - assert False - except exc.SQLError: - assert True - - bar.insert().execute(id=1,x=10) - try: - bar.insert().execute(id=2,x=5) - assert False - except exc.SQLError: - assert True - - def test_unique_constraint(self): - foo = Table('foo', metadata, - Column('id', Integer, primary_key=True), - Column('value', String(30), unique=True)) - bar = Table('bar', metadata, - Column('id', Integer, primary_key=True), - Column('value', String(30)), - Column('value2', String(30)), - UniqueConstraint('value', 'value2', name='uix1') - ) - metadata.create_all() - foo.insert().execute(id=1, value='value1') - foo.insert().execute(id=2, value='value2') - bar.insert().execute(id=1, value='a', value2='a') - bar.insert().execute(id=2, value='a', value2='b') - try: - foo.insert().execute(id=3, value='value1') - assert False - except exc.SQLError: - assert True - try: - bar.insert().execute(id=3, value='a', value2='b') - assert False - except exc.SQLError: - assert True - - def test_index_create(self): - employees = Table('employees', metadata, - Column('id', Integer, primary_key=True), - Column('first_name', String(30)), - Column('last_name', String(30)), - Column('email_address', String(30))) - employees.create() - - i = Index('employee_name_index', - employees.c.last_name, employees.c.first_name) - i.create() - assert i in employees.indexes - - i2 = Index('employee_email_index', - employees.c.email_address, unique=True) - i2.create() - assert i2 in employees.indexes - - def test_index_create_camelcase(self): - """test that mixed-case index identifiers are legal""" - employees = Table('companyEmployees', metadata, - Column('id', Integer, primary_key=True), - Column('firstName', String(30)), - Column('lastName', String(30)), - Column('emailAddress', String(30))) - - employees.create() - - i = Index('employeeNameIndex', - employees.c.lastName, employees.c.firstName) - i.create() - - i = Index('employeeEmailIndex', - employees.c.emailAddress, unique=True) - i.create() - - # Check that the table is useable. This is mostly for pg, - # which can be somewhat sticky with mixed-case identifiers - employees.insert().execute(firstName='Joe', lastName='Smith', id=0) - ss = employees.select().execute().fetchall() - assert ss[0].firstName == 'Joe' - assert ss[0].lastName == 'Smith' - - def test_index_create_inline(self): - """Test indexes defined with tables""" - - events = Table('events', metadata, - Column('id', Integer, primary_key=True), - Column('name', String(30), index=True, unique=True), - Column('location', String(30), index=True), - Column('sport', String(30)), - Column('announcer', String(30)), - Column('winner', String(30))) - - Index('sport_announcer', events.c.sport, events.c.announcer, unique=True) - Index('idx_winners', events.c.winner) - - index_names = [ ix.name for ix in events.indexes ] - assert 'ix_events_name' in index_names - assert 'ix_events_location' in index_names - assert 'sport_announcer' in index_names - assert 'idx_winners' in index_names - assert len(index_names) == 4 - - capt = [] - connection = testing.db.connect() - # TODO: hacky, put a real connection proxy in - ex = connection._Connection__execute_context - def proxy(context): - capt.append(context.statement) - capt.append(repr(context.parameters)) - ex(context) - connection._Connection__execute_context = proxy - schemagen = testing.db.dialect.schemagenerator(testing.db.dialect, connection) - schemagen.traverse(events) - - assert capt[0].strip().startswith('CREATE TABLE events') - - s = set([capt[x].strip() for x in [2,4,6,8]]) - - assert s == set([ - 'CREATE UNIQUE INDEX ix_events_name ON events (name)', - 'CREATE INDEX ix_events_location ON events (location)', - 'CREATE UNIQUE INDEX sport_announcer ON events (sport, announcer)', - 'CREATE INDEX idx_winners ON events (winner)' - ]) - - # verify that the table is functional - events.insert().execute(id=1, name='hockey finals', location='rink', - sport='hockey', announcer='some canadian', - winner='sweden') - ss = events.select().execute().fetchall() - - def test_too_long_idx_name(self): - dialect = testing.db.dialect.__class__() - dialect.max_identifier_length = 20 - - schemagen = dialect.schemagenerator(dialect, None) - schemagen.execute = lambda : None - - t1 = Table("sometable", MetaData(), Column("foo", Integer)) - schemagen.visit_index(Index("this_name_is_too_long_for_what_were_doing", t1.c.foo)) - self.assertEquals(schemagen.buffer.getvalue(), "CREATE INDEX this_name_is_t_1 ON sometable (foo)") - schemagen.buffer.truncate(0) - schemagen.visit_index(Index("this_other_name_is_too_long_for_what_were_doing", t1.c.foo)) - self.assertEquals(schemagen.buffer.getvalue(), "CREATE INDEX this_other_nam_2 ON sometable (foo)") - - schemadrop = dialect.schemadropper(dialect, None) - schemadrop.execute = lambda: None - self.assertRaises(exc.IdentifierError, schemadrop.visit_index, Index("this_name_is_too_long_for_what_were_doing", t1.c.foo)) - - -class ConstraintCompilationTest(TestBase, AssertsExecutionResults): - class accum(object): - def __init__(self): - self.statements = [] - def __call__(self, sql, *a, **kw): - self.statements.append(sql) - def __contains__(self, substring): - for s in self.statements: - if substring in s: - return True - return False - def __str__(self): - return '\n'.join([repr(x) for x in self.statements]) - def clear(self): - del self.statements[:] - - def setUp(self): - self.sql = self.accum() - opts = config.db_opts.copy() - opts['strategy'] = 'mock' - opts['executor'] = self.sql - self.engine = engines.testing_engine(options=opts) - - - def _test_deferrable(self, constraint_factory): - meta = MetaData(self.engine) - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer), - constraint_factory(deferrable=True)) - t.create() - assert 'DEFERRABLE' in self.sql, self.sql - assert 'NOT DEFERRABLE' not in self.sql, self.sql - self.sql.clear() - meta.clear() - - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer), - constraint_factory(deferrable=False)) - t.create() - assert 'NOT DEFERRABLE' in self.sql - self.sql.clear() - meta.clear() - - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer), - constraint_factory(deferrable=True, initially='IMMEDIATE')) - t.create() - assert 'NOT DEFERRABLE' not in self.sql - assert 'INITIALLY IMMEDIATE' in self.sql - self.sql.clear() - meta.clear() - - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer), - constraint_factory(deferrable=True, initially='DEFERRED')) - t.create() - - assert 'NOT DEFERRABLE' not in self.sql - assert 'INITIALLY DEFERRED' in self.sql, self.sql - - def test_deferrable_pk(self): - factory = lambda **kw: PrimaryKeyConstraint('a', **kw) - self._test_deferrable(factory) - - def test_deferrable_table_fk(self): - factory = lambda **kw: ForeignKeyConstraint(['b'], ['tbl.a'], **kw) - self._test_deferrable(factory) - - def test_deferrable_column_fk(self): - meta = MetaData(self.engine) - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer, - ForeignKey('tbl.a', deferrable=True, - initially='DEFERRED'))) - t.create() - assert 'DEFERRABLE' in self.sql, self.sql - assert 'INITIALLY DEFERRED' in self.sql, self.sql - - def test_deferrable_unique(self): - factory = lambda **kw: UniqueConstraint('b', **kw) - self._test_deferrable(factory) - - def test_deferrable_table_check(self): - factory = lambda **kw: CheckConstraint('a < b', **kw) - self._test_deferrable(factory) - - def test_deferrable_column_check(self): - meta = MetaData(self.engine) - t = Table('tbl', meta, - Column('a', Integer), - Column('b', Integer, - CheckConstraint('a < b', - deferrable=True, - initially='DEFERRED'))) - t.create() - assert 'DEFERRABLE' in self.sql, self.sql - assert 'INITIALLY DEFERRED' in self.sql, self.sql - - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/defaults.py b/test/sql/defaults.py deleted file mode 100644 index bea6dc04b..000000000 --- a/test/sql/defaults.py +++ /dev/null @@ -1,634 +0,0 @@ -import testenv; testenv.configure_for_tests() -import datetime -from sqlalchemy import Sequence, Column, func -from sqlalchemy.sql import select, text -from testlib import sa, testing -from testlib.sa import MetaData, Table, Integer, String, ForeignKey, Boolean -from testlib.testing import eq_ -from sql import _base - - -class DefaultTest(testing.TestBase): - - def setUpAll(self): - global t, f, f2, ts, currenttime, metadata, default_generator - - db = testing.db - metadata = MetaData(db) - default_generator = {'x':50} - - def mydefault(): - default_generator['x'] += 1 - return default_generator['x'] - - def myupdate_with_ctx(ctx): - conn = ctx.connection - return conn.execute(sa.select([sa.text('13')])).scalar() - - def mydefault_using_connection(ctx): - conn = ctx.connection - try: - return conn.execute(sa.select([sa.text('12')])).scalar() - finally: - # ensure a "close()" on this connection does nothing, - # since its a "branched" connection - conn.close() - - use_function_defaults = testing.against('postgres', 'mssql', 'maxdb') - is_oracle = testing.against('oracle') - - # select "count(1)" returns different results on different DBs also - # correct for "current_date" compatible as column default, value - # differences - currenttime = func.current_date(type_=sa.Date, bind=db) - if is_oracle: - ts = db.scalar(sa.select([func.trunc(func.sysdate(), sa.literal_column("'DAY'"), type_=sa.Date).label('today')])) - assert isinstance(ts, datetime.date) and not isinstance(ts, datetime.datetime) - f = sa.select([func.length('abcdef')], bind=db).scalar() - f2 = sa.select([func.length('abcdefghijk')], bind=db).scalar() - # TODO: engine propigation across nested functions not working - currenttime = func.trunc(currenttime, sa.literal_column("'DAY'"), bind=db, type_=sa.Date) - def1 = currenttime - def2 = func.trunc(sa.text("sysdate"), sa.literal_column("'DAY'"), type_=sa.Date) - - deftype = sa.Date - elif use_function_defaults: - f = sa.select([func.length('abcdef')], bind=db).scalar() - f2 = sa.select([func.length('abcdefghijk')], bind=db).scalar() - def1 = currenttime - deftype = sa.Date - if testing.against('maxdb'): - def2 = sa.text("curdate") - elif testing.against('mssql'): - def2 = sa.text("getdate()") - else: - def2 = sa.text("current_date") - ts = db.func.current_date().scalar() - else: - f = len('abcdef') - f2 = len('abcdefghijk') - def1 = def2 = "3" - ts = 3 - deftype = Integer - - t = Table('default_test1', metadata, - # python function - Column('col1', Integer, primary_key=True, - default=mydefault), - - # python literal - Column('col2', String(20), - default="imthedefault", - onupdate="im the update"), - - # preexecute expression - Column('col3', Integer, - default=func.length('abcdef'), - onupdate=func.length('abcdefghijk')), - - # SQL-side default from sql expression - Column('col4', deftype, - server_default=def1), - - # SQL-side default from literal expression - Column('col5', deftype, - server_default=def2), - - # preexecute + update timestamp - Column('col6', sa.Date, - default=currenttime, - onupdate=currenttime), - - Column('boolcol1', sa.Boolean, default=True), - Column('boolcol2', sa.Boolean, default=False), - - # python function which uses ExecutionContext - Column('col7', Integer, - default=mydefault_using_connection, - onupdate=myupdate_with_ctx), - - # python builtin - Column('col8', sa.Date, - default=datetime.date.today, - onupdate=datetime.date.today), - # combo - Column('col9', String(20), - default='py', - server_default='ddl')) - t.create() - - def tearDownAll(self): - t.drop() - - def tearDown(self): - default_generator['x'] = 50 - t.delete().execute() - - def test_bad_arg_signature(self): - ex_msg = \ - "ColumnDefault Python function takes zero or one positional arguments" - - def fn1(x, y): pass - def fn2(x, y, z=3): pass - class fn3(object): - def __init__(self, x, y): - pass - class FN4(object): - def __call__(self, x, y): - pass - fn4 = FN4() - - for fn in fn1, fn2, fn3, fn4: - self.assertRaisesMessage(sa.exc.ArgumentError, - ex_msg, - sa.ColumnDefault, fn) - - def test_arg_signature(self): - def fn1(): pass - def fn2(): pass - def fn3(x=1): pass - def fn4(x=1, y=2, z=3): pass - fn5 = list - class fn6(object): - def __init__(self, x): - pass - class fn6(object): - def __init__(self, x, y=3): - pass - class FN7(object): - def __call__(self, x): - pass - fn7 = FN7() - class FN8(object): - def __call__(self, x, y=3): - pass - fn8 = FN8() - - for fn in fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8: - c = sa.ColumnDefault(fn) - - @testing.fails_on('firebird', 'Data type unknown') - def test_standalone(self): - c = testing.db.engine.contextual_connect() - x = c.execute(t.c.col1.default) - y = t.c.col2.default.execute() - z = c.execute(t.c.col3.default) - assert 50 <= x <= 57 - eq_(y, 'imthedefault') - eq_(z, f) - eq_(f2, 11) - - def test_py_vs_server_default_detection(self): - - def has_(name, *wanted): - slots = ['default', 'onupdate', 'server_default', 'server_onupdate'] - col = tbl.c[name] - for slot in wanted: - slots.remove(slot) - assert getattr(col, slot) is not None, getattr(col, slot) - for slot in slots: - assert getattr(col, slot) is None, getattr(col, slot) - - tbl = t - has_('col1', 'default') - has_('col2', 'default', 'onupdate') - has_('col3', 'default', 'onupdate') - has_('col4', 'server_default') - has_('col5', 'server_default') - has_('col6', 'default', 'onupdate') - has_('boolcol1', 'default') - has_('boolcol2', 'default') - has_('col7', 'default', 'onupdate') - has_('col8', 'default', 'onupdate') - has_('col9', 'default', 'server_default') - - ColumnDefault, DefaultClause = sa.ColumnDefault, sa.DefaultClause - - t2 = Table('t2', MetaData(), - Column('col1', Integer, Sequence('foo')), - Column('col2', Integer, - default=Sequence('foo'), - server_default='y'), - Column('col3', Integer, - Sequence('foo'), - server_default='x'), - Column('col4', Integer, - ColumnDefault('x'), - DefaultClause('y')), - Column('col4', Integer, - ColumnDefault('x'), - DefaultClause('y'), - DefaultClause('y', for_update=True)), - Column('col5', Integer, - ColumnDefault('x'), - DefaultClause('y'), - onupdate='z'), - Column('col6', Integer, - ColumnDefault('x'), - server_default='y', - onupdate='z'), - Column('col7', Integer, - default='x', - server_default='y', - onupdate='z'), - Column('col8', Integer, - server_onupdate='u', - default='x', - server_default='y', - onupdate='z')) - tbl = t2 - has_('col1', 'default') - has_('col2', 'default', 'server_default') - has_('col3', 'default', 'server_default') - has_('col4', 'default', 'server_default', 'server_onupdate') - has_('col5', 'default', 'server_default', 'onupdate') - has_('col6', 'default', 'server_default', 'onupdate') - has_('col7', 'default', 'server_default', 'onupdate') - has_('col8', 'default', 'server_default', 'onupdate', 'server_onupdate') - - @testing.fails_on('firebird', 'Data type unknown') - def test_insert(self): - r = t.insert().execute() - assert r.lastrow_has_defaults() - eq_(set(r.context.postfetch_cols), - set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) - - r = t.insert(inline=True).execute() - assert r.lastrow_has_defaults() - eq_(set(r.context.postfetch_cols), - set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) - - t.insert().execute() - - ctexec = sa.select([currenttime.label('now')], bind=testing.db).scalar() - l = t.select().order_by(t.c.col1).execute() - today = datetime.date.today() - eq_(l.fetchall(), [ - (x, 'imthedefault', f, ts, ts, ctexec, True, False, - 12, today, 'py') - for x in range(51, 54)]) - - t.insert().execute(col9=None) - assert r.lastrow_has_defaults() - eq_(set(r.context.postfetch_cols), - set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) - - eq_(t.select(t.c.col1==54).execute().fetchall(), - [(54, 'imthedefault', f, ts, ts, ctexec, True, False, - 12, today, None)]) - - @testing.fails_on('firebird', 'Data type unknown') - def test_insertmany(self): - # MySQL-Python 1.2.2 breaks functions in execute_many :( - if (testing.against('mysql') and - testing.db.dialect.dbapi.version_info[:3] == (1, 2, 2)): - return - - r = t.insert().execute({}, {}, {}) - - ctexec = currenttime.scalar() - l = t.select().execute() - today = datetime.date.today() - eq_(l.fetchall(), - [(51, 'imthedefault', f, ts, ts, ctexec, True, False, - 12, today, 'py'), - (52, 'imthedefault', f, ts, ts, ctexec, True, False, - 12, today, 'py'), - (53, 'imthedefault', f, ts, ts, ctexec, True, False, - 12, today, 'py')]) - - def test_insert_values(self): - t.insert(values={'col3':50}).execute() - l = t.select().execute() - eq_(50, l.fetchone()['col3']) - - @testing.fails_on('firebird', 'Data type unknown') - def test_updatemany(self): - # MySQL-Python 1.2.2 breaks functions in execute_many :( - if (testing.against('mysql') and - testing.db.dialect.dbapi.version_info[:3] == (1, 2, 2)): - return - - t.insert().execute({}, {}, {}) - - t.update(t.c.col1==sa.bindparam('pkval')).execute( - {'pkval':51,'col7':None, 'col8':None, 'boolcol1':False}) - - t.update(t.c.col1==sa.bindparam('pkval')).execute( - {'pkval':51,}, - {'pkval':52,}, - {'pkval':53,}) - - l = t.select().execute() - ctexec = currenttime.scalar() - today = datetime.date.today() - eq_(l.fetchall(), - [(51, 'im the update', f2, ts, ts, ctexec, False, False, - 13, today, 'py'), - (52, 'im the update', f2, ts, ts, ctexec, True, False, - 13, today, 'py'), - (53, 'im the update', f2, ts, ts, ctexec, True, False, - 13, today, 'py')]) - - @testing.fails_on('firebird', 'Data type unknown') - def test_update(self): - r = t.insert().execute() - pk = r.last_inserted_ids()[0] - t.update(t.c.col1==pk).execute(col4=None, col5=None) - ctexec = currenttime.scalar() - l = t.select(t.c.col1==pk).execute() - l = l.fetchone() - eq_(l, - (pk, 'im the update', f2, None, None, ctexec, True, False, - 13, datetime.date.today(), 'py')) - eq_(11, f2) - - @testing.fails_on('firebird', 'Data type unknown') - def test_update_values(self): - r = t.insert().execute() - pk = r.last_inserted_ids()[0] - t.update(t.c.col1==pk, values={'col3': 55}).execute() - l = t.select(t.c.col1==pk).execute() - l = l.fetchone() - eq_(55, l['col3']) - - @testing.fails_on_everything_except('postgres') - def test_passive_override(self): - """ - Primarily for postgres, tests that when we get a primary key column - back from reflecting a table which has a default value on it, we - pre-execute that DefaultClause upon insert, even though DefaultClause - says "let the database execute this", because in postgres we must have - all the primary key values in memory before insert; otherwise we can't - locate the just inserted row. - - """ - # TODO: move this to dialect/postgres - try: - meta = MetaData(testing.db) - testing.db.execute(""" - CREATE TABLE speedy_users - ( - speedy_user_id SERIAL PRIMARY KEY, - - user_name VARCHAR NOT NULL, - user_password VARCHAR NOT NULL - ); - """, None) - - t = Table("speedy_users", meta, autoload=True) - t.insert().execute(user_name='user', user_password='lala') - l = t.select().execute().fetchall() - eq_(l, [(1, 'user', 'lala')]) - finally: - testing.db.execute("drop table speedy_users", None) - - -class PKDefaultTest(_base.TablesTest): - __requires__ = ('subqueries',) - - def define_tables(self, metadata): - t2 = Table('t2', metadata, - Column('nextid', Integer)) - - Table('t1', metadata, - Column('id', Integer, primary_key=True, - default=sa.select([func.max(t2.c.nextid)]).as_scalar()), - Column('data', String(30))) - - @testing.fails_on('mssql', 'FIXME: unknown') - @testing.resolve_artifact_names - def test_basic(self): - t2.insert().execute(nextid=1) - r = t1.insert().execute(data='hi') - eq_([1], r.last_inserted_ids()) - - t2.insert().execute(nextid=2) - r = t1.insert().execute(data='there') - eq_([2], r.last_inserted_ids()) - - -class PKIncrementTest(_base.TablesTest): - run_define_tables = 'each' - - def define_tables(self, metadata): - Table("aitable", metadata, - Column('id', Integer, Sequence('ai_id_seq', optional=True), - primary_key=True), - Column('int1', Integer), - Column('str1', String(20))) - - # TODO: add coverage for increment on a secondary column in a key - @testing.fails_on('firebird', 'Data type unknown') - @testing.resolve_artifact_names - def _test_autoincrement(self, bind): - ids = set() - rs = bind.execute(aitable.insert(), int1=1) - last = rs.last_inserted_ids()[0] - self.assert_(last) - self.assert_(last not in ids) - ids.add(last) - - rs = bind.execute(aitable.insert(), str1='row 2') - last = rs.last_inserted_ids()[0] - self.assert_(last) - self.assert_(last not in ids) - ids.add(last) - - rs = bind.execute(aitable.insert(), int1=3, str1='row 3') - last = rs.last_inserted_ids()[0] - self.assert_(last) - self.assert_(last not in ids) - ids.add(last) - - rs = bind.execute(aitable.insert(values={'int1':func.length('four')})) - last = rs.last_inserted_ids()[0] - self.assert_(last) - self.assert_(last not in ids) - ids.add(last) - - eq_(list(bind.execute(aitable.select().order_by(aitable.c.id))), - [(1, 1, None), (2, None, 'row 2'), (3, 3, 'row 3'), (4, 4, None)]) - - @testing.resolve_artifact_names - def test_autoincrement_autocommit(self): - self._test_autoincrement(testing.db) - - @testing.resolve_artifact_names - def test_autoincrement_transaction(self): - con = testing.db.connect() - tx = con.begin() - try: - try: - self._test_autoincrement(con) - except: - try: - tx.rollback() - except: - pass - raise - else: - tx.commit() - finally: - con.close() - - -class EmptyInsertTest(testing.TestBase): - @testing.exclude('sqlite', '<', (3, 3, 8), 'no empty insert support') - @testing.fails_on('oracle', 'FIXME: unknown') - def test_empty_insert(self): - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('is_true', Boolean, server_default=('1'))) - metadata.create_all() - - try: - result = t1.insert().execute() - self.assertEquals(1, select([func.count(text('*'))], from_obj=t1).scalar()) - self.assertEquals(True, t1.select().scalar()) - finally: - metadata.drop_all() - -class AutoIncrementTest(_base.TablesTest): - __requires__ = ('identity',) - run_define_tables = 'each' - - def define_tables(self, metadata): - """Each test manipulates self.metadata individually.""" - - @testing.exclude('sqlite', '<', (3, 4), 'no database support') - def test_autoincrement_single_col(self): - single = Table('single', self.metadata, - Column('id', Integer, primary_key=True)) - single.create() - - r = single.insert().execute() - id_ = r.last_inserted_ids()[0] - assert id_ is not None - eq_(1, sa.select([func.count(sa.text('*'))], from_obj=single).scalar()) - - def test_autoincrement_fk(self): - nodes = Table('nodes', self.metadata, - Column('id', Integer, primary_key=True), - Column('parent_id', Integer, ForeignKey('nodes.id')), - Column('data', String(30))) - nodes.create() - - r = nodes.insert().execute(data='foo') - id_ = r.last_inserted_ids()[0] - nodes.insert().execute(data='bar', parent_id=id_) - - @testing.fails_on('sqlite', 'FIXME: unknown') - def test_non_autoincrement(self): - # sqlite INT primary keys can be non-unique! (only for ints) - nonai = Table("nonaitest", self.metadata, - Column('id', Integer, autoincrement=False, primary_key=True), - Column('data', String(20))) - nonai.create() - - - try: - # postgres + mysql strict will fail on first row, - # mysql in legacy mode fails on second row - nonai.insert().execute(data='row 1') - nonai.insert().execute(data='row 2') - assert False - except sa.exc.SQLError, e: - assert True - - nonai.insert().execute(id=1, data='row 1') - - -class SequenceTest(testing.TestBase): - __requires__ = ('sequences',) - - def setUpAll(self): - global cartitems, sometable, metadata - metadata = MetaData(testing.db) - cartitems = Table("cartitems", metadata, - Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True), - Column("description", String(40)), - Column("createdate", sa.DateTime()) - ) - sometable = Table( 'Manager', metadata, - Column('obj_id', Integer, Sequence('obj_id_seq'), ), - Column('name', String(128)), - Column('id', Integer, Sequence('Manager_id_seq', optional=True), - primary_key=True), - ) - - metadata.create_all() - - def testseqnonpk(self): - """test sequences fire off as defaults on non-pk columns""" - - result = sometable.insert().execute(name="somename") - assert 'id' in result.postfetch_cols() - - result = sometable.insert().execute(name="someother") - assert 'id' in result.postfetch_cols() - - sometable.insert().execute( - {'name':'name3'}, - {'name':'name4'}) - eq_(sometable.select().execute().fetchall(), - [(1, "somename", 1), - (2, "someother", 2), - (3, "name3", 3), - (4, "name4", 4)]) - - def testsequence(self): - cartitems.insert().execute(description='hi') - cartitems.insert().execute(description='there') - r = cartitems.insert().execute(description='lala') - - assert r.last_inserted_ids() and r.last_inserted_ids()[0] is not None - id_ = r.last_inserted_ids()[0] - - eq_(1, - sa.select([func.count(cartitems.c.cart_id)], - sa.and_(cartitems.c.description == 'lala', - cartitems.c.cart_id == id_)).scalar()) - - cartitems.select().execute().fetchall() - - @testing.fails_on('maxdb', 'FIXME: unknown') - # maxdb db-api seems to double-execute NEXTVAL internally somewhere, - # throwing off the numbers for these tests... - def test_implicit_sequence_exec(self): - s = Sequence("my_sequence", metadata=MetaData(testing.db)) - s.create() - try: - x = s.execute() - eq_(x, 1) - finally: - s.drop() - - @testing.fails_on('maxdb', 'FIXME: unknown') - def teststandalone_explicit(self): - s = Sequence("my_sequence") - s.create(bind=testing.db) - try: - x = s.execute(testing.db) - eq_(x, 1) - finally: - s.drop(testing.db) - - def test_checkfirst(self): - s = Sequence("my_sequence") - s.create(testing.db, checkfirst=False) - s.create(testing.db, checkfirst=True) - s.drop(testing.db, checkfirst=False) - s.drop(testing.db, checkfirst=True) - - @testing.fails_on('maxdb', 'FIXME: unknown') - def teststandalone2(self): - x = cartitems.c.cart_id.sequence.execute() - self.assert_(1 <= x <= 4) - - def tearDownAll(self): - metadata.drop_all() - - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/functions.py b/test/sql/functions.py deleted file mode 100644 index 17d8a35e9..000000000 --- a/test/sql/functions.py +++ /dev/null @@ -1,319 +0,0 @@ -import testenv; testenv.configure_for_tests() -import datetime -from sqlalchemy import * -from sqlalchemy.sql import table, column -from sqlalchemy import databases, sql, util -from sqlalchemy.sql.compiler import BIND_TEMPLATES -from sqlalchemy.engine import default -from testlib.engines import all_dialects -from sqlalchemy import types as sqltypes -from testlib import * -from sqlalchemy.sql.functions import GenericFunction -from testlib.testing import eq_ -from decimal import Decimal as _python_Decimal - -from sqlalchemy.databases import * - -# FIXME! -dialects = [d for d in all_dialects() if d.name not in ('access', 'informix')] - - -class CompileTest(TestBase, AssertsCompiledSQL): - def test_compile(self): - for dialect in dialects: - bindtemplate = BIND_TEMPLATES[dialect.paramstyle] - self.assert_compile(func.current_timestamp(), "CURRENT_TIMESTAMP", dialect=dialect) - self.assert_compile(func.localtime(), "LOCALTIME", dialect=dialect) - if isinstance(dialect, firebird.dialect): - self.assert_compile(func.nosuchfunction(), "nosuchfunction", dialect=dialect) - else: - self.assert_compile(func.nosuchfunction(), "nosuchfunction()", dialect=dialect) - - # test generic function compile - class fake_func(GenericFunction): - __return_type__ = sqltypes.Integer - - def __init__(self, arg, **kwargs): - GenericFunction.__init__(self, args=[arg], **kwargs) - - self.assert_compile(fake_func('foo'), "fake_func(%s)" % bindtemplate % {'name':'param_1', 'position':1}, dialect=dialect) - - def test_use_labels(self): - self.assert_compile(select([func.foo()], use_labels=True), - "SELECT foo() AS foo_1" - ) - def test_underscores(self): - self.assert_compile(func.if_(), "if()") - - def test_generic_now(self): - assert isinstance(func.now().type, sqltypes.DateTime) - - for ret, dialect in [ - ('CURRENT_TIMESTAMP', sqlite.dialect()), - ('now()', postgres.dialect()), - ('now()', mysql.dialect()), - ('CURRENT_TIMESTAMP', oracle.dialect()) - ]: - self.assert_compile(func.now(), ret, dialect=dialect) - - def test_generic_random(self): - assert func.random().type == sqltypes.NULLTYPE - assert isinstance(func.random(type_=Integer).type, Integer) - - for ret, dialect in [ - ('random()', sqlite.dialect()), - ('random()', postgres.dialect()), - ('rand()', mysql.dialect()), - ('random()', oracle.dialect()) - ]: - self.assert_compile(func.random(), ret, dialect=dialect) - - def test_generic_count(self): - assert isinstance(func.count().type, sqltypes.Integer) - - self.assert_compile(func.count(), 'count(*)') - self.assert_compile(func.count(1), 'count(:param_1)') - c = column('abc') - self.assert_compile(func.count(c), 'count(abc)') - - def test_constructor(self): - try: - func.current_timestamp('somearg') - assert False - except TypeError: - assert True - - try: - func.char_length('a', 'b') - assert False - except TypeError: - assert True - - try: - func.char_length() - assert False - except TypeError: - assert True - - def test_return_type_detection(self): - - for fn in [func.coalesce, func.max, func.min, func.sum]: - for args, type_ in [ - ((datetime.date(2007, 10, 5), datetime.date(2005, 10, 15)), sqltypes.Date), - ((3, 5), sqltypes.Integer), - ((_python_Decimal(3), _python_Decimal(5)), sqltypes.Numeric), - (("foo", "bar"), sqltypes.String), - ((datetime.datetime(2007, 10, 5, 8, 3, 34), datetime.datetime(2005, 10, 15, 14, 45, 33)), sqltypes.DateTime) - ]: - assert isinstance(fn(*args).type, type_), "%s / %s" % (fn(), type_) - - assert isinstance(func.concat("foo", "bar").type, sqltypes.String) - - - def test_assorted(self): - table1 = table('mytable', - column('myid', Integer), - ) - - table2 = table( - 'myothertable', - column('otherid', Integer), - ) - - # test an expression with a function - self.assert_compile(func.lala(3, 4, literal("five"), table1.c.myid) * table2.c.otherid, - "lala(:lala_1, :lala_2, :param_1, mytable.myid) * myothertable.otherid") - - # test it in a SELECT - self.assert_compile(select([func.count(table1.c.myid)]), - "SELECT count(mytable.myid) AS count_1 FROM mytable") - - # test a "dotted" function name - self.assert_compile(select([func.foo.bar.lala(table1.c.myid)]), - "SELECT foo.bar.lala(mytable.myid) AS lala_1 FROM mytable") - - # test the bind parameter name with a "dotted" function name is only the name - # (limits the length of the bind param name) - self.assert_compile(select([func.foo.bar.lala(12)]), - "SELECT foo.bar.lala(:lala_2) AS lala_1") - - # test a dotted func off the engine itself - self.assert_compile(func.lala.hoho(7), "lala.hoho(:hoho_1)") - - # test None becomes NULL - self.assert_compile(func.my_func(1,2,None,3), "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)") - - # test pickling - self.assert_compile(util.pickle.loads(util.pickle.dumps(func.my_func(1, 2, None, 3))), "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)") - - # assert func raises AttributeError for __bases__ attribute, since its not a class - # fixes pydoc - try: - func.__bases__ - assert False - except AttributeError: - assert True - - def test_functions_with_cols(self): - users = table('users', column('id'), column('name'), column('fullname')) - calculate = select([column('q'), column('z'), column('r')], - from_obj=[func.calculate(bindparam('x'), bindparam('y'))]) - - self.assert_compile(select([users], users.c.id > calculate.c.z), - "SELECT users.id, users.name, users.fullname " - "FROM users, (SELECT q, z, r " - "FROM calculate(:x, :y)) " - "WHERE users.id > z" - ) - - s = select([users], users.c.id.between( - calculate.alias('c1').unique_params(x=17, y=45).c.z, - calculate.alias('c2').unique_params(x=5, y=12).c.z)) - - self.assert_compile(s, - "SELECT users.id, users.name, users.fullname " - "FROM users, (SELECT q, z, r " - "FROM calculate(:x_1, :y_1)) AS c1, (SELECT q, z, r " - "FROM calculate(:x_2, :y_2)) AS c2 " - "WHERE users.id BETWEEN c1.z AND c2.z" - , checkparams={'y_1': 45, 'x_1': 17, 'y_2': 12, 'x_2': 5}) - - -class ExecuteTest(TestBase): - - def test_standalone_execute(self): - x = testing.db.func.current_date().execute().scalar() - y = testing.db.func.current_date().select().execute().scalar() - z = testing.db.func.current_date().scalar() - assert (x == y == z) is True - - # ansi func - x = testing.db.func.current_date() - assert isinstance(x.type, Date) - assert isinstance(x.execute().scalar(), datetime.date) - - def test_conn_execute(self): - conn = testing.db.connect() - try: - x = conn.execute(func.current_date()).scalar() - y = conn.execute(func.current_date().select()).scalar() - z = conn.scalar(func.current_date()) - finally: - conn.close() - assert (x == y == z) is True - - def test_update(self): - """ - Tests sending functions and SQL expressions to the VALUES and SET - clauses of INSERT/UPDATE instances, and that column-level defaults - get overridden. - """ - - meta = MetaData(testing.db) - t = Table('t1', meta, - Column('id', Integer, Sequence('t1idseq', optional=True), primary_key=True), - Column('value', Integer) - ) - t2 = Table('t2', meta, - Column('id', Integer, Sequence('t2idseq', optional=True), primary_key=True), - Column('value', Integer, default=7), - Column('stuff', String(20), onupdate="thisisstuff") - ) - meta.create_all() - try: - t.insert(values=dict(value=func.length("one"))).execute() - assert t.select().execute().fetchone()['value'] == 3 - t.update(values=dict(value=func.length("asfda"))).execute() - assert t.select().execute().fetchone()['value'] == 5 - - r = t.insert(values=dict(value=func.length("sfsaafsda"))).execute() - id = r.last_inserted_ids()[0] - assert t.select(t.c.id==id).execute().fetchone()['value'] == 9 - t.update(values={t.c.value:func.length("asdf")}).execute() - assert t.select().execute().fetchone()['value'] == 4 - print "--------------------------" - t2.insert().execute() - t2.insert(values=dict(value=func.length("one"))).execute() - t2.insert(values=dict(value=func.length("asfda") + -19)).execute(stuff="hi") - - res = exec_sorted(select([t2.c.value, t2.c.stuff])) - self.assertEquals(res, [(-14, 'hi'), (3, None), (7, None)]) - - t2.update(values=dict(value=func.length("asdsafasd"))).execute(stuff="some stuff") - assert select([t2.c.value, t2.c.stuff]).execute().fetchall() == [(9,"some stuff"), (9,"some stuff"), (9,"some stuff")] - - t2.delete().execute() - - t2.insert(values=dict(value=func.length("one") + 8)).execute() - assert t2.select().execute().fetchone()['value'] == 11 - - t2.update(values=dict(value=func.length("asfda"))).execute() - assert select([t2.c.value, t2.c.stuff]).execute().fetchone() == (5, "thisisstuff") - - t2.update(values={t2.c.value:func.length("asfdaasdf"), t2.c.stuff:"foo"}).execute() - print "HI", select([t2.c.value, t2.c.stuff]).execute().fetchone() - assert select([t2.c.value, t2.c.stuff]).execute().fetchone() == (9, "foo") - finally: - meta.drop_all() - - @testing.fails_on_everything_except('postgres') - def test_as_from(self): - # TODO: shouldnt this work on oracle too ? - x = testing.db.func.current_date().execute().scalar() - y = testing.db.func.current_date().select().execute().scalar() - z = testing.db.func.current_date().scalar() - w = select(['*'], from_obj=[testing.db.func.current_date()]).scalar() - - # construct a column-based FROM object out of a function, like in [ticket:172] - s = select([sql.column('date', type_=DateTime)], from_obj=[testing.db.func.current_date()]) - q = s.execute().fetchone()[s.c.date] - r = s.alias('datequery').select().scalar() - - assert x == y == z == w == q == r - - def test_extract_bind(self): - """Basic common denominator execution tests for extract()""" - - date = datetime.date(2010, 5, 1) - - def execute(field): - return testing.db.execute(select([extract(field, date)])).scalar() - - assert execute('year') == 2010 - assert execute('month') == 5 - assert execute('day') == 1 - - date = datetime.datetime(2010, 5, 1, 12, 11, 10) - - assert execute('year') == 2010 - assert execute('month') == 5 - assert execute('day') == 1 - - def test_extract_expression(self): - meta = MetaData(testing.db) - table = Table('test', meta, - Column('dt', DateTime), - Column('d', Date)) - meta.create_all() - try: - table.insert().execute( - {'dt': datetime.datetime(2010, 5, 1, 12, 11, 10), - 'd': datetime.date(2010, 5, 1) }) - rs = select([extract('year', table.c.dt), - extract('month', table.c.d)]).execute() - row = rs.fetchone() - assert row[0] == 2010 - assert row[1] == 5 - rs.close() - finally: - meta.drop_all() - - -def exec_sorted(statement, *args, **kw): - """Executes a statement and returns a sorted list plain tuple rows.""" - - return sorted([tuple(row) - for row in statement.execute(*args, **kw).fetchall()]) - -if __name__ == '__main__': - testenv.main() diff --git a/test/sql/generative.py b/test/sql/generative.py deleted file mode 100644 index 3947a450f..000000000 --- a/test/sql/generative.py +++ /dev/null @@ -1,815 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy.sql import table, column, ClauseElement -from sqlalchemy.sql.expression import _clone, _from_objects -from testlib import * -from sqlalchemy.sql.visitors import * -from sqlalchemy import util -from sqlalchemy.sql import util as sql_util - - -class TraversalTest(TestBase, AssertsExecutionResults): - """test ClauseVisitor's traversal, particularly its ability to copy and modify - a ClauseElement in place.""" - - def setUpAll(self): - global A, B - - # establish two ficticious ClauseElements. - # define deep equality semantics as well as deep identity semantics. - class A(ClauseElement): - __visit_name__ = 'a' - - def __init__(self, expr): - self.expr = expr - - def is_other(self, other): - return other is self - - __hash__ = ClauseElement.__hash__ - - def __eq__(self, other): - return other.expr == self.expr - - def __ne__(self, other): - return other.expr != self.expr - - def __str__(self): - return "A(%s)" % repr(self.expr) - - class B(ClauseElement): - __visit_name__ = 'b' - - def __init__(self, *items): - self.items = items - - def is_other(self, other): - if other is not self: - return False - for i1, i2 in zip(self.items, other.items): - if i1 is not i2: - return False - return True - - __hash__ = ClauseElement.__hash__ - - def __eq__(self, other): - for i1, i2 in zip(self.items, other.items): - if i1 != i2: - return False - return True - - def __ne__(self, other): - for i1, i2 in zip(self.items, other.items): - if i1 != i2: - return True - return False - - def _copy_internals(self, clone=_clone): - self.items = [clone(i) for i in self.items] - - def get_children(self, **kwargs): - return self.items - - def __str__(self): - return "B(%s)" % repr([str(i) for i in self.items]) - - def test_test_classes(self): - a1 = A("expr1") - struct = B(a1, A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) - struct2 = B(a1, A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) - struct3 = B(a1, A("expr2"), B(A("expr1b"), A("expr2bmodified")), A("expr3")) - - assert a1.is_other(a1) - assert struct.is_other(struct) - assert struct == struct2 - assert struct != struct3 - assert not struct.is_other(struct2) - assert not struct.is_other(struct3) - - def test_clone(self): - struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) - - class Vis(CloningVisitor): - def visit_a(self, a): - pass - def visit_b(self, b): - pass - - vis = Vis() - s2 = vis.traverse(struct) - assert struct == s2 - assert not struct.is_other(s2) - - def test_no_clone(self): - struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) - - class Vis(ClauseVisitor): - def visit_a(self, a): - pass - def visit_b(self, b): - pass - - vis = Vis() - s2 = vis.traverse(struct) - assert struct == s2 - assert struct.is_other(s2) - - def test_change_in_place(self): - struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) - struct2 = B(A("expr1"), A("expr2modified"), B(A("expr1b"), A("expr2b")), A("expr3")) - struct3 = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2bmodified")), A("expr3")) - - class Vis(CloningVisitor): - def visit_a(self, a): - if a.expr == "expr2": - a.expr = "expr2modified" - def visit_b(self, b): - pass - - vis = Vis() - s2 = vis.traverse(struct) - assert struct != s2 - assert not struct.is_other(s2) - assert struct2 == s2 - - class Vis2(CloningVisitor): - def visit_a(self, a): - if a.expr == "expr2b": - a.expr = "expr2bmodified" - def visit_b(self, b): - pass - - vis2 = Vis2() - s3 = vis2.traverse(struct) - assert struct != s3 - assert struct3 == s3 - - def test_visit_name(self): - # override fns in testlib/schema.py - from sqlalchemy import Column - - class CustomObj(Column): - pass - - assert CustomObj.__visit_name__ == Column.__visit_name__ == 'column' - - foo, bar = CustomObj('foo', String), CustomObj('bar', String) - bin = foo == bar - s = set(ClauseVisitor().iterate(bin)) - assert set(ClauseVisitor().iterate(bin)) == set([foo, bar, bin]) - -class ClauseTest(TestBase, AssertsCompiledSQL): - """test copy-in-place behavior of various ClauseElements.""" - - def setUpAll(self): - global t1, t2 - t1 = table("table1", - column("col1"), - column("col2"), - column("col3"), - ) - t2 = table("table2", - column("col1"), - column("col2"), - column("col3"), - ) - - def test_binary(self): - clause = t1.c.col2 == t2.c.col2 - assert str(clause) == CloningVisitor().traverse(clause) - - def test_binary_anon_label_quirk(self): - t = table('t1', column('col1')) - - - f = t.c.col1 * 5 - self.assert_compile(select([f]), "SELECT t1.col1 * :col1_1 AS anon_1 FROM t1") - - f.anon_label - - a = t.alias() - f = sql_util.ClauseAdapter(a).traverse(f) - - self.assert_compile(select([f]), "SELECT t1_1.col1 * :col1_1 AS anon_1 FROM t1 AS t1_1") - - def test_join(self): - clause = t1.join(t2, t1.c.col2==t2.c.col2) - c1 = str(clause) - assert str(clause) == str(CloningVisitor().traverse(clause)) - - class Vis(CloningVisitor): - def visit_binary(self, binary): - binary.right = t2.c.col3 - - clause2 = Vis().traverse(clause) - assert c1 == str(clause) - assert str(clause2) == str(t1.join(t2, t1.c.col2==t2.c.col3)) - - def test_text(self): - clause = text("select * from table where foo=:bar", bindparams=[bindparam('bar')]) - c1 = str(clause) - class Vis(CloningVisitor): - def visit_textclause(self, text): - text.text = text.text + " SOME MODIFIER=:lala" - text.bindparams['lala'] = bindparam('lala') - - clause2 = Vis().traverse(clause) - assert c1 == str(clause) - assert str(clause2) == c1 + " SOME MODIFIER=:lala" - assert clause.bindparams.keys() == ['bar'] - assert set(clause2.bindparams.keys()) == set(['bar', 'lala']) - - def test_select(self): - s2 = select([t1]) - s2_assert = str(s2) - s3_assert = str(select([t1], t1.c.col2==7)) - class Vis(CloningVisitor): - def visit_select(self, select): - select.append_whereclause(t1.c.col2==7) - s3 = Vis().traverse(s2) - assert str(s3) == s3_assert - assert str(s2) == s2_assert - print str(s2) - print str(s3) - class Vis(ClauseVisitor): - def visit_select(self, select): - select.append_whereclause(t1.c.col2==7) - Vis().traverse(s2) - assert str(s2) == s3_assert - - print "------------------" - - s4_assert = str(select([t1], and_(t1.c.col2==7, t1.c.col3==9))) - class Vis(CloningVisitor): - def visit_select(self, select): - select.append_whereclause(t1.c.col3==9) - s4 = Vis().traverse(s3) - print str(s3) - print str(s4) - assert str(s4) == s4_assert - assert str(s3) == s3_assert - - print "------------------" - s5_assert = str(select([t1], and_(t1.c.col2==7, t1.c.col1==9))) - class Vis(CloningVisitor): - def visit_binary(self, binary): - if binary.left is t1.c.col3: - binary.left = t1.c.col1 - binary.right = bindparam("col1", unique=True) - s5 = Vis().traverse(s4) - print str(s4) - print str(s5) - assert str(s5) == s5_assert - assert str(s4) == s4_assert - - def test_union(self): - u = union(t1.select(), t2.select()) - u2 = CloningVisitor().traverse(u) - assert str(u) == str(u2) - assert [str(c) for c in u2.c] == [str(c) for c in u.c] - - u = union(t1.select(), t2.select()) - cols = [str(c) for c in u.c] - u2 = CloningVisitor().traverse(u) - assert str(u) == str(u2) - assert [str(c) for c in u2.c] == cols - - s1 = select([t1], t1.c.col1 == bindparam('id_param')) - s2 = select([t2]) - u = union(s1, s2) - - u2 = u.params(id_param=7) - u3 = u.params(id_param=10) - assert str(u) == str(u2) == str(u3) - assert u2.compile().params == {'id_param':7} - assert u3.compile().params == {'id_param':10} - - def test_adapt_union(self): - u = union(t1.select().where(t1.c.col1==4), t1.select().where(t1.c.col1==5)).alias() - - assert sql_util.ClauseAdapter(u).traverse(t1) is u - - def test_binds(self): - """test that unique bindparams change their name upon clone() to prevent conflicts""" - - s = select([t1], t1.c.col1==bindparam(None, unique=True)).alias() - s2 = CloningVisitor().traverse(s).alias() - s3 = select([s], s.c.col2==s2.c.col2) - - self.assert_compile(s3, "SELECT anon_1.col1, anon_1.col2, anon_1.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, "\ - "table1.col3 AS col3 FROM table1 WHERE table1.col1 = :param_1) AS anon_1, "\ - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :param_2) AS anon_2 "\ - "WHERE anon_1.col2 = anon_2.col2") - - s = select([t1], t1.c.col1==4).alias() - s2 = CloningVisitor().traverse(s).alias() - s3 = select([s], s.c.col2==s2.c.col2) - self.assert_compile(s3, "SELECT anon_1.col1, anon_1.col2, anon_1.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, "\ - "table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1, "\ - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_2) AS anon_2 "\ - "WHERE anon_1.col2 = anon_2.col2") - - @testing.emits_warning('.*replaced by another column with the same key') - def test_alias(self): - subq = t2.select().alias('subq') - s = select([t1.c.col1, subq.c.col1], from_obj=[t1, subq, t1.join(subq, t1.c.col1==subq.c.col2)]) - orig = str(s) - s2 = CloningVisitor().traverse(s) - assert orig == str(s) == str(s2) - - s4 = CloningVisitor().traverse(s2) - assert orig == str(s) == str(s2) == str(s4) - - s3 = sql_util.ClauseAdapter(table('foo')).traverse(s) - assert orig == str(s) == str(s3) - - s4 = sql_util.ClauseAdapter(table('foo')).traverse(s3) - assert orig == str(s) == str(s3) == str(s4) - - def test_correlated_select(self): - s = select(['*'], t1.c.col1==t2.c.col1, from_obj=[t1, t2]).correlate(t2) - class Vis(CloningVisitor): - def visit_select(self, select): - select.append_whereclause(t1.c.col2==7) - - self.assert_compile(Vis().traverse(s), "SELECT * FROM table1 WHERE table1.col1 = table2.col1 AND table1.col2 = :col2_1") - - def test_this_thing(self): - s = select([t1]).where(t1.c.col1=='foo').alias() - s2 = select([s.c.col1]) - - self.assert_compile(s2, "SELECT anon_1.col1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1") - t1a = t1.alias() - s2 = sql_util.ClauseAdapter(t1a).traverse(s2) - self.assert_compile(s2, "SELECT anon_1.col1 FROM (SELECT table1_1.col1 AS col1, table1_1.col2 AS col2, table1_1.col3 AS col3 FROM table1 AS table1_1 WHERE table1_1.col1 = :col1_1) AS anon_1") - - def test_select_fromtwice(self): - t1a = t1.alias() - - s = select([1], t1.c.col1==t1a.c.col1, from_obj=t1a).correlate(t1) - self.assert_compile(s, "SELECT 1 FROM table1 AS table1_1 WHERE table1.col1 = table1_1.col1") - - s = CloningVisitor().traverse(s) - self.assert_compile(s, "SELECT 1 FROM table1 AS table1_1 WHERE table1.col1 = table1_1.col1") - - s = select([t1]).where(t1.c.col1=='foo').alias() - - s2 = select([1], t1.c.col1==s.c.col1, from_obj=s).correlate(t1) - self.assert_compile(s2, "SELECT 1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1 WHERE table1.col1 = anon_1.col1") - s2 = ReplacingCloningVisitor().traverse(s2) - self.assert_compile(s2, "SELECT 1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1 WHERE table1.col1 = anon_1.col1") - -class ClauseAdapterTest(TestBase, AssertsCompiledSQL): - def setUpAll(self): - global t1, t2 - t1 = table("table1", - column("col1"), - column("col2"), - column("col3"), - ) - t2 = table("table2", - column("col1"), - column("col2"), - column("col3"), - ) - - def test_correlation_on_clone(self): - t1alias = t1.alias('t1alias') - t2alias = t2.alias('t2alias') - vis = sql_util.ClauseAdapter(t1alias) - - s = select(['*'], from_obj=[t1alias, t2alias]).as_scalar() - assert t2alias in s._froms - assert t1alias in s._froms - - self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") - s = vis.traverse(s) - - assert t2alias not in s._froms # not present because it's been cloned - - assert t1alias in s._froms # present because the adapter placed it there - - # correlate list on "s" needs to take into account the full _cloned_set for each element in _froms when correlating - self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") - - s = select(['*'], from_obj=[t1alias, t2alias]).correlate(t2alias).as_scalar() - self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") - s = vis.traverse(s) - self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") - s = CloningVisitor().traverse(s) - self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") - - s = select(['*']).where(t1.c.col1==t2.c.col1).as_scalar() - self.assert_compile(select([t1.c.col1, s]), "SELECT table1.col1, (SELECT * FROM table2 WHERE table1.col1 = table2.col1) AS anon_1 FROM table1") - vis = sql_util.ClauseAdapter(t1alias) - s = vis.traverse(s) - self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") - s = CloningVisitor().traverse(s) - self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") - - s = select(['*']).where(t1.c.col1==t2.c.col1).correlate(t1).as_scalar() - self.assert_compile(select([t1.c.col1, s]), "SELECT table1.col1, (SELECT * FROM table2 WHERE table1.col1 = table2.col1) AS anon_1 FROM table1") - vis = sql_util.ClauseAdapter(t1alias) - s = vis.traverse(s) - self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") - s = CloningVisitor().traverse(s) - self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") - - @testing.fails_on_everything_except() - def test_joins_dont_adapt(self): - # adapting to a join, i.e. ClauseAdapter(t1.join(t2)), doesn't make much sense. - # ClauseAdapter doesn't make any changes if it's against a straight join. - users = table('users', column('id')) - addresses = table('addresses', column('id'), column('user_id')) - - ualias = users.alias() - - s = select([func.count(addresses.c.id)], users.c.id==addresses.c.user_id).correlate(users) #.as_scalar().label(None) - s= sql_util.ClauseAdapter(ualias).traverse(s) - - j1 = addresses.join(ualias, addresses.c.user_id==ualias.c.id) - - self.assert_compile(sql_util.ClauseAdapter(j1).traverse(s), "SELECT count(addresses.id) AS count_1 FROM addresses WHERE users_1.id = addresses.user_id") - - def test_table_to_alias(self): - - t1alias = t1.alias('t1alias') - - vis = sql_util.ClauseAdapter(t1alias) - ff = vis.traverse(func.count(t1.c.col1).label('foo')) - assert list(_from_objects(ff)) == [t1alias] - - self.assert_compile(vis.traverse(select(['*'], from_obj=[t1])), "SELECT * FROM table1 AS t1alias") - self.assert_compile(select(['*'], t1.c.col1==t2.c.col2), "SELECT * FROM table1, table2 WHERE table1.col1 = table2.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2)), "SELECT * FROM table1 AS t1alias, table2 WHERE t1alias.col1 = table2.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2])), "SELECT * FROM table1 AS t1alias, table2 WHERE t1alias.col1 = table2.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t1)), "SELECT * FROM table2 WHERE t1alias.col1 = table2.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t2)), "SELECT * FROM table1 AS t1alias WHERE t1alias.col1 = table2.col2") - - self.assert_compile(vis.traverse(case([(t1.c.col1==5, t1.c.col2)], else_=t1.c.col1)), - "CASE WHEN (t1alias.col1 = :col1_1) THEN t1alias.col2 ELSE t1alias.col1 END" - ) - self.assert_compile(vis.traverse(case([(5, t1.c.col2)], value=t1.c.col1, else_=t1.c.col1)), - "CASE t1alias.col1 WHEN :param_1 THEN t1alias.col2 ELSE t1alias.col1 END" - ) - - - s = select(['*'], from_obj=[t1]).alias('foo') - self.assert_compile(s.select(), "SELECT foo.* FROM (SELECT * FROM table1) AS foo") - self.assert_compile(vis.traverse(s.select()), "SELECT foo.* FROM (SELECT * FROM table1 AS t1alias) AS foo") - self.assert_compile(s.select(), "SELECT foo.* FROM (SELECT * FROM table1) AS foo") - - ff = vis.traverse(func.count(t1.c.col1).label('foo')) - self.assert_compile(select([ff]), "SELECT count(t1alias.col1) AS foo FROM table1 AS t1alias") - assert list(_from_objects(ff)) == [t1alias] - -# TODO: - # self.assert_compile(vis.traverse(select([func.count(t1.c.col1).label('foo')]), clone=True), "SELECT count(t1alias.col1) AS foo FROM table1 AS t1alias") - - t2alias = t2.alias('t2alias') - vis.chain(sql_util.ClauseAdapter(t2alias)) - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2)), "SELECT * FROM table1 AS t1alias, table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2])), "SELECT * FROM table1 AS t1alias, table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t1)), "SELECT * FROM table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") - self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t2)), "SELECT * FROM table1 AS t1alias WHERE t1alias.col1 = t2alias.col2") - - def test_include_exclude(self): - m = MetaData() - a=Table( 'a',m, - Column( 'id', Integer, primary_key=True), - Column( 'xxx_id', Integer, ForeignKey( 'a.id', name='adf',use_alter=True ) ) - ) - - e = (a.c.id == a.c.xxx_id) - assert str(e) == "a.id = a.xxx_id" - b = a.alias() - - e = sql_util.ClauseAdapter( b, include= set([ a.c.id ]), - equivalents= { a.c.id: set([ a.c.id]) } - ).traverse( e) - - assert str(e) == "a_1.id = a.xxx_id" - - def test_recursive_equivalents(self): - m = MetaData() - a = Table('a', m, Column('x', Integer), Column('y', Integer)) - b = Table('b', m, Column('x', Integer), Column('y', Integer)) - c = Table('c', m, Column('x', Integer), Column('y', Integer)) - - # force a recursion overflow, by linking a.c.x<->c.c.x, and - # asking for a nonexistent col. corresponding_column should prevent - # endless depth. - adapt = sql_util.ClauseAdapter( b, equivalents= {a.c.x: set([ c.c.x]), c.c.x:set([a.c.x])}) - assert adapt._corresponding_column(a.c.x, False) is None - - def test_multilevel_equivalents(self): - m = MetaData() - a = Table('a', m, Column('x', Integer), Column('y', Integer)) - b = Table('b', m, Column('x', Integer), Column('y', Integer)) - c = Table('c', m, Column('x', Integer), Column('y', Integer)) - - alias = select([a]).select_from(a.join(b, a.c.x==b.c.x)).alias() - - # two levels of indirection from c.x->b.x->a.x, requires recursive - # corresponding_column call - adapt = sql_util.ClauseAdapter(alias, equivalents= {b.c.x: set([ a.c.x]), c.c.x:set([b.c.x])}) - assert adapt._corresponding_column(a.c.x, False) is alias.c.x - assert adapt._corresponding_column(c.c.x, False) is alias.c.x - - def test_join_to_alias(self): - metadata = MetaData() - a = Table('a', metadata, - Column('id', Integer, primary_key=True)) - b = Table('b', metadata, - Column('id', Integer, primary_key=True), - Column('aid', Integer, ForeignKey('a.id')), - ) - c = Table('c', metadata, - Column('id', Integer, primary_key=True), - Column('bid', Integer, ForeignKey('b.id')), - ) - - d = Table('d', metadata, - Column('id', Integer, primary_key=True), - Column('aid', Integer, ForeignKey('a.id')), - ) - - j1 = a.outerjoin(b) - j2 = select([j1], use_labels=True) - - j3 = c.join(j2, j2.c.b_id==c.c.bid) - - j4 = j3.outerjoin(d) - self.assert_compile(j4, "c JOIN (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid FROM a LEFT OUTER JOIN b ON a.id = b.aid) " - "ON b_id = c.bid" - " LEFT OUTER JOIN d ON a_id = d.aid") - j5 = j3.alias('foo') - j6 = sql_util.ClauseAdapter(j5).copy_and_process([j4])[0] - - # this statement takes c join(a join b), wraps it inside an aliased "select * from c join(a join b) AS foo". - # the outermost right side "left outer join d" stays the same, except "d" joins against foo.a_id instead - # of plain "a_id" - self.assert_compile(j6, "(SELECT c.id AS c_id, c.bid AS c_bid, a_id AS a_id, b_id AS b_id, b_aid AS b_aid FROM " - "c JOIN (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid FROM a LEFT OUTER JOIN b ON a.id = b.aid) " - "ON b_id = c.bid) AS foo" - " LEFT OUTER JOIN d ON foo.a_id = d.aid") - - def test_derived_from(self): - assert select([t1]).is_derived_from(t1) - assert not select([t2]).is_derived_from(t1) - assert not t1.is_derived_from(select([t1])) - assert t1.alias().is_derived_from(t1) - - - s1 = select([t1, t2]).alias('foo') - s2 = select([s1]).limit(5).offset(10).alias() - assert s2.is_derived_from(s1) - s2 = s2._clone() - assert s2.is_derived_from(s1) - - def test_aliasedselect_to_aliasedselect(self): - # original issue from ticket #904 - s1 = select([t1]).alias('foo') - s2 = select([s1]).limit(5).offset(10).alias() - - self.assert_compile(sql_util.ClauseAdapter(s2).traverse(s1), - "SELECT foo.col1, foo.col2, foo.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10") - - j = s1.outerjoin(t2, s1.c.col1==t2.c.col1) - self.assert_compile(sql_util.ClauseAdapter(s2).traverse(j).select(), - "SELECT anon_1.col1, anon_1.col2, anon_1.col3, table2.col1, table2.col2, table2.col3 FROM "\ - "(SELECT foo.col1 AS col1, foo.col2 AS col2, foo.col3 AS col3 FROM "\ - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10) AS anon_1 "\ - "LEFT OUTER JOIN table2 ON anon_1.col1 = table2.col1") - - talias = t1.alias('bar') - j = s1.outerjoin(talias, s1.c.col1==talias.c.col1) - self.assert_compile(sql_util.ClauseAdapter(s2).traverse(j).select(), - "SELECT anon_1.col1, anon_1.col2, anon_1.col3, bar.col1, bar.col2, bar.col3 FROM "\ - "(SELECT foo.col1 AS col1, foo.col2 AS col2, foo.col3 AS col3 FROM "\ - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10) AS anon_1 "\ - "LEFT OUTER JOIN table1 AS bar ON anon_1.col1 = bar.col1") - - def test_functions(self): - self.assert_compile(sql_util.ClauseAdapter(t1.alias()).traverse(func.count(t1.c.col1)), "count(table1_1.col1)") - - s = select([func.count(t1.c.col1)]) - self.assert_compile(sql_util.ClauseAdapter(t1.alias()).traverse(s), "SELECT count(table1_1.col1) AS count_1 FROM table1 AS table1_1") - - def test_recursive(self): - metadata = MetaData() - a = Table('a', metadata, - Column('id', Integer, primary_key=True)) - b = Table('b', metadata, - Column('id', Integer, primary_key=True), - Column('aid', Integer, ForeignKey('a.id')), - ) - c = Table('c', metadata, - Column('id', Integer, primary_key=True), - Column('bid', Integer, ForeignKey('b.id')), - ) - - d = Table('d', metadata, - Column('id', Integer, primary_key=True), - Column('aid', Integer, ForeignKey('a.id')), - ) - - u = union( - a.join(b).select().apply_labels(), - a.join(d).select().apply_labels() - ).alias() - - self.assert_compile( - sql_util.ClauseAdapter(u).traverse(select([c.c.bid]).where(c.c.bid==u.c.b_aid)), - "SELECT c.bid "\ - "FROM c, (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid "\ - "FROM a JOIN b ON a.id = b.aid UNION SELECT a.id AS a_id, d.id AS d_id, d.aid AS d_aid "\ - "FROM a JOIN d ON a.id = d.aid) AS anon_1 "\ - "WHERE c.bid = anon_1.b_aid" - ) - -class SpliceJoinsTest(TestBase, AssertsCompiledSQL): - def setUpAll(self): - global table1, table2, table3, table4 - def _table(name): - return table(name, column("col1"), column("col2"),column("col3")) - - table1, table2, table3, table4 = [_table(name) for name in ("table1", "table2", "table3", "table4")] - - def test_splice(self): - (t1, t2, t3, t4) = (table1, table2, table1.alias(), table2.alias()) - - j = t1.join(t2, t1.c.col1==t2.c.col1).join(t3, t2.c.col1==t3.c.col1).join(t4, t4.c.col1==t1.c.col1) - - s = select([t1]).where(t1.c.col2<5).alias() - - self.assert_compile(sql_util.splice_joins(s, j), - "(SELECT table1.col1 AS col1, table1.col2 AS col2, "\ - "table1.col3 AS col3 FROM table1 WHERE table1.col2 < :col2_1) AS anon_1 "\ - "JOIN table2 ON anon_1.col1 = table2.col1 JOIN table1 AS table1_1 ON table2.col1 = table1_1.col1 "\ - "JOIN table2 AS table2_1 ON table2_1.col1 = anon_1.col1") - - def test_stop_on(self): - (t1, t2, t3) = (table1, table2, table3) - - j1= t1.join(t2, t1.c.col1==t2.c.col1) - j2 = j1.join(t3, t2.c.col1==t3.c.col1) - - s = select([t1]).select_from(j1).alias() - - self.assert_compile(sql_util.splice_joins(s, j2), - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 JOIN table2 "\ - "ON table1.col1 = table2.col1) AS anon_1 JOIN table2 ON anon_1.col1 = table2.col1 JOIN table3 "\ - "ON table2.col1 = table3.col1" - ) - - self.assert_compile(sql_util.splice_joins(s, j2, j1), - "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 "\ - "JOIN table2 ON table1.col1 = table2.col1) AS anon_1 JOIN table3 ON table2.col1 = table3.col1") - - def test_splice_2(self): - t2a = table2.alias() - t3a = table3.alias() - j1 = table1.join(t2a, table1.c.col1==t2a.c.col1).join(t3a, t2a.c.col2==t3a.c.col2) - - t2b = table4.alias() - j2 = table1.join(t2b, table1.c.col3==t2b.c.col3) - - self.assert_compile(sql_util.splice_joins(table1, j1), - "table1 JOIN table2 AS table2_1 ON table1.col1 = table2_1.col1 "\ - "JOIN table3 AS table3_1 ON table2_1.col2 = table3_1.col2") - - self.assert_compile(sql_util.splice_joins(table1, j2), "table1 JOIN table4 AS table4_1 ON table1.col3 = table4_1.col3") - - self.assert_compile(sql_util.splice_joins(sql_util.splice_joins(table1, j1), j2), - "table1 JOIN table2 AS table2_1 ON table1.col1 = table2_1.col1 "\ - "JOIN table3 AS table3_1 ON table2_1.col2 = table3_1.col2 "\ - "JOIN table4 AS table4_1 ON table1.col3 = table4_1.col3") - - -class SelectTest(TestBase, AssertsCompiledSQL): - """tests the generative capability of Select""" - - def setUpAll(self): - global t1, t2 - t1 = table("table1", - column("col1"), - column("col2"), - column("col3"), - ) - t2 = table("table2", - column("col1"), - column("col2"), - column("col3"), - ) - - def test_select(self): - self.assert_compile(t1.select().where(t1.c.col1==5).order_by(t1.c.col3), - "SELECT table1.col1, table1.col2, table1.col3 FROM table1 WHERE table1.col1 = :col1_1 ORDER BY table1.col3") - - self.assert_compile(t1.select().select_from(select([t2], t2.c.col1==t1.c.col1)).order_by(t1.c.col3), - "SELECT table1.col1, table1.col2, table1.col3 FROM table1, (SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 "\ - "FROM table2 WHERE table2.col1 = table1.col1) ORDER BY table1.col3") - - s = select([t2], t2.c.col1==t1.c.col1, correlate=False) - s = s.correlate(t1).order_by(t2.c.col3) - self.assert_compile(t1.select().select_from(s).order_by(t1.c.col3), - "SELECT table1.col1, table1.col2, table1.col3 FROM table1, (SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 "\ - "FROM table2 WHERE table2.col1 = table1.col1 ORDER BY table2.col3) ORDER BY table1.col3") - - def test_columns(self): - s = t1.select() - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - select_copy = s.column('yyy') - self.assert_compile(select_copy, "SELECT table1.col1, table1.col2, table1.col3, yyy FROM table1") - assert s.columns is not select_copy.columns - assert s._columns is not select_copy._columns - assert s._raw_columns is not select_copy._raw_columns - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - - def test_froms(self): - s = t1.select() - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - select_copy = s.select_from(t2) - self.assert_compile(select_copy, "SELECT table1.col1, table1.col2, table1.col3 FROM table1, table2") - assert s._froms is not select_copy._froms - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - - def test_correlation(self): - s = select([t2], t1.c.col1==t2.c.col1) - self.assert_compile(s, "SELECT table2.col1, table2.col2, table2.col3 FROM table2, table1 WHERE table1.col1 = table2.col1") - s2 = select([t1], t1.c.col2==s.c.col2) - self.assert_compile(s2, "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " - "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " - "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") - - s3 = s.correlate(None) - self.assert_compile(select([t1], t1.c.col2==s3.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " - "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2, table1 " - "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") - self.assert_compile(select([t1], t1.c.col2==s.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " - "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " - "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") - s4 = s3.correlate(t1) - self.assert_compile(select([t1], t1.c.col2==s4.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " - "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " - "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") - self.assert_compile(select([t1], t1.c.col2==s3.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " - "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2, table1 " - "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") - - def test_prefixes(self): - s = t1.select() - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - select_copy = s.prefix_with("FOOBER") - self.assert_compile(select_copy, "SELECT FOOBER table1.col1, table1.col2, table1.col3 FROM table1") - self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") - - -class InsertTest(TestBase, AssertsCompiledSQL): - """Tests the generative capability of Insert""" - - # fixme: consolidate converage from elsewhere here and expand - - def setUpAll(self): - global t1, t2 - t1 = table("table1", - column("col1"), - column("col2"), - column("col3"), - ) - t2 = table("table2", - column("col1"), - column("col2"), - column("col3"), - ) - - def test_prefixes(self): - i = t1.insert() - self.assert_compile(i, - "INSERT INTO table1 (col1, col2, col3) " - "VALUES (:col1, :col2, :col3)") - - gen = i.prefix_with("foober") - self.assert_compile(gen, - "INSERT foober INTO table1 (col1, col2, col3) " - "VALUES (:col1, :col2, :col3)") - - self.assert_compile(i, - "INSERT INTO table1 (col1, col2, col3) " - "VALUES (:col1, :col2, :col3)") - - i2 = t1.insert(prefixes=['squiznart']) - self.assert_compile(i2, - "INSERT squiznart INTO table1 (col1, col2, col3) " - "VALUES (:col1, :col2, :col3)") - - gen2 = i2.prefix_with("quux") - self.assert_compile(gen2, - "INSERT squiznart quux INTO " - "table1 (col1, col2, col3) " - "VALUES (:col1, :col2, :col3)") - -if __name__ == '__main__': - testenv.main() diff --git a/test/sql/labels.py b/test/sql/labels.py deleted file mode 100644 index 94ee20342..000000000 --- a/test/sql/labels.py +++ /dev/null @@ -1,195 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy import exc as exceptions -from testlib import * -from sqlalchemy.engine import default - -IDENT_LENGTH = 29 - -class LabelTypeTest(TestBase): - def test_type(self): - m = MetaData() - t = Table('sometable', m, - Column('col1', Integer), - Column('col2', Float)) - assert isinstance(t.c.col1.label('hi').type, Integer) - assert isinstance(select([t.c.col2]).as_scalar().label('lala').type, Float) - -class LongLabelsTest(TestBase, AssertsCompiledSQL): - def setUpAll(self): - global metadata, table1, table2, maxlen - metadata = MetaData(testing.db) - table1 = Table("some_large_named_table", metadata, - Column("this_is_the_primarykey_column", Integer, Sequence("this_is_some_large_seq"), primary_key=True), - Column("this_is_the_data_column", String(30)) - ) - - table2 = Table("table_with_exactly_29_characs", metadata, - Column("this_is_the_primarykey_column", Integer, Sequence("some_seq"), primary_key=True), - Column("this_is_the_data_column", String(30)) - ) - - metadata.create_all() - - maxlen = testing.db.dialect.max_identifier_length - testing.db.dialect.max_identifier_length = IDENT_LENGTH - - def tearDown(self): - table1.delete().execute() - - def tearDownAll(self): - metadata.drop_all() - testing.db.dialect.max_identifier_length = maxlen - - def test_too_long_name_disallowed(self): - m = MetaData(testing.db) - t1 = Table("this_name_is_too_long_for_what_were_doing_in_this_test", m, Column('foo', Integer)) - self.assertRaises(exceptions.IdentifierError, m.create_all) - self.assertRaises(exceptions.IdentifierError, m.drop_all) - self.assertRaises(exceptions.IdentifierError, t1.create) - self.assertRaises(exceptions.IdentifierError, t1.drop) - - def test_result(self): - table1.insert().execute(**{"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}) - table1.insert().execute(**{"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}) - table1.insert().execute(**{"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}) - table1.insert().execute(**{"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}) - - s = table1.select(use_labels=True, order_by=[table1.c.this_is_the_primarykey_column]) - r = s.execute() - result = [] - for row in r: - result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) - assert result == [ - (1, "data1"), - (2, "data2"), - (3, "data3"), - (4, "data4"), - ], repr(result) - - # some dialects such as oracle (and possibly ms-sql in a future version) - # generate a subquery for limits/offsets. - # ensure that the generated result map corresponds to the selected table, not - # the select query - r = s.limit(2).execute() - result = [] - for row in r: - result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) - assert result == [ - (1, "data1"), - (2, "data2"), - ], repr(result) - - r = s.limit(2).offset(1).execute() - result = [] - for row in r: - result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) - assert result == [ - (2, "data2"), - (3, "data3"), - ], repr(result) - - def test_table_alias_names(self): - self.assert_compile( - table2.alias().select(), - "SELECT table_with_exactly_29_c_1.this_is_the_primarykey_column, table_with_exactly_29_c_1.this_is_the_data_column FROM table_with_exactly_29_characs AS table_with_exactly_29_c_1" - ) - - ta = table2.alias() - dialect = default.DefaultDialect() - dialect.max_identifier_length = IDENT_LENGTH - self.assert_compile( - select([table1, ta]).select_from(table1.join(ta, table1.c.this_is_the_data_column==ta.c.this_is_the_data_column)).\ - where(ta.c.this_is_the_data_column=='data3'), - - "SELECT some_large_named_table.this_is_the_primarykey_column, some_large_named_table.this_is_the_data_column, " - "table_with_exactly_29_c_1.this_is_the_primarykey_column, table_with_exactly_29_c_1.this_is_the_data_column FROM " - "some_large_named_table JOIN table_with_exactly_29_characs AS table_with_exactly_29_c_1 ON " - "some_large_named_table.this_is_the_data_column = table_with_exactly_29_c_1.this_is_the_data_column " - "WHERE table_with_exactly_29_c_1.this_is_the_data_column = :this_is_the_data_column_1", - dialect=dialect - ) - - table2.insert().execute( - {"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}, - {"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}, - {"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}, - {"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}, - ) - - r = table2.alias().select().execute() - assert r.fetchall() == [(x, "data%d" % x) for x in range(1, 5)] - - def test_colbinds(self): - table1.insert().execute(**{"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}) - table1.insert().execute(**{"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}) - table1.insert().execute(**{"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}) - table1.insert().execute(**{"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}) - - r = table1.select(table1.c.this_is_the_primarykey_column == 4).execute() - assert r.fetchall() == [(4, "data4")] - - r = table1.select(or_( - table1.c.this_is_the_primarykey_column == 4, - table1.c.this_is_the_primarykey_column == 2 - )).execute() - assert r.fetchall() == [(2, "data2"), (4, "data4")] - - def test_insert_no_pk(self): - table1.insert().execute(**{"this_is_the_data_column":"data1"}) - table1.insert().execute(**{"this_is_the_data_column":"data2"}) - table1.insert().execute(**{"this_is_the_data_column":"data3"}) - table1.insert().execute(**{"this_is_the_data_column":"data4"}) - - @testing.requires.subqueries - def test_subquery(self): - q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias('foo') - x = select([q]) - print x.execute().fetchall() - - @testing.requires.subqueries - def test_anon_alias(self): - compile_dialect = default.DefaultDialect() - compile_dialect.max_identifier_length = IDENT_LENGTH - - q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias() - x = select([q], use_labels=True) - - self.assert_compile(x, "SELECT anon_1.this_is_the_primarykey_column AS anon_1_this_is_the_prim_1, anon_1.this_is_the_data_column AS anon_1_this_is_the_data_2 " - "FROM (SELECT some_large_named_table.this_is_the_primarykey_column AS this_is_the_primarykey_column, some_large_named_table.this_is_the_data_column AS this_is_the_data_column " - "FROM some_large_named_table " - "WHERE some_large_named_table.this_is_the_primarykey_column = :this_is_the_primarykey__1) AS anon_1", dialect=compile_dialect) - - print x.execute().fetchall() - - def test_adjustable(self): - - q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias('foo') - x = select([q]) - - compile_dialect = default.DefaultDialect(label_length=10) - self.assert_compile(x, "SELECT foo.this_is_the_primarykey_column, foo.this_is_the_data_column FROM " - "(SELECT some_large_named_table.this_is_the_primarykey_column AS this_1, some_large_named_table.this_is_the_data_column " - "AS this_2 FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :this_1) AS foo", dialect=compile_dialect) - - compile_dialect = default.DefaultDialect(label_length=4) - self.assert_compile(x, "SELECT foo.this_is_the_primarykey_column, foo.this_is_the_data_column FROM " - "(SELECT some_large_named_table.this_is_the_primarykey_column AS _1, some_large_named_table.this_is_the_data_column AS _2 " - "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :_1) AS foo", dialect=compile_dialect) - - q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias() - x = select([q], use_labels=True) - - compile_dialect = default.DefaultDialect(label_length=10) - self.assert_compile(x, "SELECT anon_1.this_is_the_primarykey_column AS anon_1, anon_1.this_is_the_data_column AS anon_2 FROM " - "(SELECT some_large_named_table.this_is_the_primarykey_column AS this_3, some_large_named_table.this_is_the_data_column AS this_4 " - "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :this_1) AS anon_1", dialect=compile_dialect) - - compile_dialect = default.DefaultDialect(label_length=4) - self.assert_compile(x, "SELECT _1.this_is_the_primarykey_column AS _1, _1.this_is_the_data_column AS _2 FROM " - "(SELECT some_large_named_table.this_is_the_primarykey_column AS _3, some_large_named_table.this_is_the_data_column AS _4 " - "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :_1) AS _1", dialect=compile_dialect) - - -if __name__ == '__main__': - testenv.main() diff --git a/test/sql/query.py b/test/sql/query.py deleted file mode 100644 index b428d8991..000000000 --- a/test/sql/query.py +++ /dev/null @@ -1,1321 +0,0 @@ -import testenv; testenv.configure_for_tests() -import datetime -from sqlalchemy import * -from sqlalchemy import exc, sql -from sqlalchemy.engine import default -from testlib import * -from testlib.testing import eq_ - -class QueryTest(TestBase): - - def setUpAll(self): - global users, users2, addresses, metadata - metadata = MetaData(testing.db) - users = Table('query_users', metadata, - Column('user_id', INT, primary_key = True), - Column('user_name', VARCHAR(20)), - ) - addresses = Table('query_addresses', metadata, - Column('address_id', Integer, primary_key=True), - Column('user_id', Integer, ForeignKey('query_users.user_id')), - Column('address', String(30))) - - users2 = Table('u2', metadata, - Column('user_id', INT, primary_key = True), - Column('user_name', VARCHAR(20)), - ) - metadata.create_all() - - def tearDown(self): - addresses.delete().execute() - users.delete().execute() - users2.delete().execute() - - def tearDownAll(self): - metadata.drop_all() - - def test_insert(self): - users.insert().execute(user_id = 7, user_name = 'jack') - assert users.count().scalar() == 1 - - def test_insert_heterogeneous_params(self): - users.insert().execute( - {'user_id':7, 'user_name':'jack'}, - {'user_id':8, 'user_name':'ed'}, - {'user_id':9} - ) - assert users.select().execute().fetchall() == [(7, 'jack'), (8, 'ed'), (9, None)] - - def test_update(self): - users.insert().execute(user_id = 7, user_name = 'jack') - assert users.count().scalar() == 1 - - users.update(users.c.user_id == 7).execute(user_name = 'fred') - assert users.select(users.c.user_id==7).execute().fetchone()['user_name'] == 'fred' - - def test_lastrow_accessor(self): - """Tests the last_inserted_ids() and lastrow_has_id() functions.""" - - def insert_values(table, values): - """ - Inserts a row into a table, returns the full list of values - INSERTed including defaults that fired off on the DB side and - detects rows that had defaults and post-fetches. - """ - - result = table.insert().execute(**values) - ret = values.copy() - - for col, id in zip(table.primary_key, result.last_inserted_ids()): - ret[col.key] = id - - if result.lastrow_has_defaults(): - criterion = and_(*[col==id for col, id in zip(table.primary_key, result.last_inserted_ids())]) - row = table.select(criterion).execute().fetchone() - for c in table.c: - ret[c.key] = row[c] - return ret - - for supported, table, values, assertvalues in [ - ( - {'unsupported':['sqlite']}, - Table("t1", metadata, - Column('id', Integer, Sequence('t1_id_seq', optional=True), primary_key=True), - Column('foo', String(30), primary_key=True)), - {'foo':'hi'}, - {'id':1, 'foo':'hi'} - ), - ( - {'unsupported':['sqlite']}, - Table("t2", metadata, - Column('id', Integer, Sequence('t2_id_seq', optional=True), primary_key=True), - Column('foo', String(30), primary_key=True), - Column('bar', String(30), server_default='hi') - ), - {'foo':'hi'}, - {'id':1, 'foo':'hi', 'bar':'hi'} - ), - ( - {'unsupported':[]}, - Table("t3", metadata, - Column("id", String(40), primary_key=True), - Column('foo', String(30), primary_key=True), - Column("bar", String(30)) - ), - {'id':'hi', 'foo':'thisisfoo', 'bar':"thisisbar"}, - {'id':'hi', 'foo':'thisisfoo', 'bar':"thisisbar"} - ), - ( - {'unsupported':[]}, - Table("t4", metadata, - Column('id', Integer, Sequence('t4_id_seq', optional=True), primary_key=True), - Column('foo', String(30), primary_key=True), - Column('bar', String(30), server_default='hi') - ), - {'foo':'hi', 'id':1}, - {'id':1, 'foo':'hi', 'bar':'hi'} - ), - ( - {'unsupported':[]}, - Table("t5", metadata, - Column('id', String(10), primary_key=True), - Column('bar', String(30), server_default='hi') - ), - {'id':'id1'}, - {'id':'id1', 'bar':'hi'}, - ), - ]: - if testing.db.name in supported['unsupported']: - continue - try: - table.create() - i = insert_values(table, values) - assert i == assertvalues, repr(i) + " " + repr(assertvalues) - finally: - table.drop() - - def test_row_iteration(self): - users.insert().execute( - {'user_id':7, 'user_name':'jack'}, - {'user_id':8, 'user_name':'ed'}, - {'user_id':9, 'user_name':'fred'}, - ) - r = users.select().execute() - l = [] - for row in r: - l.append(row) - self.assert_(len(l) == 3) - - @testing.fails_on('firebird', 'Data type unknown') - @testing.requires.subqueries - def test_anonymous_rows(self): - users.insert().execute( - {'user_id':7, 'user_name':'jack'}, - {'user_id':8, 'user_name':'ed'}, - {'user_id':9, 'user_name':'fred'}, - ) - - sel = select([users.c.user_id]).where(users.c.user_name=='jack').as_scalar() - for row in select([sel + 1, sel + 3], bind=users.bind).execute(): - assert row['anon_1'] == 8 - assert row['anon_2'] == 10 - - def test_order_by_label(self): - """test that a label within an ORDER BY works on each backend. - - simple labels in ORDER BYs now render as the actual labelname - which not every database supports. - - """ - users.insert().execute( - {'user_id':7, 'user_name':'jack'}, - {'user_id':8, 'user_name':'ed'}, - {'user_id':9, 'user_name':'fred'}, - ) - - concat = ("test: " + users.c.user_name).label('thedata') - self.assertEquals( - select([concat]).order_by(concat).execute().fetchall(), - [("test: ed",), ("test: fred",), ("test: jack",)] - ) - - concat = ("test: " + users.c.user_name).label('thedata') - self.assertEquals( - select([concat]).order_by(desc(concat)).execute().fetchall(), - [("test: jack",), ("test: fred",), ("test: ed",)] - ) - - concat = ("test: " + users.c.user_name).label('thedata') - self.assertEquals( - select([concat]).order_by(concat + "x").execute().fetchall(), - [("test: ed",), ("test: fred",), ("test: jack",)] - ) - - - def test_row_comparison(self): - users.insert().execute(user_id = 7, user_name = 'jack') - rp = users.select().execute().fetchone() - - self.assert_(rp == rp) - self.assert_(not(rp != rp)) - - equal = (7, 'jack') - - self.assert_(rp == equal) - self.assert_(equal == rp) - self.assert_(not (rp != equal)) - self.assert_(not (equal != equal)) - - @testing.fails_on('mssql', 'No support for boolean logic in column select.') - @testing.fails_on('oracle', 'FIXME: unknown') - def test_or_and_as_columns(self): - true, false = literal(True), literal(False) - - self.assertEquals(testing.db.execute(select([and_(true, false)])).scalar(), False) - self.assertEquals(testing.db.execute(select([and_(true, true)])).scalar(), True) - self.assertEquals(testing.db.execute(select([or_(true, false)])).scalar(), True) - self.assertEquals(testing.db.execute(select([or_(false, false)])).scalar(), False) - self.assertEquals(testing.db.execute(select([not_(or_(false, false))])).scalar(), True) - - row = testing.db.execute(select([or_(false, false).label("x"), and_(true, false).label("y")])).fetchone() - assert row.x == False - assert row.y == False - - row = testing.db.execute(select([or_(true, false).label("x"), and_(true, false).label("y")])).fetchone() - assert row.x == True - assert row.y == False - - def test_fetchmany(self): - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'ed') - users.insert().execute(user_id = 9, user_name = 'fred') - r = users.select().execute() - l = [] - for row in r.fetchmany(size=2): - l.append(row) - self.assert_(len(l) == 2, "fetchmany(size=2) got %s rows" % len(l)) - - def test_like_ops(self): - users.insert().execute( - {'user_id':1, 'user_name':'apples'}, - {'user_id':2, 'user_name':'oranges'}, - {'user_id':3, 'user_name':'bananas'}, - {'user_id':4, 'user_name':'legumes'}, - {'user_id':5, 'user_name':'hi % there'}, - ) - - for expr, result in ( - (select([users.c.user_id]).where(users.c.user_name.startswith('apple')), [(1,)]), - (select([users.c.user_id]).where(users.c.user_name.contains('i % t')), [(5,)]), - (select([users.c.user_id]).where(users.c.user_name.endswith('anas')), [(3,)]), - ): - eq_(expr.execute().fetchall(), result) - - - @testing.emits_warning('.*now automatically escapes.*') - def test_percents_in_text(self): - for expr, result in ( - (text("select 6 % 10"), 6), - (text("select 17 % 10"), 7), - (text("select '%'"), '%'), - (text("select '%%'"), '%%'), - (text("select '%%%'"), '%%%'), - (text("select 'hello % world'"), "hello % world") - ): - eq_(testing.db.scalar(expr), result) - - def test_ilike(self): - users.insert().execute( - {'user_id':1, 'user_name':'one'}, - {'user_id':2, 'user_name':'TwO'}, - {'user_id':3, 'user_name':'ONE'}, - {'user_id':4, 'user_name':'OnE'}, - ) - - self.assertEquals(select([users.c.user_id]).where(users.c.user_name.ilike('one')).execute().fetchall(), [(1, ), (3, ), (4, )]) - - self.assertEquals(select([users.c.user_id]).where(users.c.user_name.ilike('TWO')).execute().fetchall(), [(2, )]) - - if testing.against('postgres'): - self.assertEquals(select([users.c.user_id]).where(users.c.user_name.like('one')).execute().fetchall(), [(1, )]) - self.assertEquals(select([users.c.user_id]).where(users.c.user_name.like('TWO')).execute().fetchall(), []) - - - def test_compiled_execute(self): - users.insert().execute(user_id = 7, user_name = 'jack') - s = select([users], users.c.user_id==bindparam('id')).compile() - c = testing.db.connect() - assert c.execute(s, id=7).fetchall()[0]['user_id'] == 7 - - def test_compiled_insert_execute(self): - users.insert().compile().execute(user_id = 7, user_name = 'jack') - s = select([users], users.c.user_id==bindparam('id')).compile() - c = testing.db.connect() - assert c.execute(s, id=7).fetchall()[0]['user_id'] == 7 - - def test_repeated_bindparams(self): - """Tests that a BindParam can be used more than once. - - This should be run for DB-APIs with both positional and named - paramstyles. - """ - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'fred') - - u = bindparam('userid') - s = users.select(and_(users.c.user_name==u, users.c.user_name==u)) - r = s.execute(userid='fred').fetchall() - assert len(r) == 1 - - def test_bindparam_shortname(self): - """test the 'shortname' field on BindParamClause.""" - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'fred') - u = bindparam('userid', shortname='someshortname') - s = users.select(users.c.user_name==u) - r = s.execute(someshortname='fred').fetchall() - assert len(r) == 1 - - def test_bindparam_detection(self): - dialect = default.DefaultDialect(paramstyle='qmark') - prep = lambda q: str(sql.text(q).compile(dialect=dialect)) - - def a_eq(got, wanted): - if got != wanted: - print "Wanted %s" % wanted - print "Received %s" % got - self.assert_(got == wanted, got) - - a_eq(prep('select foo'), 'select foo') - a_eq(prep("time='12:30:00'"), "time='12:30:00'") - a_eq(prep(u"time='12:30:00'"), u"time='12:30:00'") - a_eq(prep(":this:that"), ":this:that") - a_eq(prep(":this :that"), "? ?") - a_eq(prep("(:this),(:that :other)"), "(?),(? ?)") - a_eq(prep("(:this),(:that:other)"), "(?),(:that:other)") - a_eq(prep("(:this),(:that,:other)"), "(?),(?,?)") - a_eq(prep("(:that_:other)"), "(:that_:other)") - a_eq(prep("(:that_ :other)"), "(? ?)") - a_eq(prep("(:that_other)"), "(?)") - a_eq(prep("(:that$other)"), "(?)") - a_eq(prep("(:that$:other)"), "(:that$:other)") - a_eq(prep(".:that$ :other."), ".? ?.") - - a_eq(prep(r'select \foo'), r'select \foo') - a_eq(prep(r"time='12\:30:00'"), r"time='12\:30:00'") - a_eq(prep(":this \:that"), "? :that") - a_eq(prep(r"(\:that$other)"), "(:that$other)") - a_eq(prep(r".\:that$ :other."), ".:that$ ?.") - - def test_delete(self): - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'fred') - print repr(users.select().execute().fetchall()) - - users.delete(users.c.user_name == 'fred').execute() - - print repr(users.select().execute().fetchall()) - - - - @testing.exclude('mysql', '<', (5, 0, 37), 'database bug') - def test_scalar_select(self): - """test that scalar subqueries with labels get their type propagated to the result set.""" - # mysql and/or mysqldb has a bug here, type isn't propagated for scalar - # subquery. - datetable = Table('datetable', metadata, - Column('id', Integer, primary_key=True), - Column('today', DateTime)) - datetable.create() - try: - datetable.insert().execute(id=1, today=datetime.datetime(2006, 5, 12, 12, 0, 0)) - s = select([datetable.alias('x').c.today]).as_scalar() - s2 = select([datetable.c.id, s.label('somelabel')]) - #print s2.c.somelabel.type - assert isinstance(s2.execute().fetchone()['somelabel'], datetime.datetime) - finally: - datetable.drop() - - def test_order_by(self): - """Exercises ORDER BY clause generation. - - Tests simple, compound, aliased and DESC clauses. - """ - - users.insert().execute(user_id=1, user_name='c') - users.insert().execute(user_id=2, user_name='b') - users.insert().execute(user_id=3, user_name='a') - - def a_eq(executable, wanted): - got = list(executable.execute()) - self.assertEquals(got, wanted) - - for labels in False, True: - a_eq(users.select(order_by=[users.c.user_id], - use_labels=labels), - [(1, 'c'), (2, 'b'), (3, 'a')]) - - a_eq(users.select(order_by=[users.c.user_name, users.c.user_id], - use_labels=labels), - [(3, 'a'), (2, 'b'), (1, 'c')]) - - a_eq(select([users.c.user_id.label('foo')], - use_labels=labels, - order_by=[users.c.user_id]), - [(1,), (2,), (3,)]) - - a_eq(select([users.c.user_id.label('foo'), users.c.user_name], - use_labels=labels, - order_by=[users.c.user_name, users.c.user_id]), - [(3, 'a'), (2, 'b'), (1, 'c')]) - - a_eq(users.select(distinct=True, - use_labels=labels, - order_by=[users.c.user_id]), - [(1, 'c'), (2, 'b'), (3, 'a')]) - - a_eq(select([users.c.user_id.label('foo')], - distinct=True, - use_labels=labels, - order_by=[users.c.user_id]), - [(1,), (2,), (3,)]) - - a_eq(select([users.c.user_id.label('a'), - users.c.user_id.label('b'), - users.c.user_name], - use_labels=labels, - order_by=[users.c.user_id]), - [(1, 1, 'c'), (2, 2, 'b'), (3, 3, 'a')]) - - a_eq(users.select(distinct=True, - use_labels=labels, - order_by=[desc(users.c.user_id)]), - [(3, 'a'), (2, 'b'), (1, 'c')]) - - a_eq(select([users.c.user_id.label('foo')], - distinct=True, - use_labels=labels, - order_by=[users.c.user_id.desc()]), - [(3,), (2,), (1,)]) - - def test_column_accessor(self): - users.insert().execute(user_id=1, user_name='john') - users.insert().execute(user_id=2, user_name='jack') - addresses.insert().execute(address_id=1, user_id=2, address='foo@bar.com') - - r = users.select(users.c.user_id==2).execute().fetchone() - self.assert_(r.user_id == r['user_id'] == r[users.c.user_id] == 2) - self.assert_(r.user_name == r['user_name'] == r[users.c.user_name] == 'jack') - - r = text("select * from query_users where user_id=2", bind=testing.db).execute().fetchone() - self.assert_(r.user_id == r['user_id'] == r[users.c.user_id] == 2) - self.assert_(r.user_name == r['user_name'] == r[users.c.user_name] == 'jack') - - # test slices - r = text("select * from query_addresses", bind=testing.db).execute().fetchone() - self.assert_(r[0:1] == (1,)) - self.assert_(r[1:] == (2, 'foo@bar.com')) - self.assert_(r[:-1] == (1, 2)) - - # test a little sqlite weirdness - with the UNION, cols come back as "query_users.user_id" in cursor.description - r = text("select query_users.user_id, query_users.user_name from query_users " - "UNION select query_users.user_id, query_users.user_name from query_users", bind=testing.db).execute().fetchone() - self.assert_(r['user_id']) == 1 - self.assert_(r['user_name']) == "john" - - # test using literal tablename.colname - r = text('select query_users.user_id AS "query_users.user_id", query_users.user_name AS "query_users.user_name" from query_users', bind=testing.db).execute().fetchone() - self.assert_(r['query_users.user_id']) == 1 - self.assert_(r['query_users.user_name']) == "john" - - def test_row_as_args(self): - users.insert().execute(user_id=1, user_name='john') - r = users.select(users.c.user_id==1).execute().fetchone() - users.delete().execute() - users.insert().execute(r) - assert users.select().execute().fetchall() == [(1, 'john')] - - def test_result_as_args(self): - users.insert().execute([dict(user_id=1, user_name='john'), dict(user_id=2, user_name='ed')]) - r = users.select().execute() - users2.insert().execute(list(r)) - assert users2.select().execute().fetchall() == [(1, 'john'), (2, 'ed')] - - users2.delete().execute() - r = users.select().execute() - users2.insert().execute(*list(r)) - assert users2.select().execute().fetchall() == [(1, 'john'), (2, 'ed')] - - def test_ambiguous_column(self): - users.insert().execute(user_id=1, user_name='john') - r = users.outerjoin(addresses).select().execute().fetchone() - try: - print r['user_id'] - assert False - except exc.InvalidRequestError, e: - assert str(e) == "Ambiguous column name 'user_id' in result set! try 'use_labels' option on select statement." or \ - str(e) == "Ambiguous column name 'USER_ID' in result set! try 'use_labels' option on select statement." - - @testing.requires.subqueries - def test_column_label_targeting(self): - users.insert().execute(user_id=7, user_name='ed') - - for s in ( - users.select().alias('foo'), - users.select().alias(users.name), - ): - row = s.select(use_labels=True).execute().fetchone() - assert row[s.c.user_id] == 7 - assert row[s.c.user_name] == 'ed' - - def test_keys(self): - users.insert().execute(user_id=1, user_name='foo') - r = users.select().execute().fetchone() - self.assertEqual([x.lower() for x in r.keys()], ['user_id', 'user_name']) - - def test_items(self): - users.insert().execute(user_id=1, user_name='foo') - r = users.select().execute().fetchone() - self.assertEqual([(x[0].lower(), x[1]) for x in r.items()], [('user_id', 1), ('user_name', 'foo')]) - - def test_len(self): - users.insert().execute(user_id=1, user_name='foo') - r = users.select().execute().fetchone() - self.assertEqual(len(r), 2) - r.close() - r = testing.db.execute('select user_name, user_id from query_users').fetchone() - self.assertEqual(len(r), 2) - r.close() - r = testing.db.execute('select user_name from query_users').fetchone() - self.assertEqual(len(r), 1) - r.close() - - def test_cant_execute_join(self): - try: - users.join(addresses).execute() - except exc.ArgumentError, e: - assert str(e).startswith('Not an executable clause: ') - - - - def test_column_order_with_simple_query(self): - # should return values in column definition order - users.insert().execute(user_id=1, user_name='foo') - r = users.select(users.c.user_id==1).execute().fetchone() - self.assertEqual(r[0], 1) - self.assertEqual(r[1], 'foo') - self.assertEqual([x.lower() for x in r.keys()], ['user_id', 'user_name']) - self.assertEqual(r.values(), [1, 'foo']) - - def test_column_order_with_text_query(self): - # should return values in query order - users.insert().execute(user_id=1, user_name='foo') - r = testing.db.execute('select user_name, user_id from query_users').fetchone() - self.assertEqual(r[0], 'foo') - self.assertEqual(r[1], 1) - self.assertEqual([x.lower() for x in r.keys()], ['user_name', 'user_id']) - self.assertEqual(r.values(), ['foo', 1]) - - @testing.crashes('oracle', 'FIXME: unknown, varify not fails_on()') - @testing.crashes('firebird', 'An identifier must begin with a letter') - @testing.crashes('maxdb', 'FIXME: unknown, verify not fails_on()') - def test_column_accessor_shadow(self): - meta = MetaData(testing.db) - shadowed = Table('test_shadowed', meta, - Column('shadow_id', INT, primary_key = True), - Column('shadow_name', VARCHAR(20)), - Column('parent', VARCHAR(20)), - Column('row', VARCHAR(40)), - Column('__parent', VARCHAR(20)), - Column('__row', VARCHAR(20)), - ) - shadowed.create(checkfirst=True) - try: - shadowed.insert().execute(shadow_id=1, shadow_name='The Shadow', parent='The Light', row='Without light there is no shadow', __parent='Hidden parent', __row='Hidden row') - r = shadowed.select(shadowed.c.shadow_id==1).execute().fetchone() - self.assert_(r.shadow_id == r['shadow_id'] == r[shadowed.c.shadow_id] == 1) - self.assert_(r.shadow_name == r['shadow_name'] == r[shadowed.c.shadow_name] == 'The Shadow') - self.assert_(r.parent == r['parent'] == r[shadowed.c.parent] == 'The Light') - self.assert_(r.row == r['row'] == r[shadowed.c.row] == 'Without light there is no shadow') - self.assert_(r['__parent'] == 'Hidden parent') - self.assert_(r['__row'] == 'Hidden row') - try: - print r.__parent, r.__row - self.fail('Should not allow access to private attributes') - except AttributeError: - pass # expected - r.close() - finally: - shadowed.drop(checkfirst=True) - - def test_in_filtering(self): - """test the behavior of the in_() function.""" - - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'fred') - users.insert().execute(user_id = 9, user_name = None) - - s = users.select(users.c.user_name.in_([])) - r = s.execute().fetchall() - # No username is in empty set - assert len(r) == 0 - - s = users.select(not_(users.c.user_name.in_([]))) - r = s.execute().fetchall() - # All usernames with a value are outside an empty set - assert len(r) == 2 - - s = users.select(users.c.user_name.in_(['jack','fred'])) - r = s.execute().fetchall() - assert len(r) == 2 - - s = users.select(not_(users.c.user_name.in_(['jack','fred']))) - r = s.execute().fetchall() - # Null values are not outside any set - assert len(r) == 0 - - u = bindparam('search_key') - - s = users.select(u.in_([])) - r = s.execute(search_key='john').fetchall() - assert len(r) == 0 - r = s.execute(search_key=None).fetchall() - assert len(r) == 0 - - s = users.select(not_(u.in_([]))) - r = s.execute(search_key='john').fetchall() - assert len(r) == 3 - r = s.execute(search_key=None).fetchall() - assert len(r) == 0 - - @testing.fails_on('firebird', 'FIXME: unknown') - @testing.fails_on('maxdb', 'FIXME: unknown') - @testing.fails_on('oracle', 'FIXME: unknown') - @testing.fails_on('mssql', 'FIXME: unknown') - def test_in_filtering_advanced(self): - """test the behavior of the in_() function when comparing against an empty collection.""" - - users.insert().execute(user_id = 7, user_name = 'jack') - users.insert().execute(user_id = 8, user_name = 'fred') - users.insert().execute(user_id = 9, user_name = None) - - s = users.select(users.c.user_name.in_([]) == True) - r = s.execute().fetchall() - assert len(r) == 0 - s = users.select(users.c.user_name.in_([]) == False) - r = s.execute().fetchall() - assert len(r) == 2 - s = users.select(users.c.user_name.in_([]) == None) - r = s.execute().fetchall() - assert len(r) == 1 - -class PercentSchemaNamesTest(TestBase): - """tests using percent signs, spaces in table and column names. - - Doesn't pass for mysql, postgres, but this is really a - SQLAlchemy bug - we should be escaping out %% signs for this - operation the same way we do for text() and column labels. - - """ - @testing.crashes('mysql', 'mysqldb calls name % (params)') - @testing.crashes('postgres', 'postgres calls name % (params)') - def setUpAll(self): - global percent_table, metadata - metadata = MetaData(testing.db) - percent_table = Table('percent%table', metadata, - Column("percent%", Integer), - Column("%(oneofthese)s", Integer), - Column("spaces % more spaces", Integer), - ) - metadata.create_all() - - @testing.crashes('mysql', 'mysqldb calls name % (params)') - @testing.crashes('postgres', 'postgres calls name % (params)') - def tearDownAll(self): - metadata.drop_all() - - @testing.crashes('mysql', 'mysqldb calls name % (params)') - @testing.crashes('postgres', 'postgres calls name % (params)') - def test_roundtrip(self): - percent_table.insert().execute( - {'percent%':5, '%(oneofthese)s':7, 'spaces % more spaces':12}, - ) - percent_table.insert().execute( - {'percent%':7, '%(oneofthese)s':8, 'spaces % more spaces':11}, - {'percent%':9, '%(oneofthese)s':9, 'spaces % more spaces':10}, - {'percent%':11, '%(oneofthese)s':10, 'spaces % more spaces':9}, - ) - - for table in (percent_table, percent_table.alias()): - eq_( - table.select().order_by(table.c['%(oneofthese)s']).execute().fetchall(), - [ - (5, 7, 12), - (7, 8, 11), - (9, 9, 10), - (11, 10, 9) - ] - ) - - eq_( - table.select(). - where(table.c['spaces % more spaces'].in_([9, 10])). - order_by(table.c['%(oneofthese)s']).execute().fetchall(), - [ - (9, 9, 10), - (11, 10, 9) - ] - ) - - result = table.select().order_by(table.c['%(oneofthese)s']).execute() - row = result.fetchone() - eq_(row[table.c['percent%']], 5) - eq_(row[table.c['%(oneofthese)s']], 7) - eq_(row[table.c['spaces % more spaces']], 12) - row = result.fetchone() - eq_(row['percent%'], 7) - eq_(row['%(oneofthese)s'], 8) - eq_(row['spaces % more spaces'], 11) - result.close() - - percent_table.update().values({percent_table.c['%(oneofthese)s']:9, percent_table.c['spaces % more spaces']:15}).execute() - - eq_( - percent_table.select().order_by(percent_table.c['%(oneofthese)s']).execute().fetchall(), - [ - (5, 9, 15), - (7, 9, 15), - (9, 9, 15), - (11, 9, 15) - ] - ) - - - -class LimitTest(TestBase): - - def setUpAll(self): - global users, addresses, metadata - metadata = MetaData(testing.db) - users = Table('query_users', metadata, - Column('user_id', INT, primary_key = True), - Column('user_name', VARCHAR(20)), - ) - addresses = Table('query_addresses', metadata, - Column('address_id', Integer, primary_key=True), - Column('user_id', Integer, ForeignKey('query_users.user_id')), - Column('address', String(30))) - metadata.create_all() - self._data() - - def _data(self): - users.insert().execute(user_id=1, user_name='john') - addresses.insert().execute(address_id=1, user_id=1, address='addr1') - users.insert().execute(user_id=2, user_name='jack') - addresses.insert().execute(address_id=2, user_id=2, address='addr1') - users.insert().execute(user_id=3, user_name='ed') - addresses.insert().execute(address_id=3, user_id=3, address='addr2') - users.insert().execute(user_id=4, user_name='wendy') - addresses.insert().execute(address_id=4, user_id=4, address='addr3') - users.insert().execute(user_id=5, user_name='laura') - addresses.insert().execute(address_id=5, user_id=5, address='addr4') - users.insert().execute(user_id=6, user_name='ralph') - addresses.insert().execute(address_id=6, user_id=6, address='addr5') - users.insert().execute(user_id=7, user_name='fido') - addresses.insert().execute(address_id=7, user_id=7, address='addr5') - - def tearDownAll(self): - metadata.drop_all() - - def test_select_limit(self): - r = users.select(limit=3, order_by=[users.c.user_id]).execute().fetchall() - self.assert_(r == [(1, 'john'), (2, 'jack'), (3, 'ed')], repr(r)) - - @testing.fails_on('maxdb', 'FIXME: unknown') - def test_select_limit_offset(self): - """Test the interaction between limit and offset""" - - r = users.select(limit=3, offset=2, order_by=[users.c.user_id]).execute().fetchall() - self.assert_(r==[(3, 'ed'), (4, 'wendy'), (5, 'laura')]) - r = users.select(offset=5, order_by=[users.c.user_id]).execute().fetchall() - self.assert_(r==[(6, 'ralph'), (7, 'fido')]) - - def test_select_distinct_limit(self): - """Test the interaction between limit and distinct""" - - r = sorted([x[0] for x in select([addresses.c.address]).distinct().limit(3).order_by(addresses.c.address).execute().fetchall()]) - self.assert_(len(r) == 3, repr(r)) - self.assert_(r[0] != r[1] and r[1] != r[2], repr(r)) - - @testing.fails_on('mssql', 'FIXME: unknown') - def test_select_distinct_offset(self): - """Test the interaction between distinct and offset""" - - r = sorted([x[0] for x in select([addresses.c.address]).distinct().offset(1).order_by(addresses.c.address).execute().fetchall()]) - self.assert_(len(r) == 4, repr(r)) - self.assert_(r[0] != r[1] and r[1] != r[2] and r[2] != [3], repr(r)) - - def test_select_distinct_limit_offset(self): - """Test the interaction between limit and limit/offset""" - - r = select([addresses.c.address]).order_by(addresses.c.address).distinct().offset(2).limit(3).execute().fetchall() - self.assert_(len(r) == 3, repr(r)) - self.assert_(r[0] != r[1] and r[1] != r[2], repr(r)) - -class CompoundTest(TestBase): - """test compound statements like UNION, INTERSECT, particularly their ability to nest on - different databases.""" - def setUpAll(self): - global metadata, t1, t2, t3 - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('col1', Integer, Sequence('t1pkseq'), primary_key=True), - Column('col2', String(30)), - Column('col3', String(40)), - Column('col4', String(30)) - ) - t2 = Table('t2', metadata, - Column('col1', Integer, Sequence('t2pkseq'), primary_key=True), - Column('col2', String(30)), - Column('col3', String(40)), - Column('col4', String(30))) - t3 = Table('t3', metadata, - Column('col1', Integer, Sequence('t3pkseq'), primary_key=True), - Column('col2', String(30)), - Column('col3', String(40)), - Column('col4', String(30))) - metadata.create_all() - - t1.insert().execute([ - dict(col2="t1col2r1", col3="aaa", col4="aaa"), - dict(col2="t1col2r2", col3="bbb", col4="bbb"), - dict(col2="t1col2r3", col3="ccc", col4="ccc"), - ]) - t2.insert().execute([ - dict(col2="t2col2r1", col3="aaa", col4="bbb"), - dict(col2="t2col2r2", col3="bbb", col4="ccc"), - dict(col2="t2col2r3", col3="ccc", col4="aaa"), - ]) - t3.insert().execute([ - dict(col2="t3col2r1", col3="aaa", col4="ccc"), - dict(col2="t3col2r2", col3="bbb", col4="aaa"), - dict(col2="t3col2r3", col3="ccc", col4="bbb"), - ]) - - def tearDownAll(self): - metadata.drop_all() - - def _fetchall_sorted(self, executed): - return sorted([tuple(row) for row in executed.fetchall()]) - - @testing.requires.subqueries - def test_union(self): - (s1, s2) = ( - select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], - t1.c.col2.in_(["t1col2r1", "t1col2r2"])), - select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], - t2.c.col2.in_(["t2col2r2", "t2col2r3"])) - ) - u = union(s1, s2) - - wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), - ('ccc', 'aaa')] - found1 = self._fetchall_sorted(u.execute()) - self.assertEquals(found1, wanted) - - found2 = self._fetchall_sorted(u.alias('bar').select().execute()) - self.assertEquals(found2, wanted) - - def test_union_ordered(self): - (s1, s2) = ( - select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], - t1.c.col2.in_(["t1col2r1", "t1col2r2"])), - select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], - t2.c.col2.in_(["t2col2r2", "t2col2r3"])) - ) - u = union(s1, s2, order_by=['col3', 'col4']) - - wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), - ('ccc', 'aaa')] - self.assertEquals(u.execute().fetchall(), wanted) - - @testing.fails_on('maxdb', 'FIXME: unknown') - @testing.requires.subqueries - def test_union_ordered_alias(self): - (s1, s2) = ( - select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], - t1.c.col2.in_(["t1col2r1", "t1col2r2"])), - select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], - t2.c.col2.in_(["t2col2r2", "t2col2r3"])) - ) - u = union(s1, s2, order_by=['col3', 'col4']) - - wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), - ('ccc', 'aaa')] - self.assertEquals(u.alias('bar').select().execute().fetchall(), wanted) - - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('mysql', 'FIXME: unknown') - @testing.fails_on('sqlite', 'FIXME: unknown') - def test_union_all(self): - e = union_all( - select([t1.c.col3]), - union( - select([t1.c.col3]), - select([t1.c.col3]), - ) - ) - - wanted = [('aaa',),('aaa',),('bbb',), ('bbb',), ('ccc',),('ccc',)] - found1 = self._fetchall_sorted(e.execute()) - self.assertEquals(found1, wanted) - - found2 = self._fetchall_sorted(e.alias('foo').select().execute()) - self.assertEquals(found2, wanted) - - @testing.crashes('firebird', 'Does not support intersect') - @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('mysql', 'FIXME: unknown') - def test_intersect(self): - i = intersect( - select([t2.c.col3, t2.c.col4]), - select([t2.c.col3, t2.c.col4], t2.c.col4==t3.c.col3) - ) - - wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] - - found1 = self._fetchall_sorted(i.execute()) - self.assertEquals(found1, wanted) - - found2 = self._fetchall_sorted(i.alias('bar').select().execute()) - self.assertEquals(found2, wanted) - - @testing.crashes('firebird', 'Does not support except') - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('mysql', 'FIXME: unknown') - def test_except_style1(self): - e = except_(union( - select([t1.c.col3, t1.c.col4]), - select([t2.c.col3, t2.c.col4]), - select([t3.c.col3, t3.c.col4]), - ), select([t2.c.col3, t2.c.col4])) - - wanted = [('aaa', 'aaa'), ('aaa', 'ccc'), ('bbb', 'aaa'), - ('bbb', 'bbb'), ('ccc', 'bbb'), ('ccc', 'ccc')] - - found = self._fetchall_sorted(e.alias('bar').select().execute()) - self.assertEquals(found, wanted) - - @testing.crashes('firebird', 'Does not support except') - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('mysql', 'FIXME: unknown') - def test_except_style2(self): - e = except_(union( - select([t1.c.col3, t1.c.col4]), - select([t2.c.col3, t2.c.col4]), - select([t3.c.col3, t3.c.col4]), - ).alias('foo').select(), select([t2.c.col3, t2.c.col4])) - - wanted = [('aaa', 'aaa'), ('aaa', 'ccc'), ('bbb', 'aaa'), - ('bbb', 'bbb'), ('ccc', 'bbb'), ('ccc', 'ccc')] - - found1 = self._fetchall_sorted(e.execute()) - self.assertEquals(found1, wanted) - - found2 = self._fetchall_sorted(e.alias('bar').select().execute()) - self.assertEquals(found2, wanted) - - @testing.crashes('firebird', 'Does not support except') - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('mysql', 'FIXME: unknown') - @testing.fails_on('sqlite', 'FIXME: unknown') - def test_except_style3(self): - # aaa, bbb, ccc - (aaa, bbb, ccc - (ccc)) = ccc - e = except_( - select([t1.c.col3]), # aaa, bbb, ccc - except_( - select([t2.c.col3]), # aaa, bbb, ccc - select([t3.c.col3], t3.c.col3 == 'ccc'), #ccc - ) - ) - self.assertEquals(e.execute().fetchall(), [('ccc',)]) - self.assertEquals(e.alias('foo').select().execute().fetchall(), - [('ccc',)]) - - @testing.crashes('firebird', 'Does not support intersect') - @testing.fails_on('mysql', 'FIXME: unknown') - def test_composite(self): - u = intersect( - select([t2.c.col3, t2.c.col4]), - union( - select([t1.c.col3, t1.c.col4]), - select([t2.c.col3, t2.c.col4]), - select([t3.c.col3, t3.c.col4]), - ).alias('foo').select() - ) - wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] - found = self._fetchall_sorted(u.execute()) - - self.assertEquals(found, wanted) - - @testing.crashes('firebird', 'Does not support intersect') - @testing.fails_on('mysql', 'FIXME: unknown') - def test_composite_alias(self): - ua = intersect( - select([t2.c.col3, t2.c.col4]), - union( - select([t1.c.col3, t1.c.col4]), - select([t2.c.col3, t2.c.col4]), - select([t3.c.col3, t3.c.col4]), - ).alias('foo').select() - ).alias('bar') - - wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] - found = self._fetchall_sorted(ua.select().execute()) - self.assertEquals(found, wanted) - - -class JoinTest(TestBase): - """Tests join execution. - - The compiled SQL emitted by the dialect might be ANSI joins or - theta joins ('old oracle style', with (+) for OUTER). This test - tries to exercise join syntax and uncover any inconsistencies in - `JOIN rhs ON lhs.col=rhs.col` vs `rhs.col=lhs.col`. At least one - database seems to be sensitive to this. - """ - - def setUpAll(self): - global metadata - global t1, t2, t3 - - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column('t1_id', Integer, primary_key=True), - Column('name', String(32))) - t2 = Table('t2', metadata, - Column('t2_id', Integer, primary_key=True), - Column('t1_id', Integer, ForeignKey('t1.t1_id')), - Column('name', String(32))) - t3 = Table('t3', metadata, - Column('t3_id', Integer, primary_key=True), - Column('t2_id', Integer, ForeignKey('t2.t2_id')), - Column('name', String(32))) - metadata.drop_all() - metadata.create_all() - - # t1.10 -> t2.20 -> t3.30 - # t1.11 -> t2.21 - # t1.12 - t1.insert().execute({'t1_id': 10, 'name': 't1 #10'}, - {'t1_id': 11, 'name': 't1 #11'}, - {'t1_id': 12, 'name': 't1 #12'}) - t2.insert().execute({'t2_id': 20, 't1_id': 10, 'name': 't2 #20'}, - {'t2_id': 21, 't1_id': 11, 'name': 't2 #21'}) - t3.insert().execute({'t3_id': 30, 't2_id': 20, 'name': 't3 #30'}) - - def tearDownAll(self): - metadata.drop_all() - - def assertRows(self, statement, expected): - """Execute a statement and assert that rows returned equal expected.""" - - found = sorted([tuple(row) - for row in statement.execute().fetchall()]) - - self.assertEquals(found, sorted(expected)) - - def test_join_x1(self): - """Joins t1->t2.""" - - for criteria in (t1.c.t1_id==t2.c.t1_id, t2.c.t1_id==t1.c.t1_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id], - from_obj=[t1.join(t2, criteria)]) - self.assertRows(expr, [(10, 20), (11, 21)]) - - def test_join_x2(self): - """Joins t1->t2->t3.""" - - for criteria in (t1.c.t1_id==t2.c.t1_id, t2.c.t1_id==t1.c.t1_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id], - from_obj=[t1.join(t2, criteria)]) - self.assertRows(expr, [(10, 20), (11, 21)]) - - def test_outerjoin_x1(self): - """Outer joins t1->t2.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id], - from_obj=[t1.join(t2).join(t3, criteria)]) - self.assertRows(expr, [(10, 20)]) - - def test_outerjoin_x2(self): - """Outer joins t1->t2,t3.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - from_obj=[t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). \ - outerjoin(t3, criteria)]) - self.assertRows(expr, [(10, 20, 30), (11, 21, None), (12, None, None)]) - - def test_outerjoin_where_x2_t1(self): - """Outer joins t1->t2,t3, where on t1.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t1.c.name == 't1 #10', - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t1.c.t1_id < 12, - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) - - def test_outerjoin_where_x2_t2(self): - """Outer joins t1->t2,t3, where on t2.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t2.c.name == 't2 #20', - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t2.c.t2_id < 29, - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) - - def test_outerjoin_where_x2_t1t2(self): - """Outer joins t1->t2,t3, where on t1 and t2.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.t1_id < 19, 29 > t2.c.t2_id), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) - - def test_outerjoin_where_x2_t3(self): - """Outer joins t1->t2,t3, where on t3.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t3.c.name == 't3 #30', - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t3.c.t3_id < 39, - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - def test_outerjoin_where_x2_t1t3(self): - """Outer joins t1->t2,t3, where on t1 and t3.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', t3.c.name == 't3 #30'), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.t1_id < 19, t3.c.t3_id < 39), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - def test_outerjoin_where_x2_t1t2(self): - """Outer joins t1->t2,t3, where on t1 and t2.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.t1_id < 12, t2.c.t2_id < 39), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) - - def test_outerjoin_where_x2_t1t2t3(self): - """Outer joins t1->t2,t3, where on t1, t2 and t3.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', - t2.c.name == 't2 #20', - t3.c.name == 't3 #30'), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.t1_id < 19, - t2.c.t2_id < 29, - t3.c.t3_id < 39), - from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). - outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - def test_mixed(self): - """Joins t1->t2, outer t2->t3.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - print expr - self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) - - def test_mixed_where(self): - """Joins t1->t2, outer t2->t3, plus a where on each table in turn.""" - - for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t1.c.name == 't1 #10', - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t2.c.name == 't2 #20', - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - t3.c.name == 't3 #30', - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t2.c.name == 't2 #20', t3.c.name == 't3 #30'), - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - expr = select( - [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], - and_(t1.c.name == 't1 #10', - t2.c.name == 't2 #20', - t3.c.name == 't3 #30'), - from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) - self.assertRows(expr, [(10, 20, 30)]) - - -class OperatorTest(TestBase): - def setUpAll(self): - global metadata, flds - metadata = MetaData(testing.db) - flds = Table('flds', metadata, - Column('idcol', Integer, Sequence('t1pkseq'), primary_key=True), - Column('intcol', Integer), - Column('strcol', String(50)), - ) - metadata.create_all() - - flds.insert().execute([ - dict(intcol=5, strcol='foo'), - dict(intcol=13, strcol='bar') - ]) - - def tearDownAll(self): - metadata.drop_all() - - @testing.fails_on('maxdb', 'FIXME: unknown') - def test_modulo(self): - self.assertEquals( - select([flds.c.intcol % 3], - order_by=flds.c.idcol).execute().fetchall(), - [(2,),(1,)] - ) - - - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/quote.py b/test/sql/quote.py deleted file mode 100644 index 106189afe..000000000 --- a/test/sql/quote.py +++ /dev/null @@ -1,211 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from sqlalchemy import sql -from sqlalchemy.sql import compiler -from testlib import * - - -class QuoteTest(TestBase, AssertsCompiledSQL): - def setUpAll(self): - # TODO: figure out which databases/which identifiers allow special - # characters to be used, such as: spaces, quote characters, - # punctuation characters, set up tests for those as well. - global table1, table2, table3 - metadata = MetaData(testing.db) - table1 = Table('WorstCase1', metadata, - Column('lowercase', Integer, primary_key=True), - Column('UPPERCASE', Integer), - Column('MixedCase', Integer), - Column('ASC', Integer, key='a123')) - table2 = Table('WorstCase2', metadata, - Column('desc', Integer, primary_key=True, key='d123'), - Column('Union', Integer, key='u123'), - Column('MixedCase', Integer)) - table1.create() - table2.create() - - def tearDown(self): - table1.delete().execute() - table2.delete().execute() - - def tearDownAll(self): - table1.drop() - table2.drop() - - def testbasic(self): - table1.insert().execute({'lowercase':1,'UPPERCASE':2,'MixedCase':3,'a123':4}, - {'lowercase':2,'UPPERCASE':2,'MixedCase':3,'a123':4}, - {'lowercase':4,'UPPERCASE':3,'MixedCase':2,'a123':1}) - table2.insert().execute({'d123':1,'u123':2,'MixedCase':3}, - {'d123':2,'u123':2,'MixedCase':3}, - {'d123':4,'u123':3,'MixedCase':2}) - - res1 = select([table1.c.lowercase, table1.c.UPPERCASE, table1.c.MixedCase, table1.c.a123]).execute().fetchall() - print res1 - assert(res1==[(1,2,3,4),(2,2,3,4),(4,3,2,1)]) - - res2 = select([table2.c.d123, table2.c.u123, table2.c.MixedCase]).execute().fetchall() - print res2 - assert(res2==[(1,2,3),(2,2,3),(4,3,2)]) - - def testreflect(self): - meta2 = MetaData(testing.db) - t2 = Table('WorstCase2', meta2, autoload=True, quote=True) - assert 'MixedCase' in t2.c - - def testlabels(self): - table1.insert().execute({'lowercase':1,'UPPERCASE':2,'MixedCase':3,'a123':4}, - {'lowercase':2,'UPPERCASE':2,'MixedCase':3,'a123':4}, - {'lowercase':4,'UPPERCASE':3,'MixedCase':2,'a123':1}) - table2.insert().execute({'d123':1,'u123':2,'MixedCase':3}, - {'d123':2,'u123':2,'MixedCase':3}, - {'d123':4,'u123':3,'MixedCase':2}) - - res1 = select([table1.c.lowercase, table1.c.UPPERCASE, table1.c.MixedCase, table1.c.a123], use_labels=True).execute().fetchall() - print res1 - assert(res1==[(1,2,3,4),(2,2,3,4),(4,3,2,1)]) - - res2 = select([table2.c.d123, table2.c.u123, table2.c.MixedCase], use_labels=True).execute().fetchall() - print res2 - assert(res2==[(1,2,3),(2,2,3),(4,3,2)]) - - def test_quote_flag(self): - metadata = MetaData() - t1 = Table('TableOne', metadata, - Column('ColumnOne', Integer), schema="FooBar") - self.assert_compile(t1.select(), '''SELECT "FooBar"."TableOne"."ColumnOne" FROM "FooBar"."TableOne"''') - - metadata = MetaData() - t1 = Table('t1', metadata, - Column('col1', Integer, quote=True), quote=True, schema="foo", quote_schema=True) - self.assert_compile(t1.select(), '''SELECT "foo"."t1"."col1" FROM "foo"."t1"''') - - self.assert_compile(t1.select().apply_labels(), '''SELECT "foo"."t1"."col1" AS "foo_t1_col1" FROM "foo"."t1"''') - a = t1.select().alias('anon') - b = select([1], a.c.col1==2, from_obj=a) - self.assert_compile(b, - '''SELECT 1 FROM (SELECT "foo"."t1"."col1" AS "col1" FROM '''\ - '''"foo"."t1") AS anon WHERE anon."col1" = :col1_1''' - ) - - metadata = MetaData() - t1 = Table('TableOne', metadata, - Column('ColumnOne', Integer, quote=False), quote=False, schema="FooBar", quote_schema=False) - self.assert_compile(t1.select(), "SELECT FooBar.TableOne.ColumnOne FROM FooBar.TableOne") - - self.assert_compile(t1.select().apply_labels(), - "SELECT FooBar.TableOne.ColumnOne AS "\ - "FooBar_TableOne_ColumnOne FROM FooBar.TableOne" # TODO: is this what we really want here ? what if table/schema - # *are* quoted? - ) - - a = t1.select().alias('anon') - b = select([1], a.c.ColumnOne==2, from_obj=a) - self.assert_compile(b, - "SELECT 1 FROM (SELECT FooBar.TableOne.ColumnOne AS "\ - "ColumnOne FROM FooBar.TableOne) AS anon WHERE anon.ColumnOne = :ColumnOne_1" - ) - - - - def test_table_quote_flag(self): - metadata = MetaData() - t1 = Table('TableOne', metadata, - Column('id', Integer), - quote=False) - t2 = Table('TableTwo', metadata, - Column('id', Integer), - Column('t1_id', Integer, ForeignKey('TableOne.id')), - quote=False) - - self.assert_compile( - t2.join(t1).select(), - "SELECT TableTwo.id, TableTwo.t1_id, TableOne.id " - "FROM TableTwo JOIN TableOne ON TableOne.id = TableTwo.t1_id") - - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.requires.subqueries - def testlabels(self): - """test the quoting of labels. - - if labels arent quoted, a query in postgres in particular will fail since it produces: - - SELECT LaLa.lowercase, LaLa."UPPERCASE", LaLa."MixedCase", LaLa."ASC" - FROM (SELECT DISTINCT "WorstCase1".lowercase AS lowercase, "WorstCase1"."UPPERCASE" AS UPPERCASE, "WorstCase1"."MixedCase" AS MixedCase, "WorstCase1"."ASC" AS ASC \nFROM "WorstCase1") AS LaLa - - where the "UPPERCASE" column of "LaLa" doesnt exist. - """ - x = table1.select(distinct=True).alias("LaLa").select().scalar() - - def testlabels2(self): - metadata = MetaData() - table = Table("ImATable", metadata, - Column("col1", Integer)) - x = select([table.c.col1.label("ImATable_col1")]).alias("SomeAlias") - self.assert_compile(select([x.c.ImATable_col1]), - '''SELECT "SomeAlias"."ImATable_col1" FROM (SELECT "ImATable".col1 AS "ImATable_col1" FROM "ImATable") AS "SomeAlias"''') - - # note that 'foo' and 'FooCol' are literals already quoted - x = select([sql.literal_column("'foo'").label("somelabel")], from_obj=[table]).alias("AnAlias") - x = x.select() - self.assert_compile(x, - '''SELECT "AnAlias".somelabel FROM (SELECT 'foo' AS somelabel FROM "ImATable") AS "AnAlias"''') - - x = select([sql.literal_column("'FooCol'").label("SomeLabel")], from_obj=[table]) - x = x.select() - self.assert_compile(x, - '''SELECT "SomeLabel" FROM (SELECT 'FooCol' AS "SomeLabel" FROM "ImATable")''') - - -class PreparerTest(TestBase): - """Test the db-agnostic quoting services of IdentifierPreparer.""" - - def test_unformat(self): - prep = compiler.IdentifierPreparer(None) - unformat = prep.unformat_identifiers - - def a_eq(have, want): - if have != want: - print "Wanted %s" % want - print "Received %s" % have - self.assert_(have == want) - - a_eq(unformat('foo'), ['foo']) - a_eq(unformat('"foo"'), ['foo']) - a_eq(unformat("'foo'"), ["'foo'"]) - a_eq(unformat('foo.bar'), ['foo', 'bar']) - a_eq(unformat('"foo"."bar"'), ['foo', 'bar']) - a_eq(unformat('foo."bar"'), ['foo', 'bar']) - a_eq(unformat('"foo".bar'), ['foo', 'bar']) - a_eq(unformat('"foo"."b""a""r"."baz"'), ['foo', 'b"a"r', 'baz']) - - def test_unformat_custom(self): - class Custom(compiler.IdentifierPreparer): - def __init__(self, dialect): - super(Custom, self).__init__(dialect, initial_quote='`', - final_quote='`') - def _escape_identifier(self, value): - return value.replace('`', '``') - def _unescape_identifier(self, value): - return value.replace('``', '`') - - prep = Custom(None) - unformat = prep.unformat_identifiers - - def a_eq(have, want): - if have != want: - print "Wanted %s" % want - print "Received %s" % have - self.assert_(have == want) - - a_eq(unformat('foo'), ['foo']) - a_eq(unformat('`foo`'), ['foo']) - a_eq(unformat(`'foo'`), ["'foo'"]) - a_eq(unformat('foo.bar'), ['foo', 'bar']) - a_eq(unformat('`foo`.`bar`'), ['foo', 'bar']) - a_eq(unformat('foo.`bar`'), ['foo', 'bar']) - a_eq(unformat('`foo`.bar'), ['foo', 'bar']) - a_eq(unformat('`foo`.`b``a``r`.`baz`'), ['foo', 'b`a`r', 'baz']) - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/rowcount.py b/test/sql/rowcount.py deleted file mode 100644 index 3c9caad75..000000000 --- a/test/sql/rowcount.py +++ /dev/null @@ -1,71 +0,0 @@ -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from testlib import * - - -class FoundRowsTest(TestBase, AssertsExecutionResults): - """tests rowcount functionality""" - def setUpAll(self): - metadata = MetaData(testing.db) - - global employees_table - - employees_table = Table('employees', metadata, - Column('employee_id', Integer, Sequence('employee_id_seq', optional=True), primary_key=True), - Column('name', String(50)), - Column('department', String(1)), - ) - employees_table.create() - - def setUp(self): - global data - data = [ ('Angela', 'A'), - ('Andrew', 'A'), - ('Anand', 'A'), - ('Bob', 'B'), - ('Bobette', 'B'), - ('Buffy', 'B'), - ('Charlie', 'C'), - ('Cynthia', 'C'), - ('Chris', 'C') ] - - i = employees_table.insert() - i.execute(*[{'name':n, 'department':d} for n, d in data]) - def tearDown(self): - employees_table.delete().execute() - - def tearDownAll(self): - employees_table.drop() - - def testbasic(self): - s = employees_table.select() - r = s.execute().fetchall() - - assert len(r) == len(data) - - def test_update_rowcount1(self): - # WHERE matches 3, 3 rows changed - department = employees_table.c.department - r = employees_table.update(department=='C').execute(department='Z') - print "expecting 3, dialect reports %s" % r.rowcount - if testing.db.dialect.supports_sane_rowcount: - assert r.rowcount == 3 - - def test_update_rowcount2(self): - # WHERE matches 3, 0 rows changed - department = employees_table.c.department - r = employees_table.update(department=='C').execute(department='C') - print "expecting 3, dialect reports %s" % r.rowcount - if testing.db.dialect.supports_sane_rowcount: - assert r.rowcount == 3 - - def test_delete_rowcount(self): - # WHERE matches 3, 3 rows deleted - department = employees_table.c.department - r = employees_table.delete(department=='C').execute() - print "expecting 3, dialect reports %s" % r.rowcount - if testing.db.dialect.supports_sane_rowcount: - assert r.rowcount == 3 - -if __name__ == '__main__': - testenv.main() diff --git a/test/sql/select.py b/test/sql/select.py deleted file mode 100644 index 2ec5b8da5..000000000 --- a/test/sql/select.py +++ /dev/null @@ -1,1552 +0,0 @@ -import testenv; testenv.configure_for_tests() -import datetime, re, operator -from sqlalchemy import * -from sqlalchemy import exc, sql, util -from sqlalchemy.sql import table, column, label, compiler -from sqlalchemy.sql.expression import ClauseList -from sqlalchemy.engine import default -from sqlalchemy.databases import sqlite, postgres, mysql, oracle, firebird, mssql -from testlib import * - -table1 = table('mytable', - column('myid', Integer), - column('name', String), - column('description', String), -) - -table2 = table( - 'myothertable', - column('otherid', Integer), - column('othername', String), -) - -table3 = table( - 'thirdtable', - column('userid', Integer), - column('otherstuff', String), -) - -metadata = MetaData() -table4 = Table( - 'remotetable', metadata, - Column('rem_id', Integer, primary_key=True), - Column('datatype_id', Integer), - Column('value', String(20)), - schema = 'remote_owner' -) - -users = table('users', - column('user_id'), - column('user_name'), - column('password'), -) - -addresses = table('addresses', - column('address_id'), - column('user_id'), - column('street'), - column('city'), - column('state'), - column('zip') -) - -class SelectTest(TestBase, AssertsCompiledSQL): - - def test_attribute_sanity(self): - assert hasattr(table1, 'c') - assert hasattr(table1.select(), 'c') - assert not hasattr(table1.c.myid.self_group(), 'columns') - assert hasattr(table1.select().self_group(), 'columns') - assert not hasattr(select([table1.c.myid]).as_scalar().self_group(), 'columns') - assert not hasattr(table1.c.myid, 'columns') - assert not hasattr(table1.c.myid, 'c') - assert not hasattr(table1.select().c.myid, 'c') - assert not hasattr(table1.select().c.myid, 'columns') - assert not hasattr(table1.alias().c.myid, 'columns') - assert not hasattr(table1.alias().c.myid, 'c') - - def test_table_select(self): - self.assert_compile(table1.select(), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable") - - self.assert_compile(select([table1, table2]), "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, \ -myothertable.othername FROM mytable, myothertable") - - def test_from_subquery(self): - """tests placing select statements in the column clause of another select, for the - purposes of selecting from the exported columns of that select.""" - - s = select([table1], table1.c.name == 'jack') - self.assert_compile( - select( - [s], - s.c.myid == 7 - ) - , - "SELECT myid, name, description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable "\ - "WHERE mytable.name = :name_1) WHERE myid = :myid_1") - - sq = select([table1]) - self.assert_compile( - sq.select(), - "SELECT myid, name, description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable)" - ) - - sq = select( - [table1], - ).alias('sq') - - self.assert_compile( - sq.select(sq.c.myid == 7), - "SELECT sq.myid, sq.name, sq.description FROM \ -(SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable) AS sq WHERE sq.myid = :myid_1" - ) - - sq = select( - [table1, table2], - and_(table1.c.myid ==7, table2.c.otherid==table1.c.myid), - use_labels = True - ).alias('sq') - - sqstring = "SELECT mytable.myid AS mytable_myid, mytable.name AS mytable_name, \ -mytable.description AS mytable_description, myothertable.otherid AS myothertable_otherid, \ -myothertable.othername AS myothertable_othername FROM mytable, myothertable \ -WHERE mytable.myid = :myid_1 AND myothertable.otherid = mytable.myid" - - self.assert_compile(sq.select(), "SELECT sq.mytable_myid, sq.mytable_name, sq.mytable_description, sq.myothertable_otherid, \ -sq.myothertable_othername FROM (" + sqstring + ") AS sq") - - sq2 = select( - [sq], - use_labels = True - ).alias('sq2') - - self.assert_compile(sq2.select(), "SELECT sq2.sq_mytable_myid, sq2.sq_mytable_name, sq2.sq_mytable_description, \ -sq2.sq_myothertable_otherid, sq2.sq_myothertable_othername FROM \ -(SELECT sq.mytable_myid AS sq_mytable_myid, sq.mytable_name AS sq_mytable_name, \ -sq.mytable_description AS sq_mytable_description, sq.myothertable_otherid AS sq_myothertable_otherid, \ -sq.myothertable_othername AS sq_myothertable_othername FROM (" + sqstring + ") AS sq) AS sq2") - - def test_select_from_clauselist(self): - self.assert_compile( - select([ClauseList(column('a'), column('b'))]).select_from('sometable'), - 'SELECT a, b FROM sometable' - ) - - def test_use_labels(self): - self.assert_compile( - select([table1.c.myid==5], use_labels=True), - "SELECT mytable.myid = :myid_1 AS anon_1 FROM mytable" - ) - - self.assert_compile( - select([func.foo()], use_labels=True), - "SELECT foo() AS foo_1" - ) - - self.assert_compile( - select([not_(True)], use_labels=True), - "SELECT NOT :param_1" # TODO: should this make an anon label ?? - ) - - self.assert_compile( - select([cast("data", sqlite.SLInteger)], use_labels=True), # this will work with plain Integer in 0.6 - "SELECT CAST(:param_1 AS INTEGER) AS anon_1" - ) - - - - def test_nested_uselabels(self): - """test nested anonymous label generation. this - essentially tests the ANONYMOUS_LABEL regex. - - """ - s1 = table1.select() - s2 = s1.alias() - s3 = select([s2], use_labels=True) - s4 = s3.alias() - s5 = select([s4], use_labels=True) - self.assert_compile(s5, "SELECT anon_1.anon_2_myid AS anon_1_anon_2_myid, anon_1.anon_2_name AS anon_1_anon_2_name, "\ - "anon_1.anon_2_description AS anon_1_anon_2_description FROM (SELECT anon_2.myid AS anon_2_myid, anon_2.name AS anon_2_name, "\ - "anon_2.description AS anon_2_description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description "\ - "AS description FROM mytable) AS anon_2) AS anon_1") - - def test_dont_overcorrelate(self): - self.assert_compile(select([table1], from_obj=[table1, table1.select()]), """SELECT mytable.myid, mytable.name, mytable.description FROM mytable, (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable)""") - - def test_full_correlate(self): - # intentional - t = table('t', column('a'), column('b')) - s = select([t.c.a]).where(t.c.a==1).correlate(t).as_scalar() - - s2 = select([t.c.a, s]) - self.assert_compile(s2, """SELECT t.a, (SELECT t.a WHERE t.a = :a_1) AS anon_1 FROM t""") - - # unintentional - t2 = table('t2', column('c'), column('d')) - s = select([t.c.a]).where(t.c.a==t2.c.d).as_scalar() - s2 =select([t, t2, s]) - self.assertRaises(exc.InvalidRequestError, str, s2) - - # intentional again - s = s.correlate(t, t2) - s2 =select([t, t2, s]) - self.assert_compile(s, "SELECT t.a WHERE t.a = t2.d") - - def test_exists(self): - self.assert_compile(exists([table1.c.myid], table1.c.myid==5).select(), "SELECT EXISTS (SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1)", params={'mytable_myid':5}) - - self.assert_compile(select([table1, exists([1], from_obj=table2)]), "SELECT mytable.myid, mytable.name, mytable.description, EXISTS (SELECT 1 FROM myothertable) FROM mytable", params={}) - - self.assert_compile(select([table1, exists([1], from_obj=table2).label('foo')]), "SELECT mytable.myid, mytable.name, mytable.description, EXISTS (SELECT 1 FROM myothertable) AS foo FROM mytable", params={}) - - self.assert_compile( - table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = mytable.myid)" - ) - - self.assert_compile( - table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = mytable.myid)" - ) - - self.assert_compile( - table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)).replace_selectable(table2, table2.alias()), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable AS myothertable_1 WHERE myothertable_1.otherid = mytable.myid)" - ) - - self.assert_compile( - table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)).select_from(table1.join(table2, table1.c.myid==table2.c.otherid)).replace_selectable(table2, table2.alias()), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable JOIN myothertable AS myothertable_1 ON mytable.myid = myothertable_1.otherid WHERE EXISTS (SELECT * FROM myothertable AS myothertable_1 WHERE myothertable_1.otherid = mytable.myid)" - ) - - self.assert_compile( - select([ - or_( - exists().where(table2.c.otherid=='foo'), - exists().where(table2.c.otherid=='bar') - ) - ]), - "SELECT (EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = :otherid_1)) "\ - "OR (EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = :otherid_2)) AS anon_1" - ) - - - def test_where_subquery(self): - s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') - self.assert_compile( - select([users, s.c.street], from_obj=s), - """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") - - self.assert_compile( - table1.select(table1.c.myid == select([table1.c.myid], table1.c.name=='jack')), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable WHERE mytable.name = :name_1)" - ) - - self.assert_compile( - table1.select(table1.c.myid == select([table2.c.otherid], table1.c.name == table2.c.othername)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT myothertable.otherid FROM myothertable WHERE mytable.name = myothertable.othername)" - ) - - self.assert_compile( - table1.select(exists([1], table2.c.otherid == table1.c.myid)), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" - ) - - - talias = table1.alias('ta') - s = subquery('sq2', [talias], exists([1], table2.c.otherid == talias.c.myid)) - self.assert_compile( - select([s, table1]) - ,"SELECT sq2.myid, sq2.name, sq2.description, mytable.myid, mytable.name, mytable.description FROM (SELECT ta.myid AS myid, ta.name AS name, ta.description AS description FROM mytable AS ta WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = ta.myid)) AS sq2, mytable") - - s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') - self.assert_compile( - select([users, s.c.street], from_obj=s), - """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") - - # test constructing the outer query via append_column(), which occurs in the ORM's Query object - s = select([], exists([1], table2.c.otherid==table1.c.myid), from_obj=table1) - s.append_column(table1) - self.assert_compile( - s, - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" - ) - - - def test_orderby_subquery(self): - self.assert_compile( - table1.select(order_by=[select([table2.c.otherid], table1.c.myid==table2.c.otherid)]), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable ORDER BY (SELECT myothertable.otherid FROM myothertable WHERE mytable.myid = myothertable.otherid)" - ) - self.assert_compile( - table1.select(order_by=[desc(select([table2.c.otherid], table1.c.myid==table2.c.otherid))]), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable ORDER BY (SELECT myothertable.otherid FROM myothertable WHERE mytable.myid = myothertable.otherid) DESC" - ) - - @testing.uses_deprecated('scalar option') - def test_scalar_select(self): - try: - s = select([table1.c.myid, table1.c.name]).as_scalar() - assert False - except exc.InvalidRequestError, err: - assert str(err) == "Scalar select can only be created from a Select object that has exactly one column expression.", str(err) - - try: - # generic function which will look at the type of expression - func.coalesce(select([table1.c.myid])) - assert False - except exc.InvalidRequestError, err: - assert str(err) == "Select objects don't have a type. Call as_scalar() on this Select object to return a 'scalar' version of this Select.", str(err) - - s = select([table1.c.myid], scalar=True, correlate=False) - self.assert_compile(select([table1, s]), "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable) AS anon_1 FROM mytable") - - s = select([table1.c.myid], scalar=True) - self.assert_compile(select([table2, s]), "SELECT myothertable.otherid, myothertable.othername, (SELECT mytable.myid FROM mytable) AS anon_1 FROM myothertable") - - s = select([table1.c.myid]).correlate(None).as_scalar() - self.assert_compile(select([table1, s]), "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable) AS anon_1 FROM mytable") - - # test that aliases use as_scalar() when used in an explicitly scalar context - s = select([table1.c.myid]).alias() - self.assert_compile(select([table1.c.myid]).where(table1.c.myid==s), "SELECT mytable.myid FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable)") - self.assert_compile(select([table1.c.myid]).where(s > table1.c.myid), "SELECT mytable.myid FROM mytable WHERE mytable.myid < (SELECT mytable.myid FROM mytable)") - - - s = select([table1.c.myid]).as_scalar() - self.assert_compile(select([table2, s]), "SELECT myothertable.otherid, myothertable.othername, (SELECT mytable.myid FROM mytable) AS anon_1 FROM myothertable") - - # test expressions against scalar selects - self.assert_compile(select([s - literal(8)]), "SELECT (SELECT mytable.myid FROM mytable) - :param_1 AS anon_1") - self.assert_compile(select([select([table1.c.name]).as_scalar() + literal('x')]), "SELECT (SELECT mytable.name FROM mytable) || :param_1 AS anon_1") - self.assert_compile(select([s > literal(8)]), "SELECT (SELECT mytable.myid FROM mytable) > :param_1 AS anon_1") - - self.assert_compile(select([select([table1.c.name]).label('foo')]), "SELECT (SELECT mytable.name FROM mytable) AS foo") - - # scalar selects should not have any attributes on their 'c' or 'columns' attribute - s = select([table1.c.myid]).as_scalar() - try: - s.c.foo - except exc.InvalidRequestError, err: - assert str(err) == 'Scalar Select expression has no columns; use this object directly within a column-level expression.' - - try: - s.columns.foo - except exc.InvalidRequestError, err: - assert str(err) == 'Scalar Select expression has no columns; use this object directly within a column-level expression.' - - zips = table('zips', - column('zipcode'), - column('latitude'), - column('longitude'), - ) - places = table('places', - column('id'), - column('nm') - ) - zip = '12345' - qlat = select([zips.c.latitude], zips.c.zipcode == zip).correlate(None).as_scalar() - qlng = select([zips.c.longitude], zips.c.zipcode == zip).correlate(None).as_scalar() - - q = select([places.c.id, places.c.nm, zips.c.zipcode, func.latlondist(qlat, qlng).label('dist')], - zips.c.zipcode==zip, - order_by = ['dist', places.c.nm] - ) - - self.assert_compile(q,"SELECT places.id, places.nm, zips.zipcode, latlondist((SELECT zips.latitude FROM zips WHERE " - "zips.zipcode = :zipcode_1), (SELECT zips.longitude FROM zips WHERE zips.zipcode = :zipcode_2)) AS dist " - "FROM places, zips WHERE zips.zipcode = :zipcode_3 ORDER BY dist, places.nm") - - zalias = zips.alias('main_zip') - qlat = select([zips.c.latitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) - qlng = select([zips.c.longitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) - q = select([places.c.id, places.c.nm, zalias.c.zipcode, func.latlondist(qlat, qlng).label('dist')], - order_by = ['dist', places.c.nm] - ) - self.assert_compile(q, "SELECT places.id, places.nm, main_zip.zipcode, latlondist((SELECT zips.latitude FROM zips WHERE zips.zipcode = main_zip.zipcode), (SELECT zips.longitude FROM zips WHERE zips.zipcode = main_zip.zipcode)) AS dist FROM places, zips AS main_zip ORDER BY dist, places.nm") - - a1 = table2.alias('t2alias') - s1 = select([a1.c.otherid], table1.c.myid==a1.c.otherid, scalar=True) - j1 = table1.join(table2, table1.c.myid==table2.c.otherid) - s2 = select([table1, s1], from_obj=j1) - self.assert_compile(s2, "SELECT mytable.myid, mytable.name, mytable.description, (SELECT t2alias.otherid FROM myothertable AS t2alias WHERE mytable.myid = t2alias.otherid) AS anon_1 FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid") - - def test_label_comparison(self): - x = func.lala(table1.c.myid).label('foo') - self.assert_compile(select([x], x==5), "SELECT lala(mytable.myid) AS foo FROM mytable WHERE lala(mytable.myid) = :param_1") - - self.assert_compile(label('bar', column('foo', type_=String)) + "foo", "foo || :param_1") - - - def test_conjunctions(self): - a, b, c = 'a', 'b', 'c' - x = and_(a, b, c) - assert isinstance(x.type, Boolean) - assert str(x) == 'a AND b AND c' - self.assert_compile( - select([x.label('foo')]), - 'SELECT a AND b AND c AS foo' - ) - - self.assert_compile( - and_(table1.c.myid == 12, table1.c.name=='asdf', table2.c.othername == 'foo', "sysdate() = today()"), - "mytable.myid = :myid_1 AND mytable.name = :name_1 "\ - "AND myothertable.othername = :othername_1 AND sysdate() = today()" - ) - - self.assert_compile( - and_( - table1.c.myid == 12, - or_(table2.c.othername=='asdf', table2.c.othername == 'foo', table2.c.otherid == 9), - "sysdate() = today()", - ), - "mytable.myid = :myid_1 AND (myothertable.othername = :othername_1 OR "\ - "myothertable.othername = :othername_2 OR myothertable.otherid = :otherid_1) AND sysdate() = today()", - checkparams = {'othername_1': 'asdf', 'othername_2':'foo', 'otherid_1': 9, 'myid_1': 12} - ) - - - def test_distinct(self): - self.assert_compile( - select([table1.c.myid.distinct()]), "SELECT DISTINCT mytable.myid FROM mytable" - ) - - self.assert_compile( - select([distinct(table1.c.myid)]), "SELECT DISTINCT mytable.myid FROM mytable" - ) - - self.assert_compile( - select([table1.c.myid]).distinct(), "SELECT DISTINCT mytable.myid FROM mytable" - ) - - self.assert_compile( - select([func.count(table1.c.myid.distinct())]), "SELECT count(DISTINCT mytable.myid) AS count_1 FROM mytable" - ) - - self.assert_compile( - select([func.count(distinct(table1.c.myid))]), "SELECT count(DISTINCT mytable.myid) AS count_1 FROM mytable" - ) - - def test_operators(self): - for (py_op, sql_op) in ((operator.add, '+'), (operator.mul, '*'), - (operator.sub, '-'), (operator.div, '/'), - ): - for (lhs, rhs, res) in ( - (5, table1.c.myid, ':myid_1 %s mytable.myid'), - (5, literal(5), ':param_1 %s :param_2'), - (table1.c.myid, 'b', 'mytable.myid %s :myid_1'), - (table1.c.myid, literal(2.7), 'mytable.myid %s :param_1'), - (table1.c.myid, table1.c.myid, 'mytable.myid %s mytable.myid'), - (literal(5), 8, ':param_1 %s :param_2'), - (literal(6), table1.c.myid, ':param_1 %s mytable.myid'), - (literal(7), literal(5.5), ':param_1 %s :param_2'), - ): - self.assert_compile(py_op(lhs, rhs), res % sql_op) - - dt = datetime.datetime.today() - # exercise comparison operators - for (py_op, fwd_op, rev_op) in ((operator.lt, '<', '>'), - (operator.gt, '>', '<'), - (operator.eq, '=', '='), - (operator.ne, '!=', '!='), - (operator.le, '<=', '>='), - (operator.ge, '>=', '<=')): - for (lhs, rhs, l_sql, r_sql) in ( - ('a', table1.c.myid, ':myid_1', 'mytable.myid'), - ('a', literal('b'), ':param_2', ':param_1'), # note swap! - (table1.c.myid, 'b', 'mytable.myid', ':myid_1'), - (table1.c.myid, literal('b'), 'mytable.myid', ':param_1'), - (table1.c.myid, table1.c.myid, 'mytable.myid', 'mytable.myid'), - (literal('a'), 'b', ':param_1', ':param_2'), - (literal('a'), table1.c.myid, ':param_1', 'mytable.myid'), - (literal('a'), literal('b'), ':param_1', ':param_2'), - (dt, literal('b'), ':param_2', ':param_1'), - (literal('b'), dt, ':param_1', ':param_2'), - ): - - # the compiled clause should match either (e.g.): - # 'a' < 'b' -or- 'b' > 'a'. - compiled = str(py_op(lhs, rhs)) - fwd_sql = "%s %s %s" % (l_sql, fwd_op, r_sql) - rev_sql = "%s %s %s" % (r_sql, rev_op, l_sql) - - self.assert_(compiled == fwd_sql or compiled == rev_sql, - "\n'" + compiled + "'\n does not match\n'" + - fwd_sql + "'\n or\n'" + rev_sql + "'") - - self.assert_compile( - table1.select((table1.c.myid != 12) & ~(table1.c.name=='john')), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND mytable.name != :name_1" - ) - - self.assert_compile( - table1.select((table1.c.myid != 12) & ~(table1.c.name.between('jack','john'))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND "\ - "NOT (mytable.name BETWEEN :name_1 AND :name_2)" - ) - - self.assert_compile( - table1.select((table1.c.myid != 12) & ~and_(table1.c.name=='john', table1.c.name=='ed', table1.c.name=='fred')), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND "\ - "NOT (mytable.name = :name_1 AND mytable.name = :name_2 AND mytable.name = :name_3)" - ) - - self.assert_compile( - table1.select((table1.c.myid != 12) & ~table1.c.name), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND NOT mytable.name" - ) - - self.assert_compile( - literal("a") + literal("b") * literal("c"), ":param_1 || :param_2 * :param_3" - ) - - # test the op() function, also that its results are further usable in expressions - self.assert_compile( - table1.select(table1.c.myid.op('hoho')(12)==14), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE (mytable.myid hoho :myid_1) = :param_1" - ) - - # test that clauses can be pickled (operators need to be module-level, etc.) - clause = (table1.c.myid == 12) & table1.c.myid.between(15, 20) & table1.c.myid.like('hoho') - assert str(clause) == str(util.pickle.loads(util.pickle.dumps(clause))) - - - def test_like(self): - for expr, check, dialect in [ - (table1.c.myid.like('somstr'), "mytable.myid LIKE :myid_1", None), - (~table1.c.myid.like('somstr'), "mytable.myid NOT LIKE :myid_1", None), - (table1.c.myid.like('somstr', escape='\\'), "mytable.myid LIKE :myid_1 ESCAPE '\\'", None), - (~table1.c.myid.like('somstr', escape='\\'), "mytable.myid NOT LIKE :myid_1 ESCAPE '\\'", None), - (table1.c.myid.ilike('somstr', escape='\\'), "lower(mytable.myid) LIKE lower(:myid_1) ESCAPE '\\'", None), - (~table1.c.myid.ilike('somstr', escape='\\'), "lower(mytable.myid) NOT LIKE lower(:myid_1) ESCAPE '\\'", None), - (table1.c.myid.ilike('somstr', escape='\\'), "mytable.myid ILIKE %(myid_1)s ESCAPE '\\'", postgres.PGDialect()), - (~table1.c.myid.ilike('somstr', escape='\\'), "mytable.myid NOT ILIKE %(myid_1)s ESCAPE '\\'", postgres.PGDialect()), - (table1.c.name.ilike('%something%'), "lower(mytable.name) LIKE lower(:name_1)", None), - (table1.c.name.ilike('%something%'), "mytable.name ILIKE %(name_1)s", postgres.PGDialect()), - (~table1.c.name.ilike('%something%'), "lower(mytable.name) NOT LIKE lower(:name_1)", None), - (~table1.c.name.ilike('%something%'), "mytable.name NOT ILIKE %(name_1)s", postgres.PGDialect()), - ]: - self.assert_compile(expr, check, dialect=dialect) - - def test_match(self): - for expr, check, dialect in [ - (table1.c.myid.match('somstr'), "mytable.myid MATCH ?", sqlite.SQLiteDialect()), - (table1.c.myid.match('somstr'), "MATCH (mytable.myid) AGAINST (%s IN BOOLEAN MODE)", mysql.MySQLDialect()), - (table1.c.myid.match('somstr'), "CONTAINS (mytable.myid, :myid_1)", mssql.MSSQLDialect()), - (table1.c.myid.match('somstr'), "mytable.myid @@ to_tsquery(%(myid_1)s)", postgres.PGDialect()), - (table1.c.myid.match('somstr'), "CONTAINS (mytable.myid, :myid_1)", oracle.OracleDialect()), - ]: - self.assert_compile(expr, check, dialect=dialect) - - def test_composed_string_comparators(self): - self.assert_compile( - table1.c.name.contains('jo'), "mytable.name LIKE '%%' || :name_1 || '%%'" , checkparams = {'name_1': u'jo'}, - ) - self.assert_compile( - table1.c.name.contains('jo'), "mytable.name LIKE concat(concat('%%', %s), '%%')" , checkparams = {'name_1': u'jo'}, - dialect=mysql.dialect() - ) - self.assert_compile( - table1.c.name.contains('jo', escape='\\'), "mytable.name LIKE '%%' || :name_1 || '%%' ESCAPE '\\'" , checkparams = {'name_1': u'jo'}, - ) - self.assert_compile( table1.c.name.startswith('jo', escape='\\'), "mytable.name LIKE :name_1 || '%%' ESCAPE '\\'" ) - self.assert_compile( table1.c.name.endswith('jo', escape='\\'), "mytable.name LIKE '%%' || :name_1 ESCAPE '\\'" ) - self.assert_compile( table1.c.name.endswith('hn'), "mytable.name LIKE '%%' || :name_1", checkparams = {'name_1': u'hn'}, ) - self.assert_compile( - table1.c.name.endswith('hn'), "mytable.name LIKE concat('%%', %s)", - checkparams = {'name_1': u'hn'}, dialect=mysql.dialect() - ) - self.assert_compile( - table1.c.name.startswith(u"hi \xf6 \xf5"), "mytable.name LIKE :name_1 || '%%'", - checkparams = {'name_1': u'hi \xf6 \xf5'}, - ) - self.assert_compile(column('name').endswith(text("'foo'")), "name LIKE '%%' || 'foo'" ) - self.assert_compile(column('name').endswith(literal_column("'foo'")), "name LIKE '%%' || 'foo'" ) - self.assert_compile(column('name').startswith(text("'foo'")), "name LIKE 'foo' || '%%'" ) - self.assert_compile(column('name').startswith(text("'foo'")), "name LIKE concat('foo', '%%')", dialect=mysql.dialect()) - self.assert_compile(column('name').startswith(literal_column("'foo'")), "name LIKE 'foo' || '%%'" ) - self.assert_compile(column('name').startswith(literal_column("'foo'")), "name LIKE concat('foo', '%%')", dialect=mysql.dialect()) - - def test_multiple_col_binds(self): - self.assert_compile( - select(["*"], or_(table1.c.myid == 12, table1.c.myid=='asdf', table1.c.myid == 'foo')), - "SELECT * FROM mytable WHERE mytable.myid = :myid_1 OR mytable.myid = :myid_2 OR mytable.myid = :myid_3" - ) - - def test_orderby_groupby(self): - self.assert_compile( - table2.select(order_by = [table2.c.otherid, asc(table2.c.othername)]), - "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername ASC" - ) - - self.assert_compile( - table2.select(order_by = [table2.c.otherid, table2.c.othername.desc()]), - "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername DESC" - ) - - # generative order_by - self.assert_compile( - table2.select().order_by(table2.c.otherid).order_by(table2.c.othername.desc()), - "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername DESC" - ) - - self.assert_compile( - table2.select().order_by(table2.c.otherid).order_by(table2.c.othername.desc()).order_by(None), - "SELECT myothertable.otherid, myothertable.othername FROM myothertable" - ) - - self.assert_compile( - select([table2.c.othername, func.count(table2.c.otherid)], group_by = [table2.c.othername]), - "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername" - ) - - # generative group by - self.assert_compile( - select([table2.c.othername, func.count(table2.c.otherid)]).group_by(table2.c.othername), - "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername" - ) - - self.assert_compile( - select([table2.c.othername, func.count(table2.c.otherid)]).group_by(table2.c.othername).group_by(None), - "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable" - ) - - self.assert_compile( - select([table2.c.othername, func.count(table2.c.otherid)], group_by = [table2.c.othername], order_by = [table2.c.othername]), - "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername ORDER BY myothertable.othername" - ) - - def test_for_update(self): - self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE") - - self.assert_compile(table1.select(table1.c.myid==7, for_update="nowait"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE") - - self.assert_compile(table1.select(table1.c.myid==7, for_update="nowait"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE NOWAIT", dialect=oracle.dialect()) - - self.assert_compile(table1.select(table1.c.myid==7, for_update="read"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = %s LOCK IN SHARE MODE", dialect=mysql.dialect()) - - self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = %s FOR UPDATE", dialect=mysql.dialect()) - - self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE", dialect=oracle.dialect()) - - def test_alias(self): - # test the alias for a table1. column names stay the same, table name "changes" to "foo". - self.assert_compile( - select([table1.alias('foo')]) - ,"SELECT foo.myid, foo.name, foo.description FROM mytable AS foo") - - for dialect in (firebird.dialect(), oracle.dialect()): - self.assert_compile( - select([table1.alias('foo')]) - ,"SELECT foo.myid, foo.name, foo.description FROM mytable foo" - ,dialect=dialect) - - self.assert_compile( - select([table1.alias()]) - ,"SELECT mytable_1.myid, mytable_1.name, mytable_1.description FROM mytable AS mytable_1") - - # create a select for a join of two tables. use_labels means the column names will have - # labels tablename_columnname, which become the column keys accessible off the Selectable object. - # also, only use one column from the second table and all columns from the first table1. - q = select([table1, table2.c.otherid], table1.c.myid == table2.c.otherid, use_labels = True) - - # make an alias of the "selectable". column names stay the same (i.e. the labels), table name "changes" to "t2view". - a = alias(q, 't2view') - - # select from that alias, also using labels. two levels of labels should produce two underscores. - # also, reference the column "mytable_myid" off of the t2view alias. - self.assert_compile( - a.select(a.c.mytable_myid == 9, use_labels = True), - "SELECT t2view.mytable_myid AS t2view_mytable_myid, t2view.mytable_name AS t2view_mytable_name, \ -t2view.mytable_description AS t2view_mytable_description, t2view.myothertable_otherid AS t2view_myothertable_otherid FROM \ -(SELECT mytable.myid AS mytable_myid, mytable.name AS mytable_name, mytable.description AS mytable_description, \ -myothertable.otherid AS myothertable_otherid FROM mytable, myothertable \ -WHERE mytable.myid = myothertable.otherid) AS t2view WHERE t2view.mytable_myid = :mytable_myid_1" - ) - - - def test_prefixes(self): - self.assert_compile(table1.select().prefix_with("SQL_CALC_FOUND_ROWS").prefix_with("SQL_SOME_WEIRD_MYSQL_THING"), - "SELECT SQL_CALC_FOUND_ROWS SQL_SOME_WEIRD_MYSQL_THING mytable.myid, mytable.name, mytable.description FROM mytable" - ) - - def test_text(self): - self.assert_compile( - text("select * from foo where lala = bar") , - "select * from foo where lala = bar" - ) - - # test bytestring - self.assert_compile(select( - ["foobar(a)", "pk_foo_bar(syslaal)"], - "a = 12", - from_obj = ["foobar left outer join lala on foobar.foo = lala.foo"] - ), - "SELECT foobar(a), pk_foo_bar(syslaal) FROM foobar left outer join lala on foobar.foo = lala.foo WHERE a = 12") - - # test unicode - self.assert_compile(select( - [u"foobar(a)", u"pk_foo_bar(syslaal)"], - u"a = 12", - from_obj = [u"foobar left outer join lala on foobar.foo = lala.foo"] - ), - u"SELECT foobar(a), pk_foo_bar(syslaal) FROM foobar left outer join lala on foobar.foo = lala.foo WHERE a = 12") - - # test building a select query programmatically with text - s = select() - s.append_column("column1") - s.append_column("column2") - s.append_whereclause("column1=12") - s.append_whereclause("column2=19") - s = s.order_by("column1") - s.append_from("table1") - self.assert_compile(s, "SELECT column1, column2 FROM table1 WHERE column1=12 AND column2=19 ORDER BY column1") - - self.assert_compile( - select(["column1", "column2"], from_obj=table1).alias('somealias').select(), - "SELECT somealias.column1, somealias.column2 FROM (SELECT column1, column2 FROM mytable) AS somealias" - ) - - # test that use_labels doesnt interfere with literal columns - self.assert_compile( - select(["column1", "column2", table1.c.myid], from_obj=table1, use_labels=True), - "SELECT column1, column2, mytable.myid AS mytable_myid FROM mytable" - ) - - # test that use_labels doesnt interfere with literal columns that have textual labels - self.assert_compile( - select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=table1, use_labels=True), - "SELECT column1 AS foobar, column2 AS hoho, mytable.myid AS mytable_myid FROM mytable" - ) - - print "---------------------------------------------" - s1 = select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=[table1]) - print "---------------------------------------------" - # test that "auto-labeling of subquery columns" doesnt interfere with literal columns, - # exported columns dont get quoted - self.assert_compile( - select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=[table1]).select(), - "SELECT column1 AS foobar, column2 AS hoho, myid FROM (SELECT column1 AS foobar, column2 AS hoho, mytable.myid AS myid FROM mytable)" - ) - - self.assert_compile( - select(['col1','col2'], from_obj='tablename').alias('myalias'), - "SELECT col1, col2 FROM tablename" - ) - - def test_binds_in_text(self): - self.assert_compile( - text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar', 4), bindparam('whee', 7)]), - "select * from foo where lala=:bar and hoho=:whee", - checkparams={'bar':4, 'whee': 7}, - ) - - self.assert_compile( - text("select * from foo where clock='05:06:07'"), - "select * from foo where clock='05:06:07'", - checkparams={}, - params={}, - ) - - dialect = postgres.dialect() - self.assert_compile( - text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar',4), bindparam('whee',7)]), - "select * from foo where lala=%(bar)s and hoho=%(whee)s", - checkparams={'bar':4, 'whee': 7}, - dialect=dialect - ) - - # test escaping out text() params with a backslash - self.assert_compile( - text("select * from foo where clock='05:06:07' and mork='\:mindy'"), - "select * from foo where clock='05:06:07' and mork=':mindy'", - checkparams={}, - params={}, - dialect=dialect - ) - - dialect = sqlite.dialect() - self.assert_compile( - text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar',4), bindparam('whee',7)]), - "select * from foo where lala=? and hoho=?", - checkparams={'bar':4, 'whee':7}, - dialect=dialect - ) - - self.assert_compile(select( - [table1, table2.c.otherid, "sysdate()", "foo, bar, lala"], - and_( - "foo.id = foofoo(lala)", - "datetime(foo) = Today", - table1.c.myid == table2.c.otherid, - ) - ), - "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, sysdate(), foo, bar, lala \ -FROM mytable, myothertable WHERE foo.id = foofoo(lala) AND datetime(foo) = Today AND mytable.myid = myothertable.otherid") - - self.assert_compile(select( - [alias(table1, 't'), "foo.f"], - "foo.f = t.id", - from_obj = ["(select f from bar where lala=heyhey) foo"] - ), - "SELECT t.myid, t.name, t.description, foo.f FROM mytable AS t, (select f from bar where lala=heyhey) foo WHERE foo.f = t.id") - - # test Text embedded within select_from(), using binds - generate_series = text("generate_series(:x, :y, :z) as s(a)", bindparams=[bindparam('x'), bindparam('y'), bindparam('z')]) - - s =select([(func.current_date() + literal_column("s.a")).label("dates")]).select_from(generate_series) - self.assert_compile(s, "SELECT CURRENT_DATE + s.a AS dates FROM generate_series(:x, :y, :z) as s(a)", checkparams={'y': None, 'x': None, 'z': None}) - - self.assert_compile(s.params(x=5, y=6, z=7), "SELECT CURRENT_DATE + s.a AS dates FROM generate_series(:x, :y, :z) as s(a)", checkparams={'y': 6, 'x': 5, 'z': 7}) - - - def test_literal(self): - - self.assert_compile(select([literal('foo')]), "SELECT :param_1") - - self.assert_compile(select([literal("foo") + literal("bar")], from_obj=[table1]), - "SELECT :param_1 || :param_2 AS anon_1 FROM mytable") - - def test_calculated_columns(self): - value_tbl = table('values', - column('id', Integer), - column('val1', Float), - column('val2', Float), - ) - - self.assert_compile( - select([value_tbl.c.id, (value_tbl.c.val2 - - value_tbl.c.val1)/value_tbl.c.val1]), - "SELECT values.id, (values.val2 - values.val1) / values.val1 AS anon_1 FROM values" - ) - - self.assert_compile( - select([value_tbl.c.id], (value_tbl.c.val2 - - value_tbl.c.val1)/value_tbl.c.val1 > 2.0), - "SELECT values.id FROM values WHERE (values.val2 - values.val1) / values.val1 > :param_1" - ) - - self.assert_compile( - select([value_tbl.c.id], value_tbl.c.val1 / (value_tbl.c.val2 - value_tbl.c.val1) /value_tbl.c.val1 > 2.0), - "SELECT values.id FROM values WHERE values.val1 / (values.val2 - values.val1) / values.val1 > :param_1" - ) - - def test_collate(self): - for expr in (select([table1.c.name.collate('latin1_german2_ci')]), - select([collate(table1.c.name, 'latin1_german2_ci')])): - self.assert_compile( - expr, "SELECT mytable.name COLLATE latin1_german2_ci AS anon_1 FROM mytable") - - assert table1.c.name.collate('latin1_german2_ci').type is table1.c.name.type - - expr = select([table1.c.name.collate('latin1_german2_ci').label('k1')]).order_by('k1') - self.assert_compile(expr,"SELECT mytable.name COLLATE latin1_german2_ci AS k1 FROM mytable ORDER BY k1") - - expr = select([collate('foo', 'latin1_german2_ci').label('k1')]) - self.assert_compile(expr,"SELECT :param_1 COLLATE latin1_german2_ci AS k1") - - expr = select([table1.c.name.collate('latin1_german2_ci').like('%x%')]) - self.assert_compile(expr, - "SELECT mytable.name COLLATE latin1_german2_ci " - "LIKE :param_1 AS anon_1 FROM mytable") - - expr = select([table1.c.name.like(collate('%x%', 'latin1_german2_ci'))]) - self.assert_compile(expr, - "SELECT mytable.name " - "LIKE :param_1 COLLATE latin1_german2_ci AS anon_1 " - "FROM mytable") - - expr = select([table1.c.name.collate('col1').like( - collate('%x%', 'col2'))]) - self.assert_compile(expr, - "SELECT mytable.name COLLATE col1 " - "LIKE :param_1 COLLATE col2 AS anon_1 " - "FROM mytable") - - expr = select([func.concat('a', 'b').collate('latin1_german2_ci').label('x')]) - self.assert_compile(expr, - "SELECT concat(:param_1, :param_2) " - "COLLATE latin1_german2_ci AS x") - - - expr = select([table1.c.name]).order_by(table1.c.name.collate('latin1_german2_ci')) - self.assert_compile(expr, "SELECT mytable.name FROM mytable ORDER BY mytable.name COLLATE latin1_german2_ci") - - def test_percent_chars(self): - t = table("table%name", - column("percent%"), - column("%(oneofthese)s"), - column("spaces % more spaces"), - ) - self.assert_compile( - t.select(use_labels=True), - '''SELECT "table%name"."percent%" AS "table%name_percent%", '''\ - '''"table%name"."%(oneofthese)s" AS "table%name_%(oneofthese)s", '''\ - '''"table%name"."spaces % more spaces" AS "table%name_spaces % more spaces" FROM "table%name"''' - ) - - - def test_joins(self): - self.assert_compile( - join(table2, table1, table1.c.myid == table2.c.otherid).select(), - "SELECT myothertable.otherid, myothertable.othername, mytable.myid, mytable.name, \ -mytable.description FROM myothertable JOIN mytable ON mytable.myid = myothertable.otherid" - ) - - self.assert_compile( - select( - [table1], - from_obj = [join(table1, table2, table1.c.myid == table2.c.otherid)] - ), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid") - - self.assert_compile( - select( - [join(join(table1, table2, table1.c.myid == table2.c.otherid), table3, table1.c.myid == table3.c.userid)] - ), - "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid JOIN thirdtable ON mytable.myid = thirdtable.userid" - ) - - self.assert_compile( - join(users, addresses, users.c.user_id==addresses.c.user_id).select(), - "SELECT users.user_id, users.user_name, users.password, addresses.address_id, addresses.user_id, addresses.street, addresses.city, addresses.state, addresses.zip FROM users JOIN addresses ON users.user_id = addresses.user_id" - ) - - self.assert_compile( - select([table1, table2, table3], - - from_obj = [join(table1, table2, table1.c.myid == table2.c.otherid).outerjoin(table3, table1.c.myid==table3.c.userid)] - - #from_obj = [outerjoin(join(table, table2, table1.c.myid == table2.c.otherid), table3, table1.c.myid==table3.c.userid)] - ) - ,"SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid LEFT OUTER JOIN thirdtable ON mytable.myid = thirdtable.userid" - ) - self.assert_compile( - select([table1, table2, table3], - from_obj = [outerjoin(table1, join(table2, table3, table2.c.otherid == table3.c.userid), table1.c.myid==table2.c.otherid)] - ) - ,"SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable LEFT OUTER JOIN (myothertable JOIN thirdtable ON myothertable.otherid = thirdtable.userid) ON mytable.myid = myothertable.otherid" - ) - - query = select( - [table1, table2], - or_( - table1.c.name == 'fred', - table1.c.myid == 10, - table2.c.othername != 'jack', - "EXISTS (select yay from foo where boo = lar)" - ), - from_obj = [ outerjoin(table1, table2, table1.c.myid == table2.c.otherid) ] - ) - self.assert_compile(query, - "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername \ -FROM mytable LEFT OUTER JOIN myothertable ON mytable.myid = myothertable.otherid \ -WHERE mytable.name = :name_1 OR mytable.myid = :myid_1 OR \ -myothertable.othername != :othername_1 OR \ -EXISTS (select yay from foo where boo = lar)", - ) - - def test_compound_selects(self): - try: - union(table3.select(), table1.select()) - except exc.ArgumentError, err: - assert str(err) == "All selectables passed to CompoundSelect must have identical numbers of columns; select #1 has 2 columns, select #2 has 3" - - x = union( - select([table1], table1.c.myid == 5), - select([table1], table1.c.myid == 12), - order_by = [table1.c.myid], - ) - - self.assert_compile(x, "SELECT mytable.myid, mytable.name, mytable.description \ -FROM mytable WHERE mytable.myid = :myid_1 UNION \ -SELECT mytable.myid, mytable.name, mytable.description \ -FROM mytable WHERE mytable.myid = :myid_2 ORDER BY mytable.myid") - - u1 = union( - select([table1.c.myid, table1.c.name]), - select([table2]), - select([table3]) - ) - self.assert_compile(u1, - "SELECT mytable.myid, mytable.name \ -FROM mytable UNION SELECT myothertable.otherid, myothertable.othername \ -FROM myothertable UNION SELECT thirdtable.userid, thirdtable.otherstuff FROM thirdtable") - - assert u1.corresponding_column(table2.c.otherid) is u1.c.myid - - # TODO - why is there an extra space before the LIMIT ? - self.assert_compile( - union( - select([table1.c.myid, table1.c.name]), - select([table2]), - order_by=['myid'], - offset=10, - limit=5 - ) - , "SELECT mytable.myid, mytable.name \ -FROM mytable UNION SELECT myothertable.otherid, myothertable.othername \ -FROM myothertable ORDER BY myid LIMIT 5 OFFSET 10" - ) - - self.assert_compile( - union( - select([table1.c.myid, table1.c.name, func.max(table1.c.description)], table1.c.name=='name2', group_by=[table1.c.myid, table1.c.name]), - table1.select(table1.c.name=='name1') - ) - , - "SELECT mytable.myid, mytable.name, max(mytable.description) AS max_1 FROM mytable \ -WHERE mytable.name = :name_1 GROUP BY mytable.myid, mytable.name UNION SELECT mytable.myid, mytable.name, mytable.description \ -FROM mytable WHERE mytable.name = :name_2" - ) - - self.assert_compile( - union( - select([literal(100).label('value')]), - select([literal(200).label('value')]) - ), - "SELECT :param_1 AS value UNION SELECT :param_2 AS value" - ) - - self.assert_compile( - union_all( - select([table1.c.myid]), - union( - select([table2.c.otherid]), - select([table3.c.userid]), - ) - ) - , - "SELECT mytable.myid FROM mytable UNION ALL (SELECT myothertable.otherid FROM myothertable UNION \ -SELECT thirdtable.userid FROM thirdtable)" - ) - # This doesn't need grouping, so don't group to not give sqlite unnecessarily hard time - self.assert_compile( - union( - except_( - select([table2.c.otherid]), - select([table3.c.userid]), - ), - select([table1.c.myid]) - ) - , - "SELECT myothertable.otherid FROM myothertable EXCEPT SELECT thirdtable.userid FROM thirdtable \ -UNION SELECT mytable.myid FROM mytable" - ) - - s = select([column('foo'), column('bar')]) - s = union(s, s) - s = union(s, s) - self.assert_compile(s, "SELECT foo, bar UNION SELECT foo, bar UNION (SELECT foo, bar UNION SELECT foo, bar)") - - s = select([column('foo'), column('bar')]) - # ORDER BY's even though not supported by all DB's, are rendered if requested - self.assert_compile(union(s.order_by("foo"), s.order_by("bar")), - "SELECT foo, bar ORDER BY foo UNION SELECT foo, bar ORDER BY bar" - ) - # self_group() is honored - self.assert_compile(union(s.order_by("foo").self_group(), s.order_by("bar").limit(10).self_group()), - "(SELECT foo, bar ORDER BY foo) UNION (SELECT foo, bar ORDER BY bar LIMIT 10)" - ) - - - @testing.uses_deprecated() - def test_binds(self): - for ( - stmt, - expected_named_stmt, - expected_positional_stmt, - expected_default_params_dict, - expected_default_params_list, - test_param_dict, - expected_test_params_dict, - expected_test_params_list - ) in [ - ( - select( - [table1, table2], - and_( - table1.c.myid == table2.c.otherid, - table1.c.name == bindparam('mytablename') - )), - """SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable.name = :mytablename""", - """SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable.name = ?""", - {'mytablename':None}, [None], - {'mytablename':5}, {'mytablename':5}, [5] - ), - ( - select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myid'))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid", - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", - {'myid':None}, [None, None], - {'myid':5}, {'myid':5}, [5,5] - ), - ( - text("SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid"), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid", - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", - {'myid':None}, [None, None], - {'myid':5}, {'myid':5}, [5,5] - ), - ( - select([table1], or_(table1.c.myid==bindparam('myid', unique=True), table2.c.otherid==bindparam('myid', unique=True))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid_1 OR myothertable.otherid = :myid_2", - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", - {'myid_1':None, 'myid_2':None}, [None, None], - {'myid_1':5, 'myid_2': 6}, {'myid_1':5, 'myid_2':6}, [5,6] - ), - ( - bindparam('test', type_=String) + text("'hi'"), - ":test || 'hi'", - "? || 'hi'", - {'test':None}, [None], - {}, {'test':None}, [None] - ), - ( - select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myotherid'))).params({'myid':8, 'myotherid':7}), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myotherid", - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", - {'myid':8, 'myotherid':7}, [8, 7], - {'myid':5}, {'myid':5, 'myotherid':7}, [5,7] - ), - ( - select([table1], or_(table1.c.myid==bindparam('myid', value=7, unique=True), table2.c.otherid==bindparam('myid', value=8, unique=True))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid_1 OR myothertable.otherid = :myid_2", - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", - {'myid_1':7, 'myid_2':8}, [7,8], - {'myid_1':5, 'myid_2':6}, {'myid_1':5, 'myid_2':6}, [5,6] - ), - ]: - - self.assert_compile(stmt, expected_named_stmt, params=expected_default_params_dict) - self.assert_compile(stmt, expected_positional_stmt, dialect=sqlite.dialect()) - nonpositional = stmt.compile() - positional = stmt.compile(dialect=sqlite.dialect()) - pp = positional.get_params() - assert [pp[k] for k in positional.positiontup] == expected_default_params_list - assert nonpositional.get_params(**test_param_dict) == expected_test_params_dict, "expected :%s got %s" % (str(expected_test_params_dict), str(nonpositional.get_params(**test_param_dict))) - pp = positional.get_params(**test_param_dict) - assert [pp[k] for k in positional.positiontup] == expected_test_params_list - - # check that params() doesnt modify original statement - s = select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myotherid'))) - s2 = s.params({'myid':8, 'myotherid':7}) - s3 = s2.params({'myid':9}) - assert s.compile().params == {'myid':None, 'myotherid':None} - assert s2.compile().params == {'myid':8, 'myotherid':7} - assert s3.compile().params == {'myid':9, 'myotherid':7} - - # test using same 'unique' param object twice in one compile - s = select([table1.c.myid]).where(table1.c.myid==12).as_scalar() - s2 = select([table1, s], table1.c.myid==s) - self.assert_compile(s2, - "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable WHERE mytable.myid = "\ - ":myid_1) AS anon_1 FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1)") - positional = s2.compile(dialect=sqlite.dialect()) - - pp = positional.get_params() - assert [pp[k] for k in positional.positiontup] == [12, 12] - - # check that conflicts with "unique" params are caught - s = select([table1], or_(table1.c.myid==7, table1.c.myid==bindparam('myid_1'))) - self.assertRaisesMessage(exc.CompileError, "conflicts with unique bind parameter of the same name", str, s) - - s = select([table1], or_(table1.c.myid==7, table1.c.myid==8, table1.c.myid==bindparam('myid_1'))) - self.assertRaisesMessage(exc.CompileError, "conflicts with unique bind parameter of the same name", str, s) - - def test_binds_no_hash_collision(self): - """test that construct_params doesn't corrupt dict due to hash collisions""" - - total_params = 100000 - - in_clause = [':in%d' % i for i in range(total_params)] - params = dict(('in%d' % i, i) for i in range(total_params)) - sql = 'text clause %s' % ', '.join(in_clause) - t = text(sql) - assert len(t.bindparams) == total_params - c = t.compile() - pp = c.construct_params(params) - assert len(set(pp)) == total_params - assert len(set(pp.values())) == total_params - - - def test_bind_as_col(self): - t = table('foo', column('id')) - - s = select([t, literal('lala').label('hoho')]) - self.assert_compile(s, "SELECT foo.id, :param_1 AS hoho FROM foo") - - assert [str(c) for c in s.c] == ["id", "hoho"] - - def test_in(self): - self.assert_compile(select([table1], table1.c.myid.in_(['a'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1)") - - self.assert_compile(select([table1], ~table1.c.myid.in_(['a'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid NOT IN (:myid_1)") - - self.assert_compile(select([table1], table1.c.myid.in_(['a', 'b'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2)") - - self.assert_compile(select([table1], table1.c.myid.in_(iter(['a', 'b']))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a')])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), 'b'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :myid_1)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), literal('b')])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :param_2)") - - self.assert_compile(select([table1], table1.c.myid.in_(['a', literal('b')])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :param_1)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal(1) + 'a'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 + :param_2)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a') +'a', 'b'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 || :param_2, :myid_1)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a') + literal('a'), literal('b')])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 || :param_2, :param_3)") - - self.assert_compile(select([table1], table1.c.myid.in_([1, literal(3) + 4])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :param_1 + :param_2)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a') < 'b'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 < :param_2)") - - self.assert_compile(select([table1], table1.c.myid.in_([table1.c.myid])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (mytable.myid)") - - self.assert_compile(select([table1], table1.c.myid.in_(['a', table1.c.myid])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, mytable.myid)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), table1.c.myid])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, mytable.myid)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), table1.c.myid +'a'])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, mytable.myid + :myid_1)") - - self.assert_compile(select([table1], table1.c.myid.in_([literal(1), 'a' + table1.c.myid])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :myid_1 + mytable.myid)") - - self.assert_compile(select([table1], table1.c.myid.in_([1, 2, 3])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2, :myid_3)") - - self.assert_compile(select([table1], table1.c.myid.in_(select([table2.c.otherid]))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (SELECT myothertable.otherid FROM myothertable)") - - self.assert_compile(select([table1], ~table1.c.myid.in_(select([table2.c.otherid]))), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid NOT IN (SELECT myothertable.otherid FROM myothertable)") - - self.assert_compile(select([table1], table1.c.myid.in_( - union( - select([table1.c.myid], table1.c.myid == 5), - select([table1.c.myid], table1.c.myid == 12), - ) - )), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable \ -WHERE mytable.myid IN (\ -SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1 \ -UNION SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_2)") - - # test that putting a select in an IN clause does not blow away its ORDER BY clause - self.assert_compile( - select([table1, table2], - table2.c.otherid.in_( - select([table2.c.otherid], order_by=[table2.c.othername], limit=10, correlate=False) - ), - from_obj=[table1.join(table2, table1.c.myid==table2.c.otherid)], order_by=[table1.c.myid] - ), - "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable "\ - "JOIN myothertable ON mytable.myid = myothertable.otherid WHERE myothertable.otherid IN (SELECT myothertable.otherid "\ - "FROM myothertable ORDER BY myothertable.othername LIMIT 10) ORDER BY mytable.myid" - ) - - # test empty in clause - self.assert_compile(select([table1], table1.c.myid.in_([])), - "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != mytable.myid") - - self.assert_compile( - select([table1.c.myid.in_(select([table2.c.otherid]))]), - "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable" - ) - self.assert_compile( - select([table1.c.myid.in_(select([table2.c.otherid]).as_scalar())]), - "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable" - ) - - def test_cast(self): - tbl = table('casttest', - column('id', Integer), - column('v1', Float), - column('v2', Float), - column('ts', TIMESTAMP), - ) - - def check_results(dialect, expected_results, literal): - self.assertEqual(len(expected_results), 5, 'Incorrect number of expected results') - self.assertEqual(str(cast(tbl.c.v1, Numeric).compile(dialect=dialect)), 'CAST(casttest.v1 AS %s)' %expected_results[0]) - self.assertEqual(str(cast(tbl.c.v1, Numeric(12, 9)).compile(dialect=dialect)), 'CAST(casttest.v1 AS %s)' %expected_results[1]) - self.assertEqual(str(cast(tbl.c.ts, Date).compile(dialect=dialect)), 'CAST(casttest.ts AS %s)' %expected_results[2]) - self.assertEqual(str(cast(1234, TEXT).compile(dialect=dialect)), 'CAST(%s AS %s)' %(literal, expected_results[3])) - self.assertEqual(str(cast('test', String(20)).compile(dialect=dialect)), 'CAST(%s AS %s)' %(literal, expected_results[4])) - # fixme: shoving all of this dialect-specific stuff in one test - # is now officialy completely ridiculous AND non-obviously omits - # coverage on other dialects. - sel = select([tbl, cast(tbl.c.v1, Numeric)]).compile(dialect=dialect) - if isinstance(dialect, type(mysql.dialect())): - self.assertEqual(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS DECIMAL(10, 2)) AS anon_1 \nFROM casttest") - else: - self.assertEqual(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS NUMERIC(10, 2)) AS anon_1 \nFROM casttest") - - # first test with Postgres engine - check_results(postgres.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%(param_1)s') - - # then the Oracle engine - check_results(oracle.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'CLOB', 'VARCHAR(20)'], ':param_1') - - # then the sqlite engine - check_results(sqlite.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '?') - - # then the MySQL engine - check_results(mysql.dialect(), ['DECIMAL(10, 2)', 'DECIMAL(12, 9)', 'DATE', 'CHAR', 'CHAR(20)'], '%s') - - self.assert_compile(cast(text('NULL'), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) - self.assert_compile(cast(null(), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) - self.assert_compile(cast(literal_column('NULL'), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) - - def test_date_between(self): - import datetime - table = Table('dt', metadata, - Column('date', Date)) - self.assert_compile(table.select(table.c.date.between(datetime.date(2006,6,1), datetime.date(2006,6,5))), - "SELECT dt.date FROM dt WHERE dt.date BETWEEN :date_1 AND :date_2", checkparams={'date_1':datetime.date(2006,6,1), 'date_2':datetime.date(2006,6,5)}) - - self.assert_compile(table.select(sql.between(table.c.date, datetime.date(2006,6,1), datetime.date(2006,6,5))), - "SELECT dt.date FROM dt WHERE dt.date BETWEEN :param_1 AND :param_2", checkparams={'param_1':datetime.date(2006,6,1), 'param_2':datetime.date(2006,6,5)}) - - def test_operator_precedence(self): - table = Table('op', metadata, - Column('field', Integer)) - self.assert_compile(table.select((table.c.field == 5) == None), - "SELECT op.field FROM op WHERE (op.field = :field_1) IS NULL") - self.assert_compile(table.select((table.c.field + 5) == table.c.field), - "SELECT op.field FROM op WHERE op.field + :field_1 = op.field") - self.assert_compile(table.select((table.c.field + 5) * 6), - "SELECT op.field FROM op WHERE (op.field + :field_1) * :param_1") - self.assert_compile(table.select((table.c.field * 5) + 6), - "SELECT op.field FROM op WHERE op.field * :field_1 + :param_1") - self.assert_compile(table.select(5 + table.c.field.in_([5,6])), - "SELECT op.field FROM op WHERE :param_1 + (op.field IN (:field_1, :field_2))") - self.assert_compile(table.select((5 + table.c.field).in_([5,6])), - "SELECT op.field FROM op WHERE :field_1 + op.field IN (:param_1, :param_2)") - self.assert_compile(table.select(not_(and_(table.c.field == 5, table.c.field == 7))), - "SELECT op.field FROM op WHERE NOT (op.field = :field_1 AND op.field = :field_2)") - self.assert_compile(table.select(not_(table.c.field == 5)), - "SELECT op.field FROM op WHERE op.field != :field_1") - self.assert_compile(table.select(not_(table.c.field.between(5, 6))), - "SELECT op.field FROM op WHERE NOT (op.field BETWEEN :field_1 AND :field_2)") - self.assert_compile(table.select(not_(table.c.field) == 5), - "SELECT op.field FROM op WHERE (NOT op.field) = :param_1") - self.assert_compile(table.select((table.c.field == table.c.field).between(False, True)), - "SELECT op.field FROM op WHERE (op.field = op.field) BETWEEN :param_1 AND :param_2") - self.assert_compile(table.select(between((table.c.field == table.c.field), False, True)), - "SELECT op.field FROM op WHERE (op.field = op.field) BETWEEN :param_1 AND :param_2") - - def test_naming(self): - s1 = select([table1.c.myid, table1.c.myid.label('foobar'), func.hoho(table1.c.name), func.lala(table1.c.name).label('gg')]) - assert s1.c.keys() == ['myid', 'foobar', 'hoho(mytable.name)', 'gg'] - - from sqlalchemy.databases.sqlite import SLNumeric - meta = MetaData() - t1 = Table('mytable', meta, Column('col1', Integer)) - - for col, key, expr, label in ( - (table1.c.name, 'name', 'mytable.name', None), - (table1.c.myid==12, 'mytable.myid = :myid_1', 'mytable.myid = :myid_1', 'anon_1'), - (func.hoho(table1.c.myid), 'hoho(mytable.myid)', 'hoho(mytable.myid)', 'hoho_1'), - (cast(table1.c.name, SLNumeric), 'CAST(mytable.name AS NUMERIC(10, 2))', 'CAST(mytable.name AS NUMERIC(10, 2))', 'anon_1'), - (t1.c.col1, 'col1', 'mytable.col1', None), - (column('some wacky thing'), 'some wacky thing', '"some wacky thing"', '') - ): - s1 = select([col], from_obj=getattr(col, 'table', None) or table1) - assert s1.c.keys() == [key], s1.c.keys() - - if label: - self.assert_compile(s1, "SELECT %s AS %s FROM mytable" % (expr, label)) - else: - self.assert_compile(s1, "SELECT %s FROM mytable" % (expr,)) - - s1 = select([s1]) - if label: - self.assert_compile(s1, "SELECT %s FROM (SELECT %s AS %s FROM mytable)" % (label, expr, label)) - elif col.table is not None: - # sqlite rule labels subquery columns - self.assert_compile(s1, "SELECT %s FROM (SELECT %s AS %s FROM mytable)" % (key,expr, key)) - else: - self.assert_compile(s1, "SELECT %s FROM (SELECT %s FROM mytable)" % (expr,expr)) - -class CRUDTest(TestBase, AssertsCompiledSQL): - def test_insert(self): - # generic insert, will create bind params for all columns - self.assert_compile(insert(table1), "INSERT INTO mytable (myid, name, description) VALUES (:myid, :name, :description)") - - # insert with user-supplied bind params for specific columns, - # cols provided literally - self.assert_compile( - insert(table1, {table1.c.myid : bindparam('userid'), table1.c.name : bindparam('username')}), - "INSERT INTO mytable (myid, name) VALUES (:userid, :username)") - - # insert with user-supplied bind params for specific columns, cols - # provided as strings - self.assert_compile( - insert(table1, dict(myid = 3, name = 'jack')), - "INSERT INTO mytable (myid, name) VALUES (:myid, :name)" - ) - - # test with a tuple of params instead of named - self.assert_compile( - insert(table1, (3, 'jack', 'mydescription')), - "INSERT INTO mytable (myid, name, description) VALUES (:myid, :name, :description)", - checkparams = {'myid':3, 'name':'jack', 'description':'mydescription'} - ) - - self.assert_compile( - insert(table1, values={table1.c.myid : bindparam('userid')}).values({table1.c.name : bindparam('username')}), - "INSERT INTO mytable (myid, name) VALUES (:userid, :username)" - ) - - self.assert_compile(insert(table1, values=dict(myid=func.lala())), "INSERT INTO mytable (myid) VALUES (lala())") - - def test_inline_insert(self): - metadata = MetaData() - table = Table('sometable', metadata, - Column('id', Integer, primary_key=True), - Column('foo', Integer, default=func.foobar())) - self.assert_compile(table.insert(values={}, inline=True), "INSERT INTO sometable (foo) VALUES (foobar())") - self.assert_compile(table.insert(inline=True), "INSERT INTO sometable (foo) VALUES (foobar())", params={}) - - def test_update(self): - self.assert_compile(update(table1, table1.c.myid == 7), "UPDATE mytable SET name=:name WHERE mytable.myid = :myid_1", params = {table1.c.name:'fred'}) - self.assert_compile(table1.update().where(table1.c.myid==7).values({table1.c.myid:5}), "UPDATE mytable SET myid=:myid WHERE mytable.myid = :myid_1", checkparams={'myid':5, 'myid_1':7}) - self.assert_compile(update(table1, table1.c.myid == 7), "UPDATE mytable SET name=:name WHERE mytable.myid = :myid_1", params = {'name':'fred'}) - self.assert_compile(update(table1, values = {table1.c.name : table1.c.myid}), "UPDATE mytable SET name=mytable.myid") - self.assert_compile(update(table1, whereclause = table1.c.name == bindparam('crit'), values = {table1.c.name : 'hi'}), "UPDATE mytable SET name=:name WHERE mytable.name = :crit", params = {'crit' : 'notthere'}, checkparams={'crit':'notthere', 'name':'hi'}) - self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.name : table1.c.myid}), "UPDATE mytable SET name=mytable.myid, description=:description WHERE mytable.myid = :myid_1", params = {'description':'test'}, checkparams={'description':'test', 'myid_1':12}) - self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.myid : 9}), "UPDATE mytable SET myid=:myid, description=:description WHERE mytable.myid = :myid_1", params = {'myid_1': 12, 'myid': 9, 'description': 'test'}) - self.assert_compile(update(table1, table1.c.myid ==12), "UPDATE mytable SET myid=:myid WHERE mytable.myid = :myid_1", params={'myid':18}, checkparams={'myid':18, 'myid_1':12}) - s = table1.update(table1.c.myid == 12, values = {table1.c.name : 'lala'}) - c = s.compile(column_keys=['id', 'name']) - self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.name : table1.c.myid}).values({table1.c.name:table1.c.name + 'foo'}), "UPDATE mytable SET name=(mytable.name || :name_1), description=:description WHERE mytable.myid = :myid_1", params = {'description':'test'}) - self.assert_(str(s) == str(c)) - - self.assert_compile(update(table1, - (table1.c.myid == func.hoho(4)) & - (table1.c.name == literal('foo') + table1.c.name + literal('lala')), - values = { - table1.c.name : table1.c.name + "lala", - table1.c.myid : func.do_stuff(table1.c.myid, literal('hoho')) - }), "UPDATE mytable SET myid=do_stuff(mytable.myid, :param_1), name=(mytable.name || :name_1) " - "WHERE mytable.myid = hoho(:hoho_1) AND mytable.name = :param_2 || mytable.name || :param_3") - - def test_correlated_update(self): - # test against a straight text subquery - u = update(table1, values = {table1.c.name : text("(select name from mytable where id=mytable.id)")}) - self.assert_compile(u, "UPDATE mytable SET name=(select name from mytable where id=mytable.id)") - - mt = table1.alias() - u = update(table1, values = {table1.c.name : select([mt.c.name], mt.c.myid==table1.c.myid)}) - self.assert_compile(u, "UPDATE mytable SET name=(SELECT mytable_1.name FROM mytable AS mytable_1 WHERE mytable_1.myid = mytable.myid)") - - # test against a regular constructed subquery - s = select([table2], table2.c.otherid == table1.c.myid) - u = update(table1, table1.c.name == 'jack', values = {table1.c.name : s}) - self.assert_compile(u, "UPDATE mytable SET name=(SELECT myothertable.otherid, myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid) WHERE mytable.name = :name_1") - - # test a non-correlated WHERE clause - s = select([table2.c.othername], table2.c.otherid == 7) - u = update(table1, table1.c.name==s) - self.assert_compile(u, "UPDATE mytable SET myid=:myid, name=:name, description=:description WHERE mytable.name = "\ - "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = :otherid_1)") - - # test one that is actually correlated... - s = select([table2.c.othername], table2.c.otherid == table1.c.myid) - u = table1.update(table1.c.name==s) - self.assert_compile(u, "UPDATE mytable SET myid=:myid, name=:name, description=:description WHERE mytable.name = "\ - "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid)") - - def test_delete(self): - self.assert_compile(delete(table1, table1.c.myid == 7), "DELETE FROM mytable WHERE mytable.myid = :myid_1") - self.assert_compile(table1.delete().where(table1.c.myid == 7), "DELETE FROM mytable WHERE mytable.myid = :myid_1") - self.assert_compile(table1.delete().where(table1.c.myid == 7).where(table1.c.name=='somename'), "DELETE FROM mytable WHERE mytable.myid = :myid_1 AND mytable.name = :name_1") - - def test_correlated_delete(self): - # test a non-correlated WHERE clause - s = select([table2.c.othername], table2.c.otherid == 7) - u = delete(table1, table1.c.name==s) - self.assert_compile(u, "DELETE FROM mytable WHERE mytable.name = "\ - "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = :otherid_1)") - - # test one that is actually correlated... - s = select([table2.c.othername], table2.c.otherid == table1.c.myid) - u = table1.delete(table1.c.name==s) - self.assert_compile(u, "DELETE FROM mytable WHERE mytable.name = (SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid)") - -class InlineDefaultTest(TestBase, AssertsCompiledSQL): - def test_insert(self): - m = MetaData() - foo = Table('foo', m, - Column('id', Integer)) - - t = Table('test', m, - Column('col1', Integer, default=func.foo(1)), - Column('col2', Integer, default=select([func.coalesce(func.max(foo.c.id))])), - ) - - self.assert_compile(t.insert(inline=True, values={}), "INSERT INTO test (col1, col2) VALUES (foo(:foo_1), (SELECT coalesce(max(foo.id)) AS coalesce_1 FROM foo))") - - def test_update(self): - m = MetaData() - foo = Table('foo', m, - Column('id', Integer)) - - t = Table('test', m, - Column('col1', Integer, onupdate=func.foo(1)), - Column('col2', Integer, onupdate=select([func.coalesce(func.max(foo.c.id))])), - Column('col3', String(30)) - ) - - self.assert_compile(t.update(inline=True, values={'col3':'foo'}), "UPDATE test SET col1=foo(:foo_1), col2=(SELECT coalesce(max(foo.id)) AS coalesce_1 FROM foo), col3=:col3") - -class SchemaTest(TestBase, AssertsCompiledSQL): - def test_select(self): - # these tests will fail with the MS-SQL compiler since it will alias schema-qualified tables - self.assert_compile(table4.select(), "SELECT remote_owner.remotetable.rem_id, remote_owner.remotetable.datatype_id, remote_owner.remotetable.value FROM remote_owner.remotetable") - self.assert_compile(table4.select(and_(table4.c.datatype_id==7, table4.c.value=='hi')), - "SELECT remote_owner.remotetable.rem_id, remote_owner.remotetable.datatype_id, remote_owner.remotetable.value FROM remote_owner.remotetable WHERE "\ - "remote_owner.remotetable.datatype_id = :datatype_id_1 AND remote_owner.remotetable.value = :value_1") - - s = table4.select(and_(table4.c.datatype_id==7, table4.c.value=='hi')) - s.use_labels = True - self.assert_compile(s, "SELECT remote_owner.remotetable.rem_id AS remote_owner_remotetable_rem_id, remote_owner.remotetable.datatype_id AS remote_owner_remotetable_datatype_id, remote_owner.remotetable.value "\ - "AS remote_owner_remotetable_value FROM remote_owner.remotetable WHERE "\ - "remote_owner.remotetable.datatype_id = :datatype_id_1 AND remote_owner.remotetable.value = :value_1") - - def test_alias(self): - a = alias(table4, 'remtable') - self.assert_compile(a.select(a.c.datatype_id==7), "SELECT remtable.rem_id, remtable.datatype_id, remtable.value FROM remote_owner.remotetable AS remtable "\ - "WHERE remtable.datatype_id = :datatype_id_1") - - def test_update(self): - self.assert_compile(table4.update(table4.c.value=='test', values={table4.c.datatype_id:12}), "UPDATE remote_owner.remotetable SET datatype_id=:datatype_id "\ - "WHERE remote_owner.remotetable.value = :value_1") - - def test_insert(self): - self.assert_compile(table4.insert(values=(2, 5, 'test')), "INSERT INTO remote_owner.remotetable (rem_id, datatype_id, value) VALUES "\ - "(:rem_id, :datatype_id, :value)") - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/selectable.py b/test/sql/selectable.py deleted file mode 100755 index e9ed5f565..000000000 --- a/test/sql/selectable.py +++ /dev/null @@ -1,526 +0,0 @@ -"""Test various algorithmic properties of selectables.""" - -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from testlib import * -from sqlalchemy.sql import util as sql_util, visitors -from sqlalchemy import exc -from sqlalchemy.sql import table, column -from sqlalchemy import util - -metadata = MetaData() -table1 = Table('table1', metadata, - Column('col1', Integer, primary_key=True), - Column('col2', String(20)), - Column('col3', Integer), - Column('colx', Integer), - -) - -table2 = Table('table2', metadata, - Column('col1', Integer, primary_key=True), - Column('col2', Integer, ForeignKey('table1.col1')), - Column('col3', String(20)), - Column('coly', Integer), -) - -class SelectableTest(TestBase, AssertsExecutionResults): - def test_distance_on_labels(self): - # same column three times - s = select([table1.c.col1.label('c2'), table1.c.col1, table1.c.col1.label('c1')]) - - # didnt do this yet...col.label().make_proxy() has same "distance" as col.make_proxy() so far - #assert s.corresponding_column(table1.c.col1) is s.c.col1 - assert s.corresponding_column(s.c.col1) is s.c.col1 - assert s.corresponding_column(s.c.c1) is s.c.c1 - - def test_distance_on_aliases(self): - a1 = table1.alias('a1') - - for s in ( - select([a1, table1], use_labels=True), - select([table1, a1], use_labels=True) - ): - assert s.corresponding_column(table1.c.col1) is s.c.table1_col1 - assert s.corresponding_column(a1.c.col1) is s.c.a1_col1 - - - def test_join_against_self(self): - jj = select([table1.c.col1.label('bar_col1')]) - jjj = join(table1, jj, table1.c.col1==jj.c.bar_col1) - - # test column directly agaisnt itself - assert jjj.corresponding_column(jjj.c.table1_col1) is jjj.c.table1_col1 - - assert jjj.corresponding_column(jj.c.bar_col1) is jjj.c.bar_col1 - - # test alias of the join - j2 = jjj.alias('foo') - assert j2.corresponding_column(table1.c.col1) is j2.c.table1_col1 - - def test_select_on_table(self): - sel = select([table1, table2], use_labels=True) - assert sel.corresponding_column(table1.c.col1) is sel.c.table1_col1 - assert sel.corresponding_column(table1.c.col1, require_embedded=True) is sel.c.table1_col1 - assert table1.corresponding_column(sel.c.table1_col1) is table1.c.col1 - assert table1.corresponding_column(sel.c.table1_col1, require_embedded=True) is None - - def test_join_against_join(self): - j = outerjoin(table1, table2, table1.c.col1==table2.c.col2) - jj = select([ table1.c.col1.label('bar_col1')],from_obj=[j]).alias('foo') - jjj = join(table1, jj, table1.c.col1==jj.c.bar_col1) - assert jjj.corresponding_column(jjj.c.table1_col1) is jjj.c.table1_col1 - - j2 = jjj.alias('foo') - assert j2.corresponding_column(jjj.c.table1_col1) is j2.c.table1_col1 - - assert jjj.corresponding_column(jj.c.bar_col1) is jj.c.bar_col1 - - def test_table_alias(self): - a = table1.alias('a') - - j = join(a, table2) - - criterion = a.c.col1 == table2.c.col2 - self.assert_(criterion.compare(j.onclause)) - - def test_union(self): - # tests that we can correspond a column in a Select statement with a certain Table, against - # a column in a Union where one of its underlying Selects matches to that same Table - u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( - select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) - ) - s1 = table1.select(use_labels=True) - s2 = table2.select(use_labels=True) - c = u.corresponding_column(s1.c.table1_col2) - assert u.corresponding_column(s1.c.table1_col2) is u.c.col2 - assert u.corresponding_column(s2.c.table2_col2) is u.c.col2 - - def test_union_precedence(self): - # conflicting column correspondence should be resolved based on - # the order of the select()s in the union - - s1 = select([table1.c.col1, table1.c.col2]) - s2 = select([table1.c.col2, table1.c.col1]) - s3 = select([table1.c.col3, table1.c.colx]) - s4 = select([table1.c.colx, table1.c.col3]) - - u1 = union(s1, s2) - assert u1.corresponding_column(table1.c.col1) is u1.c.col1 - assert u1.corresponding_column(table1.c.col2) is u1.c.col2 - - u1 = union(s1, s2, s3, s4) - assert u1.corresponding_column(table1.c.col1) is u1.c.col1 - assert u1.corresponding_column(table1.c.col2) is u1.c.col2 - assert u1.corresponding_column(table1.c.colx) is u1.c.col2 - assert u1.corresponding_column(table1.c.col3) is u1.c.col1 - - def test_singular_union(self): - u = union(select([table1.c.col1, table1.c.col2, table1.c.col3]), select([table1.c.col1, table1.c.col2, table1.c.col3])) - - u = union(select([table1.c.col1, table1.c.col2, table1.c.col3])) - assert u.c.col1 - assert u.c.col2 - assert u.c.col3 - - def test_alias_union(self): - # same as testunion, except its an alias of the union - u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( - select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) - ).alias('analias') - s1 = table1.select(use_labels=True) - s2 = table2.select(use_labels=True) - assert u.corresponding_column(s1.c.table1_col2) is u.c.col2 - assert u.corresponding_column(s2.c.table2_col2) is u.c.col2 - assert u.corresponding_column(s2.c.table2_coly) is u.c.coly - assert s2.corresponding_column(u.c.coly) is s2.c.table2_coly - - def test_select_union(self): - # like testaliasunion, but off a Select off the union. - u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( - select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) - ).alias('analias') - s = select([u]) - s1 = table1.select(use_labels=True) - s2 = table2.select(use_labels=True) - assert s.corresponding_column(s1.c.table1_col2) is s.c.col2 - assert s.corresponding_column(s2.c.table2_col2) is s.c.col2 - - def test_union_against_join(self): - # same as testunion, except its an alias of the union - u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( - select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) - ).alias('analias') - j1 = table1.join(table2) - assert u.corresponding_column(j1.c.table1_colx) is u.c.colx - assert j1.corresponding_column(u.c.colx) is j1.c.table1_colx - - def test_join(self): - a = join(table1, table2) - print str(a.select(use_labels=True)) - b = table2.alias('b') - j = join(a, b) - print str(j) - criterion = a.c.table1_col1 == b.c.col2 - self.assert_(criterion.compare(j.onclause)) - - def test_select_alias(self): - a = table1.select().alias('a') - j = join(a, table2) - - criterion = a.c.col1 == table2.c.col2 - self.assert_(criterion.compare(j.onclause)) - - def test_select_labels(self): - a = table1.select(use_labels=True) - j = join(a, table2) - - criterion = a.c.table1_col1 == table2.c.col2 - self.assert_(criterion.compare(j.onclause)) - - def test_column_labels(self): - a = select([table1.c.col1.label('acol1'), table1.c.col2.label('acol2'), table1.c.col3.label('acol3')]) - j = join(a, table2) - criterion = a.c.acol1 == table2.c.col2 - self.assert_(criterion.compare(j.onclause)) - - def test_labeled_select_correspoinding(self): - l1 = select([func.max(table1.c.col1)]).label('foo') - - s = select([l1]) - assert s.corresponding_column(l1).name == s.c.foo - - s = select([table1.c.col1, l1]) - assert s.corresponding_column(l1).name == s.c.foo - - def test_select_alias_labels(self): - a = table2.select(use_labels=True).alias('a') - j = join(a, table1) - - criterion = table1.c.col1 == a.c.table2_col2 - self.assert_(criterion.compare(j.onclause)) - - def test_table_joined_to_select_of_table(self): - metadata = MetaData() - a = Table('a', metadata, - Column('id', Integer, primary_key=True)) - b = Table('b', metadata, - Column('id', Integer, primary_key=True), - Column('aid', Integer, ForeignKey('a.id')), - ) - - j1 = a.outerjoin(b) - j2 = select([a.c.id.label('aid')]).alias('bar') - - j3 = a.join(j2, j2.c.aid==a.c.id) - - j4 = select([j3]).alias('foo') - assert j4.corresponding_column(j2.c.aid) is j4.c.aid - assert j4.corresponding_column(a.c.id) is j4.c.id - - def test_two_metadata_join_raises(self): - m = MetaData() - m2 = MetaData() - - t1 = Table('t1', m, Column('id', Integer), Column('id2', Integer)) - t2 = Table('t2', m, Column('id', Integer, ForeignKey('t1.id'))) - t3 = Table('t3', m2, Column('id', Integer, ForeignKey('t1.id2'))) - - s = select([t2, t3], use_labels=True) - - self.assertRaises(exc.NoReferencedTableError, s.join, t1) - -class PrimaryKeyTest(TestBase, AssertsExecutionResults): - def test_join_pk_collapse_implicit(self): - """test that redundant columns in a join get 'collapsed' into a minimal primary key, - which is the root column along a chain of foreign key relationships.""" - - meta = MetaData() - a = Table('a', meta, Column('id', Integer, primary_key=True)) - b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True)) - c = Table('c', meta, Column('id', Integer, ForeignKey('b.id'), primary_key=True)) - d = Table('d', meta, Column('id', Integer, ForeignKey('c.id'), primary_key=True)) - - assert c.c.id.references(b.c.id) - assert not d.c.id.references(a.c.id) - - assert list(a.join(b).primary_key) == [a.c.id] - assert list(b.join(c).primary_key) == [b.c.id] - assert list(a.join(b).join(c).primary_key) == [a.c.id] - assert list(b.join(c).join(d).primary_key) == [b.c.id] - assert list(d.join(c).join(b).primary_key) == [b.c.id] - assert list(a.join(b).join(c).join(d).primary_key) == [a.c.id] - - def test_join_pk_collapse_explicit(self): - """test that redundant columns in a join get 'collapsed' into a minimal primary key, - which is the root column along a chain of explicit join conditions.""" - - meta = MetaData() - a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) - b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer)) - c = Table('c', meta, Column('id', Integer, ForeignKey('b.id'), primary_key=True), Column('x', Integer)) - d = Table('d', meta, Column('id', Integer, ForeignKey('c.id'), primary_key=True), Column('x', Integer)) - - print list(a.join(b, a.c.x==b.c.id).primary_key) - assert list(a.join(b, a.c.x==b.c.id).primary_key) == [b.c.id] - assert list(b.join(c, b.c.x==c.c.id).primary_key) == [b.c.id] - assert list(a.join(b).join(c, c.c.id==b.c.x).primary_key) == [a.c.id] - assert list(b.join(c, c.c.x==b.c.id).join(d).primary_key) == [c.c.id] - assert list(b.join(c, c.c.id==b.c.x).join(d).primary_key) == [b.c.id] - assert list(d.join(b, d.c.id==b.c.id).join(c, b.c.id==c.c.x).primary_key) == [c.c.id] - assert list(a.join(b).join(c, c.c.id==b.c.x).join(d).primary_key) == [a.c.id] - - assert list(a.join(b, and_(a.c.id==b.c.id, a.c.x==b.c.id)).primary_key) == [a.c.id] - - def test_init_doesnt_blowitaway(self): - meta = MetaData() - a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) - b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer)) - - j = a.join(b) - assert list(j.primary_key) == [a.c.id] - - j.foreign_keys - assert list(j.primary_key) == [a.c.id] - - def test_non_column_clause(self): - meta = MetaData() - a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) - b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer, primary_key=True)) - - j = a.join(b, and_(a.c.id==b.c.id, b.c.x==5)) - assert str(j) == "a JOIN b ON a.id = b.id AND b.x = :x_1", str(j) - assert list(j.primary_key) == [a.c.id, b.c.x] - - def test_onclause_direction(self): - metadata = MetaData() - - employee = Table( 'Employee', metadata, - Column('name', String(100)), - Column('id', Integer, primary_key= True), - ) - - engineer = Table( 'Engineer', metadata, - Column('id', Integer, ForeignKey( 'Employee.id', ), primary_key=True), - ) - - self.assertEquals( - util.column_set(employee.join(engineer, employee.c.id==engineer.c.id).primary_key), - util.column_set([employee.c.id]) - ) - - self.assertEquals( - util.column_set(employee.join(engineer, engineer.c.id==employee.c.id).primary_key), - util.column_set([employee.c.id]) - ) - - -class ReduceTest(TestBase, AssertsExecutionResults): - def test_reduce(self): - meta = MetaData() - t1 = Table('t1', meta, - Column('t1id', Integer, primary_key=True), - Column('t1data', String(30))) - t2 = Table('t2', meta, - Column('t2id', Integer, ForeignKey('t1.t1id'), primary_key=True), - Column('t2data', String(30))) - t3 = Table('t3', meta, - Column('t3id', Integer, ForeignKey('t2.t2id'), primary_key=True), - Column('t3data', String(30))) - - - self.assertEquals( - util.column_set(sql_util.reduce_columns([t1.c.t1id, t1.c.t1data, t2.c.t2id, t2.c.t2data, t3.c.t3id, t3.c.t3data])), - util.column_set([t1.c.t1id, t1.c.t1data, t2.c.t2data, t3.c.t3data]) - ) - - def test_reduce_selectable(self): - metadata = MetaData() - - engineers = Table('engineers', metadata, - Column('engineer_id', Integer, primary_key=True), - Column('engineer_name', String(50)), - ) - - managers = Table('managers', metadata, - Column('manager_id', Integer, primary_key=True), - Column('manager_name', String(50)) - ) - - s = select([engineers, managers]).where(engineers.c.engineer_name==managers.c.manager_name) - - self.assertEquals(util.column_set(sql_util.reduce_columns(list(s.c), s)), - util.column_set([s.c.engineer_id, s.c.engineer_name, s.c.manager_id]) - ) - - def test_reduce_aliased_join(self): - metadata = MetaData() - people = Table('people', metadata, - Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True), - Column('name', String(50)), - Column('type', String(30))) - - engineers = Table('engineers', metadata, - Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('status', String(30)), - Column('engineer_name', String(50)), - Column('primary_language', String(50)), - ) - - managers = Table('managers', metadata, - Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), - Column('status', String(30)), - Column('manager_name', String(50)) - ) - - pjoin = people.outerjoin(engineers).outerjoin(managers).select(use_labels=True).alias('pjoin') - self.assertEquals( - util.column_set(sql_util.reduce_columns([pjoin.c.people_person_id, pjoin.c.engineers_person_id, pjoin.c.managers_person_id])), - util.column_set([pjoin.c.people_person_id]) - ) - - def test_reduce_aliased_union(self): - metadata = MetaData() - item_table = Table( - 'item', metadata, - Column('id', Integer, ForeignKey('base_item.id'), primary_key=True), - Column('dummy', Integer, default=0)) - - base_item_table = Table( - 'base_item', metadata, - Column('id', Integer, primary_key=True), - Column('child_name', String(255), default=None)) - - from sqlalchemy.orm.util import polymorphic_union - - item_join = polymorphic_union( { - 'BaseItem':base_item_table.select(base_item_table.c.child_name=='BaseItem'), - 'Item':base_item_table.join(item_table), - }, None, 'item_join') - - self.assertEquals( - util.column_set(sql_util.reduce_columns([item_join.c.id, item_join.c.dummy, item_join.c.child_name])), - util.column_set([item_join.c.id, item_join.c.dummy, item_join.c.child_name]) - ) - - def test_reduce_aliased_union_2(self): - metadata = MetaData() - - page_table = Table('page', metadata, - Column('id', Integer, primary_key=True), - ) - magazine_page_table = Table('magazine_page', metadata, - Column('page_id', Integer, ForeignKey('page.id'), primary_key=True), - ) - classified_page_table = Table('classified_page', metadata, - Column('magazine_page_id', Integer, ForeignKey('magazine_page.page_id'), primary_key=True), - ) - - from sqlalchemy.orm.util import polymorphic_union - pjoin = polymorphic_union( - { - 'm': page_table.join(magazine_page_table), - 'c': page_table.join(magazine_page_table).join(classified_page_table), - }, None, 'page_join') - - self.assertEquals( - util.column_set(sql_util.reduce_columns([pjoin.c.id, pjoin.c.page_id, pjoin.c.magazine_page_id])), - util.column_set([pjoin.c.id]) - ) - - -class DerivedTest(TestBase, AssertsExecutionResults): - def test_table(self): - meta = MetaData() - t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - - assert t1.is_derived_from(t1) - assert not t2.is_derived_from(t1) - - def test_alias(self): - meta = MetaData() - t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - - assert t1.alias().is_derived_from(t1) - assert not t2.alias().is_derived_from(t1) - assert not t1.is_derived_from(t1.alias()) - assert not t1.is_derived_from(t2.alias()) - - def test_select(self): - meta = MetaData() - t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) - - assert t1.select().is_derived_from(t1) - assert not t2.select().is_derived_from(t1) - - assert select([t1, t2]).is_derived_from(t1) - - assert t1.select().alias('foo').is_derived_from(t1) - assert select([t1, t2]).alias('foo').is_derived_from(t1) - assert not t2.select().alias('foo').is_derived_from(t1) - -class AnnotationsTest(TestBase): - def test_annotated_corresponding_column(self): - table1 = table('table1', column("col1")) - - s1 = select([table1.c.col1]) - t1 = s1._annotate({}) - t2 = s1 - - # t1 needs to share the same _make_proxy() columns as t2, even though it's - # annotated. otherwise paths will diverge once they are corresponded against "inner" below. - assert t1.c is t2.c - assert t1.c.col1 is t2.c.col1 - - inner = select([s1]) - assert inner.corresponding_column(t2.c.col1, require_embedded=False) is inner.corresponding_column(t2.c.col1, require_embedded=True) is inner.c.col1 - assert inner.corresponding_column(t1.c.col1, require_embedded=False) is inner.corresponding_column(t1.c.col1, require_embedded=True) is inner.c.col1 - - def test_annotated_visit(self): - table1 = table('table1', column("col1"), column("col2")) - - bin = table1.c.col1 == bindparam('foo', value=None) - assert str(bin) == "table1.col1 = :foo" - def visit_binary(b): - b.right = table1.c.col2 - - b2 = visitors.cloned_traverse(bin, {}, {'binary':visit_binary}) - assert str(b2) == "table1.col1 = table1.col2" - - b3 = visitors.cloned_traverse(bin._annotate({}), {}, {'binary':visit_binary}) - assert str(b3) == "table1.col1 = table1.col2" - - def visit_binary(b): - b.left = bindparam('bar') - - b4 = visitors.cloned_traverse(b2, {}, {'binary':visit_binary}) - assert str(b4) == ":bar = table1.col2" - - b5 = visitors.cloned_traverse(b3, {}, {'binary':visit_binary}) - assert str(b5) == ":bar = table1.col2" - - def test_deannotate(self): - table1 = table('table1', column("col1"), column("col2")) - - bin = table1.c.col1 == bindparam('foo', value=None) - - b2 = sql_util._deep_annotate(bin, {'_orm_adapt':True}) - b3 = sql_util._deep_deannotate(b2) - b4 = sql_util._deep_deannotate(bin) - - for elem in (b2._annotations, b2.left._annotations): - assert '_orm_adapt' in elem - - for elem in (b3._annotations, b3.left._annotations, b4._annotations, b4.left._annotations): - assert elem == {} - - assert b2.left is not bin.left - assert b3.left is not b2.left is not bin.left - assert b4.left is bin.left # since column is immutable - assert b4.right is not bin.right is not b2.right is not b3.right - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/test_case_statement.py b/test/sql/test_case_statement.py new file mode 100644 index 000000000..3f3abe7e1 --- /dev/null +++ b/test/sql/test_case_statement.py @@ -0,0 +1,137 @@ +from sqlalchemy.test.testing import assert_raises, assert_raises_message +import sys +from sqlalchemy import * +from sqlalchemy.test import * +from sqlalchemy import util, exc +from sqlalchemy.sql import table, column + + +class CaseTest(TestBase, AssertsCompiledSQL): + + @classmethod + def setup_class(cls): + metadata = MetaData(testing.db) + global info_table + info_table = Table('infos', metadata, + Column('pk', Integer, primary_key=True), + Column('info', String(30))) + + info_table.create() + + info_table.insert().execute( + {'pk':1, 'info':'pk_1_data'}, + {'pk':2, 'info':'pk_2_data'}, + {'pk':3, 'info':'pk_3_data'}, + {'pk':4, 'info':'pk_4_data'}, + {'pk':5, 'info':'pk_5_data'}, + {'pk':6, 'info':'pk_6_data'}) + @classmethod + def teardown_class(cls): + info_table.drop() + + @testing.fails_on('firebird', 'FIXME: unknown') + @testing.fails_on('maxdb', 'FIXME: unknown') + @testing.requires.subqueries + def testcase(self): + inner = select([case([ + [info_table.c.pk < 3, + 'lessthan3'], + [and_(info_table.c.pk >= 3, info_table.c.pk < 7), + 'gt3']]).label('x'), + info_table.c.pk, info_table.c.info], + from_obj=[info_table]).alias('q_inner') + + inner_result = inner.execute().fetchall() + + # Outputs: + # lessthan3 1 pk_1_data + # lessthan3 2 pk_2_data + # gt3 3 pk_3_data + # gt3 4 pk_4_data + # gt3 5 pk_5_data + # gt3 6 pk_6_data + assert inner_result == [ + ('lessthan3', 1, 'pk_1_data'), + ('lessthan3', 2, 'pk_2_data'), + ('gt3', 3, 'pk_3_data'), + ('gt3', 4, 'pk_4_data'), + ('gt3', 5, 'pk_5_data'), + ('gt3', 6, 'pk_6_data') + ] + + outer = select([inner]) + + outer_result = outer.execute().fetchall() + + assert outer_result == [ + ('lessthan3', 1, 'pk_1_data'), + ('lessthan3', 2, 'pk_2_data'), + ('gt3', 3, 'pk_3_data'), + ('gt3', 4, 'pk_4_data'), + ('gt3', 5, 'pk_5_data'), + ('gt3', 6, 'pk_6_data') + ] + + w_else = select([case([ + [info_table.c.pk < 3, + 3], + [and_(info_table.c.pk >= 3, info_table.c.pk < 6), + 6]], + else_ = 0).label('x'), + info_table.c.pk, info_table.c.info], + from_obj=[info_table]).alias('q_inner') + + else_result = w_else.execute().fetchall() + + assert else_result == [ + (3, 1, 'pk_1_data'), + (3, 2, 'pk_2_data'), + (6, 3, 'pk_3_data'), + (6, 4, 'pk_4_data'), + (6, 5, 'pk_5_data'), + (0, 6, 'pk_6_data') + ] + + def test_literal_interpretation(self): + t = table('test', column('col1')) + + assert_raises(exc.ArgumentError, case, [("x", "y")]) + + self.assert_compile(case([("x", "y")], value=t.c.col1), "CASE test.col1 WHEN :param_1 THEN :param_2 END") + self.assert_compile(case([(t.c.col1==7, "y")], else_="z"), "CASE WHEN (test.col1 = :col1_1) THEN :param_1 ELSE :param_2 END") + + + @testing.fails_on('firebird', 'FIXME: unknown') + @testing.fails_on('maxdb', 'FIXME: unknown') + def testcase_with_dict(self): + query = select([case({ + info_table.c.pk < 3: 'lessthan3', + info_table.c.pk >= 3: 'gt3', + }, else_='other'), + info_table.c.pk, info_table.c.info + ], + from_obj=[info_table]) + assert query.execute().fetchall() == [ + ('lessthan3', 1, 'pk_1_data'), + ('lessthan3', 2, 'pk_2_data'), + ('gt3', 3, 'pk_3_data'), + ('gt3', 4, 'pk_4_data'), + ('gt3', 5, 'pk_5_data'), + ('gt3', 6, 'pk_6_data') + ] + + simple_query = select([case({ + 1: 'one', + 2: 'two', + }, value=info_table.c.pk, else_='other'), + info_table.c.pk + ], + whereclause=info_table.c.pk < 4, + from_obj=[info_table]) + + assert simple_query.execute().fetchall() == [ + ('one', 1), + ('two', 2), + ('other', 3), + ] + diff --git a/test/sql/test_columns.py b/test/sql/test_columns.py new file mode 100644 index 000000000..e9dabe142 --- /dev/null +++ b/test/sql/test_columns.py @@ -0,0 +1,58 @@ +from sqlalchemy.test.testing import assert_raises, assert_raises_message +from sqlalchemy import * +from sqlalchemy import exc, sql +from sqlalchemy.test import * +from sqlalchemy import Table, Column # don't use testlib's wrappers + + +class ColumnDefinitionTest(TestBase): + """Test Column() construction.""" + + # flesh this out with explicit coverage... + + def columns(self): + return [ Column(), + Column('b'), + Column(Integer), + Column('d', Integer), + Column(name='e'), + Column(type_=Integer), + Column(Integer()), + Column('h', Integer()), + Column(type_=Integer()) ] + + def test_basic(self): + c = self.columns() + + for i, v in ((0, 'a'), (2, 'c'), (5, 'f'), (6, 'g'), (8, 'i')): + c[i].name = v + c[i].key = v + del i, v + + tbl = Table('table', MetaData(), *c) + + for i, col in enumerate(tbl.c): + assert col.name == c[i].name + + def test_incomplete(self): + c = self.columns() + + assert_raises(exc.ArgumentError, Table, 't', MetaData(), *c) + + def test_incomplete_key(self): + c = Column(Integer) + assert c.name is None + assert c.key is None + + c.name = 'named' + t = Table('t', MetaData(), c) + + assert c.name == 'named' + assert c.name == c.key + + + def test_bogus(self): + assert_raises(exc.ArgumentError, Column, 'foo', name='bar') + assert_raises(exc.ArgumentError, Column, 'foo', Integer, + type_=Integer()) + diff --git a/test/sql/test_constraints.py b/test/sql/test_constraints.py new file mode 100644 index 000000000..8abeb3533 --- /dev/null +++ b/test/sql/test_constraints.py @@ -0,0 +1,335 @@ +from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy import * +from sqlalchemy import exc +from sqlalchemy.test import * +from sqlalchemy.test import config, engines + +class ConstraintTest(TestBase, AssertsExecutionResults): + + def setup(self): + global metadata + metadata = MetaData(testing.db) + + def teardown(self): + metadata.drop_all() + + def test_constraint(self): + employees = Table('employees', metadata, + Column('id', Integer), + Column('soc', String(40)), + Column('name', String(30)), + PrimaryKeyConstraint('id', 'soc') + ) + elements = Table('elements', metadata, + Column('id', Integer), + Column('stuff', String(30)), + Column('emp_id', Integer), + Column('emp_soc', String(40)), + PrimaryKeyConstraint('id', name='elements_primkey'), + ForeignKeyConstraint(['emp_id', 'emp_soc'], ['employees.id', 'employees.soc']) + ) + metadata.create_all() + + def test_double_fk_usage_raises(self): + f = ForeignKey('b.id') + + assert_raises(exc.InvalidRequestError, Table, "a", metadata, + Column('x', Integer, f), + Column('y', Integer, f) + ) + + + def test_circular_constraint(self): + a = Table("a", metadata, + Column('id', Integer, primary_key=True), + Column('bid', Integer), + ForeignKeyConstraint(["bid"], ["b.id"], name="afk") + ) + b = Table("b", metadata, + Column('id', Integer, primary_key=True), + Column("aid", Integer), + ForeignKeyConstraint(["aid"], ["a.id"], use_alter=True, name="bfk") + ) + metadata.create_all() + + def test_circular_constraint_2(self): + a = Table("a", metadata, + Column('id', Integer, primary_key=True), + Column('bid', Integer, ForeignKey("b.id")), + ) + b = Table("b", metadata, + Column('id', Integer, primary_key=True), + Column("aid", Integer, ForeignKey("a.id", use_alter=True, name="bfk")), + ) + metadata.create_all() + + @testing.fails_on('mysql', 'FIXME: unknown') + def test_check_constraint(self): + foo = Table('foo', metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer), + Column('y', Integer), + CheckConstraint('x>y')) + bar = Table('bar', metadata, + Column('id', Integer, primary_key=True), + Column('x', Integer, CheckConstraint('x>7')), + Column('z', Integer) + ) + + metadata.create_all() + foo.insert().execute(id=1,x=9,y=5) + try: + foo.insert().execute(id=2,x=5,y=9) + assert False + except exc.SQLError: + assert True + + bar.insert().execute(id=1,x=10) + try: + bar.insert().execute(id=2,x=5) + assert False + except exc.SQLError: + assert True + + def test_unique_constraint(self): + foo = Table('foo', metadata, + Column('id', Integer, primary_key=True), + Column('value', String(30), unique=True)) + bar = Table('bar', metadata, + Column('id', Integer, primary_key=True), + Column('value', String(30)), + Column('value2', String(30)), + UniqueConstraint('value', 'value2', name='uix1') + ) + metadata.create_all() + foo.insert().execute(id=1, value='value1') + foo.insert().execute(id=2, value='value2') + bar.insert().execute(id=1, value='a', value2='a') + bar.insert().execute(id=2, value='a', value2='b') + try: + foo.insert().execute(id=3, value='value1') + assert False + except exc.SQLError: + assert True + try: + bar.insert().execute(id=3, value='a', value2='b') + assert False + except exc.SQLError: + assert True + + def test_index_create(self): + employees = Table('employees', metadata, + Column('id', Integer, primary_key=True), + Column('first_name', String(30)), + Column('last_name', String(30)), + Column('email_address', String(30))) + employees.create() + + i = Index('employee_name_index', + employees.c.last_name, employees.c.first_name) + i.create() + assert i in employees.indexes + + i2 = Index('employee_email_index', + employees.c.email_address, unique=True) + i2.create() + assert i2 in employees.indexes + + def test_index_create_camelcase(self): + """test that mixed-case index identifiers are legal""" + employees = Table('companyEmployees', metadata, + Column('id', Integer, primary_key=True), + Column('firstName', String(30)), + Column('lastName', String(30)), + Column('emailAddress', String(30))) + + employees.create() + + i = Index('employeeNameIndex', + employees.c.lastName, employees.c.firstName) + i.create() + + i = Index('employeeEmailIndex', + employees.c.emailAddress, unique=True) + i.create() + + # Check that the table is useable. This is mostly for pg, + # which can be somewhat sticky with mixed-case identifiers + employees.insert().execute(firstName='Joe', lastName='Smith', id=0) + ss = employees.select().execute().fetchall() + assert ss[0].firstName == 'Joe' + assert ss[0].lastName == 'Smith' + + def test_index_create_inline(self): + """Test indexes defined with tables""" + + events = Table('events', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(30), index=True, unique=True), + Column('location', String(30), index=True), + Column('sport', String(30)), + Column('announcer', String(30)), + Column('winner', String(30))) + + Index('sport_announcer', events.c.sport, events.c.announcer, unique=True) + Index('idx_winners', events.c.winner) + + index_names = [ ix.name for ix in events.indexes ] + assert 'ix_events_name' in index_names + assert 'ix_events_location' in index_names + assert 'sport_announcer' in index_names + assert 'idx_winners' in index_names + assert len(index_names) == 4 + + capt = [] + connection = testing.db.connect() + # TODO: hacky, put a real connection proxy in + ex = connection._Connection__execute_context + def proxy(context): + capt.append(context.statement) + capt.append(repr(context.parameters)) + ex(context) + connection._Connection__execute_context = proxy + schemagen = testing.db.dialect.schemagenerator(testing.db.dialect, connection) + schemagen.traverse(events) + + assert capt[0].strip().startswith('CREATE TABLE events') + + s = set([capt[x].strip() for x in [2,4,6,8]]) + + assert s == set([ + 'CREATE UNIQUE INDEX ix_events_name ON events (name)', + 'CREATE INDEX ix_events_location ON events (location)', + 'CREATE UNIQUE INDEX sport_announcer ON events (sport, announcer)', + 'CREATE INDEX idx_winners ON events (winner)' + ]) + + # verify that the table is functional + events.insert().execute(id=1, name='hockey finals', location='rink', + sport='hockey', announcer='some canadian', + winner='sweden') + ss = events.select().execute().fetchall() + + def test_too_long_idx_name(self): + dialect = testing.db.dialect.__class__() + dialect.max_identifier_length = 20 + + schemagen = dialect.schemagenerator(dialect, None) + schemagen.execute = lambda : None + + t1 = Table("sometable", MetaData(), Column("foo", Integer)) + schemagen.visit_index(Index("this_name_is_too_long_for_what_were_doing", t1.c.foo)) + eq_(schemagen.buffer.getvalue(), "CREATE INDEX this_name_is_t_1 ON sometable (foo)") + schemagen.buffer.truncate(0) + schemagen.visit_index(Index("this_other_name_is_too_long_for_what_were_doing", t1.c.foo)) + eq_(schemagen.buffer.getvalue(), "CREATE INDEX this_other_nam_2 ON sometable (foo)") + + schemadrop = dialect.schemadropper(dialect, None) + schemadrop.execute = lambda: None + assert_raises(exc.IdentifierError, schemadrop.visit_index, Index("this_name_is_too_long_for_what_were_doing", t1.c.foo)) + + +class ConstraintCompilationTest(TestBase, AssertsExecutionResults): + class accum(object): + def __init__(self): + self.statements = [] + def __call__(self, sql, *a, **kw): + self.statements.append(sql) + def __contains__(self, substring): + for s in self.statements: + if substring in s: + return True + return False + def __str__(self): + return '\n'.join([repr(x) for x in self.statements]) + def clear(self): + del self.statements[:] + + def setup(self): + self.sql = self.accum() + opts = config.db_opts.copy() + opts['strategy'] = 'mock' + opts['executor'] = self.sql + self.engine = engines.testing_engine(options=opts) + + + def _test_deferrable(self, constraint_factory): + meta = MetaData(self.engine) + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer), + constraint_factory(deferrable=True)) + t.create() + assert 'DEFERRABLE' in self.sql, self.sql + assert 'NOT DEFERRABLE' not in self.sql, self.sql + self.sql.clear() + meta.clear() + + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer), + constraint_factory(deferrable=False)) + t.create() + assert 'NOT DEFERRABLE' in self.sql + self.sql.clear() + meta.clear() + + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer), + constraint_factory(deferrable=True, initially='IMMEDIATE')) + t.create() + assert 'NOT DEFERRABLE' not in self.sql + assert 'INITIALLY IMMEDIATE' in self.sql + self.sql.clear() + meta.clear() + + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer), + constraint_factory(deferrable=True, initially='DEFERRED')) + t.create() + + assert 'NOT DEFERRABLE' not in self.sql + assert 'INITIALLY DEFERRED' in self.sql, self.sql + + def test_deferrable_pk(self): + factory = lambda **kw: PrimaryKeyConstraint('a', **kw) + self._test_deferrable(factory) + + def test_deferrable_table_fk(self): + factory = lambda **kw: ForeignKeyConstraint(['b'], ['tbl.a'], **kw) + self._test_deferrable(factory) + + def test_deferrable_column_fk(self): + meta = MetaData(self.engine) + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer, + ForeignKey('tbl.a', deferrable=True, + initially='DEFERRED'))) + t.create() + assert 'DEFERRABLE' in self.sql, self.sql + assert 'INITIALLY DEFERRED' in self.sql, self.sql + + def test_deferrable_unique(self): + factory = lambda **kw: UniqueConstraint('b', **kw) + self._test_deferrable(factory) + + def test_deferrable_table_check(self): + factory = lambda **kw: CheckConstraint('a < b', **kw) + self._test_deferrable(factory) + + def test_deferrable_column_check(self): + meta = MetaData(self.engine) + t = Table('tbl', meta, + Column('a', Integer), + Column('b', Integer, + CheckConstraint('a < b', + deferrable=True, + initially='DEFERRED'))) + t.create() + assert 'DEFERRABLE' in self.sql, self.sql + assert 'INITIALLY DEFERRED' in self.sql, self.sql + + diff --git a/test/sql/test_defaults.py b/test/sql/test_defaults.py new file mode 100644 index 000000000..964157466 --- /dev/null +++ b/test/sql/test_defaults.py @@ -0,0 +1,641 @@ +from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message +import datetime +from sqlalchemy import Sequence, Column, func +from sqlalchemy.sql import select, text +import sqlalchemy as sa +from sqlalchemy.test import testing +from sqlalchemy import MetaData, Integer, String, ForeignKey, Boolean +from sqlalchemy.test.schema import Table +from sqlalchemy.test.testing import eq_ +from test.sql import _base + + +class DefaultTest(testing.TestBase): + + @classmethod + def setup_class(cls): + global t, f, f2, ts, currenttime, metadata, default_generator + + db = testing.db + metadata = MetaData(db) + default_generator = {'x':50} + + def mydefault(): + default_generator['x'] += 1 + return default_generator['x'] + + def myupdate_with_ctx(ctx): + conn = ctx.connection + return conn.execute(sa.select([sa.text('13')])).scalar() + + def mydefault_using_connection(ctx): + conn = ctx.connection + try: + return conn.execute(sa.select([sa.text('12')])).scalar() + finally: + # ensure a "close()" on this connection does nothing, + # since its a "branched" connection + conn.close() + + use_function_defaults = testing.against('postgres', 'mssql', 'maxdb') + is_oracle = testing.against('oracle') + + # select "count(1)" returns different results on different DBs also + # correct for "current_date" compatible as column default, value + # differences + currenttime = func.current_date(type_=sa.Date, bind=db) + if is_oracle: + ts = db.scalar(sa.select([func.trunc(func.sysdate(), sa.literal_column("'DAY'"), type_=sa.Date).label('today')])) + assert isinstance(ts, datetime.date) and not isinstance(ts, datetime.datetime) + f = sa.select([func.length('abcdef')], bind=db).scalar() + f2 = sa.select([func.length('abcdefghijk')], bind=db).scalar() + # TODO: engine propigation across nested functions not working + currenttime = func.trunc(currenttime, sa.literal_column("'DAY'"), bind=db, type_=sa.Date) + def1 = currenttime + def2 = func.trunc(sa.text("sysdate"), sa.literal_column("'DAY'"), type_=sa.Date) + + deftype = sa.Date + elif use_function_defaults: + f = sa.select([func.length('abcdef')], bind=db).scalar() + f2 = sa.select([func.length('abcdefghijk')], bind=db).scalar() + def1 = currenttime + deftype = sa.Date + if testing.against('maxdb'): + def2 = sa.text("curdate") + elif testing.against('mssql'): + def2 = sa.text("getdate()") + else: + def2 = sa.text("current_date") + ts = db.func.current_date().scalar() + else: + f = len('abcdef') + f2 = len('abcdefghijk') + def1 = def2 = "3" + ts = 3 + deftype = Integer + + t = Table('default_test1', metadata, + # python function + Column('col1', Integer, primary_key=True, + default=mydefault), + + # python literal + Column('col2', String(20), + default="imthedefault", + onupdate="im the update"), + + # preexecute expression + Column('col3', Integer, + default=func.length('abcdef'), + onupdate=func.length('abcdefghijk')), + + # SQL-side default from sql expression + Column('col4', deftype, + server_default=def1), + + # SQL-side default from literal expression + Column('col5', deftype, + server_default=def2), + + # preexecute + update timestamp + Column('col6', sa.Date, + default=currenttime, + onupdate=currenttime), + + Column('boolcol1', sa.Boolean, default=True), + Column('boolcol2', sa.Boolean, default=False), + + # python function which uses ExecutionContext + Column('col7', Integer, + default=mydefault_using_connection, + onupdate=myupdate_with_ctx), + + # python builtin + Column('col8', sa.Date, + default=datetime.date.today, + onupdate=datetime.date.today), + # combo + Column('col9', String(20), + default='py', + server_default='ddl')) + t.create() + + @classmethod + def teardown_class(cls): + t.drop() + + def teardown(self): + default_generator['x'] = 50 + t.delete().execute() + + def test_bad_arg_signature(self): + ex_msg = \ + "ColumnDefault Python function takes zero or one positional arguments" + + def fn1(x, y): pass + def fn2(x, y, z=3): pass + class fn3(object): + def __init__(self, x, y): + pass + class FN4(object): + def __call__(self, x, y): + pass + fn4 = FN4() + + for fn in fn1, fn2, fn3, fn4: + assert_raises_message(sa.exc.ArgumentError, + ex_msg, + sa.ColumnDefault, fn) + + def test_arg_signature(self): + def fn1(): pass + def fn2(): pass + def fn3(x=1): pass + def fn4(x=1, y=2, z=3): pass + fn5 = list + class fn6(object): + def __init__(self, x): + pass + class fn6(object): + def __init__(self, x, y=3): + pass + class FN7(object): + def __call__(self, x): + pass + fn7 = FN7() + class FN8(object): + def __call__(self, x, y=3): + pass + fn8 = FN8() + + for fn in fn1, fn2, fn3, fn4, fn5, fn6, fn7, fn8: + c = sa.ColumnDefault(fn) + + @testing.fails_on('firebird', 'Data type unknown') + def test_standalone(self): + c = testing.db.engine.contextual_connect() + x = c.execute(t.c.col1.default) + y = t.c.col2.default.execute() + z = c.execute(t.c.col3.default) + assert 50 <= x <= 57 + eq_(y, 'imthedefault') + eq_(z, f) + eq_(f2, 11) + + def test_py_vs_server_default_detection(self): + + def has_(name, *wanted): + slots = ['default', 'onupdate', 'server_default', 'server_onupdate'] + col = tbl.c[name] + for slot in wanted: + slots.remove(slot) + assert getattr(col, slot) is not None, getattr(col, slot) + for slot in slots: + assert getattr(col, slot) is None, getattr(col, slot) + + tbl = t + has_('col1', 'default') + has_('col2', 'default', 'onupdate') + has_('col3', 'default', 'onupdate') + has_('col4', 'server_default') + has_('col5', 'server_default') + has_('col6', 'default', 'onupdate') + has_('boolcol1', 'default') + has_('boolcol2', 'default') + has_('col7', 'default', 'onupdate') + has_('col8', 'default', 'onupdate') + has_('col9', 'default', 'server_default') + + ColumnDefault, DefaultClause = sa.ColumnDefault, sa.DefaultClause + + t2 = Table('t2', MetaData(), + Column('col1', Integer, Sequence('foo')), + Column('col2', Integer, + default=Sequence('foo'), + server_default='y'), + Column('col3', Integer, + Sequence('foo'), + server_default='x'), + Column('col4', Integer, + ColumnDefault('x'), + DefaultClause('y')), + Column('col4', Integer, + ColumnDefault('x'), + DefaultClause('y'), + DefaultClause('y', for_update=True)), + Column('col5', Integer, + ColumnDefault('x'), + DefaultClause('y'), + onupdate='z'), + Column('col6', Integer, + ColumnDefault('x'), + server_default='y', + onupdate='z'), + Column('col7', Integer, + default='x', + server_default='y', + onupdate='z'), + Column('col8', Integer, + server_onupdate='u', + default='x', + server_default='y', + onupdate='z')) + tbl = t2 + has_('col1', 'default') + has_('col2', 'default', 'server_default') + has_('col3', 'default', 'server_default') + has_('col4', 'default', 'server_default', 'server_onupdate') + has_('col5', 'default', 'server_default', 'onupdate') + has_('col6', 'default', 'server_default', 'onupdate') + has_('col7', 'default', 'server_default', 'onupdate') + has_('col8', 'default', 'server_default', 'onupdate', 'server_onupdate') + + @testing.fails_on('firebird', 'Data type unknown') + def test_insert(self): + r = t.insert().execute() + assert r.lastrow_has_defaults() + eq_(set(r.context.postfetch_cols), + set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) + + r = t.insert(inline=True).execute() + assert r.lastrow_has_defaults() + eq_(set(r.context.postfetch_cols), + set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) + + t.insert().execute() + + ctexec = sa.select([currenttime.label('now')], bind=testing.db).scalar() + l = t.select().order_by(t.c.col1).execute() + today = datetime.date.today() + eq_(l.fetchall(), [ + (x, 'imthedefault', f, ts, ts, ctexec, True, False, + 12, today, 'py') + for x in range(51, 54)]) + + t.insert().execute(col9=None) + assert r.lastrow_has_defaults() + eq_(set(r.context.postfetch_cols), + set([t.c.col3, t.c.col5, t.c.col4, t.c.col6])) + + eq_(t.select(t.c.col1==54).execute().fetchall(), + [(54, 'imthedefault', f, ts, ts, ctexec, True, False, + 12, today, None)]) + + @testing.fails_on('firebird', 'Data type unknown') + def test_insertmany(self): + # MySQL-Python 1.2.2 breaks functions in execute_many :( + if (testing.against('mysql') and + testing.db.dialect.dbapi.version_info[:3] == (1, 2, 2)): + return + + r = t.insert().execute({}, {}, {}) + + ctexec = currenttime.scalar() + l = t.select().execute() + today = datetime.date.today() + eq_(l.fetchall(), + [(51, 'imthedefault', f, ts, ts, ctexec, True, False, + 12, today, 'py'), + (52, 'imthedefault', f, ts, ts, ctexec, True, False, + 12, today, 'py'), + (53, 'imthedefault', f, ts, ts, ctexec, True, False, + 12, today, 'py')]) + + def test_insert_values(self): + t.insert(values={'col3':50}).execute() + l = t.select().execute() + eq_(50, l.fetchone()['col3']) + + @testing.fails_on('firebird', 'Data type unknown') + def test_updatemany(self): + # MySQL-Python 1.2.2 breaks functions in execute_many :( + if (testing.against('mysql') and + testing.db.dialect.dbapi.version_info[:3] == (1, 2, 2)): + return + + t.insert().execute({}, {}, {}) + + t.update(t.c.col1==sa.bindparam('pkval')).execute( + {'pkval':51,'col7':None, 'col8':None, 'boolcol1':False}) + + t.update(t.c.col1==sa.bindparam('pkval')).execute( + {'pkval':51,}, + {'pkval':52,}, + {'pkval':53,}) + + l = t.select().execute() + ctexec = currenttime.scalar() + today = datetime.date.today() + eq_(l.fetchall(), + [(51, 'im the update', f2, ts, ts, ctexec, False, False, + 13, today, 'py'), + (52, 'im the update', f2, ts, ts, ctexec, True, False, + 13, today, 'py'), + (53, 'im the update', f2, ts, ts, ctexec, True, False, + 13, today, 'py')]) + + @testing.fails_on('firebird', 'Data type unknown') + def test_update(self): + r = t.insert().execute() + pk = r.last_inserted_ids()[0] + t.update(t.c.col1==pk).execute(col4=None, col5=None) + ctexec = currenttime.scalar() + l = t.select(t.c.col1==pk).execute() + l = l.fetchone() + eq_(l, + (pk, 'im the update', f2, None, None, ctexec, True, False, + 13, datetime.date.today(), 'py')) + eq_(11, f2) + + @testing.fails_on('firebird', 'Data type unknown') + def test_update_values(self): + r = t.insert().execute() + pk = r.last_inserted_ids()[0] + t.update(t.c.col1==pk, values={'col3': 55}).execute() + l = t.select(t.c.col1==pk).execute() + l = l.fetchone() + eq_(55, l['col3']) + + @testing.fails_on_everything_except('postgres') + def test_passive_override(self): + """ + Primarily for postgres, tests that when we get a primary key column + back from reflecting a table which has a default value on it, we + pre-execute that DefaultClause upon insert, even though DefaultClause + says "let the database execute this", because in postgres we must have + all the primary key values in memory before insert; otherwise we can't + locate the just inserted row. + + """ + # TODO: move this to dialect/postgres + try: + meta = MetaData(testing.db) + testing.db.execute(""" + CREATE TABLE speedy_users + ( + speedy_user_id SERIAL PRIMARY KEY, + + user_name VARCHAR NOT NULL, + user_password VARCHAR NOT NULL + ); + """, None) + + t = Table("speedy_users", meta, autoload=True) + t.insert().execute(user_name='user', user_password='lala') + l = t.select().execute().fetchall() + eq_(l, [(1, 'user', 'lala')]) + finally: + testing.db.execute("drop table speedy_users", None) + + +class PKDefaultTest(_base.TablesTest): + __requires__ = ('subqueries',) + + @classmethod + def define_tables(cls, metadata): + t2 = Table('t2', metadata, + Column('nextid', Integer)) + + Table('t1', metadata, + Column('id', Integer, primary_key=True, + default=sa.select([func.max(t2.c.nextid)]).as_scalar()), + Column('data', String(30))) + + @testing.fails_on('mssql', 'FIXME: unknown') + @testing.resolve_artifact_names + def test_basic(self): + t2.insert().execute(nextid=1) + r = t1.insert().execute(data='hi') + eq_([1], r.last_inserted_ids()) + + t2.insert().execute(nextid=2) + r = t1.insert().execute(data='there') + eq_([2], r.last_inserted_ids()) + + +class PKIncrementTest(_base.TablesTest): + run_define_tables = 'each' + + @classmethod + def define_tables(cls, metadata): + Table("aitable", metadata, + Column('id', Integer, Sequence('ai_id_seq', optional=True), + primary_key=True), + Column('int1', Integer), + Column('str1', String(20))) + + # TODO: add coverage for increment on a secondary column in a key + @testing.fails_on('firebird', 'Data type unknown') + @testing.resolve_artifact_names + def _test_autoincrement(self, bind): + ids = set() + rs = bind.execute(aitable.insert(), int1=1) + last = rs.last_inserted_ids()[0] + self.assert_(last) + self.assert_(last not in ids) + ids.add(last) + + rs = bind.execute(aitable.insert(), str1='row 2') + last = rs.last_inserted_ids()[0] + self.assert_(last) + self.assert_(last not in ids) + ids.add(last) + + rs = bind.execute(aitable.insert(), int1=3, str1='row 3') + last = rs.last_inserted_ids()[0] + self.assert_(last) + self.assert_(last not in ids) + ids.add(last) + + rs = bind.execute(aitable.insert(values={'int1':func.length('four')})) + last = rs.last_inserted_ids()[0] + self.assert_(last) + self.assert_(last not in ids) + ids.add(last) + + eq_(list(bind.execute(aitable.select().order_by(aitable.c.id))), + [(1, 1, None), (2, None, 'row 2'), (3, 3, 'row 3'), (4, 4, None)]) + + @testing.resolve_artifact_names + def test_autoincrement_autocommit(self): + self._test_autoincrement(testing.db) + + @testing.resolve_artifact_names + def test_autoincrement_transaction(self): + con = testing.db.connect() + tx = con.begin() + try: + try: + self._test_autoincrement(con) + except: + try: + tx.rollback() + except: + pass + raise + else: + tx.commit() + finally: + con.close() + + +class EmptyInsertTest(testing.TestBase): + @testing.exclude('sqlite', '<', (3, 3, 8), 'no empty insert support') + @testing.fails_on('oracle', 'FIXME: unknown') + def test_empty_insert(self): + metadata = MetaData(testing.db) + t1 = Table('t1', metadata, + Column('is_true', Boolean, server_default=('1'))) + metadata.create_all() + + try: + result = t1.insert().execute() + eq_(1, select([func.count(text('*'))], from_obj=t1).scalar()) + eq_(True, t1.select().scalar()) + finally: + metadata.drop_all() + +class AutoIncrementTest(_base.TablesTest): + __requires__ = ('identity',) + run_define_tables = 'each' + + @classmethod + def define_tables(cls, metadata): + """Each test manipulates self.metadata individually.""" + + @testing.exclude('sqlite', '<', (3, 4), 'no database support') + def test_autoincrement_single_col(self): + single = Table('single', self.metadata, + Column('id', Integer, primary_key=True)) + single.create() + + r = single.insert().execute() + id_ = r.last_inserted_ids()[0] + assert id_ is not None + eq_(1, sa.select([func.count(sa.text('*'))], from_obj=single).scalar()) + + def test_autoincrement_fk(self): + nodes = Table('nodes', self.metadata, + Column('id', Integer, primary_key=True), + Column('parent_id', Integer, ForeignKey('nodes.id')), + Column('data', String(30))) + nodes.create() + + r = nodes.insert().execute(data='foo') + id_ = r.last_inserted_ids()[0] + nodes.insert().execute(data='bar', parent_id=id_) + + @testing.fails_on('sqlite', 'FIXME: unknown') + def test_non_autoincrement(self): + # sqlite INT primary keys can be non-unique! (only for ints) + nonai = Table("nonaitest", self.metadata, + Column('id', Integer, autoincrement=False, primary_key=True), + Column('data', String(20))) + nonai.create() + + + try: + # postgres + mysql strict will fail on first row, + # mysql in legacy mode fails on second row + nonai.insert().execute(data='row 1') + nonai.insert().execute(data='row 2') + assert False + except sa.exc.SQLError, e: + assert True + + nonai.insert().execute(id=1, data='row 1') + + +class SequenceTest(testing.TestBase): + __requires__ = ('sequences',) + + @classmethod + def setup_class(cls): + global cartitems, sometable, metadata + metadata = MetaData(testing.db) + cartitems = Table("cartitems", metadata, + Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True), + Column("description", String(40)), + Column("createdate", sa.DateTime()) + ) + sometable = Table( 'Manager', metadata, + Column('obj_id', Integer, Sequence('obj_id_seq'), ), + Column('name', String(128)), + Column('id', Integer, Sequence('Manager_id_seq', optional=True), + primary_key=True), + ) + + metadata.create_all() + + def testseqnonpk(self): + """test sequences fire off as defaults on non-pk columns""" + + result = sometable.insert().execute(name="somename") + assert 'id' in result.postfetch_cols() + + result = sometable.insert().execute(name="someother") + assert 'id' in result.postfetch_cols() + + sometable.insert().execute( + {'name':'name3'}, + {'name':'name4'}) + eq_(sometable.select().execute().fetchall(), + [(1, "somename", 1), + (2, "someother", 2), + (3, "name3", 3), + (4, "name4", 4)]) + + def testsequence(self): + cartitems.insert().execute(description='hi') + cartitems.insert().execute(description='there') + r = cartitems.insert().execute(description='lala') + + assert r.last_inserted_ids() and r.last_inserted_ids()[0] is not None + id_ = r.last_inserted_ids()[0] + + eq_(1, + sa.select([func.count(cartitems.c.cart_id)], + sa.and_(cartitems.c.description == 'lala', + cartitems.c.cart_id == id_)).scalar()) + + cartitems.select().execute().fetchall() + + @testing.fails_on('maxdb', 'FIXME: unknown') + # maxdb db-api seems to double-execute NEXTVAL internally somewhere, + # throwing off the numbers for these tests... + def test_implicit_sequence_exec(self): + s = Sequence("my_sequence", metadata=MetaData(testing.db)) + s.create() + try: + x = s.execute() + eq_(x, 1) + finally: + s.drop() + + @testing.fails_on('maxdb', 'FIXME: unknown') + def teststandalone_explicit(self): + s = Sequence("my_sequence") + s.create(bind=testing.db) + try: + x = s.execute(testing.db) + eq_(x, 1) + finally: + s.drop(testing.db) + + def test_checkfirst(self): + s = Sequence("my_sequence") + s.create(testing.db, checkfirst=False) + s.create(testing.db, checkfirst=True) + s.drop(testing.db, checkfirst=False) + s.drop(testing.db, checkfirst=True) + + @testing.fails_on('maxdb', 'FIXME: unknown') + def teststandalone2(self): + x = cartitems.c.cart_id.sequence.execute() + self.assert_(1 <= x <= 4) + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + diff --git a/test/sql/test_functions.py b/test/sql/test_functions.py new file mode 100644 index 000000000..e9bf49ce3 --- /dev/null +++ b/test/sql/test_functions.py @@ -0,0 +1,317 @@ +from sqlalchemy.test.testing import eq_ +import datetime +from sqlalchemy import * +from sqlalchemy.sql import table, column +from sqlalchemy import databases, sql, util +from sqlalchemy.sql.compiler import BIND_TEMPLATES +from sqlalchemy.engine import default +from sqlalchemy.test.engines import all_dialects +from sqlalchemy import types as sqltypes +from sqlalchemy.test import * +from sqlalchemy.sql.functions import GenericFunction +from sqlalchemy.test.testing import eq_ +from decimal import Decimal as _python_Decimal + +from sqlalchemy.databases import * + +# FIXME! +dialects = [d for d in all_dialects() if d.name not in ('access', 'informix')] + + +class CompileTest(TestBase, AssertsCompiledSQL): + def test_compile(self): + for dialect in dialects: + bindtemplate = BIND_TEMPLATES[dialect.paramstyle] + self.assert_compile(func.current_timestamp(), "CURRENT_TIMESTAMP", dialect=dialect) + self.assert_compile(func.localtime(), "LOCALTIME", dialect=dialect) + if isinstance(dialect, firebird.dialect): + self.assert_compile(func.nosuchfunction(), "nosuchfunction", dialect=dialect) + else: + self.assert_compile(func.nosuchfunction(), "nosuchfunction()", dialect=dialect) + + # test generic function compile + class fake_func(GenericFunction): + __return_type__ = sqltypes.Integer + + def __init__(self, arg, **kwargs): + GenericFunction.__init__(self, args=[arg], **kwargs) + + self.assert_compile(fake_func('foo'), "fake_func(%s)" % bindtemplate % {'name':'param_1', 'position':1}, dialect=dialect) + + def test_use_labels(self): + self.assert_compile(select([func.foo()], use_labels=True), + "SELECT foo() AS foo_1" + ) + def test_underscores(self): + self.assert_compile(func.if_(), "if()") + + def test_generic_now(self): + assert isinstance(func.now().type, sqltypes.DateTime) + + for ret, dialect in [ + ('CURRENT_TIMESTAMP', sqlite.dialect()), + ('now()', postgres.dialect()), + ('now()', mysql.dialect()), + ('CURRENT_TIMESTAMP', oracle.dialect()) + ]: + self.assert_compile(func.now(), ret, dialect=dialect) + + def test_generic_random(self): + assert func.random().type == sqltypes.NULLTYPE + assert isinstance(func.random(type_=Integer).type, Integer) + + for ret, dialect in [ + ('random()', sqlite.dialect()), + ('random()', postgres.dialect()), + ('rand()', mysql.dialect()), + ('random()', oracle.dialect()) + ]: + self.assert_compile(func.random(), ret, dialect=dialect) + + def test_generic_count(self): + assert isinstance(func.count().type, sqltypes.Integer) + + self.assert_compile(func.count(), 'count(*)') + self.assert_compile(func.count(1), 'count(:param_1)') + c = column('abc') + self.assert_compile(func.count(c), 'count(abc)') + + def test_constructor(self): + try: + func.current_timestamp('somearg') + assert False + except TypeError: + assert True + + try: + func.char_length('a', 'b') + assert False + except TypeError: + assert True + + try: + func.char_length() + assert False + except TypeError: + assert True + + def test_return_type_detection(self): + + for fn in [func.coalesce, func.max, func.min, func.sum]: + for args, type_ in [ + ((datetime.date(2007, 10, 5), datetime.date(2005, 10, 15)), sqltypes.Date), + ((3, 5), sqltypes.Integer), + ((_python_Decimal(3), _python_Decimal(5)), sqltypes.Numeric), + (("foo", "bar"), sqltypes.String), + ((datetime.datetime(2007, 10, 5, 8, 3, 34), datetime.datetime(2005, 10, 15, 14, 45, 33)), sqltypes.DateTime) + ]: + assert isinstance(fn(*args).type, type_), "%s / %s" % (fn(), type_) + + assert isinstance(func.concat("foo", "bar").type, sqltypes.String) + + + def test_assorted(self): + table1 = table('mytable', + column('myid', Integer), + ) + + table2 = table( + 'myothertable', + column('otherid', Integer), + ) + + # test an expression with a function + self.assert_compile(func.lala(3, 4, literal("five"), table1.c.myid) * table2.c.otherid, + "lala(:lala_1, :lala_2, :param_1, mytable.myid) * myothertable.otherid") + + # test it in a SELECT + self.assert_compile(select([func.count(table1.c.myid)]), + "SELECT count(mytable.myid) AS count_1 FROM mytable") + + # test a "dotted" function name + self.assert_compile(select([func.foo.bar.lala(table1.c.myid)]), + "SELECT foo.bar.lala(mytable.myid) AS lala_1 FROM mytable") + + # test the bind parameter name with a "dotted" function name is only the name + # (limits the length of the bind param name) + self.assert_compile(select([func.foo.bar.lala(12)]), + "SELECT foo.bar.lala(:lala_2) AS lala_1") + + # test a dotted func off the engine itself + self.assert_compile(func.lala.hoho(7), "lala.hoho(:hoho_1)") + + # test None becomes NULL + self.assert_compile(func.my_func(1,2,None,3), "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)") + + # test pickling + self.assert_compile(util.pickle.loads(util.pickle.dumps(func.my_func(1, 2, None, 3))), "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)") + + # assert func raises AttributeError for __bases__ attribute, since its not a class + # fixes pydoc + try: + func.__bases__ + assert False + except AttributeError: + assert True + + def test_functions_with_cols(self): + users = table('users', column('id'), column('name'), column('fullname')) + calculate = select([column('q'), column('z'), column('r')], + from_obj=[func.calculate(bindparam('x'), bindparam('y'))]) + + self.assert_compile(select([users], users.c.id > calculate.c.z), + "SELECT users.id, users.name, users.fullname " + "FROM users, (SELECT q, z, r " + "FROM calculate(:x, :y)) " + "WHERE users.id > z" + ) + + s = select([users], users.c.id.between( + calculate.alias('c1').unique_params(x=17, y=45).c.z, + calculate.alias('c2').unique_params(x=5, y=12).c.z)) + + self.assert_compile(s, + "SELECT users.id, users.name, users.fullname " + "FROM users, (SELECT q, z, r " + "FROM calculate(:x_1, :y_1)) AS c1, (SELECT q, z, r " + "FROM calculate(:x_2, :y_2)) AS c2 " + "WHERE users.id BETWEEN c1.z AND c2.z" + , checkparams={'y_1': 45, 'x_1': 17, 'y_2': 12, 'x_2': 5}) + + +class ExecuteTest(TestBase): + + def test_standalone_execute(self): + x = testing.db.func.current_date().execute().scalar() + y = testing.db.func.current_date().select().execute().scalar() + z = testing.db.func.current_date().scalar() + assert (x == y == z) is True + + # ansi func + x = testing.db.func.current_date() + assert isinstance(x.type, Date) + assert isinstance(x.execute().scalar(), datetime.date) + + def test_conn_execute(self): + conn = testing.db.connect() + try: + x = conn.execute(func.current_date()).scalar() + y = conn.execute(func.current_date().select()).scalar() + z = conn.scalar(func.current_date()) + finally: + conn.close() + assert (x == y == z) is True + + def test_update(self): + """ + Tests sending functions and SQL expressions to the VALUES and SET + clauses of INSERT/UPDATE instances, and that column-level defaults + get overridden. + """ + + meta = MetaData(testing.db) + t = Table('t1', meta, + Column('id', Integer, Sequence('t1idseq', optional=True), primary_key=True), + Column('value', Integer) + ) + t2 = Table('t2', meta, + Column('id', Integer, Sequence('t2idseq', optional=True), primary_key=True), + Column('value', Integer, default=7), + Column('stuff', String(20), onupdate="thisisstuff") + ) + meta.create_all() + try: + t.insert(values=dict(value=func.length("one"))).execute() + assert t.select().execute().fetchone()['value'] == 3 + t.update(values=dict(value=func.length("asfda"))).execute() + assert t.select().execute().fetchone()['value'] == 5 + + r = t.insert(values=dict(value=func.length("sfsaafsda"))).execute() + id = r.last_inserted_ids()[0] + assert t.select(t.c.id==id).execute().fetchone()['value'] == 9 + t.update(values={t.c.value:func.length("asdf")}).execute() + assert t.select().execute().fetchone()['value'] == 4 + print "--------------------------" + t2.insert().execute() + t2.insert(values=dict(value=func.length("one"))).execute() + t2.insert(values=dict(value=func.length("asfda") + -19)).execute(stuff="hi") + + res = exec_sorted(select([t2.c.value, t2.c.stuff])) + eq_(res, [(-14, 'hi'), (3, None), (7, None)]) + + t2.update(values=dict(value=func.length("asdsafasd"))).execute(stuff="some stuff") + assert select([t2.c.value, t2.c.stuff]).execute().fetchall() == [(9,"some stuff"), (9,"some stuff"), (9,"some stuff")] + + t2.delete().execute() + + t2.insert(values=dict(value=func.length("one") + 8)).execute() + assert t2.select().execute().fetchone()['value'] == 11 + + t2.update(values=dict(value=func.length("asfda"))).execute() + assert select([t2.c.value, t2.c.stuff]).execute().fetchone() == (5, "thisisstuff") + + t2.update(values={t2.c.value:func.length("asfdaasdf"), t2.c.stuff:"foo"}).execute() + print "HI", select([t2.c.value, t2.c.stuff]).execute().fetchone() + assert select([t2.c.value, t2.c.stuff]).execute().fetchone() == (9, "foo") + finally: + meta.drop_all() + + @testing.fails_on_everything_except('postgres') + def test_as_from(self): + # TODO: shouldnt this work on oracle too ? + x = testing.db.func.current_date().execute().scalar() + y = testing.db.func.current_date().select().execute().scalar() + z = testing.db.func.current_date().scalar() + w = select(['*'], from_obj=[testing.db.func.current_date()]).scalar() + + # construct a column-based FROM object out of a function, like in [ticket:172] + s = select([sql.column('date', type_=DateTime)], from_obj=[testing.db.func.current_date()]) + q = s.execute().fetchone()[s.c.date] + r = s.alias('datequery').select().scalar() + + assert x == y == z == w == q == r + + def test_extract_bind(self): + """Basic common denominator execution tests for extract()""" + + date = datetime.date(2010, 5, 1) + + def execute(field): + return testing.db.execute(select([extract(field, date)])).scalar() + + assert execute('year') == 2010 + assert execute('month') == 5 + assert execute('day') == 1 + + date = datetime.datetime(2010, 5, 1, 12, 11, 10) + + assert execute('year') == 2010 + assert execute('month') == 5 + assert execute('day') == 1 + + def test_extract_expression(self): + meta = MetaData(testing.db) + table = Table('test', meta, + Column('dt', DateTime), + Column('d', Date)) + meta.create_all() + try: + table.insert().execute( + {'dt': datetime.datetime(2010, 5, 1, 12, 11, 10), + 'd': datetime.date(2010, 5, 1) }) + rs = select([extract('year', table.c.dt), + extract('month', table.c.d)]).execute() + row = rs.fetchone() + assert row[0] == 2010 + assert row[1] == 5 + rs.close() + finally: + meta.drop_all() + + +def exec_sorted(statement, *args, **kw): + """Executes a statement and returns a sorted list plain tuple rows.""" + + return sorted([tuple(row) + for row in statement.execute(*args, **kw).fetchall()]) + diff --git a/test/sql/test_generative.py b/test/sql/test_generative.py new file mode 100644 index 000000000..ca427ca5f --- /dev/null +++ b/test/sql/test_generative.py @@ -0,0 +1,818 @@ +from sqlalchemy import * +from sqlalchemy.sql import table, column, ClauseElement +from sqlalchemy.sql.expression import _clone, _from_objects +from sqlalchemy.test import * +from sqlalchemy.sql.visitors import * +from sqlalchemy import util +from sqlalchemy.sql import util as sql_util + + +class TraversalTest(TestBase, AssertsExecutionResults): + """test ClauseVisitor's traversal, particularly its ability to copy and modify + a ClauseElement in place.""" + + @classmethod + def setup_class(cls): + global A, B + + # establish two ficticious ClauseElements. + # define deep equality semantics as well as deep identity semantics. + class A(ClauseElement): + __visit_name__ = 'a' + + def __init__(self, expr): + self.expr = expr + + def is_other(self, other): + return other is self + + __hash__ = ClauseElement.__hash__ + + def __eq__(self, other): + return other.expr == self.expr + + def __ne__(self, other): + return other.expr != self.expr + + def __str__(self): + return "A(%s)" % repr(self.expr) + + class B(ClauseElement): + __visit_name__ = 'b' + + def __init__(self, *items): + self.items = items + + def is_other(self, other): + if other is not self: + return False + for i1, i2 in zip(self.items, other.items): + if i1 is not i2: + return False + return True + + __hash__ = ClauseElement.__hash__ + + def __eq__(self, other): + for i1, i2 in zip(self.items, other.items): + if i1 != i2: + return False + return True + + def __ne__(self, other): + for i1, i2 in zip(self.items, other.items): + if i1 != i2: + return True + return False + + def _copy_internals(self, clone=_clone): + self.items = [clone(i) for i in self.items] + + def get_children(self, **kwargs): + return self.items + + def __str__(self): + return "B(%s)" % repr([str(i) for i in self.items]) + + def test_test_classes(self): + a1 = A("expr1") + struct = B(a1, A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) + struct2 = B(a1, A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) + struct3 = B(a1, A("expr2"), B(A("expr1b"), A("expr2bmodified")), A("expr3")) + + assert a1.is_other(a1) + assert struct.is_other(struct) + assert struct == struct2 + assert struct != struct3 + assert not struct.is_other(struct2) + assert not struct.is_other(struct3) + + def test_clone(self): + struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) + + class Vis(CloningVisitor): + def visit_a(self, a): + pass + def visit_b(self, b): + pass + + vis = Vis() + s2 = vis.traverse(struct) + assert struct == s2 + assert not struct.is_other(s2) + + def test_no_clone(self): + struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) + + class Vis(ClauseVisitor): + def visit_a(self, a): + pass + def visit_b(self, b): + pass + + vis = Vis() + s2 = vis.traverse(struct) + assert struct == s2 + assert struct.is_other(s2) + + def test_change_in_place(self): + struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) + struct2 = B(A("expr1"), A("expr2modified"), B(A("expr1b"), A("expr2b")), A("expr3")) + struct3 = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2bmodified")), A("expr3")) + + class Vis(CloningVisitor): + def visit_a(self, a): + if a.expr == "expr2": + a.expr = "expr2modified" + def visit_b(self, b): + pass + + vis = Vis() + s2 = vis.traverse(struct) + assert struct != s2 + assert not struct.is_other(s2) + assert struct2 == s2 + + class Vis2(CloningVisitor): + def visit_a(self, a): + if a.expr == "expr2b": + a.expr = "expr2bmodified" + def visit_b(self, b): + pass + + vis2 = Vis2() + s3 = vis2.traverse(struct) + assert struct != s3 + assert struct3 == s3 + + def test_visit_name(self): + # override fns in testlib/schema.py + from sqlalchemy import Column + + class CustomObj(Column): + pass + + assert CustomObj.__visit_name__ == Column.__visit_name__ == 'column' + + foo, bar = CustomObj('foo', String), CustomObj('bar', String) + bin = foo == bar + s = set(ClauseVisitor().iterate(bin)) + assert set(ClauseVisitor().iterate(bin)) == set([foo, bar, bin]) + +class ClauseTest(TestBase, AssertsCompiledSQL): + """test copy-in-place behavior of various ClauseElements.""" + + @classmethod + def setup_class(cls): + global t1, t2 + t1 = table("table1", + column("col1"), + column("col2"), + column("col3"), + ) + t2 = table("table2", + column("col1"), + column("col2"), + column("col3"), + ) + + def test_binary(self): + clause = t1.c.col2 == t2.c.col2 + assert str(clause) == CloningVisitor().traverse(clause) + + def test_binary_anon_label_quirk(self): + t = table('t1', column('col1')) + + + f = t.c.col1 * 5 + self.assert_compile(select([f]), "SELECT t1.col1 * :col1_1 AS anon_1 FROM t1") + + f.anon_label + + a = t.alias() + f = sql_util.ClauseAdapter(a).traverse(f) + + self.assert_compile(select([f]), "SELECT t1_1.col1 * :col1_1 AS anon_1 FROM t1 AS t1_1") + + def test_join(self): + clause = t1.join(t2, t1.c.col2==t2.c.col2) + c1 = str(clause) + assert str(clause) == str(CloningVisitor().traverse(clause)) + + class Vis(CloningVisitor): + def visit_binary(self, binary): + binary.right = t2.c.col3 + + clause2 = Vis().traverse(clause) + assert c1 == str(clause) + assert str(clause2) == str(t1.join(t2, t1.c.col2==t2.c.col3)) + + def test_text(self): + clause = text("select * from table where foo=:bar", bindparams=[bindparam('bar')]) + c1 = str(clause) + class Vis(CloningVisitor): + def visit_textclause(self, text): + text.text = text.text + " SOME MODIFIER=:lala" + text.bindparams['lala'] = bindparam('lala') + + clause2 = Vis().traverse(clause) + assert c1 == str(clause) + assert str(clause2) == c1 + " SOME MODIFIER=:lala" + assert clause.bindparams.keys() == ['bar'] + assert set(clause2.bindparams.keys()) == set(['bar', 'lala']) + + def test_select(self): + s2 = select([t1]) + s2_assert = str(s2) + s3_assert = str(select([t1], t1.c.col2==7)) + class Vis(CloningVisitor): + def visit_select(self, select): + select.append_whereclause(t1.c.col2==7) + s3 = Vis().traverse(s2) + assert str(s3) == s3_assert + assert str(s2) == s2_assert + print str(s2) + print str(s3) + class Vis(ClauseVisitor): + def visit_select(self, select): + select.append_whereclause(t1.c.col2==7) + Vis().traverse(s2) + assert str(s2) == s3_assert + + print "------------------" + + s4_assert = str(select([t1], and_(t1.c.col2==7, t1.c.col3==9))) + class Vis(CloningVisitor): + def visit_select(self, select): + select.append_whereclause(t1.c.col3==9) + s4 = Vis().traverse(s3) + print str(s3) + print str(s4) + assert str(s4) == s4_assert + assert str(s3) == s3_assert + + print "------------------" + s5_assert = str(select([t1], and_(t1.c.col2==7, t1.c.col1==9))) + class Vis(CloningVisitor): + def visit_binary(self, binary): + if binary.left is t1.c.col3: + binary.left = t1.c.col1 + binary.right = bindparam("col1", unique=True) + s5 = Vis().traverse(s4) + print str(s4) + print str(s5) + assert str(s5) == s5_assert + assert str(s4) == s4_assert + + def test_union(self): + u = union(t1.select(), t2.select()) + u2 = CloningVisitor().traverse(u) + assert str(u) == str(u2) + assert [str(c) for c in u2.c] == [str(c) for c in u.c] + + u = union(t1.select(), t2.select()) + cols = [str(c) for c in u.c] + u2 = CloningVisitor().traverse(u) + assert str(u) == str(u2) + assert [str(c) for c in u2.c] == cols + + s1 = select([t1], t1.c.col1 == bindparam('id_param')) + s2 = select([t2]) + u = union(s1, s2) + + u2 = u.params(id_param=7) + u3 = u.params(id_param=10) + assert str(u) == str(u2) == str(u3) + assert u2.compile().params == {'id_param':7} + assert u3.compile().params == {'id_param':10} + + def test_adapt_union(self): + u = union(t1.select().where(t1.c.col1==4), t1.select().where(t1.c.col1==5)).alias() + + assert sql_util.ClauseAdapter(u).traverse(t1) is u + + def test_binds(self): + """test that unique bindparams change their name upon clone() to prevent conflicts""" + + s = select([t1], t1.c.col1==bindparam(None, unique=True)).alias() + s2 = CloningVisitor().traverse(s).alias() + s3 = select([s], s.c.col2==s2.c.col2) + + self.assert_compile(s3, "SELECT anon_1.col1, anon_1.col2, anon_1.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, "\ + "table1.col3 AS col3 FROM table1 WHERE table1.col1 = :param_1) AS anon_1, "\ + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :param_2) AS anon_2 "\ + "WHERE anon_1.col2 = anon_2.col2") + + s = select([t1], t1.c.col1==4).alias() + s2 = CloningVisitor().traverse(s).alias() + s3 = select([s], s.c.col2==s2.c.col2) + self.assert_compile(s3, "SELECT anon_1.col1, anon_1.col2, anon_1.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, "\ + "table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1, "\ + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_2) AS anon_2 "\ + "WHERE anon_1.col2 = anon_2.col2") + + @testing.emits_warning('.*replaced by another column with the same key') + def test_alias(self): + subq = t2.select().alias('subq') + s = select([t1.c.col1, subq.c.col1], from_obj=[t1, subq, t1.join(subq, t1.c.col1==subq.c.col2)]) + orig = str(s) + s2 = CloningVisitor().traverse(s) + assert orig == str(s) == str(s2) + + s4 = CloningVisitor().traverse(s2) + assert orig == str(s) == str(s2) == str(s4) + + s3 = sql_util.ClauseAdapter(table('foo')).traverse(s) + assert orig == str(s) == str(s3) + + s4 = sql_util.ClauseAdapter(table('foo')).traverse(s3) + assert orig == str(s) == str(s3) == str(s4) + + def test_correlated_select(self): + s = select(['*'], t1.c.col1==t2.c.col1, from_obj=[t1, t2]).correlate(t2) + class Vis(CloningVisitor): + def visit_select(self, select): + select.append_whereclause(t1.c.col2==7) + + self.assert_compile(Vis().traverse(s), "SELECT * FROM table1 WHERE table1.col1 = table2.col1 AND table1.col2 = :col2_1") + + def test_this_thing(self): + s = select([t1]).where(t1.c.col1=='foo').alias() + s2 = select([s.c.col1]) + + self.assert_compile(s2, "SELECT anon_1.col1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1") + t1a = t1.alias() + s2 = sql_util.ClauseAdapter(t1a).traverse(s2) + self.assert_compile(s2, "SELECT anon_1.col1 FROM (SELECT table1_1.col1 AS col1, table1_1.col2 AS col2, table1_1.col3 AS col3 FROM table1 AS table1_1 WHERE table1_1.col1 = :col1_1) AS anon_1") + + def test_select_fromtwice(self): + t1a = t1.alias() + + s = select([1], t1.c.col1==t1a.c.col1, from_obj=t1a).correlate(t1) + self.assert_compile(s, "SELECT 1 FROM table1 AS table1_1 WHERE table1.col1 = table1_1.col1") + + s = CloningVisitor().traverse(s) + self.assert_compile(s, "SELECT 1 FROM table1 AS table1_1 WHERE table1.col1 = table1_1.col1") + + s = select([t1]).where(t1.c.col1=='foo').alias() + + s2 = select([1], t1.c.col1==s.c.col1, from_obj=s).correlate(t1) + self.assert_compile(s2, "SELECT 1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1 WHERE table1.col1 = anon_1.col1") + s2 = ReplacingCloningVisitor().traverse(s2) + self.assert_compile(s2, "SELECT 1 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 WHERE table1.col1 = :col1_1) AS anon_1 WHERE table1.col1 = anon_1.col1") + +class ClauseAdapterTest(TestBase, AssertsCompiledSQL): + @classmethod + def setup_class(cls): + global t1, t2 + t1 = table("table1", + column("col1"), + column("col2"), + column("col3"), + ) + t2 = table("table2", + column("col1"), + column("col2"), + column("col3"), + ) + + def test_correlation_on_clone(self): + t1alias = t1.alias('t1alias') + t2alias = t2.alias('t2alias') + vis = sql_util.ClauseAdapter(t1alias) + + s = select(['*'], from_obj=[t1alias, t2alias]).as_scalar() + assert t2alias in s._froms + assert t1alias in s._froms + + self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") + s = vis.traverse(s) + + assert t2alias not in s._froms # not present because it's been cloned + + assert t1alias in s._froms # present because the adapter placed it there + + # correlate list on "s" needs to take into account the full _cloned_set for each element in _froms when correlating + self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") + + s = select(['*'], from_obj=[t1alias, t2alias]).correlate(t2alias).as_scalar() + self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") + s = vis.traverse(s) + self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") + s = CloningVisitor().traverse(s) + self.assert_compile(select(['*'], t2alias.c.col1==s), "SELECT * FROM table2 AS t2alias WHERE t2alias.col1 = (SELECT * FROM table1 AS t1alias)") + + s = select(['*']).where(t1.c.col1==t2.c.col1).as_scalar() + self.assert_compile(select([t1.c.col1, s]), "SELECT table1.col1, (SELECT * FROM table2 WHERE table1.col1 = table2.col1) AS anon_1 FROM table1") + vis = sql_util.ClauseAdapter(t1alias) + s = vis.traverse(s) + self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") + s = CloningVisitor().traverse(s) + self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") + + s = select(['*']).where(t1.c.col1==t2.c.col1).correlate(t1).as_scalar() + self.assert_compile(select([t1.c.col1, s]), "SELECT table1.col1, (SELECT * FROM table2 WHERE table1.col1 = table2.col1) AS anon_1 FROM table1") + vis = sql_util.ClauseAdapter(t1alias) + s = vis.traverse(s) + self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") + s = CloningVisitor().traverse(s) + self.assert_compile(select([t1alias.c.col1, s]), "SELECT t1alias.col1, (SELECT * FROM table2 WHERE t1alias.col1 = table2.col1) AS anon_1 FROM table1 AS t1alias") + + @testing.fails_on_everything_except() + def test_joins_dont_adapt(self): + # adapting to a join, i.e. ClauseAdapter(t1.join(t2)), doesn't make much sense. + # ClauseAdapter doesn't make any changes if it's against a straight join. + users = table('users', column('id')) + addresses = table('addresses', column('id'), column('user_id')) + + ualias = users.alias() + + s = select([func.count(addresses.c.id)], users.c.id==addresses.c.user_id).correlate(users) #.as_scalar().label(None) + s= sql_util.ClauseAdapter(ualias).traverse(s) + + j1 = addresses.join(ualias, addresses.c.user_id==ualias.c.id) + + self.assert_compile(sql_util.ClauseAdapter(j1).traverse(s), "SELECT count(addresses.id) AS count_1 FROM addresses WHERE users_1.id = addresses.user_id") + + def test_table_to_alias(self): + + t1alias = t1.alias('t1alias') + + vis = sql_util.ClauseAdapter(t1alias) + ff = vis.traverse(func.count(t1.c.col1).label('foo')) + assert list(_from_objects(ff)) == [t1alias] + + self.assert_compile(vis.traverse(select(['*'], from_obj=[t1])), "SELECT * FROM table1 AS t1alias") + self.assert_compile(select(['*'], t1.c.col1==t2.c.col2), "SELECT * FROM table1, table2 WHERE table1.col1 = table2.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2)), "SELECT * FROM table1 AS t1alias, table2 WHERE t1alias.col1 = table2.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2])), "SELECT * FROM table1 AS t1alias, table2 WHERE t1alias.col1 = table2.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t1)), "SELECT * FROM table2 WHERE t1alias.col1 = table2.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t2)), "SELECT * FROM table1 AS t1alias WHERE t1alias.col1 = table2.col2") + + self.assert_compile(vis.traverse(case([(t1.c.col1==5, t1.c.col2)], else_=t1.c.col1)), + "CASE WHEN (t1alias.col1 = :col1_1) THEN t1alias.col2 ELSE t1alias.col1 END" + ) + self.assert_compile(vis.traverse(case([(5, t1.c.col2)], value=t1.c.col1, else_=t1.c.col1)), + "CASE t1alias.col1 WHEN :param_1 THEN t1alias.col2 ELSE t1alias.col1 END" + ) + + + s = select(['*'], from_obj=[t1]).alias('foo') + self.assert_compile(s.select(), "SELECT foo.* FROM (SELECT * FROM table1) AS foo") + self.assert_compile(vis.traverse(s.select()), "SELECT foo.* FROM (SELECT * FROM table1 AS t1alias) AS foo") + self.assert_compile(s.select(), "SELECT foo.* FROM (SELECT * FROM table1) AS foo") + + ff = vis.traverse(func.count(t1.c.col1).label('foo')) + self.assert_compile(select([ff]), "SELECT count(t1alias.col1) AS foo FROM table1 AS t1alias") + assert list(_from_objects(ff)) == [t1alias] + +# TODO: + # self.assert_compile(vis.traverse(select([func.count(t1.c.col1).label('foo')]), clone=True), "SELECT count(t1alias.col1) AS foo FROM table1 AS t1alias") + + t2alias = t2.alias('t2alias') + vis.chain(sql_util.ClauseAdapter(t2alias)) + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2)), "SELECT * FROM table1 AS t1alias, table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2])), "SELECT * FROM table1 AS t1alias, table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t1)), "SELECT * FROM table2 AS t2alias WHERE t1alias.col1 = t2alias.col2") + self.assert_compile(vis.traverse(select(['*'], t1.c.col1==t2.c.col2, from_obj=[t1, t2]).correlate(t2)), "SELECT * FROM table1 AS t1alias WHERE t1alias.col1 = t2alias.col2") + + def test_include_exclude(self): + m = MetaData() + a=Table( 'a',m, + Column( 'id', Integer, primary_key=True), + Column( 'xxx_id', Integer, ForeignKey( 'a.id', name='adf',use_alter=True ) ) + ) + + e = (a.c.id == a.c.xxx_id) + assert str(e) == "a.id = a.xxx_id" + b = a.alias() + + e = sql_util.ClauseAdapter( b, include= set([ a.c.id ]), + equivalents= { a.c.id: set([ a.c.id]) } + ).traverse( e) + + assert str(e) == "a_1.id = a.xxx_id" + + def test_recursive_equivalents(self): + m = MetaData() + a = Table('a', m, Column('x', Integer), Column('y', Integer)) + b = Table('b', m, Column('x', Integer), Column('y', Integer)) + c = Table('c', m, Column('x', Integer), Column('y', Integer)) + + # force a recursion overflow, by linking a.c.x<->c.c.x, and + # asking for a nonexistent col. corresponding_column should prevent + # endless depth. + adapt = sql_util.ClauseAdapter( b, equivalents= {a.c.x: set([ c.c.x]), c.c.x:set([a.c.x])}) + assert adapt._corresponding_column(a.c.x, False) is None + + def test_multilevel_equivalents(self): + m = MetaData() + a = Table('a', m, Column('x', Integer), Column('y', Integer)) + b = Table('b', m, Column('x', Integer), Column('y', Integer)) + c = Table('c', m, Column('x', Integer), Column('y', Integer)) + + alias = select([a]).select_from(a.join(b, a.c.x==b.c.x)).alias() + + # two levels of indirection from c.x->b.x->a.x, requires recursive + # corresponding_column call + adapt = sql_util.ClauseAdapter(alias, equivalents= {b.c.x: set([ a.c.x]), c.c.x:set([b.c.x])}) + assert adapt._corresponding_column(a.c.x, False) is alias.c.x + assert adapt._corresponding_column(c.c.x, False) is alias.c.x + + def test_join_to_alias(self): + metadata = MetaData() + a = Table('a', metadata, + Column('id', Integer, primary_key=True)) + b = Table('b', metadata, + Column('id', Integer, primary_key=True), + Column('aid', Integer, ForeignKey('a.id')), + ) + c = Table('c', metadata, + Column('id', Integer, primary_key=True), + Column('bid', Integer, ForeignKey('b.id')), + ) + + d = Table('d', metadata, + Column('id', Integer, primary_key=True), + Column('aid', Integer, ForeignKey('a.id')), + ) + + j1 = a.outerjoin(b) + j2 = select([j1], use_labels=True) + + j3 = c.join(j2, j2.c.b_id==c.c.bid) + + j4 = j3.outerjoin(d) + self.assert_compile(j4, "c JOIN (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid FROM a LEFT OUTER JOIN b ON a.id = b.aid) " + "ON b_id = c.bid" + " LEFT OUTER JOIN d ON a_id = d.aid") + j5 = j3.alias('foo') + j6 = sql_util.ClauseAdapter(j5).copy_and_process([j4])[0] + + # this statement takes c join(a join b), wraps it inside an aliased "select * from c join(a join b) AS foo". + # the outermost right side "left outer join d" stays the same, except "d" joins against foo.a_id instead + # of plain "a_id" + self.assert_compile(j6, "(SELECT c.id AS c_id, c.bid AS c_bid, a_id AS a_id, b_id AS b_id, b_aid AS b_aid FROM " + "c JOIN (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid FROM a LEFT OUTER JOIN b ON a.id = b.aid) " + "ON b_id = c.bid) AS foo" + " LEFT OUTER JOIN d ON foo.a_id = d.aid") + + def test_derived_from(self): + assert select([t1]).is_derived_from(t1) + assert not select([t2]).is_derived_from(t1) + assert not t1.is_derived_from(select([t1])) + assert t1.alias().is_derived_from(t1) + + + s1 = select([t1, t2]).alias('foo') + s2 = select([s1]).limit(5).offset(10).alias() + assert s2.is_derived_from(s1) + s2 = s2._clone() + assert s2.is_derived_from(s1) + + def test_aliasedselect_to_aliasedselect(self): + # original issue from ticket #904 + s1 = select([t1]).alias('foo') + s2 = select([s1]).limit(5).offset(10).alias() + + self.assert_compile(sql_util.ClauseAdapter(s2).traverse(s1), + "SELECT foo.col1, foo.col2, foo.col3 FROM (SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10") + + j = s1.outerjoin(t2, s1.c.col1==t2.c.col1) + self.assert_compile(sql_util.ClauseAdapter(s2).traverse(j).select(), + "SELECT anon_1.col1, anon_1.col2, anon_1.col3, table2.col1, table2.col2, table2.col3 FROM "\ + "(SELECT foo.col1 AS col1, foo.col2 AS col2, foo.col3 AS col3 FROM "\ + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10) AS anon_1 "\ + "LEFT OUTER JOIN table2 ON anon_1.col1 = table2.col1") + + talias = t1.alias('bar') + j = s1.outerjoin(talias, s1.c.col1==talias.c.col1) + self.assert_compile(sql_util.ClauseAdapter(s2).traverse(j).select(), + "SELECT anon_1.col1, anon_1.col2, anon_1.col3, bar.col1, bar.col2, bar.col3 FROM "\ + "(SELECT foo.col1 AS col1, foo.col2 AS col2, foo.col3 AS col3 FROM "\ + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1) AS foo LIMIT 5 OFFSET 10) AS anon_1 "\ + "LEFT OUTER JOIN table1 AS bar ON anon_1.col1 = bar.col1") + + def test_functions(self): + self.assert_compile(sql_util.ClauseAdapter(t1.alias()).traverse(func.count(t1.c.col1)), "count(table1_1.col1)") + + s = select([func.count(t1.c.col1)]) + self.assert_compile(sql_util.ClauseAdapter(t1.alias()).traverse(s), "SELECT count(table1_1.col1) AS count_1 FROM table1 AS table1_1") + + def test_recursive(self): + metadata = MetaData() + a = Table('a', metadata, + Column('id', Integer, primary_key=True)) + b = Table('b', metadata, + Column('id', Integer, primary_key=True), + Column('aid', Integer, ForeignKey('a.id')), + ) + c = Table('c', metadata, + Column('id', Integer, primary_key=True), + Column('bid', Integer, ForeignKey('b.id')), + ) + + d = Table('d', metadata, + Column('id', Integer, primary_key=True), + Column('aid', Integer, ForeignKey('a.id')), + ) + + u = union( + a.join(b).select().apply_labels(), + a.join(d).select().apply_labels() + ).alias() + + self.assert_compile( + sql_util.ClauseAdapter(u).traverse(select([c.c.bid]).where(c.c.bid==u.c.b_aid)), + "SELECT c.bid "\ + "FROM c, (SELECT a.id AS a_id, b.id AS b_id, b.aid AS b_aid "\ + "FROM a JOIN b ON a.id = b.aid UNION SELECT a.id AS a_id, d.id AS d_id, d.aid AS d_aid "\ + "FROM a JOIN d ON a.id = d.aid) AS anon_1 "\ + "WHERE c.bid = anon_1.b_aid" + ) + +class SpliceJoinsTest(TestBase, AssertsCompiledSQL): + @classmethod + def setup_class(cls): + global table1, table2, table3, table4 + def _table(name): + return table(name, column("col1"), column("col2"),column("col3")) + + table1, table2, table3, table4 = [_table(name) for name in ("table1", "table2", "table3", "table4")] + + def test_splice(self): + (t1, t2, t3, t4) = (table1, table2, table1.alias(), table2.alias()) + + j = t1.join(t2, t1.c.col1==t2.c.col1).join(t3, t2.c.col1==t3.c.col1).join(t4, t4.c.col1==t1.c.col1) + + s = select([t1]).where(t1.c.col2<5).alias() + + self.assert_compile(sql_util.splice_joins(s, j), + "(SELECT table1.col1 AS col1, table1.col2 AS col2, "\ + "table1.col3 AS col3 FROM table1 WHERE table1.col2 < :col2_1) AS anon_1 "\ + "JOIN table2 ON anon_1.col1 = table2.col1 JOIN table1 AS table1_1 ON table2.col1 = table1_1.col1 "\ + "JOIN table2 AS table2_1 ON table2_1.col1 = anon_1.col1") + + def test_stop_on(self): + (t1, t2, t3) = (table1, table2, table3) + + j1= t1.join(t2, t1.c.col1==t2.c.col1) + j2 = j1.join(t3, t2.c.col1==t3.c.col1) + + s = select([t1]).select_from(j1).alias() + + self.assert_compile(sql_util.splice_joins(s, j2), + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 JOIN table2 "\ + "ON table1.col1 = table2.col1) AS anon_1 JOIN table2 ON anon_1.col1 = table2.col1 JOIN table3 "\ + "ON table2.col1 = table3.col1" + ) + + self.assert_compile(sql_util.splice_joins(s, j2, j1), + "(SELECT table1.col1 AS col1, table1.col2 AS col2, table1.col3 AS col3 FROM table1 "\ + "JOIN table2 ON table1.col1 = table2.col1) AS anon_1 JOIN table3 ON table2.col1 = table3.col1") + + def test_splice_2(self): + t2a = table2.alias() + t3a = table3.alias() + j1 = table1.join(t2a, table1.c.col1==t2a.c.col1).join(t3a, t2a.c.col2==t3a.c.col2) + + t2b = table4.alias() + j2 = table1.join(t2b, table1.c.col3==t2b.c.col3) + + self.assert_compile(sql_util.splice_joins(table1, j1), + "table1 JOIN table2 AS table2_1 ON table1.col1 = table2_1.col1 "\ + "JOIN table3 AS table3_1 ON table2_1.col2 = table3_1.col2") + + self.assert_compile(sql_util.splice_joins(table1, j2), "table1 JOIN table4 AS table4_1 ON table1.col3 = table4_1.col3") + + self.assert_compile(sql_util.splice_joins(sql_util.splice_joins(table1, j1), j2), + "table1 JOIN table2 AS table2_1 ON table1.col1 = table2_1.col1 "\ + "JOIN table3 AS table3_1 ON table2_1.col2 = table3_1.col2 "\ + "JOIN table4 AS table4_1 ON table1.col3 = table4_1.col3") + + +class SelectTest(TestBase, AssertsCompiledSQL): + """tests the generative capability of Select""" + + @classmethod + def setup_class(cls): + global t1, t2 + t1 = table("table1", + column("col1"), + column("col2"), + column("col3"), + ) + t2 = table("table2", + column("col1"), + column("col2"), + column("col3"), + ) + + def test_select(self): + self.assert_compile(t1.select().where(t1.c.col1==5).order_by(t1.c.col3), + "SELECT table1.col1, table1.col2, table1.col3 FROM table1 WHERE table1.col1 = :col1_1 ORDER BY table1.col3") + + self.assert_compile(t1.select().select_from(select([t2], t2.c.col1==t1.c.col1)).order_by(t1.c.col3), + "SELECT table1.col1, table1.col2, table1.col3 FROM table1, (SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 "\ + "FROM table2 WHERE table2.col1 = table1.col1) ORDER BY table1.col3") + + s = select([t2], t2.c.col1==t1.c.col1, correlate=False) + s = s.correlate(t1).order_by(t2.c.col3) + self.assert_compile(t1.select().select_from(s).order_by(t1.c.col3), + "SELECT table1.col1, table1.col2, table1.col3 FROM table1, (SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 "\ + "FROM table2 WHERE table2.col1 = table1.col1 ORDER BY table2.col3) ORDER BY table1.col3") + + def test_columns(self): + s = t1.select() + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + select_copy = s.column('yyy') + self.assert_compile(select_copy, "SELECT table1.col1, table1.col2, table1.col3, yyy FROM table1") + assert s.columns is not select_copy.columns + assert s._columns is not select_copy._columns + assert s._raw_columns is not select_copy._raw_columns + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + + def test_froms(self): + s = t1.select() + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + select_copy = s.select_from(t2) + self.assert_compile(select_copy, "SELECT table1.col1, table1.col2, table1.col3 FROM table1, table2") + assert s._froms is not select_copy._froms + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + + def test_correlation(self): + s = select([t2], t1.c.col1==t2.c.col1) + self.assert_compile(s, "SELECT table2.col1, table2.col2, table2.col3 FROM table2, table1 WHERE table1.col1 = table2.col1") + s2 = select([t1], t1.c.col2==s.c.col2) + self.assert_compile(s2, "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " + "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " + "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") + + s3 = s.correlate(None) + self.assert_compile(select([t1], t1.c.col2==s3.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " + "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2, table1 " + "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") + self.assert_compile(select([t1], t1.c.col2==s.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " + "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " + "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") + s4 = s3.correlate(t1) + self.assert_compile(select([t1], t1.c.col2==s4.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " + "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2 " + "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") + self.assert_compile(select([t1], t1.c.col2==s3.c.col2), "SELECT table1.col1, table1.col2, table1.col3 FROM table1, " + "(SELECT table2.col1 AS col1, table2.col2 AS col2, table2.col3 AS col3 FROM table2, table1 " + "WHERE table1.col1 = table2.col1) WHERE table1.col2 = col2") + + def test_prefixes(self): + s = t1.select() + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + select_copy = s.prefix_with("FOOBER") + self.assert_compile(select_copy, "SELECT FOOBER table1.col1, table1.col2, table1.col3 FROM table1") + self.assert_compile(s, "SELECT table1.col1, table1.col2, table1.col3 FROM table1") + + +class InsertTest(TestBase, AssertsCompiledSQL): + """Tests the generative capability of Insert""" + + # fixme: consolidate converage from elsewhere here and expand + + @classmethod + def setup_class(cls): + global t1, t2 + t1 = table("table1", + column("col1"), + column("col2"), + column("col3"), + ) + t2 = table("table2", + column("col1"), + column("col2"), + column("col3"), + ) + + def test_prefixes(self): + i = t1.insert() + self.assert_compile(i, + "INSERT INTO table1 (col1, col2, col3) " + "VALUES (:col1, :col2, :col3)") + + gen = i.prefix_with("foober") + self.assert_compile(gen, + "INSERT foober INTO table1 (col1, col2, col3) " + "VALUES (:col1, :col2, :col3)") + + self.assert_compile(i, + "INSERT INTO table1 (col1, col2, col3) " + "VALUES (:col1, :col2, :col3)") + + i2 = t1.insert(prefixes=['squiznart']) + self.assert_compile(i2, + "INSERT squiznart INTO table1 (col1, col2, col3) " + "VALUES (:col1, :col2, :col3)") + + gen2 = i2.prefix_with("quux") + self.assert_compile(gen2, + "INSERT squiznart quux INTO " + "table1 (col1, col2, col3) " + "VALUES (:col1, :col2, :col3)") + diff --git a/test/sql/test_labels.py b/test/sql/test_labels.py new file mode 100644 index 000000000..b946b0ae9 --- /dev/null +++ b/test/sql/test_labels.py @@ -0,0 +1,195 @@ +from sqlalchemy.test.testing import assert_raises, assert_raises_message +from sqlalchemy import * +from sqlalchemy import exc as exceptions +from sqlalchemy.test import * +from sqlalchemy.engine import default + +IDENT_LENGTH = 29 + +class LabelTypeTest(TestBase): + def test_type(self): + m = MetaData() + t = Table('sometable', m, + Column('col1', Integer), + Column('col2', Float)) + assert isinstance(t.c.col1.label('hi').type, Integer) + assert isinstance(select([t.c.col2]).as_scalar().label('lala').type, Float) + +class LongLabelsTest(TestBase, AssertsCompiledSQL): + @classmethod + def setup_class(cls): + global metadata, table1, table2, maxlen + metadata = MetaData(testing.db) + table1 = Table("some_large_named_table", metadata, + Column("this_is_the_primarykey_column", Integer, Sequence("this_is_some_large_seq"), primary_key=True), + Column("this_is_the_data_column", String(30)) + ) + + table2 = Table("table_with_exactly_29_characs", metadata, + Column("this_is_the_primarykey_column", Integer, Sequence("some_seq"), primary_key=True), + Column("this_is_the_data_column", String(30)) + ) + + metadata.create_all() + + maxlen = testing.db.dialect.max_identifier_length + testing.db.dialect.max_identifier_length = IDENT_LENGTH + + def teardown(self): + table1.delete().execute() + + @classmethod + def teardown_class(cls): + metadata.drop_all() + testing.db.dialect.max_identifier_length = maxlen + + def test_too_long_name_disallowed(self): + m = MetaData(testing.db) + t1 = Table("this_name_is_too_long_for_what_were_doing_in_this_test", m, Column('foo', Integer)) + assert_raises(exceptions.IdentifierError, m.create_all) + assert_raises(exceptions.IdentifierError, m.drop_all) + assert_raises(exceptions.IdentifierError, t1.create) + assert_raises(exceptions.IdentifierError, t1.drop) + + def test_result(self): + table1.insert().execute(**{"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}) + table1.insert().execute(**{"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}) + table1.insert().execute(**{"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}) + table1.insert().execute(**{"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}) + + s = table1.select(use_labels=True, order_by=[table1.c.this_is_the_primarykey_column]) + r = s.execute() + result = [] + for row in r: + result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) + assert result == [ + (1, "data1"), + (2, "data2"), + (3, "data3"), + (4, "data4"), + ], repr(result) + + # some dialects such as oracle (and possibly ms-sql in a future version) + # generate a subquery for limits/offsets. + # ensure that the generated result map corresponds to the selected table, not + # the select query + r = s.limit(2).execute() + result = [] + for row in r: + result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) + assert result == [ + (1, "data1"), + (2, "data2"), + ], repr(result) + + r = s.limit(2).offset(1).execute() + result = [] + for row in r: + result.append((row[table1.c.this_is_the_primarykey_column], row[table1.c.this_is_the_data_column])) + assert result == [ + (2, "data2"), + (3, "data3"), + ], repr(result) + + def test_table_alias_names(self): + self.assert_compile( + table2.alias().select(), + "SELECT table_with_exactly_29_c_1.this_is_the_primarykey_column, table_with_exactly_29_c_1.this_is_the_data_column FROM table_with_exactly_29_characs AS table_with_exactly_29_c_1" + ) + + ta = table2.alias() + dialect = default.DefaultDialect() + dialect.max_identifier_length = IDENT_LENGTH + self.assert_compile( + select([table1, ta]).select_from(table1.join(ta, table1.c.this_is_the_data_column==ta.c.this_is_the_data_column)).\ + where(ta.c.this_is_the_data_column=='data3'), + + "SELECT some_large_named_table.this_is_the_primarykey_column, some_large_named_table.this_is_the_data_column, " + "table_with_exactly_29_c_1.this_is_the_primarykey_column, table_with_exactly_29_c_1.this_is_the_data_column FROM " + "some_large_named_table JOIN table_with_exactly_29_characs AS table_with_exactly_29_c_1 ON " + "some_large_named_table.this_is_the_data_column = table_with_exactly_29_c_1.this_is_the_data_column " + "WHERE table_with_exactly_29_c_1.this_is_the_data_column = :this_is_the_data_column_1", + dialect=dialect + ) + + table2.insert().execute( + {"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}, + {"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}, + {"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}, + {"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}, + ) + + r = table2.alias().select().execute() + assert r.fetchall() == [(x, "data%d" % x) for x in range(1, 5)] + + def test_colbinds(self): + table1.insert().execute(**{"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"}) + table1.insert().execute(**{"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"}) + table1.insert().execute(**{"this_is_the_primarykey_column":3, "this_is_the_data_column":"data3"}) + table1.insert().execute(**{"this_is_the_primarykey_column":4, "this_is_the_data_column":"data4"}) + + r = table1.select(table1.c.this_is_the_primarykey_column == 4).execute() + assert r.fetchall() == [(4, "data4")] + + r = table1.select(or_( + table1.c.this_is_the_primarykey_column == 4, + table1.c.this_is_the_primarykey_column == 2 + )).execute() + assert r.fetchall() == [(2, "data2"), (4, "data4")] + + def test_insert_no_pk(self): + table1.insert().execute(**{"this_is_the_data_column":"data1"}) + table1.insert().execute(**{"this_is_the_data_column":"data2"}) + table1.insert().execute(**{"this_is_the_data_column":"data3"}) + table1.insert().execute(**{"this_is_the_data_column":"data4"}) + + @testing.requires.subqueries + def test_subquery(self): + q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias('foo') + x = select([q]) + print x.execute().fetchall() + + @testing.requires.subqueries + def test_anon_alias(self): + compile_dialect = default.DefaultDialect() + compile_dialect.max_identifier_length = IDENT_LENGTH + + q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias() + x = select([q], use_labels=True) + + self.assert_compile(x, "SELECT anon_1.this_is_the_primarykey_column AS anon_1_this_is_the_prim_1, anon_1.this_is_the_data_column AS anon_1_this_is_the_data_2 " + "FROM (SELECT some_large_named_table.this_is_the_primarykey_column AS this_is_the_primarykey_column, some_large_named_table.this_is_the_data_column AS this_is_the_data_column " + "FROM some_large_named_table " + "WHERE some_large_named_table.this_is_the_primarykey_column = :this_is_the_primarykey__1) AS anon_1", dialect=compile_dialect) + + print x.execute().fetchall() + + def test_adjustable(self): + + q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias('foo') + x = select([q]) + + compile_dialect = default.DefaultDialect(label_length=10) + self.assert_compile(x, "SELECT foo.this_is_the_primarykey_column, foo.this_is_the_data_column FROM " + "(SELECT some_large_named_table.this_is_the_primarykey_column AS this_1, some_large_named_table.this_is_the_data_column " + "AS this_2 FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :this_1) AS foo", dialect=compile_dialect) + + compile_dialect = default.DefaultDialect(label_length=4) + self.assert_compile(x, "SELECT foo.this_is_the_primarykey_column, foo.this_is_the_data_column FROM " + "(SELECT some_large_named_table.this_is_the_primarykey_column AS _1, some_large_named_table.this_is_the_data_column AS _2 " + "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :_1) AS foo", dialect=compile_dialect) + + q = table1.select(table1.c.this_is_the_primarykey_column == 4).alias() + x = select([q], use_labels=True) + + compile_dialect = default.DefaultDialect(label_length=10) + self.assert_compile(x, "SELECT anon_1.this_is_the_primarykey_column AS anon_1, anon_1.this_is_the_data_column AS anon_2 FROM " + "(SELECT some_large_named_table.this_is_the_primarykey_column AS this_3, some_large_named_table.this_is_the_data_column AS this_4 " + "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :this_1) AS anon_1", dialect=compile_dialect) + + compile_dialect = default.DefaultDialect(label_length=4) + self.assert_compile(x, "SELECT _1.this_is_the_primarykey_column AS _1, _1.this_is_the_data_column AS _2 FROM " + "(SELECT some_large_named_table.this_is_the_primarykey_column AS _3, some_large_named_table.this_is_the_data_column AS _4 " + "FROM some_large_named_table WHERE some_large_named_table.this_is_the_primarykey_column = :_1) AS _1", dialect=compile_dialect) + + diff --git a/test/sql/test_query.py b/test/sql/test_query.py new file mode 100644 index 000000000..c9305b615 --- /dev/null +++ b/test/sql/test_query.py @@ -0,0 +1,1325 @@ +import datetime +from sqlalchemy import * +from sqlalchemy import exc, sql +from sqlalchemy.engine import default +from sqlalchemy.test import * +from sqlalchemy.test.testing import eq_ + +class QueryTest(TestBase): + + @classmethod + def setup_class(cls): + global users, users2, addresses, metadata + metadata = MetaData(testing.db) + users = Table('query_users', metadata, + Column('user_id', INT, primary_key = True), + Column('user_name', VARCHAR(20)), + ) + addresses = Table('query_addresses', metadata, + Column('address_id', Integer, primary_key=True), + Column('user_id', Integer, ForeignKey('query_users.user_id')), + Column('address', String(30))) + + users2 = Table('u2', metadata, + Column('user_id', INT, primary_key = True), + Column('user_name', VARCHAR(20)), + ) + metadata.create_all() + + def tearDown(self): + addresses.delete().execute() + users.delete().execute() + users2.delete().execute() + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def test_insert(self): + users.insert().execute(user_id = 7, user_name = 'jack') + assert users.count().scalar() == 1 + + def test_insert_heterogeneous_params(self): + users.insert().execute( + {'user_id':7, 'user_name':'jack'}, + {'user_id':8, 'user_name':'ed'}, + {'user_id':9} + ) + assert users.select().execute().fetchall() == [(7, 'jack'), (8, 'ed'), (9, None)] + + def test_update(self): + users.insert().execute(user_id = 7, user_name = 'jack') + assert users.count().scalar() == 1 + + users.update(users.c.user_id == 7).execute(user_name = 'fred') + assert users.select(users.c.user_id==7).execute().fetchone()['user_name'] == 'fred' + + def test_lastrow_accessor(self): + """Tests the last_inserted_ids() and lastrow_has_id() functions.""" + + def insert_values(table, values): + """ + Inserts a row into a table, returns the full list of values + INSERTed including defaults that fired off on the DB side and + detects rows that had defaults and post-fetches. + """ + + result = table.insert().execute(**values) + ret = values.copy() + + for col, id in zip(table.primary_key, result.last_inserted_ids()): + ret[col.key] = id + + if result.lastrow_has_defaults(): + criterion = and_(*[col==id for col, id in zip(table.primary_key, result.last_inserted_ids())]) + row = table.select(criterion).execute().fetchone() + for c in table.c: + ret[c.key] = row[c] + return ret + + for supported, table, values, assertvalues in [ + ( + {'unsupported':['sqlite']}, + Table("t1", metadata, + Column('id', Integer, Sequence('t1_id_seq', optional=True), primary_key=True), + Column('foo', String(30), primary_key=True)), + {'foo':'hi'}, + {'id':1, 'foo':'hi'} + ), + ( + {'unsupported':['sqlite']}, + Table("t2", metadata, + Column('id', Integer, Sequence('t2_id_seq', optional=True), primary_key=True), + Column('foo', String(30), primary_key=True), + Column('bar', String(30), server_default='hi') + ), + {'foo':'hi'}, + {'id':1, 'foo':'hi', 'bar':'hi'} + ), + ( + {'unsupported':[]}, + Table("t3", metadata, + Column("id", String(40), primary_key=True), + Column('foo', String(30), primary_key=True), + Column("bar", String(30)) + ), + {'id':'hi', 'foo':'thisisfoo', 'bar':"thisisbar"}, + {'id':'hi', 'foo':'thisisfoo', 'bar':"thisisbar"} + ), + ( + {'unsupported':[]}, + Table("t4", metadata, + Column('id', Integer, Sequence('t4_id_seq', optional=True), primary_key=True), + Column('foo', String(30), primary_key=True), + Column('bar', String(30), server_default='hi') + ), + {'foo':'hi', 'id':1}, + {'id':1, 'foo':'hi', 'bar':'hi'} + ), + ( + {'unsupported':[]}, + Table("t5", metadata, + Column('id', String(10), primary_key=True), + Column('bar', String(30), server_default='hi') + ), + {'id':'id1'}, + {'id':'id1', 'bar':'hi'}, + ), + ]: + if testing.db.name in supported['unsupported']: + continue + try: + table.create() + i = insert_values(table, values) + assert i == assertvalues, repr(i) + " " + repr(assertvalues) + finally: + table.drop() + + def test_row_iteration(self): + users.insert().execute( + {'user_id':7, 'user_name':'jack'}, + {'user_id':8, 'user_name':'ed'}, + {'user_id':9, 'user_name':'fred'}, + ) + r = users.select().execute() + l = [] + for row in r: + l.append(row) + self.assert_(len(l) == 3) + + @testing.fails_on('firebird', 'Data type unknown') + @testing.requires.subqueries + def test_anonymous_rows(self): + users.insert().execute( + {'user_id':7, 'user_name':'jack'}, + {'user_id':8, 'user_name':'ed'}, + {'user_id':9, 'user_name':'fred'}, + ) + + sel = select([users.c.user_id]).where(users.c.user_name=='jack').as_scalar() + for row in select([sel + 1, sel + 3], bind=users.bind).execute(): + assert row['anon_1'] == 8 + assert row['anon_2'] == 10 + + def test_order_by_label(self): + """test that a label within an ORDER BY works on each backend. + + simple labels in ORDER BYs now render as the actual labelname + which not every database supports. + + """ + users.insert().execute( + {'user_id':7, 'user_name':'jack'}, + {'user_id':8, 'user_name':'ed'}, + {'user_id':9, 'user_name':'fred'}, + ) + + concat = ("test: " + users.c.user_name).label('thedata') + eq_( + select([concat]).order_by(concat).execute().fetchall(), + [("test: ed",), ("test: fred",), ("test: jack",)] + ) + + concat = ("test: " + users.c.user_name).label('thedata') + eq_( + select([concat]).order_by(desc(concat)).execute().fetchall(), + [("test: jack",), ("test: fred",), ("test: ed",)] + ) + + concat = ("test: " + users.c.user_name).label('thedata') + eq_( + select([concat]).order_by(concat + "x").execute().fetchall(), + [("test: ed",), ("test: fred",), ("test: jack",)] + ) + + + def test_row_comparison(self): + users.insert().execute(user_id = 7, user_name = 'jack') + rp = users.select().execute().fetchone() + + self.assert_(rp == rp) + self.assert_(not(rp != rp)) + + equal = (7, 'jack') + + self.assert_(rp == equal) + self.assert_(equal == rp) + self.assert_(not (rp != equal)) + self.assert_(not (equal != equal)) + + @testing.fails_on('mssql', 'No support for boolean logic in column select.') + @testing.fails_on('oracle', 'FIXME: unknown') + def test_or_and_as_columns(self): + true, false = literal(True), literal(False) + + eq_(testing.db.execute(select([and_(true, false)])).scalar(), False) + eq_(testing.db.execute(select([and_(true, true)])).scalar(), True) + eq_(testing.db.execute(select([or_(true, false)])).scalar(), True) + eq_(testing.db.execute(select([or_(false, false)])).scalar(), False) + eq_(testing.db.execute(select([not_(or_(false, false))])).scalar(), True) + + row = testing.db.execute(select([or_(false, false).label("x"), and_(true, false).label("y")])).fetchone() + assert row.x == False + assert row.y == False + + row = testing.db.execute(select([or_(true, false).label("x"), and_(true, false).label("y")])).fetchone() + assert row.x == True + assert row.y == False + + def test_fetchmany(self): + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'ed') + users.insert().execute(user_id = 9, user_name = 'fred') + r = users.select().execute() + l = [] + for row in r.fetchmany(size=2): + l.append(row) + self.assert_(len(l) == 2, "fetchmany(size=2) got %s rows" % len(l)) + + def test_like_ops(self): + users.insert().execute( + {'user_id':1, 'user_name':'apples'}, + {'user_id':2, 'user_name':'oranges'}, + {'user_id':3, 'user_name':'bananas'}, + {'user_id':4, 'user_name':'legumes'}, + {'user_id':5, 'user_name':'hi % there'}, + ) + + for expr, result in ( + (select([users.c.user_id]).where(users.c.user_name.startswith('apple')), [(1,)]), + (select([users.c.user_id]).where(users.c.user_name.contains('i % t')), [(5,)]), + (select([users.c.user_id]).where(users.c.user_name.endswith('anas')), [(3,)]), + ): + eq_(expr.execute().fetchall(), result) + + + @testing.emits_warning('.*now automatically escapes.*') + def test_percents_in_text(self): + for expr, result in ( + (text("select 6 % 10"), 6), + (text("select 17 % 10"), 7), + (text("select '%'"), '%'), + (text("select '%%'"), '%%'), + (text("select '%%%'"), '%%%'), + (text("select 'hello % world'"), "hello % world") + ): + eq_(testing.db.scalar(expr), result) + + def test_ilike(self): + users.insert().execute( + {'user_id':1, 'user_name':'one'}, + {'user_id':2, 'user_name':'TwO'}, + {'user_id':3, 'user_name':'ONE'}, + {'user_id':4, 'user_name':'OnE'}, + ) + + eq_(select([users.c.user_id]).where(users.c.user_name.ilike('one')).execute().fetchall(), [(1, ), (3, ), (4, )]) + + eq_(select([users.c.user_id]).where(users.c.user_name.ilike('TWO')).execute().fetchall(), [(2, )]) + + if testing.against('postgres'): + eq_(select([users.c.user_id]).where(users.c.user_name.like('one')).execute().fetchall(), [(1, )]) + eq_(select([users.c.user_id]).where(users.c.user_name.like('TWO')).execute().fetchall(), []) + + + def test_compiled_execute(self): + users.insert().execute(user_id = 7, user_name = 'jack') + s = select([users], users.c.user_id==bindparam('id')).compile() + c = testing.db.connect() + assert c.execute(s, id=7).fetchall()[0]['user_id'] == 7 + + def test_compiled_insert_execute(self): + users.insert().compile().execute(user_id = 7, user_name = 'jack') + s = select([users], users.c.user_id==bindparam('id')).compile() + c = testing.db.connect() + assert c.execute(s, id=7).fetchall()[0]['user_id'] == 7 + + def test_repeated_bindparams(self): + """Tests that a BindParam can be used more than once. + + This should be run for DB-APIs with both positional and named + paramstyles. + """ + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'fred') + + u = bindparam('userid') + s = users.select(and_(users.c.user_name==u, users.c.user_name==u)) + r = s.execute(userid='fred').fetchall() + assert len(r) == 1 + + def test_bindparam_shortname(self): + """test the 'shortname' field on BindParamClause.""" + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'fred') + u = bindparam('userid', shortname='someshortname') + s = users.select(users.c.user_name==u) + r = s.execute(someshortname='fred').fetchall() + assert len(r) == 1 + + def test_bindparam_detection(self): + dialect = default.DefaultDialect(paramstyle='qmark') + prep = lambda q: str(sql.text(q).compile(dialect=dialect)) + + def a_eq(got, wanted): + if got != wanted: + print "Wanted %s" % wanted + print "Received %s" % got + self.assert_(got == wanted, got) + + a_eq(prep('select foo'), 'select foo') + a_eq(prep("time='12:30:00'"), "time='12:30:00'") + a_eq(prep(u"time='12:30:00'"), u"time='12:30:00'") + a_eq(prep(":this:that"), ":this:that") + a_eq(prep(":this :that"), "? ?") + a_eq(prep("(:this),(:that :other)"), "(?),(? ?)") + a_eq(prep("(:this),(:that:other)"), "(?),(:that:other)") + a_eq(prep("(:this),(:that,:other)"), "(?),(?,?)") + a_eq(prep("(:that_:other)"), "(:that_:other)") + a_eq(prep("(:that_ :other)"), "(? ?)") + a_eq(prep("(:that_other)"), "(?)") + a_eq(prep("(:that$other)"), "(?)") + a_eq(prep("(:that$:other)"), "(:that$:other)") + a_eq(prep(".:that$ :other."), ".? ?.") + + a_eq(prep(r'select \foo'), r'select \foo') + a_eq(prep(r"time='12\:30:00'"), r"time='12\:30:00'") + a_eq(prep(":this \:that"), "? :that") + a_eq(prep(r"(\:that$other)"), "(:that$other)") + a_eq(prep(r".\:that$ :other."), ".:that$ ?.") + + def test_delete(self): + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'fred') + print repr(users.select().execute().fetchall()) + + users.delete(users.c.user_name == 'fred').execute() + + print repr(users.select().execute().fetchall()) + + + + @testing.exclude('mysql', '<', (5, 0, 37), 'database bug') + def test_scalar_select(self): + """test that scalar subqueries with labels get their type propagated to the result set.""" + # mysql and/or mysqldb has a bug here, type isn't propagated for scalar + # subquery. + datetable = Table('datetable', metadata, + Column('id', Integer, primary_key=True), + Column('today', DateTime)) + datetable.create() + try: + datetable.insert().execute(id=1, today=datetime.datetime(2006, 5, 12, 12, 0, 0)) + s = select([datetable.alias('x').c.today]).as_scalar() + s2 = select([datetable.c.id, s.label('somelabel')]) + #print s2.c.somelabel.type + assert isinstance(s2.execute().fetchone()['somelabel'], datetime.datetime) + finally: + datetable.drop() + + def test_order_by(self): + """Exercises ORDER BY clause generation. + + Tests simple, compound, aliased and DESC clauses. + """ + + users.insert().execute(user_id=1, user_name='c') + users.insert().execute(user_id=2, user_name='b') + users.insert().execute(user_id=3, user_name='a') + + def a_eq(executable, wanted): + got = list(executable.execute()) + eq_(got, wanted) + + for labels in False, True: + a_eq(users.select(order_by=[users.c.user_id], + use_labels=labels), + [(1, 'c'), (2, 'b'), (3, 'a')]) + + a_eq(users.select(order_by=[users.c.user_name, users.c.user_id], + use_labels=labels), + [(3, 'a'), (2, 'b'), (1, 'c')]) + + a_eq(select([users.c.user_id.label('foo')], + use_labels=labels, + order_by=[users.c.user_id]), + [(1,), (2,), (3,)]) + + a_eq(select([users.c.user_id.label('foo'), users.c.user_name], + use_labels=labels, + order_by=[users.c.user_name, users.c.user_id]), + [(3, 'a'), (2, 'b'), (1, 'c')]) + + a_eq(users.select(distinct=True, + use_labels=labels, + order_by=[users.c.user_id]), + [(1, 'c'), (2, 'b'), (3, 'a')]) + + a_eq(select([users.c.user_id.label('foo')], + distinct=True, + use_labels=labels, + order_by=[users.c.user_id]), + [(1,), (2,), (3,)]) + + a_eq(select([users.c.user_id.label('a'), + users.c.user_id.label('b'), + users.c.user_name], + use_labels=labels, + order_by=[users.c.user_id]), + [(1, 1, 'c'), (2, 2, 'b'), (3, 3, 'a')]) + + a_eq(users.select(distinct=True, + use_labels=labels, + order_by=[desc(users.c.user_id)]), + [(3, 'a'), (2, 'b'), (1, 'c')]) + + a_eq(select([users.c.user_id.label('foo')], + distinct=True, + use_labels=labels, + order_by=[users.c.user_id.desc()]), + [(3,), (2,), (1,)]) + + def test_column_accessor(self): + users.insert().execute(user_id=1, user_name='john') + users.insert().execute(user_id=2, user_name='jack') + addresses.insert().execute(address_id=1, user_id=2, address='foo@bar.com') + + r = users.select(users.c.user_id==2).execute().fetchone() + self.assert_(r.user_id == r['user_id'] == r[users.c.user_id] == 2) + self.assert_(r.user_name == r['user_name'] == r[users.c.user_name] == 'jack') + + r = text("select * from query_users where user_id=2", bind=testing.db).execute().fetchone() + self.assert_(r.user_id == r['user_id'] == r[users.c.user_id] == 2) + self.assert_(r.user_name == r['user_name'] == r[users.c.user_name] == 'jack') + + # test slices + r = text("select * from query_addresses", bind=testing.db).execute().fetchone() + self.assert_(r[0:1] == (1,)) + self.assert_(r[1:] == (2, 'foo@bar.com')) + self.assert_(r[:-1] == (1, 2)) + + # test a little sqlite weirdness - with the UNION, cols come back as "query_users.user_id" in cursor.description + r = text("select query_users.user_id, query_users.user_name from query_users " + "UNION select query_users.user_id, query_users.user_name from query_users", bind=testing.db).execute().fetchone() + self.assert_(r['user_id']) == 1 + self.assert_(r['user_name']) == "john" + + # test using literal tablename.colname + r = text('select query_users.user_id AS "query_users.user_id", query_users.user_name AS "query_users.user_name" from query_users', bind=testing.db).execute().fetchone() + self.assert_(r['query_users.user_id']) == 1 + self.assert_(r['query_users.user_name']) == "john" + + def test_row_as_args(self): + users.insert().execute(user_id=1, user_name='john') + r = users.select(users.c.user_id==1).execute().fetchone() + users.delete().execute() + users.insert().execute(r) + assert users.select().execute().fetchall() == [(1, 'john')] + + def test_result_as_args(self): + users.insert().execute([dict(user_id=1, user_name='john'), dict(user_id=2, user_name='ed')]) + r = users.select().execute() + users2.insert().execute(list(r)) + assert users2.select().execute().fetchall() == [(1, 'john'), (2, 'ed')] + + users2.delete().execute() + r = users.select().execute() + users2.insert().execute(*list(r)) + assert users2.select().execute().fetchall() == [(1, 'john'), (2, 'ed')] + + def test_ambiguous_column(self): + users.insert().execute(user_id=1, user_name='john') + r = users.outerjoin(addresses).select().execute().fetchone() + try: + print r['user_id'] + assert False + except exc.InvalidRequestError, e: + assert str(e) == "Ambiguous column name 'user_id' in result set! try 'use_labels' option on select statement." or \ + str(e) == "Ambiguous column name 'USER_ID' in result set! try 'use_labels' option on select statement." + + @testing.requires.subqueries + def test_column_label_targeting(self): + users.insert().execute(user_id=7, user_name='ed') + + for s in ( + users.select().alias('foo'), + users.select().alias(users.name), + ): + row = s.select(use_labels=True).execute().fetchone() + assert row[s.c.user_id] == 7 + assert row[s.c.user_name] == 'ed' + + def test_keys(self): + users.insert().execute(user_id=1, user_name='foo') + r = users.select().execute().fetchone() + eq_([x.lower() for x in r.keys()], ['user_id', 'user_name']) + + def test_items(self): + users.insert().execute(user_id=1, user_name='foo') + r = users.select().execute().fetchone() + eq_([(x[0].lower(), x[1]) for x in r.items()], [('user_id', 1), ('user_name', 'foo')]) + + def test_len(self): + users.insert().execute(user_id=1, user_name='foo') + r = users.select().execute().fetchone() + eq_(len(r), 2) + r.close() + r = testing.db.execute('select user_name, user_id from query_users').fetchone() + eq_(len(r), 2) + r.close() + r = testing.db.execute('select user_name from query_users').fetchone() + eq_(len(r), 1) + r.close() + + def test_cant_execute_join(self): + try: + users.join(addresses).execute() + except exc.ArgumentError, e: + assert str(e).startswith('Not an executable clause: ') + + + + def test_column_order_with_simple_query(self): + # should return values in column definition order + users.insert().execute(user_id=1, user_name='foo') + r = users.select(users.c.user_id==1).execute().fetchone() + eq_(r[0], 1) + eq_(r[1], 'foo') + eq_([x.lower() for x in r.keys()], ['user_id', 'user_name']) + eq_(r.values(), [1, 'foo']) + + def test_column_order_with_text_query(self): + # should return values in query order + users.insert().execute(user_id=1, user_name='foo') + r = testing.db.execute('select user_name, user_id from query_users').fetchone() + eq_(r[0], 'foo') + eq_(r[1], 1) + eq_([x.lower() for x in r.keys()], ['user_name', 'user_id']) + eq_(r.values(), ['foo', 1]) + + @testing.crashes('oracle', 'FIXME: unknown, varify not fails_on()') + @testing.crashes('firebird', 'An identifier must begin with a letter') + @testing.crashes('maxdb', 'FIXME: unknown, verify not fails_on()') + def test_column_accessor_shadow(self): + meta = MetaData(testing.db) + shadowed = Table('test_shadowed', meta, + Column('shadow_id', INT, primary_key = True), + Column('shadow_name', VARCHAR(20)), + Column('parent', VARCHAR(20)), + Column('row', VARCHAR(40)), + Column('__parent', VARCHAR(20)), + Column('__row', VARCHAR(20)), + ) + shadowed.create(checkfirst=True) + try: + shadowed.insert().execute(shadow_id=1, shadow_name='The Shadow', parent='The Light', row='Without light there is no shadow', __parent='Hidden parent', __row='Hidden row') + r = shadowed.select(shadowed.c.shadow_id==1).execute().fetchone() + self.assert_(r.shadow_id == r['shadow_id'] == r[shadowed.c.shadow_id] == 1) + self.assert_(r.shadow_name == r['shadow_name'] == r[shadowed.c.shadow_name] == 'The Shadow') + self.assert_(r.parent == r['parent'] == r[shadowed.c.parent] == 'The Light') + self.assert_(r.row == r['row'] == r[shadowed.c.row] == 'Without light there is no shadow') + self.assert_(r['__parent'] == 'Hidden parent') + self.assert_(r['__row'] == 'Hidden row') + try: + print r.__parent, r.__row + self.fail('Should not allow access to private attributes') + except AttributeError: + pass # expected + r.close() + finally: + shadowed.drop(checkfirst=True) + + def test_in_filtering(self): + """test the behavior of the in_() function.""" + + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'fred') + users.insert().execute(user_id = 9, user_name = None) + + s = users.select(users.c.user_name.in_([])) + r = s.execute().fetchall() + # No username is in empty set + assert len(r) == 0 + + s = users.select(not_(users.c.user_name.in_([]))) + r = s.execute().fetchall() + # All usernames with a value are outside an empty set + assert len(r) == 2 + + s = users.select(users.c.user_name.in_(['jack','fred'])) + r = s.execute().fetchall() + assert len(r) == 2 + + s = users.select(not_(users.c.user_name.in_(['jack','fred']))) + r = s.execute().fetchall() + # Null values are not outside any set + assert len(r) == 0 + + u = bindparam('search_key') + + s = users.select(u.in_([])) + r = s.execute(search_key='john').fetchall() + assert len(r) == 0 + r = s.execute(search_key=None).fetchall() + assert len(r) == 0 + + s = users.select(not_(u.in_([]))) + r = s.execute(search_key='john').fetchall() + assert len(r) == 3 + r = s.execute(search_key=None).fetchall() + assert len(r) == 0 + + @testing.fails_on('firebird', 'FIXME: unknown') + @testing.fails_on('maxdb', 'FIXME: unknown') + @testing.fails_on('oracle', 'FIXME: unknown') + @testing.fails_on('mssql', 'FIXME: unknown') + def test_in_filtering_advanced(self): + """test the behavior of the in_() function when comparing against an empty collection.""" + + users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 8, user_name = 'fred') + users.insert().execute(user_id = 9, user_name = None) + + s = users.select(users.c.user_name.in_([]) == True) + r = s.execute().fetchall() + assert len(r) == 0 + s = users.select(users.c.user_name.in_([]) == False) + r = s.execute().fetchall() + assert len(r) == 2 + s = users.select(users.c.user_name.in_([]) == None) + r = s.execute().fetchall() + assert len(r) == 1 + +class PercentSchemaNamesTest(TestBase): + """tests using percent signs, spaces in table and column names. + + Doesn't pass for mysql, postgres, but this is really a + SQLAlchemy bug - we should be escaping out %% signs for this + operation the same way we do for text() and column labels. + + """ + @classmethod + @testing.crashes('mysql', 'mysqldb calls name % (params)') + @testing.crashes('postgres', 'postgres calls name % (params)') + def setup_class(cls): + global percent_table, metadata + metadata = MetaData(testing.db) + percent_table = Table('percent%table', metadata, + Column("percent%", Integer), + Column("%(oneofthese)s", Integer), + Column("spaces % more spaces", Integer), + ) + metadata.create_all() + + @classmethod + @testing.crashes('mysql', 'mysqldb calls name % (params)') + @testing.crashes('postgres', 'postgres calls name % (params)') + def teardown_class(cls): + metadata.drop_all() + + @testing.crashes('mysql', 'mysqldb calls name % (params)') + @testing.crashes('postgres', 'postgres calls name % (params)') + def test_roundtrip(self): + percent_table.insert().execute( + {'percent%':5, '%(oneofthese)s':7, 'spaces % more spaces':12}, + ) + percent_table.insert().execute( + {'percent%':7, '%(oneofthese)s':8, 'spaces % more spaces':11}, + {'percent%':9, '%(oneofthese)s':9, 'spaces % more spaces':10}, + {'percent%':11, '%(oneofthese)s':10, 'spaces % more spaces':9}, + ) + + for table in (percent_table, percent_table.alias()): + eq_( + table.select().order_by(table.c['%(oneofthese)s']).execute().fetchall(), + [ + (5, 7, 12), + (7, 8, 11), + (9, 9, 10), + (11, 10, 9) + ] + ) + + eq_( + table.select(). + where(table.c['spaces % more spaces'].in_([9, 10])). + order_by(table.c['%(oneofthese)s']).execute().fetchall(), + [ + (9, 9, 10), + (11, 10, 9) + ] + ) + + result = table.select().order_by(table.c['%(oneofthese)s']).execute() + row = result.fetchone() + eq_(row[table.c['percent%']], 5) + eq_(row[table.c['%(oneofthese)s']], 7) + eq_(row[table.c['spaces % more spaces']], 12) + row = result.fetchone() + eq_(row['percent%'], 7) + eq_(row['%(oneofthese)s'], 8) + eq_(row['spaces % more spaces'], 11) + result.close() + + percent_table.update().values({percent_table.c['%(oneofthese)s']:9, percent_table.c['spaces % more spaces']:15}).execute() + + eq_( + percent_table.select().order_by(percent_table.c['%(oneofthese)s']).execute().fetchall(), + [ + (5, 9, 15), + (7, 9, 15), + (9, 9, 15), + (11, 9, 15) + ] + ) + + + +class LimitTest(TestBase): + + @classmethod + def setup_class(cls): + global users, addresses, metadata + metadata = MetaData(testing.db) + users = Table('query_users', metadata, + Column('user_id', INT, primary_key = True), + Column('user_name', VARCHAR(20)), + ) + addresses = Table('query_addresses', metadata, + Column('address_id', Integer, primary_key=True), + Column('user_id', Integer, ForeignKey('query_users.user_id')), + Column('address', String(30))) + metadata.create_all() + + users.insert().execute(user_id=1, user_name='john') + addresses.insert().execute(address_id=1, user_id=1, address='addr1') + users.insert().execute(user_id=2, user_name='jack') + addresses.insert().execute(address_id=2, user_id=2, address='addr1') + users.insert().execute(user_id=3, user_name='ed') + addresses.insert().execute(address_id=3, user_id=3, address='addr2') + users.insert().execute(user_id=4, user_name='wendy') + addresses.insert().execute(address_id=4, user_id=4, address='addr3') + users.insert().execute(user_id=5, user_name='laura') + addresses.insert().execute(address_id=5, user_id=5, address='addr4') + users.insert().execute(user_id=6, user_name='ralph') + addresses.insert().execute(address_id=6, user_id=6, address='addr5') + users.insert().execute(user_id=7, user_name='fido') + addresses.insert().execute(address_id=7, user_id=7, address='addr5') + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def test_select_limit(self): + r = users.select(limit=3, order_by=[users.c.user_id]).execute().fetchall() + self.assert_(r == [(1, 'john'), (2, 'jack'), (3, 'ed')], repr(r)) + + @testing.fails_on('maxdb', 'FIXME: unknown') + def test_select_limit_offset(self): + """Test the interaction between limit and offset""" + + r = users.select(limit=3, offset=2, order_by=[users.c.user_id]).execute().fetchall() + self.assert_(r==[(3, 'ed'), (4, 'wendy'), (5, 'laura')]) + r = users.select(offset=5, order_by=[users.c.user_id]).execute().fetchall() + self.assert_(r==[(6, 'ralph'), (7, 'fido')]) + + def test_select_distinct_limit(self): + """Test the interaction between limit and distinct""" + + r = sorted([x[0] for x in select([addresses.c.address]).distinct().limit(3).order_by(addresses.c.address).execute().fetchall()]) + self.assert_(len(r) == 3, repr(r)) + self.assert_(r[0] != r[1] and r[1] != r[2], repr(r)) + + @testing.fails_on('mssql', 'FIXME: unknown') + def test_select_distinct_offset(self): + """Test the interaction between distinct and offset""" + + r = sorted([x[0] for x in select([addresses.c.address]).distinct().offset(1).order_by(addresses.c.address).execute().fetchall()]) + self.assert_(len(r) == 4, repr(r)) + self.assert_(r[0] != r[1] and r[1] != r[2] and r[2] != [3], repr(r)) + + def test_select_distinct_limit_offset(self): + """Test the interaction between limit and limit/offset""" + + r = select([addresses.c.address]).order_by(addresses.c.address).distinct().offset(2).limit(3).execute().fetchall() + self.assert_(len(r) == 3, repr(r)) + self.assert_(r[0] != r[1] and r[1] != r[2], repr(r)) + +class CompoundTest(TestBase): + """test compound statements like UNION, INTERSECT, particularly their ability to nest on + different databases.""" + @classmethod + def setup_class(cls): + global metadata, t1, t2, t3 + metadata = MetaData(testing.db) + t1 = Table('t1', metadata, + Column('col1', Integer, Sequence('t1pkseq'), primary_key=True), + Column('col2', String(30)), + Column('col3', String(40)), + Column('col4', String(30)) + ) + t2 = Table('t2', metadata, + Column('col1', Integer, Sequence('t2pkseq'), primary_key=True), + Column('col2', String(30)), + Column('col3', String(40)), + Column('col4', String(30))) + t3 = Table('t3', metadata, + Column('col1', Integer, Sequence('t3pkseq'), primary_key=True), + Column('col2', String(30)), + Column('col3', String(40)), + Column('col4', String(30))) + metadata.create_all() + + t1.insert().execute([ + dict(col2="t1col2r1", col3="aaa", col4="aaa"), + dict(col2="t1col2r2", col3="bbb", col4="bbb"), + dict(col2="t1col2r3", col3="ccc", col4="ccc"), + ]) + t2.insert().execute([ + dict(col2="t2col2r1", col3="aaa", col4="bbb"), + dict(col2="t2col2r2", col3="bbb", col4="ccc"), + dict(col2="t2col2r3", col3="ccc", col4="aaa"), + ]) + t3.insert().execute([ + dict(col2="t3col2r1", col3="aaa", col4="ccc"), + dict(col2="t3col2r2", col3="bbb", col4="aaa"), + dict(col2="t3col2r3", col3="ccc", col4="bbb"), + ]) + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def _fetchall_sorted(self, executed): + return sorted([tuple(row) for row in executed.fetchall()]) + + @testing.requires.subqueries + def test_union(self): + (s1, s2) = ( + select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], + t1.c.col2.in_(["t1col2r1", "t1col2r2"])), + select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], + t2.c.col2.in_(["t2col2r2", "t2col2r3"])) + ) + u = union(s1, s2) + + wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), + ('ccc', 'aaa')] + found1 = self._fetchall_sorted(u.execute()) + eq_(found1, wanted) + + found2 = self._fetchall_sorted(u.alias('bar').select().execute()) + eq_(found2, wanted) + + def test_union_ordered(self): + (s1, s2) = ( + select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], + t1.c.col2.in_(["t1col2r1", "t1col2r2"])), + select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], + t2.c.col2.in_(["t2col2r2", "t2col2r3"])) + ) + u = union(s1, s2, order_by=['col3', 'col4']) + + wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), + ('ccc', 'aaa')] + eq_(u.execute().fetchall(), wanted) + + @testing.fails_on('maxdb', 'FIXME: unknown') + @testing.requires.subqueries + def test_union_ordered_alias(self): + (s1, s2) = ( + select([t1.c.col3.label('col3'), t1.c.col4.label('col4')], + t1.c.col2.in_(["t1col2r1", "t1col2r2"])), + select([t2.c.col3.label('col3'), t2.c.col4.label('col4')], + t2.c.col2.in_(["t2col2r2", "t2col2r3"])) + ) + u = union(s1, s2, order_by=['col3', 'col4']) + + wanted = [('aaa', 'aaa'), ('bbb', 'bbb'), ('bbb', 'ccc'), + ('ccc', 'aaa')] + eq_(u.alias('bar').select().execute().fetchall(), wanted) + + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'FIXME: unknown') + def test_union_all(self): + e = union_all( + select([t1.c.col3]), + union( + select([t1.c.col3]), + select([t1.c.col3]), + ) + ) + + wanted = [('aaa',),('aaa',),('bbb',), ('bbb',), ('ccc',),('ccc',)] + found1 = self._fetchall_sorted(e.execute()) + eq_(found1, wanted) + + found2 = self._fetchall_sorted(e.alias('foo').select().execute()) + eq_(found2, wanted) + + @testing.crashes('firebird', 'Does not support intersect') + @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('mysql', 'FIXME: unknown') + def test_intersect(self): + i = intersect( + select([t2.c.col3, t2.c.col4]), + select([t2.c.col3, t2.c.col4], t2.c.col4==t3.c.col3) + ) + + wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] + + found1 = self._fetchall_sorted(i.execute()) + eq_(found1, wanted) + + found2 = self._fetchall_sorted(i.alias('bar').select().execute()) + eq_(found2, wanted) + + @testing.crashes('firebird', 'Does not support except') + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('mysql', 'FIXME: unknown') + def test_except_style1(self): + e = except_(union( + select([t1.c.col3, t1.c.col4]), + select([t2.c.col3, t2.c.col4]), + select([t3.c.col3, t3.c.col4]), + ), select([t2.c.col3, t2.c.col4])) + + wanted = [('aaa', 'aaa'), ('aaa', 'ccc'), ('bbb', 'aaa'), + ('bbb', 'bbb'), ('ccc', 'bbb'), ('ccc', 'ccc')] + + found = self._fetchall_sorted(e.alias('bar').select().execute()) + eq_(found, wanted) + + @testing.crashes('firebird', 'Does not support except') + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('mysql', 'FIXME: unknown') + def test_except_style2(self): + e = except_(union( + select([t1.c.col3, t1.c.col4]), + select([t2.c.col3, t2.c.col4]), + select([t3.c.col3, t3.c.col4]), + ).alias('foo').select(), select([t2.c.col3, t2.c.col4])) + + wanted = [('aaa', 'aaa'), ('aaa', 'ccc'), ('bbb', 'aaa'), + ('bbb', 'bbb'), ('ccc', 'bbb'), ('ccc', 'ccc')] + + found1 = self._fetchall_sorted(e.execute()) + eq_(found1, wanted) + + found2 = self._fetchall_sorted(e.alias('bar').select().execute()) + eq_(found2, wanted) + + @testing.crashes('firebird', 'Does not support except') + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.crashes('sybase', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('sqlite', 'FIXME: unknown') + def test_except_style3(self): + # aaa, bbb, ccc - (aaa, bbb, ccc - (ccc)) = ccc + e = except_( + select([t1.c.col3]), # aaa, bbb, ccc + except_( + select([t2.c.col3]), # aaa, bbb, ccc + select([t3.c.col3], t3.c.col3 == 'ccc'), #ccc + ) + ) + eq_(e.execute().fetchall(), [('ccc',)]) + eq_(e.alias('foo').select().execute().fetchall(), + [('ccc',)]) + + @testing.crashes('firebird', 'Does not support intersect') + @testing.fails_on('mysql', 'FIXME: unknown') + def test_composite(self): + u = intersect( + select([t2.c.col3, t2.c.col4]), + union( + select([t1.c.col3, t1.c.col4]), + select([t2.c.col3, t2.c.col4]), + select([t3.c.col3, t3.c.col4]), + ).alias('foo').select() + ) + wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] + found = self._fetchall_sorted(u.execute()) + + eq_(found, wanted) + + @testing.crashes('firebird', 'Does not support intersect') + @testing.fails_on('mysql', 'FIXME: unknown') + def test_composite_alias(self): + ua = intersect( + select([t2.c.col3, t2.c.col4]), + union( + select([t1.c.col3, t1.c.col4]), + select([t2.c.col3, t2.c.col4]), + select([t3.c.col3, t3.c.col4]), + ).alias('foo').select() + ).alias('bar') + + wanted = [('aaa', 'bbb'), ('bbb', 'ccc'), ('ccc', 'aaa')] + found = self._fetchall_sorted(ua.select().execute()) + eq_(found, wanted) + + +class JoinTest(TestBase): + """Tests join execution. + + The compiled SQL emitted by the dialect might be ANSI joins or + theta joins ('old oracle style', with (+) for OUTER). This test + tries to exercise join syntax and uncover any inconsistencies in + `JOIN rhs ON lhs.col=rhs.col` vs `rhs.col=lhs.col`. At least one + database seems to be sensitive to this. + """ + + @classmethod + def setup_class(cls): + global metadata + global t1, t2, t3 + + metadata = MetaData(testing.db) + t1 = Table('t1', metadata, + Column('t1_id', Integer, primary_key=True), + Column('name', String(32))) + t2 = Table('t2', metadata, + Column('t2_id', Integer, primary_key=True), + Column('t1_id', Integer, ForeignKey('t1.t1_id')), + Column('name', String(32))) + t3 = Table('t3', metadata, + Column('t3_id', Integer, primary_key=True), + Column('t2_id', Integer, ForeignKey('t2.t2_id')), + Column('name', String(32))) + metadata.drop_all() + metadata.create_all() + + # t1.10 -> t2.20 -> t3.30 + # t1.11 -> t2.21 + # t1.12 + t1.insert().execute({'t1_id': 10, 'name': 't1 #10'}, + {'t1_id': 11, 'name': 't1 #11'}, + {'t1_id': 12, 'name': 't1 #12'}) + t2.insert().execute({'t2_id': 20, 't1_id': 10, 'name': 't2 #20'}, + {'t2_id': 21, 't1_id': 11, 'name': 't2 #21'}) + t3.insert().execute({'t3_id': 30, 't2_id': 20, 'name': 't3 #30'}) + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def assertRows(self, statement, expected): + """Execute a statement and assert that rows returned equal expected.""" + + found = sorted([tuple(row) + for row in statement.execute().fetchall()]) + + eq_(found, sorted(expected)) + + def test_join_x1(self): + """Joins t1->t2.""" + + for criteria in (t1.c.t1_id==t2.c.t1_id, t2.c.t1_id==t1.c.t1_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id], + from_obj=[t1.join(t2, criteria)]) + self.assertRows(expr, [(10, 20), (11, 21)]) + + def test_join_x2(self): + """Joins t1->t2->t3.""" + + for criteria in (t1.c.t1_id==t2.c.t1_id, t2.c.t1_id==t1.c.t1_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id], + from_obj=[t1.join(t2, criteria)]) + self.assertRows(expr, [(10, 20), (11, 21)]) + + def test_outerjoin_x1(self): + """Outer joins t1->t2.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id], + from_obj=[t1.join(t2).join(t3, criteria)]) + self.assertRows(expr, [(10, 20)]) + + def test_outerjoin_x2(self): + """Outer joins t1->t2,t3.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + from_obj=[t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). \ + outerjoin(t3, criteria)]) + self.assertRows(expr, [(10, 20, 30), (11, 21, None), (12, None, None)]) + + def test_outerjoin_where_x2_t1(self): + """Outer joins t1->t2,t3, where on t1.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t1.c.name == 't1 #10', + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t1.c.t1_id < 12, + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) + + def test_outerjoin_where_x2_t2(self): + """Outer joins t1->t2,t3, where on t2.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t2.c.name == 't2 #20', + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t2.c.t2_id < 29, + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) + + def test_outerjoin_where_x2_t1t2(self): + """Outer joins t1->t2,t3, where on t1 and t2.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.t1_id < 19, 29 > t2.c.t2_id), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) + + def test_outerjoin_where_x2_t3(self): + """Outer joins t1->t2,t3, where on t3.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t3.c.name == 't3 #30', + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t3.c.t3_id < 39, + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + def test_outerjoin_where_x2_t1t3(self): + """Outer joins t1->t2,t3, where on t1 and t3.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', t3.c.name == 't3 #30'), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.t1_id < 19, t3.c.t3_id < 39), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + def test_outerjoin_where_x2_t1t2(self): + """Outer joins t1->t2,t3, where on t1 and t2.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.t1_id < 12, t2.c.t2_id < 39), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) + + def test_outerjoin_where_x2_t1t2t3(self): + """Outer joins t1->t2,t3, where on t1, t2 and t3.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', + t2.c.name == 't2 #20', + t3.c.name == 't3 #30'), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.t1_id < 19, + t2.c.t2_id < 29, + t3.c.t3_id < 39), + from_obj=[(t1.outerjoin(t2, t1.c.t1_id==t2.c.t1_id). + outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + def test_mixed(self): + """Joins t1->t2, outer t2->t3.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + print expr + self.assertRows(expr, [(10, 20, 30), (11, 21, None)]) + + def test_mixed_where(self): + """Joins t1->t2, outer t2->t3, plus a where on each table in turn.""" + + for criteria in (t2.c.t2_id==t3.c.t2_id, t3.c.t2_id==t2.c.t2_id): + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t1.c.name == 't1 #10', + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t2.c.name == 't2 #20', + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + t3.c.name == 't3 #30', + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', t2.c.name == 't2 #20'), + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t2.c.name == 't2 #20', t3.c.name == 't3 #30'), + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + expr = select( + [t1.c.t1_id, t2.c.t2_id, t3.c.t3_id], + and_(t1.c.name == 't1 #10', + t2.c.name == 't2 #20', + t3.c.name == 't3 #30'), + from_obj=[(t1.join(t2).outerjoin(t3, criteria))]) + self.assertRows(expr, [(10, 20, 30)]) + + +class OperatorTest(TestBase): + @classmethod + def setup_class(cls): + global metadata, flds + metadata = MetaData(testing.db) + flds = Table('flds', metadata, + Column('idcol', Integer, Sequence('t1pkseq'), primary_key=True), + Column('intcol', Integer), + Column('strcol', String(50)), + ) + metadata.create_all() + + flds.insert().execute([ + dict(intcol=5, strcol='foo'), + dict(intcol=13, strcol='bar') + ]) + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + @testing.fails_on('maxdb', 'FIXME: unknown') + def test_modulo(self): + eq_( + select([flds.c.intcol % 3], + order_by=flds.c.idcol).execute().fetchall(), + [(2,),(1,)] + ) diff --git a/test/sql/test_quote.py b/test/sql/test_quote.py new file mode 100644 index 000000000..64e097b85 --- /dev/null +++ b/test/sql/test_quote.py @@ -0,0 +1,210 @@ +from sqlalchemy import * +from sqlalchemy import sql +from sqlalchemy.sql import compiler +from sqlalchemy.test import * + + +class QuoteTest(TestBase, AssertsCompiledSQL): + @classmethod + def setup_class(cls): + # TODO: figure out which databases/which identifiers allow special + # characters to be used, such as: spaces, quote characters, + # punctuation characters, set up tests for those as well. + global table1, table2, table3 + metadata = MetaData(testing.db) + table1 = Table('WorstCase1', metadata, + Column('lowercase', Integer, primary_key=True), + Column('UPPERCASE', Integer), + Column('MixedCase', Integer), + Column('ASC', Integer, key='a123')) + table2 = Table('WorstCase2', metadata, + Column('desc', Integer, primary_key=True, key='d123'), + Column('Union', Integer, key='u123'), + Column('MixedCase', Integer)) + table1.create() + table2.create() + + def teardown(self): + table1.delete().execute() + table2.delete().execute() + + @classmethod + def teardown_class(cls): + table1.drop() + table2.drop() + + def testbasic(self): + table1.insert().execute({'lowercase':1,'UPPERCASE':2,'MixedCase':3,'a123':4}, + {'lowercase':2,'UPPERCASE':2,'MixedCase':3,'a123':4}, + {'lowercase':4,'UPPERCASE':3,'MixedCase':2,'a123':1}) + table2.insert().execute({'d123':1,'u123':2,'MixedCase':3}, + {'d123':2,'u123':2,'MixedCase':3}, + {'d123':4,'u123':3,'MixedCase':2}) + + res1 = select([table1.c.lowercase, table1.c.UPPERCASE, table1.c.MixedCase, table1.c.a123]).execute().fetchall() + print res1 + assert(res1==[(1,2,3,4),(2,2,3,4),(4,3,2,1)]) + + res2 = select([table2.c.d123, table2.c.u123, table2.c.MixedCase]).execute().fetchall() + print res2 + assert(res2==[(1,2,3),(2,2,3),(4,3,2)]) + + def testreflect(self): + meta2 = MetaData(testing.db) + t2 = Table('WorstCase2', meta2, autoload=True, quote=True) + assert 'MixedCase' in t2.c + + def testlabels(self): + table1.insert().execute({'lowercase':1,'UPPERCASE':2,'MixedCase':3,'a123':4}, + {'lowercase':2,'UPPERCASE':2,'MixedCase':3,'a123':4}, + {'lowercase':4,'UPPERCASE':3,'MixedCase':2,'a123':1}) + table2.insert().execute({'d123':1,'u123':2,'MixedCase':3}, + {'d123':2,'u123':2,'MixedCase':3}, + {'d123':4,'u123':3,'MixedCase':2}) + + res1 = select([table1.c.lowercase, table1.c.UPPERCASE, table1.c.MixedCase, table1.c.a123], use_labels=True).execute().fetchall() + print res1 + assert(res1==[(1,2,3,4),(2,2,3,4),(4,3,2,1)]) + + res2 = select([table2.c.d123, table2.c.u123, table2.c.MixedCase], use_labels=True).execute().fetchall() + print res2 + assert(res2==[(1,2,3),(2,2,3),(4,3,2)]) + + def test_quote_flag(self): + metadata = MetaData() + t1 = Table('TableOne', metadata, + Column('ColumnOne', Integer), schema="FooBar") + self.assert_compile(t1.select(), '''SELECT "FooBar"."TableOne"."ColumnOne" FROM "FooBar"."TableOne"''') + + metadata = MetaData() + t1 = Table('t1', metadata, + Column('col1', Integer, quote=True), quote=True, schema="foo", quote_schema=True) + self.assert_compile(t1.select(), '''SELECT "foo"."t1"."col1" FROM "foo"."t1"''') + + self.assert_compile(t1.select().apply_labels(), '''SELECT "foo"."t1"."col1" AS "foo_t1_col1" FROM "foo"."t1"''') + a = t1.select().alias('anon') + b = select([1], a.c.col1==2, from_obj=a) + self.assert_compile(b, + '''SELECT 1 FROM (SELECT "foo"."t1"."col1" AS "col1" FROM '''\ + '''"foo"."t1") AS anon WHERE anon."col1" = :col1_1''' + ) + + metadata = MetaData() + t1 = Table('TableOne', metadata, + Column('ColumnOne', Integer, quote=False), quote=False, schema="FooBar", quote_schema=False) + self.assert_compile(t1.select(), "SELECT FooBar.TableOne.ColumnOne FROM FooBar.TableOne") + + self.assert_compile(t1.select().apply_labels(), + "SELECT FooBar.TableOne.ColumnOne AS "\ + "FooBar_TableOne_ColumnOne FROM FooBar.TableOne" # TODO: is this what we really want here ? what if table/schema + # *are* quoted? + ) + + a = t1.select().alias('anon') + b = select([1], a.c.ColumnOne==2, from_obj=a) + self.assert_compile(b, + "SELECT 1 FROM (SELECT FooBar.TableOne.ColumnOne AS "\ + "ColumnOne FROM FooBar.TableOne) AS anon WHERE anon.ColumnOne = :ColumnOne_1" + ) + + + + def test_table_quote_flag(self): + metadata = MetaData() + t1 = Table('TableOne', metadata, + Column('id', Integer), + quote=False) + t2 = Table('TableTwo', metadata, + Column('id', Integer), + Column('t1_id', Integer, ForeignKey('TableOne.id')), + quote=False) + + self.assert_compile( + t2.join(t1).select(), + "SELECT TableTwo.id, TableTwo.t1_id, TableOne.id " + "FROM TableTwo JOIN TableOne ON TableOne.id = TableTwo.t1_id") + + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.requires.subqueries + def testlabels(self): + """test the quoting of labels. + + if labels arent quoted, a query in postgres in particular will fail since it produces: + + SELECT LaLa.lowercase, LaLa."UPPERCASE", LaLa."MixedCase", LaLa."ASC" + FROM (SELECT DISTINCT "WorstCase1".lowercase AS lowercase, "WorstCase1"."UPPERCASE" AS UPPERCASE, "WorstCase1"."MixedCase" AS MixedCase, "WorstCase1"."ASC" AS ASC \nFROM "WorstCase1") AS LaLa + + where the "UPPERCASE" column of "LaLa" doesnt exist. + """ + x = table1.select(distinct=True).alias("LaLa").select().scalar() + + def testlabels2(self): + metadata = MetaData() + table = Table("ImATable", metadata, + Column("col1", Integer)) + x = select([table.c.col1.label("ImATable_col1")]).alias("SomeAlias") + self.assert_compile(select([x.c.ImATable_col1]), + '''SELECT "SomeAlias"."ImATable_col1" FROM (SELECT "ImATable".col1 AS "ImATable_col1" FROM "ImATable") AS "SomeAlias"''') + + # note that 'foo' and 'FooCol' are literals already quoted + x = select([sql.literal_column("'foo'").label("somelabel")], from_obj=[table]).alias("AnAlias") + x = x.select() + self.assert_compile(x, + '''SELECT "AnAlias".somelabel FROM (SELECT 'foo' AS somelabel FROM "ImATable") AS "AnAlias"''') + + x = select([sql.literal_column("'FooCol'").label("SomeLabel")], from_obj=[table]) + x = x.select() + self.assert_compile(x, + '''SELECT "SomeLabel" FROM (SELECT 'FooCol' AS "SomeLabel" FROM "ImATable")''') + + +class PreparerTest(TestBase): + """Test the db-agnostic quoting services of IdentifierPreparer.""" + + def test_unformat(self): + prep = compiler.IdentifierPreparer(None) + unformat = prep.unformat_identifiers + + def a_eq(have, want): + if have != want: + print "Wanted %s" % want + print "Received %s" % have + self.assert_(have == want) + + a_eq(unformat('foo'), ['foo']) + a_eq(unformat('"foo"'), ['foo']) + a_eq(unformat("'foo'"), ["'foo'"]) + a_eq(unformat('foo.bar'), ['foo', 'bar']) + a_eq(unformat('"foo"."bar"'), ['foo', 'bar']) + a_eq(unformat('foo."bar"'), ['foo', 'bar']) + a_eq(unformat('"foo".bar'), ['foo', 'bar']) + a_eq(unformat('"foo"."b""a""r"."baz"'), ['foo', 'b"a"r', 'baz']) + + def test_unformat_custom(self): + class Custom(compiler.IdentifierPreparer): + def __init__(self, dialect): + super(Custom, self).__init__(dialect, initial_quote='`', + final_quote='`') + def _escape_identifier(self, value): + return value.replace('`', '``') + def _unescape_identifier(self, value): + return value.replace('``', '`') + + prep = Custom(None) + unformat = prep.unformat_identifiers + + def a_eq(have, want): + if have != want: + print "Wanted %s" % want + print "Received %s" % have + self.assert_(have == want) + + a_eq(unformat('foo'), ['foo']) + a_eq(unformat('`foo`'), ['foo']) + a_eq(unformat(`'foo'`), ["'foo'"]) + a_eq(unformat('foo.bar'), ['foo', 'bar']) + a_eq(unformat('`foo`.`bar`'), ['foo', 'bar']) + a_eq(unformat('foo.`bar`'), ['foo', 'bar']) + a_eq(unformat('`foo`.bar'), ['foo', 'bar']) + a_eq(unformat('`foo`.`b``a``r`.`baz`'), ['foo', 'b`a`r', 'baz']) + diff --git a/test/sql/test_rowcount.py b/test/sql/test_rowcount.py new file mode 100644 index 000000000..82301a4a5 --- /dev/null +++ b/test/sql/test_rowcount.py @@ -0,0 +1,70 @@ +from sqlalchemy import * +from sqlalchemy.test import * + + +class FoundRowsTest(TestBase, AssertsExecutionResults): + """tests rowcount functionality""" + @classmethod + def setup_class(cls): + metadata = MetaData(testing.db) + + global employees_table + + employees_table = Table('employees', metadata, + Column('employee_id', Integer, Sequence('employee_id_seq', optional=True), primary_key=True), + Column('name', String(50)), + Column('department', String(1)), + ) + employees_table.create() + + def setup(self): + global data + data = [ ('Angela', 'A'), + ('Andrew', 'A'), + ('Anand', 'A'), + ('Bob', 'B'), + ('Bobette', 'B'), + ('Buffy', 'B'), + ('Charlie', 'C'), + ('Cynthia', 'C'), + ('Chris', 'C') ] + + i = employees_table.insert() + i.execute(*[{'name':n, 'department':d} for n, d in data]) + def teardown(self): + employees_table.delete().execute() + + @classmethod + def teardown_class(cls): + employees_table.drop() + + def testbasic(self): + s = employees_table.select() + r = s.execute().fetchall() + + assert len(r) == len(data) + + def test_update_rowcount1(self): + # WHERE matches 3, 3 rows changed + department = employees_table.c.department + r = employees_table.update(department=='C').execute(department='Z') + print "expecting 3, dialect reports %s" % r.rowcount + if testing.db.dialect.supports_sane_rowcount: + assert r.rowcount == 3 + + def test_update_rowcount2(self): + # WHERE matches 3, 0 rows changed + department = employees_table.c.department + r = employees_table.update(department=='C').execute(department='C') + print "expecting 3, dialect reports %s" % r.rowcount + if testing.db.dialect.supports_sane_rowcount: + assert r.rowcount == 3 + + def test_delete_rowcount(self): + # WHERE matches 3, 3 rows deleted + department = employees_table.c.department + r = employees_table.delete(department=='C').execute() + print "expecting 3, dialect reports %s" % r.rowcount + if testing.db.dialect.supports_sane_rowcount: + assert r.rowcount == 3 + diff --git a/test/sql/test_select.py b/test/sql/test_select.py new file mode 100644 index 000000000..1d9e531de --- /dev/null +++ b/test/sql/test_select.py @@ -0,0 +1,1550 @@ +from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message +import datetime, re, operator +from sqlalchemy import * +from sqlalchemy import exc, sql, util +from sqlalchemy.sql import table, column, label, compiler +from sqlalchemy.sql.expression import ClauseList +from sqlalchemy.engine import default +from sqlalchemy.databases import sqlite, postgres, mysql, oracle, firebird, mssql +from sqlalchemy.test import * + +table1 = table('mytable', + column('myid', Integer), + column('name', String), + column('description', String), +) + +table2 = table( + 'myothertable', + column('otherid', Integer), + column('othername', String), +) + +table3 = table( + 'thirdtable', + column('userid', Integer), + column('otherstuff', String), +) + +metadata = MetaData() +table4 = Table( + 'remotetable', metadata, + Column('rem_id', Integer, primary_key=True), + Column('datatype_id', Integer), + Column('value', String(20)), + schema = 'remote_owner' +) + +users = table('users', + column('user_id'), + column('user_name'), + column('password'), +) + +addresses = table('addresses', + column('address_id'), + column('user_id'), + column('street'), + column('city'), + column('state'), + column('zip') +) + +class SelectTest(TestBase, AssertsCompiledSQL): + + def test_attribute_sanity(self): + assert hasattr(table1, 'c') + assert hasattr(table1.select(), 'c') + assert not hasattr(table1.c.myid.self_group(), 'columns') + assert hasattr(table1.select().self_group(), 'columns') + assert not hasattr(select([table1.c.myid]).as_scalar().self_group(), 'columns') + assert not hasattr(table1.c.myid, 'columns') + assert not hasattr(table1.c.myid, 'c') + assert not hasattr(table1.select().c.myid, 'c') + assert not hasattr(table1.select().c.myid, 'columns') + assert not hasattr(table1.alias().c.myid, 'columns') + assert not hasattr(table1.alias().c.myid, 'c') + + def test_table_select(self): + self.assert_compile(table1.select(), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable") + + self.assert_compile(select([table1, table2]), "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, \ +myothertable.othername FROM mytable, myothertable") + + def test_from_subquery(self): + """tests placing select statements in the column clause of another select, for the + purposes of selecting from the exported columns of that select.""" + + s = select([table1], table1.c.name == 'jack') + self.assert_compile( + select( + [s], + s.c.myid == 7 + ) + , + "SELECT myid, name, description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable "\ + "WHERE mytable.name = :name_1) WHERE myid = :myid_1") + + sq = select([table1]) + self.assert_compile( + sq.select(), + "SELECT myid, name, description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable)" + ) + + sq = select( + [table1], + ).alias('sq') + + self.assert_compile( + sq.select(sq.c.myid == 7), + "SELECT sq.myid, sq.name, sq.description FROM \ +(SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable) AS sq WHERE sq.myid = :myid_1" + ) + + sq = select( + [table1, table2], + and_(table1.c.myid ==7, table2.c.otherid==table1.c.myid), + use_labels = True + ).alias('sq') + + sqstring = "SELECT mytable.myid AS mytable_myid, mytable.name AS mytable_name, \ +mytable.description AS mytable_description, myothertable.otherid AS myothertable_otherid, \ +myothertable.othername AS myothertable_othername FROM mytable, myothertable \ +WHERE mytable.myid = :myid_1 AND myothertable.otherid = mytable.myid" + + self.assert_compile(sq.select(), "SELECT sq.mytable_myid, sq.mytable_name, sq.mytable_description, sq.myothertable_otherid, \ +sq.myothertable_othername FROM (" + sqstring + ") AS sq") + + sq2 = select( + [sq], + use_labels = True + ).alias('sq2') + + self.assert_compile(sq2.select(), "SELECT sq2.sq_mytable_myid, sq2.sq_mytable_name, sq2.sq_mytable_description, \ +sq2.sq_myothertable_otherid, sq2.sq_myothertable_othername FROM \ +(SELECT sq.mytable_myid AS sq_mytable_myid, sq.mytable_name AS sq_mytable_name, \ +sq.mytable_description AS sq_mytable_description, sq.myothertable_otherid AS sq_myothertable_otherid, \ +sq.myothertable_othername AS sq_myothertable_othername FROM (" + sqstring + ") AS sq) AS sq2") + + def test_select_from_clauselist(self): + self.assert_compile( + select([ClauseList(column('a'), column('b'))]).select_from('sometable'), + 'SELECT a, b FROM sometable' + ) + + def test_use_labels(self): + self.assert_compile( + select([table1.c.myid==5], use_labels=True), + "SELECT mytable.myid = :myid_1 AS anon_1 FROM mytable" + ) + + self.assert_compile( + select([func.foo()], use_labels=True), + "SELECT foo() AS foo_1" + ) + + self.assert_compile( + select([not_(True)], use_labels=True), + "SELECT NOT :param_1" # TODO: should this make an anon label ?? + ) + + self.assert_compile( + select([cast("data", sqlite.SLInteger)], use_labels=True), # this will work with plain Integer in 0.6 + "SELECT CAST(:param_1 AS INTEGER) AS anon_1" + ) + + + + def test_nested_uselabels(self): + """test nested anonymous label generation. this + essentially tests the ANONYMOUS_LABEL regex. + + """ + s1 = table1.select() + s2 = s1.alias() + s3 = select([s2], use_labels=True) + s4 = s3.alias() + s5 = select([s4], use_labels=True) + self.assert_compile(s5, "SELECT anon_1.anon_2_myid AS anon_1_anon_2_myid, anon_1.anon_2_name AS anon_1_anon_2_name, "\ + "anon_1.anon_2_description AS anon_1_anon_2_description FROM (SELECT anon_2.myid AS anon_2_myid, anon_2.name AS anon_2_name, "\ + "anon_2.description AS anon_2_description FROM (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description "\ + "AS description FROM mytable) AS anon_2) AS anon_1") + + def test_dont_overcorrelate(self): + self.assert_compile(select([table1], from_obj=[table1, table1.select()]), """SELECT mytable.myid, mytable.name, mytable.description FROM mytable, (SELECT mytable.myid AS myid, mytable.name AS name, mytable.description AS description FROM mytable)""") + + def test_full_correlate(self): + # intentional + t = table('t', column('a'), column('b')) + s = select([t.c.a]).where(t.c.a==1).correlate(t).as_scalar() + + s2 = select([t.c.a, s]) + self.assert_compile(s2, """SELECT t.a, (SELECT t.a WHERE t.a = :a_1) AS anon_1 FROM t""") + + # unintentional + t2 = table('t2', column('c'), column('d')) + s = select([t.c.a]).where(t.c.a==t2.c.d).as_scalar() + s2 =select([t, t2, s]) + assert_raises(exc.InvalidRequestError, str, s2) + + # intentional again + s = s.correlate(t, t2) + s2 =select([t, t2, s]) + self.assert_compile(s, "SELECT t.a WHERE t.a = t2.d") + + def test_exists(self): + self.assert_compile(exists([table1.c.myid], table1.c.myid==5).select(), "SELECT EXISTS (SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1)", params={'mytable_myid':5}) + + self.assert_compile(select([table1, exists([1], from_obj=table2)]), "SELECT mytable.myid, mytable.name, mytable.description, EXISTS (SELECT 1 FROM myothertable) FROM mytable", params={}) + + self.assert_compile(select([table1, exists([1], from_obj=table2).label('foo')]), "SELECT mytable.myid, mytable.name, mytable.description, EXISTS (SELECT 1 FROM myothertable) AS foo FROM mytable", params={}) + + self.assert_compile( + table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = mytable.myid)" + ) + + self.assert_compile( + table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = mytable.myid)" + ) + + self.assert_compile( + table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)).replace_selectable(table2, table2.alias()), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT * FROM myothertable AS myothertable_1 WHERE myothertable_1.otherid = mytable.myid)" + ) + + self.assert_compile( + table1.select(exists().where(table2.c.otherid == table1.c.myid).correlate(table1)).select_from(table1.join(table2, table1.c.myid==table2.c.otherid)).replace_selectable(table2, table2.alias()), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable JOIN myothertable AS myothertable_1 ON mytable.myid = myothertable_1.otherid WHERE EXISTS (SELECT * FROM myothertable AS myothertable_1 WHERE myothertable_1.otherid = mytable.myid)" + ) + + self.assert_compile( + select([ + or_( + exists().where(table2.c.otherid=='foo'), + exists().where(table2.c.otherid=='bar') + ) + ]), + "SELECT (EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = :otherid_1)) "\ + "OR (EXISTS (SELECT * FROM myothertable WHERE myothertable.otherid = :otherid_2)) AS anon_1" + ) + + + def test_where_subquery(self): + s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') + self.assert_compile( + select([users, s.c.street], from_obj=s), + """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") + + self.assert_compile( + table1.select(table1.c.myid == select([table1.c.myid], table1.c.name=='jack')), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable WHERE mytable.name = :name_1)" + ) + + self.assert_compile( + table1.select(table1.c.myid == select([table2.c.otherid], table1.c.name == table2.c.othername)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = (SELECT myothertable.otherid FROM myothertable WHERE mytable.name = myothertable.othername)" + ) + + self.assert_compile( + table1.select(exists([1], table2.c.otherid == table1.c.myid)), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" + ) + + + talias = table1.alias('ta') + s = subquery('sq2', [talias], exists([1], table2.c.otherid == talias.c.myid)) + self.assert_compile( + select([s, table1]) + ,"SELECT sq2.myid, sq2.name, sq2.description, mytable.myid, mytable.name, mytable.description FROM (SELECT ta.myid AS myid, ta.name AS name, ta.description AS description FROM mytable AS ta WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = ta.myid)) AS sq2, mytable") + + s = select([addresses.c.street], addresses.c.user_id==users.c.user_id, correlate=True).alias('s') + self.assert_compile( + select([users, s.c.street], from_obj=s), + """SELECT users.user_id, users.user_name, users.password, s.street FROM users, (SELECT addresses.street AS street FROM addresses WHERE addresses.user_id = users.user_id) AS s""") + + # test constructing the outer query via append_column(), which occurs in the ORM's Query object + s = select([], exists([1], table2.c.otherid==table1.c.myid), from_obj=table1) + s.append_column(table1) + self.assert_compile( + s, + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE EXISTS (SELECT 1 FROM myothertable WHERE myothertable.otherid = mytable.myid)" + ) + + + def test_orderby_subquery(self): + self.assert_compile( + table1.select(order_by=[select([table2.c.otherid], table1.c.myid==table2.c.otherid)]), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable ORDER BY (SELECT myothertable.otherid FROM myothertable WHERE mytable.myid = myothertable.otherid)" + ) + self.assert_compile( + table1.select(order_by=[desc(select([table2.c.otherid], table1.c.myid==table2.c.otherid))]), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable ORDER BY (SELECT myothertable.otherid FROM myothertable WHERE mytable.myid = myothertable.otherid) DESC" + ) + + @testing.uses_deprecated('scalar option') + def test_scalar_select(self): + try: + s = select([table1.c.myid, table1.c.name]).as_scalar() + assert False + except exc.InvalidRequestError, err: + assert str(err) == "Scalar select can only be created from a Select object that has exactly one column expression.", str(err) + + try: + # generic function which will look at the type of expression + func.coalesce(select([table1.c.myid])) + assert False + except exc.InvalidRequestError, err: + assert str(err) == "Select objects don't have a type. Call as_scalar() on this Select object to return a 'scalar' version of this Select.", str(err) + + s = select([table1.c.myid], scalar=True, correlate=False) + self.assert_compile(select([table1, s]), "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable) AS anon_1 FROM mytable") + + s = select([table1.c.myid], scalar=True) + self.assert_compile(select([table2, s]), "SELECT myothertable.otherid, myothertable.othername, (SELECT mytable.myid FROM mytable) AS anon_1 FROM myothertable") + + s = select([table1.c.myid]).correlate(None).as_scalar() + self.assert_compile(select([table1, s]), "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable) AS anon_1 FROM mytable") + + # test that aliases use as_scalar() when used in an explicitly scalar context + s = select([table1.c.myid]).alias() + self.assert_compile(select([table1.c.myid]).where(table1.c.myid==s), "SELECT mytable.myid FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable)") + self.assert_compile(select([table1.c.myid]).where(s > table1.c.myid), "SELECT mytable.myid FROM mytable WHERE mytable.myid < (SELECT mytable.myid FROM mytable)") + + + s = select([table1.c.myid]).as_scalar() + self.assert_compile(select([table2, s]), "SELECT myothertable.otherid, myothertable.othername, (SELECT mytable.myid FROM mytable) AS anon_1 FROM myothertable") + + # test expressions against scalar selects + self.assert_compile(select([s - literal(8)]), "SELECT (SELECT mytable.myid FROM mytable) - :param_1 AS anon_1") + self.assert_compile(select([select([table1.c.name]).as_scalar() + literal('x')]), "SELECT (SELECT mytable.name FROM mytable) || :param_1 AS anon_1") + self.assert_compile(select([s > literal(8)]), "SELECT (SELECT mytable.myid FROM mytable) > :param_1 AS anon_1") + + self.assert_compile(select([select([table1.c.name]).label('foo')]), "SELECT (SELECT mytable.name FROM mytable) AS foo") + + # scalar selects should not have any attributes on their 'c' or 'columns' attribute + s = select([table1.c.myid]).as_scalar() + try: + s.c.foo + except exc.InvalidRequestError, err: + assert str(err) == 'Scalar Select expression has no columns; use this object directly within a column-level expression.' + + try: + s.columns.foo + except exc.InvalidRequestError, err: + assert str(err) == 'Scalar Select expression has no columns; use this object directly within a column-level expression.' + + zips = table('zips', + column('zipcode'), + column('latitude'), + column('longitude'), + ) + places = table('places', + column('id'), + column('nm') + ) + zip = '12345' + qlat = select([zips.c.latitude], zips.c.zipcode == zip).correlate(None).as_scalar() + qlng = select([zips.c.longitude], zips.c.zipcode == zip).correlate(None).as_scalar() + + q = select([places.c.id, places.c.nm, zips.c.zipcode, func.latlondist(qlat, qlng).label('dist')], + zips.c.zipcode==zip, + order_by = ['dist', places.c.nm] + ) + + self.assert_compile(q,"SELECT places.id, places.nm, zips.zipcode, latlondist((SELECT zips.latitude FROM zips WHERE " + "zips.zipcode = :zipcode_1), (SELECT zips.longitude FROM zips WHERE zips.zipcode = :zipcode_2)) AS dist " + "FROM places, zips WHERE zips.zipcode = :zipcode_3 ORDER BY dist, places.nm") + + zalias = zips.alias('main_zip') + qlat = select([zips.c.latitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) + qlng = select([zips.c.longitude], zips.c.zipcode == zalias.c.zipcode, scalar=True) + q = select([places.c.id, places.c.nm, zalias.c.zipcode, func.latlondist(qlat, qlng).label('dist')], + order_by = ['dist', places.c.nm] + ) + self.assert_compile(q, "SELECT places.id, places.nm, main_zip.zipcode, latlondist((SELECT zips.latitude FROM zips WHERE zips.zipcode = main_zip.zipcode), (SELECT zips.longitude FROM zips WHERE zips.zipcode = main_zip.zipcode)) AS dist FROM places, zips AS main_zip ORDER BY dist, places.nm") + + a1 = table2.alias('t2alias') + s1 = select([a1.c.otherid], table1.c.myid==a1.c.otherid, scalar=True) + j1 = table1.join(table2, table1.c.myid==table2.c.otherid) + s2 = select([table1, s1], from_obj=j1) + self.assert_compile(s2, "SELECT mytable.myid, mytable.name, mytable.description, (SELECT t2alias.otherid FROM myothertable AS t2alias WHERE mytable.myid = t2alias.otherid) AS anon_1 FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid") + + def test_label_comparison(self): + x = func.lala(table1.c.myid).label('foo') + self.assert_compile(select([x], x==5), "SELECT lala(mytable.myid) AS foo FROM mytable WHERE lala(mytable.myid) = :param_1") + + self.assert_compile(label('bar', column('foo', type_=String)) + "foo", "foo || :param_1") + + + def test_conjunctions(self): + a, b, c = 'a', 'b', 'c' + x = and_(a, b, c) + assert isinstance(x.type, Boolean) + assert str(x) == 'a AND b AND c' + self.assert_compile( + select([x.label('foo')]), + 'SELECT a AND b AND c AS foo' + ) + + self.assert_compile( + and_(table1.c.myid == 12, table1.c.name=='asdf', table2.c.othername == 'foo', "sysdate() = today()"), + "mytable.myid = :myid_1 AND mytable.name = :name_1 "\ + "AND myothertable.othername = :othername_1 AND sysdate() = today()" + ) + + self.assert_compile( + and_( + table1.c.myid == 12, + or_(table2.c.othername=='asdf', table2.c.othername == 'foo', table2.c.otherid == 9), + "sysdate() = today()", + ), + "mytable.myid = :myid_1 AND (myothertable.othername = :othername_1 OR "\ + "myothertable.othername = :othername_2 OR myothertable.otherid = :otherid_1) AND sysdate() = today()", + checkparams = {'othername_1': 'asdf', 'othername_2':'foo', 'otherid_1': 9, 'myid_1': 12} + ) + + + def test_distinct(self): + self.assert_compile( + select([table1.c.myid.distinct()]), "SELECT DISTINCT mytable.myid FROM mytable" + ) + + self.assert_compile( + select([distinct(table1.c.myid)]), "SELECT DISTINCT mytable.myid FROM mytable" + ) + + self.assert_compile( + select([table1.c.myid]).distinct(), "SELECT DISTINCT mytable.myid FROM mytable" + ) + + self.assert_compile( + select([func.count(table1.c.myid.distinct())]), "SELECT count(DISTINCT mytable.myid) AS count_1 FROM mytable" + ) + + self.assert_compile( + select([func.count(distinct(table1.c.myid))]), "SELECT count(DISTINCT mytable.myid) AS count_1 FROM mytable" + ) + + def test_operators(self): + for (py_op, sql_op) in ((operator.add, '+'), (operator.mul, '*'), + (operator.sub, '-'), (operator.div, '/'), + ): + for (lhs, rhs, res) in ( + (5, table1.c.myid, ':myid_1 %s mytable.myid'), + (5, literal(5), ':param_1 %s :param_2'), + (table1.c.myid, 'b', 'mytable.myid %s :myid_1'), + (table1.c.myid, literal(2.7), 'mytable.myid %s :param_1'), + (table1.c.myid, table1.c.myid, 'mytable.myid %s mytable.myid'), + (literal(5), 8, ':param_1 %s :param_2'), + (literal(6), table1.c.myid, ':param_1 %s mytable.myid'), + (literal(7), literal(5.5), ':param_1 %s :param_2'), + ): + self.assert_compile(py_op(lhs, rhs), res % sql_op) + + dt = datetime.datetime.today() + # exercise comparison operators + for (py_op, fwd_op, rev_op) in ((operator.lt, '<', '>'), + (operator.gt, '>', '<'), + (operator.eq, '=', '='), + (operator.ne, '!=', '!='), + (operator.le, '<=', '>='), + (operator.ge, '>=', '<=')): + for (lhs, rhs, l_sql, r_sql) in ( + ('a', table1.c.myid, ':myid_1', 'mytable.myid'), + ('a', literal('b'), ':param_2', ':param_1'), # note swap! + (table1.c.myid, 'b', 'mytable.myid', ':myid_1'), + (table1.c.myid, literal('b'), 'mytable.myid', ':param_1'), + (table1.c.myid, table1.c.myid, 'mytable.myid', 'mytable.myid'), + (literal('a'), 'b', ':param_1', ':param_2'), + (literal('a'), table1.c.myid, ':param_1', 'mytable.myid'), + (literal('a'), literal('b'), ':param_1', ':param_2'), + (dt, literal('b'), ':param_2', ':param_1'), + (literal('b'), dt, ':param_1', ':param_2'), + ): + + # the compiled clause should match either (e.g.): + # 'a' < 'b' -or- 'b' > 'a'. + compiled = str(py_op(lhs, rhs)) + fwd_sql = "%s %s %s" % (l_sql, fwd_op, r_sql) + rev_sql = "%s %s %s" % (r_sql, rev_op, l_sql) + + self.assert_(compiled == fwd_sql or compiled == rev_sql, + "\n'" + compiled + "'\n does not match\n'" + + fwd_sql + "'\n or\n'" + rev_sql + "'") + + self.assert_compile( + table1.select((table1.c.myid != 12) & ~(table1.c.name=='john')), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND mytable.name != :name_1" + ) + + self.assert_compile( + table1.select((table1.c.myid != 12) & ~(table1.c.name.between('jack','john'))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND "\ + "NOT (mytable.name BETWEEN :name_1 AND :name_2)" + ) + + self.assert_compile( + table1.select((table1.c.myid != 12) & ~and_(table1.c.name=='john', table1.c.name=='ed', table1.c.name=='fred')), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND "\ + "NOT (mytable.name = :name_1 AND mytable.name = :name_2 AND mytable.name = :name_3)" + ) + + self.assert_compile( + table1.select((table1.c.myid != 12) & ~table1.c.name), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != :myid_1 AND NOT mytable.name" + ) + + self.assert_compile( + literal("a") + literal("b") * literal("c"), ":param_1 || :param_2 * :param_3" + ) + + # test the op() function, also that its results are further usable in expressions + self.assert_compile( + table1.select(table1.c.myid.op('hoho')(12)==14), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE (mytable.myid hoho :myid_1) = :param_1" + ) + + # test that clauses can be pickled (operators need to be module-level, etc.) + clause = (table1.c.myid == 12) & table1.c.myid.between(15, 20) & table1.c.myid.like('hoho') + assert str(clause) == str(util.pickle.loads(util.pickle.dumps(clause))) + + + def test_like(self): + for expr, check, dialect in [ + (table1.c.myid.like('somstr'), "mytable.myid LIKE :myid_1", None), + (~table1.c.myid.like('somstr'), "mytable.myid NOT LIKE :myid_1", None), + (table1.c.myid.like('somstr', escape='\\'), "mytable.myid LIKE :myid_1 ESCAPE '\\'", None), + (~table1.c.myid.like('somstr', escape='\\'), "mytable.myid NOT LIKE :myid_1 ESCAPE '\\'", None), + (table1.c.myid.ilike('somstr', escape='\\'), "lower(mytable.myid) LIKE lower(:myid_1) ESCAPE '\\'", None), + (~table1.c.myid.ilike('somstr', escape='\\'), "lower(mytable.myid) NOT LIKE lower(:myid_1) ESCAPE '\\'", None), + (table1.c.myid.ilike('somstr', escape='\\'), "mytable.myid ILIKE %(myid_1)s ESCAPE '\\'", postgres.PGDialect()), + (~table1.c.myid.ilike('somstr', escape='\\'), "mytable.myid NOT ILIKE %(myid_1)s ESCAPE '\\'", postgres.PGDialect()), + (table1.c.name.ilike('%something%'), "lower(mytable.name) LIKE lower(:name_1)", None), + (table1.c.name.ilike('%something%'), "mytable.name ILIKE %(name_1)s", postgres.PGDialect()), + (~table1.c.name.ilike('%something%'), "lower(mytable.name) NOT LIKE lower(:name_1)", None), + (~table1.c.name.ilike('%something%'), "mytable.name NOT ILIKE %(name_1)s", postgres.PGDialect()), + ]: + self.assert_compile(expr, check, dialect=dialect) + + def test_match(self): + for expr, check, dialect in [ + (table1.c.myid.match('somstr'), "mytable.myid MATCH ?", sqlite.SQLiteDialect()), + (table1.c.myid.match('somstr'), "MATCH (mytable.myid) AGAINST (%s IN BOOLEAN MODE)", mysql.MySQLDialect()), + (table1.c.myid.match('somstr'), "CONTAINS (mytable.myid, :myid_1)", mssql.MSSQLDialect()), + (table1.c.myid.match('somstr'), "mytable.myid @@ to_tsquery(%(myid_1)s)", postgres.PGDialect()), + (table1.c.myid.match('somstr'), "CONTAINS (mytable.myid, :myid_1)", oracle.OracleDialect()), + ]: + self.assert_compile(expr, check, dialect=dialect) + + def test_composed_string_comparators(self): + self.assert_compile( + table1.c.name.contains('jo'), "mytable.name LIKE '%%' || :name_1 || '%%'" , checkparams = {'name_1': u'jo'}, + ) + self.assert_compile( + table1.c.name.contains('jo'), "mytable.name LIKE concat(concat('%%', %s), '%%')" , checkparams = {'name_1': u'jo'}, + dialect=mysql.dialect() + ) + self.assert_compile( + table1.c.name.contains('jo', escape='\\'), "mytable.name LIKE '%%' || :name_1 || '%%' ESCAPE '\\'" , checkparams = {'name_1': u'jo'}, + ) + self.assert_compile( table1.c.name.startswith('jo', escape='\\'), "mytable.name LIKE :name_1 || '%%' ESCAPE '\\'" ) + self.assert_compile( table1.c.name.endswith('jo', escape='\\'), "mytable.name LIKE '%%' || :name_1 ESCAPE '\\'" ) + self.assert_compile( table1.c.name.endswith('hn'), "mytable.name LIKE '%%' || :name_1", checkparams = {'name_1': u'hn'}, ) + self.assert_compile( + table1.c.name.endswith('hn'), "mytable.name LIKE concat('%%', %s)", + checkparams = {'name_1': u'hn'}, dialect=mysql.dialect() + ) + self.assert_compile( + table1.c.name.startswith(u"hi \xf6 \xf5"), "mytable.name LIKE :name_1 || '%%'", + checkparams = {'name_1': u'hi \xf6 \xf5'}, + ) + self.assert_compile(column('name').endswith(text("'foo'")), "name LIKE '%%' || 'foo'" ) + self.assert_compile(column('name').endswith(literal_column("'foo'")), "name LIKE '%%' || 'foo'" ) + self.assert_compile(column('name').startswith(text("'foo'")), "name LIKE 'foo' || '%%'" ) + self.assert_compile(column('name').startswith(text("'foo'")), "name LIKE concat('foo', '%%')", dialect=mysql.dialect()) + self.assert_compile(column('name').startswith(literal_column("'foo'")), "name LIKE 'foo' || '%%'" ) + self.assert_compile(column('name').startswith(literal_column("'foo'")), "name LIKE concat('foo', '%%')", dialect=mysql.dialect()) + + def test_multiple_col_binds(self): + self.assert_compile( + select(["*"], or_(table1.c.myid == 12, table1.c.myid=='asdf', table1.c.myid == 'foo')), + "SELECT * FROM mytable WHERE mytable.myid = :myid_1 OR mytable.myid = :myid_2 OR mytable.myid = :myid_3" + ) + + def test_orderby_groupby(self): + self.assert_compile( + table2.select(order_by = [table2.c.otherid, asc(table2.c.othername)]), + "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername ASC" + ) + + self.assert_compile( + table2.select(order_by = [table2.c.otherid, table2.c.othername.desc()]), + "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername DESC" + ) + + # generative order_by + self.assert_compile( + table2.select().order_by(table2.c.otherid).order_by(table2.c.othername.desc()), + "SELECT myothertable.otherid, myothertable.othername FROM myothertable ORDER BY myothertable.otherid, myothertable.othername DESC" + ) + + self.assert_compile( + table2.select().order_by(table2.c.otherid).order_by(table2.c.othername.desc()).order_by(None), + "SELECT myothertable.otherid, myothertable.othername FROM myothertable" + ) + + self.assert_compile( + select([table2.c.othername, func.count(table2.c.otherid)], group_by = [table2.c.othername]), + "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername" + ) + + # generative group by + self.assert_compile( + select([table2.c.othername, func.count(table2.c.otherid)]).group_by(table2.c.othername), + "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername" + ) + + self.assert_compile( + select([table2.c.othername, func.count(table2.c.otherid)]).group_by(table2.c.othername).group_by(None), + "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable" + ) + + self.assert_compile( + select([table2.c.othername, func.count(table2.c.otherid)], group_by = [table2.c.othername], order_by = [table2.c.othername]), + "SELECT myothertable.othername, count(myothertable.otherid) AS count_1 FROM myothertable GROUP BY myothertable.othername ORDER BY myothertable.othername" + ) + + def test_for_update(self): + self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE") + + self.assert_compile(table1.select(table1.c.myid==7, for_update="nowait"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE") + + self.assert_compile(table1.select(table1.c.myid==7, for_update="nowait"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE NOWAIT", dialect=oracle.dialect()) + + self.assert_compile(table1.select(table1.c.myid==7, for_update="read"), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = %s LOCK IN SHARE MODE", dialect=mysql.dialect()) + + self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = %s FOR UPDATE", dialect=mysql.dialect()) + + self.assert_compile(table1.select(table1.c.myid==7, for_update=True), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE", dialect=oracle.dialect()) + + def test_alias(self): + # test the alias for a table1. column names stay the same, table name "changes" to "foo". + self.assert_compile( + select([table1.alias('foo')]) + ,"SELECT foo.myid, foo.name, foo.description FROM mytable AS foo") + + for dialect in (firebird.dialect(), oracle.dialect()): + self.assert_compile( + select([table1.alias('foo')]) + ,"SELECT foo.myid, foo.name, foo.description FROM mytable foo" + ,dialect=dialect) + + self.assert_compile( + select([table1.alias()]) + ,"SELECT mytable_1.myid, mytable_1.name, mytable_1.description FROM mytable AS mytable_1") + + # create a select for a join of two tables. use_labels means the column names will have + # labels tablename_columnname, which become the column keys accessible off the Selectable object. + # also, only use one column from the second table and all columns from the first table1. + q = select([table1, table2.c.otherid], table1.c.myid == table2.c.otherid, use_labels = True) + + # make an alias of the "selectable". column names stay the same (i.e. the labels), table name "changes" to "t2view". + a = alias(q, 't2view') + + # select from that alias, also using labels. two levels of labels should produce two underscores. + # also, reference the column "mytable_myid" off of the t2view alias. + self.assert_compile( + a.select(a.c.mytable_myid == 9, use_labels = True), + "SELECT t2view.mytable_myid AS t2view_mytable_myid, t2view.mytable_name AS t2view_mytable_name, \ +t2view.mytable_description AS t2view_mytable_description, t2view.myothertable_otherid AS t2view_myothertable_otherid FROM \ +(SELECT mytable.myid AS mytable_myid, mytable.name AS mytable_name, mytable.description AS mytable_description, \ +myothertable.otherid AS myothertable_otherid FROM mytable, myothertable \ +WHERE mytable.myid = myothertable.otherid) AS t2view WHERE t2view.mytable_myid = :mytable_myid_1" + ) + + + def test_prefixes(self): + self.assert_compile(table1.select().prefix_with("SQL_CALC_FOUND_ROWS").prefix_with("SQL_SOME_WEIRD_MYSQL_THING"), + "SELECT SQL_CALC_FOUND_ROWS SQL_SOME_WEIRD_MYSQL_THING mytable.myid, mytable.name, mytable.description FROM mytable" + ) + + def test_text(self): + self.assert_compile( + text("select * from foo where lala = bar") , + "select * from foo where lala = bar" + ) + + # test bytestring + self.assert_compile(select( + ["foobar(a)", "pk_foo_bar(syslaal)"], + "a = 12", + from_obj = ["foobar left outer join lala on foobar.foo = lala.foo"] + ), + "SELECT foobar(a), pk_foo_bar(syslaal) FROM foobar left outer join lala on foobar.foo = lala.foo WHERE a = 12") + + # test unicode + self.assert_compile(select( + [u"foobar(a)", u"pk_foo_bar(syslaal)"], + u"a = 12", + from_obj = [u"foobar left outer join lala on foobar.foo = lala.foo"] + ), + u"SELECT foobar(a), pk_foo_bar(syslaal) FROM foobar left outer join lala on foobar.foo = lala.foo WHERE a = 12") + + # test building a select query programmatically with text + s = select() + s.append_column("column1") + s.append_column("column2") + s.append_whereclause("column1=12") + s.append_whereclause("column2=19") + s = s.order_by("column1") + s.append_from("table1") + self.assert_compile(s, "SELECT column1, column2 FROM table1 WHERE column1=12 AND column2=19 ORDER BY column1") + + self.assert_compile( + select(["column1", "column2"], from_obj=table1).alias('somealias').select(), + "SELECT somealias.column1, somealias.column2 FROM (SELECT column1, column2 FROM mytable) AS somealias" + ) + + # test that use_labels doesnt interfere with literal columns + self.assert_compile( + select(["column1", "column2", table1.c.myid], from_obj=table1, use_labels=True), + "SELECT column1, column2, mytable.myid AS mytable_myid FROM mytable" + ) + + # test that use_labels doesnt interfere with literal columns that have textual labels + self.assert_compile( + select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=table1, use_labels=True), + "SELECT column1 AS foobar, column2 AS hoho, mytable.myid AS mytable_myid FROM mytable" + ) + + print "---------------------------------------------" + s1 = select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=[table1]) + print "---------------------------------------------" + # test that "auto-labeling of subquery columns" doesnt interfere with literal columns, + # exported columns dont get quoted + self.assert_compile( + select(["column1 AS foobar", "column2 AS hoho", table1.c.myid], from_obj=[table1]).select(), + "SELECT column1 AS foobar, column2 AS hoho, myid FROM (SELECT column1 AS foobar, column2 AS hoho, mytable.myid AS myid FROM mytable)" + ) + + self.assert_compile( + select(['col1','col2'], from_obj='tablename').alias('myalias'), + "SELECT col1, col2 FROM tablename" + ) + + def test_binds_in_text(self): + self.assert_compile( + text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar', 4), bindparam('whee', 7)]), + "select * from foo where lala=:bar and hoho=:whee", + checkparams={'bar':4, 'whee': 7}, + ) + + self.assert_compile( + text("select * from foo where clock='05:06:07'"), + "select * from foo where clock='05:06:07'", + checkparams={}, + params={}, + ) + + dialect = postgres.dialect() + self.assert_compile( + text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar',4), bindparam('whee',7)]), + "select * from foo where lala=%(bar)s and hoho=%(whee)s", + checkparams={'bar':4, 'whee': 7}, + dialect=dialect + ) + + # test escaping out text() params with a backslash + self.assert_compile( + text("select * from foo where clock='05:06:07' and mork='\:mindy'"), + "select * from foo where clock='05:06:07' and mork=':mindy'", + checkparams={}, + params={}, + dialect=dialect + ) + + dialect = sqlite.dialect() + self.assert_compile( + text("select * from foo where lala=:bar and hoho=:whee", bindparams=[bindparam('bar',4), bindparam('whee',7)]), + "select * from foo where lala=? and hoho=?", + checkparams={'bar':4, 'whee':7}, + dialect=dialect + ) + + self.assert_compile(select( + [table1, table2.c.otherid, "sysdate()", "foo, bar, lala"], + and_( + "foo.id = foofoo(lala)", + "datetime(foo) = Today", + table1.c.myid == table2.c.otherid, + ) + ), + "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, sysdate(), foo, bar, lala \ +FROM mytable, myothertable WHERE foo.id = foofoo(lala) AND datetime(foo) = Today AND mytable.myid = myothertable.otherid") + + self.assert_compile(select( + [alias(table1, 't'), "foo.f"], + "foo.f = t.id", + from_obj = ["(select f from bar where lala=heyhey) foo"] + ), + "SELECT t.myid, t.name, t.description, foo.f FROM mytable AS t, (select f from bar where lala=heyhey) foo WHERE foo.f = t.id") + + # test Text embedded within select_from(), using binds + generate_series = text("generate_series(:x, :y, :z) as s(a)", bindparams=[bindparam('x'), bindparam('y'), bindparam('z')]) + + s =select([(func.current_date() + literal_column("s.a")).label("dates")]).select_from(generate_series) + self.assert_compile(s, "SELECT CURRENT_DATE + s.a AS dates FROM generate_series(:x, :y, :z) as s(a)", checkparams={'y': None, 'x': None, 'z': None}) + + self.assert_compile(s.params(x=5, y=6, z=7), "SELECT CURRENT_DATE + s.a AS dates FROM generate_series(:x, :y, :z) as s(a)", checkparams={'y': 6, 'x': 5, 'z': 7}) + + + def test_literal(self): + + self.assert_compile(select([literal('foo')]), "SELECT :param_1") + + self.assert_compile(select([literal("foo") + literal("bar")], from_obj=[table1]), + "SELECT :param_1 || :param_2 AS anon_1 FROM mytable") + + def test_calculated_columns(self): + value_tbl = table('values', + column('id', Integer), + column('val1', Float), + column('val2', Float), + ) + + self.assert_compile( + select([value_tbl.c.id, (value_tbl.c.val2 - + value_tbl.c.val1)/value_tbl.c.val1]), + "SELECT values.id, (values.val2 - values.val1) / values.val1 AS anon_1 FROM values" + ) + + self.assert_compile( + select([value_tbl.c.id], (value_tbl.c.val2 - + value_tbl.c.val1)/value_tbl.c.val1 > 2.0), + "SELECT values.id FROM values WHERE (values.val2 - values.val1) / values.val1 > :param_1" + ) + + self.assert_compile( + select([value_tbl.c.id], value_tbl.c.val1 / (value_tbl.c.val2 - value_tbl.c.val1) /value_tbl.c.val1 > 2.0), + "SELECT values.id FROM values WHERE values.val1 / (values.val2 - values.val1) / values.val1 > :param_1" + ) + + def test_collate(self): + for expr in (select([table1.c.name.collate('latin1_german2_ci')]), + select([collate(table1.c.name, 'latin1_german2_ci')])): + self.assert_compile( + expr, "SELECT mytable.name COLLATE latin1_german2_ci AS anon_1 FROM mytable") + + assert table1.c.name.collate('latin1_german2_ci').type is table1.c.name.type + + expr = select([table1.c.name.collate('latin1_german2_ci').label('k1')]).order_by('k1') + self.assert_compile(expr,"SELECT mytable.name COLLATE latin1_german2_ci AS k1 FROM mytable ORDER BY k1") + + expr = select([collate('foo', 'latin1_german2_ci').label('k1')]) + self.assert_compile(expr,"SELECT :param_1 COLLATE latin1_german2_ci AS k1") + + expr = select([table1.c.name.collate('latin1_german2_ci').like('%x%')]) + self.assert_compile(expr, + "SELECT mytable.name COLLATE latin1_german2_ci " + "LIKE :param_1 AS anon_1 FROM mytable") + + expr = select([table1.c.name.like(collate('%x%', 'latin1_german2_ci'))]) + self.assert_compile(expr, + "SELECT mytable.name " + "LIKE :param_1 COLLATE latin1_german2_ci AS anon_1 " + "FROM mytable") + + expr = select([table1.c.name.collate('col1').like( + collate('%x%', 'col2'))]) + self.assert_compile(expr, + "SELECT mytable.name COLLATE col1 " + "LIKE :param_1 COLLATE col2 AS anon_1 " + "FROM mytable") + + expr = select([func.concat('a', 'b').collate('latin1_german2_ci').label('x')]) + self.assert_compile(expr, + "SELECT concat(:param_1, :param_2) " + "COLLATE latin1_german2_ci AS x") + + + expr = select([table1.c.name]).order_by(table1.c.name.collate('latin1_german2_ci')) + self.assert_compile(expr, "SELECT mytable.name FROM mytable ORDER BY mytable.name COLLATE latin1_german2_ci") + + def test_percent_chars(self): + t = table("table%name", + column("percent%"), + column("%(oneofthese)s"), + column("spaces % more spaces"), + ) + self.assert_compile( + t.select(use_labels=True), + '''SELECT "table%name"."percent%" AS "table%name_percent%", '''\ + '''"table%name"."%(oneofthese)s" AS "table%name_%(oneofthese)s", '''\ + '''"table%name"."spaces % more spaces" AS "table%name_spaces % more spaces" FROM "table%name"''' + ) + + + def test_joins(self): + self.assert_compile( + join(table2, table1, table1.c.myid == table2.c.otherid).select(), + "SELECT myothertable.otherid, myothertable.othername, mytable.myid, mytable.name, \ +mytable.description FROM myothertable JOIN mytable ON mytable.myid = myothertable.otherid" + ) + + self.assert_compile( + select( + [table1], + from_obj = [join(table1, table2, table1.c.myid == table2.c.otherid)] + ), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid") + + self.assert_compile( + select( + [join(join(table1, table2, table1.c.myid == table2.c.otherid), table3, table1.c.myid == table3.c.userid)] + ), + "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid JOIN thirdtable ON mytable.myid = thirdtable.userid" + ) + + self.assert_compile( + join(users, addresses, users.c.user_id==addresses.c.user_id).select(), + "SELECT users.user_id, users.user_name, users.password, addresses.address_id, addresses.user_id, addresses.street, addresses.city, addresses.state, addresses.zip FROM users JOIN addresses ON users.user_id = addresses.user_id" + ) + + self.assert_compile( + select([table1, table2, table3], + + from_obj = [join(table1, table2, table1.c.myid == table2.c.otherid).outerjoin(table3, table1.c.myid==table3.c.userid)] + + #from_obj = [outerjoin(join(table, table2, table1.c.myid == table2.c.otherid), table3, table1.c.myid==table3.c.userid)] + ) + ,"SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable JOIN myothertable ON mytable.myid = myothertable.otherid LEFT OUTER JOIN thirdtable ON mytable.myid = thirdtable.userid" + ) + self.assert_compile( + select([table1, table2, table3], + from_obj = [outerjoin(table1, join(table2, table3, table2.c.otherid == table3.c.userid), table1.c.myid==table2.c.otherid)] + ) + ,"SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername, thirdtable.userid, thirdtable.otherstuff FROM mytable LEFT OUTER JOIN (myothertable JOIN thirdtable ON myothertable.otherid = thirdtable.userid) ON mytable.myid = myothertable.otherid" + ) + + query = select( + [table1, table2], + or_( + table1.c.name == 'fred', + table1.c.myid == 10, + table2.c.othername != 'jack', + "EXISTS (select yay from foo where boo = lar)" + ), + from_obj = [ outerjoin(table1, table2, table1.c.myid == table2.c.otherid) ] + ) + self.assert_compile(query, + "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername \ +FROM mytable LEFT OUTER JOIN myothertable ON mytable.myid = myothertable.otherid \ +WHERE mytable.name = :name_1 OR mytable.myid = :myid_1 OR \ +myothertable.othername != :othername_1 OR \ +EXISTS (select yay from foo where boo = lar)", + ) + + def test_compound_selects(self): + try: + union(table3.select(), table1.select()) + except exc.ArgumentError, err: + assert str(err) == "All selectables passed to CompoundSelect must have identical numbers of columns; select #1 has 2 columns, select #2 has 3" + + x = union( + select([table1], table1.c.myid == 5), + select([table1], table1.c.myid == 12), + order_by = [table1.c.myid], + ) + + self.assert_compile(x, "SELECT mytable.myid, mytable.name, mytable.description \ +FROM mytable WHERE mytable.myid = :myid_1 UNION \ +SELECT mytable.myid, mytable.name, mytable.description \ +FROM mytable WHERE mytable.myid = :myid_2 ORDER BY mytable.myid") + + u1 = union( + select([table1.c.myid, table1.c.name]), + select([table2]), + select([table3]) + ) + self.assert_compile(u1, + "SELECT mytable.myid, mytable.name \ +FROM mytable UNION SELECT myothertable.otherid, myothertable.othername \ +FROM myothertable UNION SELECT thirdtable.userid, thirdtable.otherstuff FROM thirdtable") + + assert u1.corresponding_column(table2.c.otherid) is u1.c.myid + + # TODO - why is there an extra space before the LIMIT ? + self.assert_compile( + union( + select([table1.c.myid, table1.c.name]), + select([table2]), + order_by=['myid'], + offset=10, + limit=5 + ) + , "SELECT mytable.myid, mytable.name \ +FROM mytable UNION SELECT myothertable.otherid, myothertable.othername \ +FROM myothertable ORDER BY myid LIMIT 5 OFFSET 10" + ) + + self.assert_compile( + union( + select([table1.c.myid, table1.c.name, func.max(table1.c.description)], table1.c.name=='name2', group_by=[table1.c.myid, table1.c.name]), + table1.select(table1.c.name=='name1') + ) + , + "SELECT mytable.myid, mytable.name, max(mytable.description) AS max_1 FROM mytable \ +WHERE mytable.name = :name_1 GROUP BY mytable.myid, mytable.name UNION SELECT mytable.myid, mytable.name, mytable.description \ +FROM mytable WHERE mytable.name = :name_2" + ) + + self.assert_compile( + union( + select([literal(100).label('value')]), + select([literal(200).label('value')]) + ), + "SELECT :param_1 AS value UNION SELECT :param_2 AS value" + ) + + self.assert_compile( + union_all( + select([table1.c.myid]), + union( + select([table2.c.otherid]), + select([table3.c.userid]), + ) + ) + , + "SELECT mytable.myid FROM mytable UNION ALL (SELECT myothertable.otherid FROM myothertable UNION \ +SELECT thirdtable.userid FROM thirdtable)" + ) + # This doesn't need grouping, so don't group to not give sqlite unnecessarily hard time + self.assert_compile( + union( + except_( + select([table2.c.otherid]), + select([table3.c.userid]), + ), + select([table1.c.myid]) + ) + , + "SELECT myothertable.otherid FROM myothertable EXCEPT SELECT thirdtable.userid FROM thirdtable \ +UNION SELECT mytable.myid FROM mytable" + ) + + s = select([column('foo'), column('bar')]) + s = union(s, s) + s = union(s, s) + self.assert_compile(s, "SELECT foo, bar UNION SELECT foo, bar UNION (SELECT foo, bar UNION SELECT foo, bar)") + + s = select([column('foo'), column('bar')]) + # ORDER BY's even though not supported by all DB's, are rendered if requested + self.assert_compile(union(s.order_by("foo"), s.order_by("bar")), + "SELECT foo, bar ORDER BY foo UNION SELECT foo, bar ORDER BY bar" + ) + # self_group() is honored + self.assert_compile(union(s.order_by("foo").self_group(), s.order_by("bar").limit(10).self_group()), + "(SELECT foo, bar ORDER BY foo) UNION (SELECT foo, bar ORDER BY bar LIMIT 10)" + ) + + + @testing.uses_deprecated() + def test_binds(self): + for ( + stmt, + expected_named_stmt, + expected_positional_stmt, + expected_default_params_dict, + expected_default_params_list, + test_param_dict, + expected_test_params_dict, + expected_test_params_list + ) in [ + ( + select( + [table1, table2], + and_( + table1.c.myid == table2.c.otherid, + table1.c.name == bindparam('mytablename') + )), + """SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable.name = :mytablename""", + """SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable.name = ?""", + {'mytablename':None}, [None], + {'mytablename':5}, {'mytablename':5}, [5] + ), + ( + select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myid'))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid", + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", + {'myid':None}, [None, None], + {'myid':5}, {'myid':5}, [5,5] + ), + ( + text("SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid"), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myid", + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", + {'myid':None}, [None, None], + {'myid':5}, {'myid':5}, [5,5] + ), + ( + select([table1], or_(table1.c.myid==bindparam('myid', unique=True), table2.c.otherid==bindparam('myid', unique=True))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid_1 OR myothertable.otherid = :myid_2", + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", + {'myid_1':None, 'myid_2':None}, [None, None], + {'myid_1':5, 'myid_2': 6}, {'myid_1':5, 'myid_2':6}, [5,6] + ), + ( + bindparam('test', type_=String) + text("'hi'"), + ":test || 'hi'", + "? || 'hi'", + {'test':None}, [None], + {}, {'test':None}, [None] + ), + ( + select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myotherid'))).params({'myid':8, 'myotherid':7}), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid OR myothertable.otherid = :myotherid", + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", + {'myid':8, 'myotherid':7}, [8, 7], + {'myid':5}, {'myid':5, 'myotherid':7}, [5,7] + ), + ( + select([table1], or_(table1.c.myid==bindparam('myid', value=7, unique=True), table2.c.otherid==bindparam('myid', value=8, unique=True))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = :myid_1 OR myothertable.otherid = :myid_2", + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable, myothertable WHERE mytable.myid = ? OR myothertable.otherid = ?", + {'myid_1':7, 'myid_2':8}, [7,8], + {'myid_1':5, 'myid_2':6}, {'myid_1':5, 'myid_2':6}, [5,6] + ), + ]: + + self.assert_compile(stmt, expected_named_stmt, params=expected_default_params_dict) + self.assert_compile(stmt, expected_positional_stmt, dialect=sqlite.dialect()) + nonpositional = stmt.compile() + positional = stmt.compile(dialect=sqlite.dialect()) + pp = positional.get_params() + assert [pp[k] for k in positional.positiontup] == expected_default_params_list + assert nonpositional.get_params(**test_param_dict) == expected_test_params_dict, "expected :%s got %s" % (str(expected_test_params_dict), str(nonpositional.get_params(**test_param_dict))) + pp = positional.get_params(**test_param_dict) + assert [pp[k] for k in positional.positiontup] == expected_test_params_list + + # check that params() doesnt modify original statement + s = select([table1], or_(table1.c.myid==bindparam('myid'), table2.c.otherid==bindparam('myotherid'))) + s2 = s.params({'myid':8, 'myotherid':7}) + s3 = s2.params({'myid':9}) + assert s.compile().params == {'myid':None, 'myotherid':None} + assert s2.compile().params == {'myid':8, 'myotherid':7} + assert s3.compile().params == {'myid':9, 'myotherid':7} + + # test using same 'unique' param object twice in one compile + s = select([table1.c.myid]).where(table1.c.myid==12).as_scalar() + s2 = select([table1, s], table1.c.myid==s) + self.assert_compile(s2, + "SELECT mytable.myid, mytable.name, mytable.description, (SELECT mytable.myid FROM mytable WHERE mytable.myid = "\ + ":myid_1) AS anon_1 FROM mytable WHERE mytable.myid = (SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1)") + positional = s2.compile(dialect=sqlite.dialect()) + + pp = positional.get_params() + assert [pp[k] for k in positional.positiontup] == [12, 12] + + # check that conflicts with "unique" params are caught + s = select([table1], or_(table1.c.myid==7, table1.c.myid==bindparam('myid_1'))) + assert_raises_message(exc.CompileError, "conflicts with unique bind parameter of the same name", str, s) + + s = select([table1], or_(table1.c.myid==7, table1.c.myid==8, table1.c.myid==bindparam('myid_1'))) + assert_raises_message(exc.CompileError, "conflicts with unique bind parameter of the same name", str, s) + + def test_binds_no_hash_collision(self): + """test that construct_params doesn't corrupt dict due to hash collisions""" + + total_params = 100000 + + in_clause = [':in%d' % i for i in range(total_params)] + params = dict(('in%d' % i, i) for i in range(total_params)) + sql = 'text clause %s' % ', '.join(in_clause) + t = text(sql) + assert len(t.bindparams) == total_params + c = t.compile() + pp = c.construct_params(params) + assert len(set(pp)) == total_params + assert len(set(pp.values())) == total_params + + + def test_bind_as_col(self): + t = table('foo', column('id')) + + s = select([t, literal('lala').label('hoho')]) + self.assert_compile(s, "SELECT foo.id, :param_1 AS hoho FROM foo") + + assert [str(c) for c in s.c] == ["id", "hoho"] + + def test_in(self): + self.assert_compile(select([table1], table1.c.myid.in_(['a'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1)") + + self.assert_compile(select([table1], ~table1.c.myid.in_(['a'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid NOT IN (:myid_1)") + + self.assert_compile(select([table1], table1.c.myid.in_(['a', 'b'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2)") + + self.assert_compile(select([table1], table1.c.myid.in_(iter(['a', 'b']))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a')])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), 'b'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :myid_1)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), literal('b')])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :param_2)") + + self.assert_compile(select([table1], table1.c.myid.in_(['a', literal('b')])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :param_1)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal(1) + 'a'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 + :param_2)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a') +'a', 'b'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 || :param_2, :myid_1)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a') + literal('a'), literal('b')])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 || :param_2, :param_3)") + + self.assert_compile(select([table1], table1.c.myid.in_([1, literal(3) + 4])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :param_1 + :param_2)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a') < 'b'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1 < :param_2)") + + self.assert_compile(select([table1], table1.c.myid.in_([table1.c.myid])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (mytable.myid)") + + self.assert_compile(select([table1], table1.c.myid.in_(['a', table1.c.myid])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, mytable.myid)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), table1.c.myid])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, mytable.myid)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal('a'), table1.c.myid +'a'])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, mytable.myid + :myid_1)") + + self.assert_compile(select([table1], table1.c.myid.in_([literal(1), 'a' + table1.c.myid])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:param_1, :myid_1 + mytable.myid)") + + self.assert_compile(select([table1], table1.c.myid.in_([1, 2, 3])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (:myid_1, :myid_2, :myid_3)") + + self.assert_compile(select([table1], table1.c.myid.in_(select([table2.c.otherid]))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (SELECT myothertable.otherid FROM myothertable)") + + self.assert_compile(select([table1], ~table1.c.myid.in_(select([table2.c.otherid]))), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid NOT IN (SELECT myothertable.otherid FROM myothertable)") + + self.assert_compile(select([table1], table1.c.myid.in_( + union( + select([table1.c.myid], table1.c.myid == 5), + select([table1.c.myid], table1.c.myid == 12), + ) + )), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable \ +WHERE mytable.myid IN (\ +SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_1 \ +UNION SELECT mytable.myid FROM mytable WHERE mytable.myid = :myid_2)") + + # test that putting a select in an IN clause does not blow away its ORDER BY clause + self.assert_compile( + select([table1, table2], + table2.c.otherid.in_( + select([table2.c.otherid], order_by=[table2.c.othername], limit=10, correlate=False) + ), + from_obj=[table1.join(table2, table1.c.myid==table2.c.otherid)], order_by=[table1.c.myid] + ), + "SELECT mytable.myid, mytable.name, mytable.description, myothertable.otherid, myothertable.othername FROM mytable "\ + "JOIN myothertable ON mytable.myid = myothertable.otherid WHERE myothertable.otherid IN (SELECT myothertable.otherid "\ + "FROM myothertable ORDER BY myothertable.othername LIMIT 10) ORDER BY mytable.myid" + ) + + # test empty in clause + self.assert_compile(select([table1], table1.c.myid.in_([])), + "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid != mytable.myid") + + self.assert_compile( + select([table1.c.myid.in_(select([table2.c.otherid]))]), + "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable" + ) + self.assert_compile( + select([table1.c.myid.in_(select([table2.c.otherid]).as_scalar())]), + "SELECT mytable.myid IN (SELECT myothertable.otherid FROM myothertable) AS anon_1 FROM mytable" + ) + + def test_cast(self): + tbl = table('casttest', + column('id', Integer), + column('v1', Float), + column('v2', Float), + column('ts', TIMESTAMP), + ) + + def check_results(dialect, expected_results, literal): + eq_(len(expected_results), 5, 'Incorrect number of expected results') + eq_(str(cast(tbl.c.v1, Numeric).compile(dialect=dialect)), 'CAST(casttest.v1 AS %s)' %expected_results[0]) + eq_(str(cast(tbl.c.v1, Numeric(12, 9)).compile(dialect=dialect)), 'CAST(casttest.v1 AS %s)' %expected_results[1]) + eq_(str(cast(tbl.c.ts, Date).compile(dialect=dialect)), 'CAST(casttest.ts AS %s)' %expected_results[2]) + eq_(str(cast(1234, TEXT).compile(dialect=dialect)), 'CAST(%s AS %s)' %(literal, expected_results[3])) + eq_(str(cast('test', String(20)).compile(dialect=dialect)), 'CAST(%s AS %s)' %(literal, expected_results[4])) + # fixme: shoving all of this dialect-specific stuff in one test + # is now officialy completely ridiculous AND non-obviously omits + # coverage on other dialects. + sel = select([tbl, cast(tbl.c.v1, Numeric)]).compile(dialect=dialect) + if isinstance(dialect, type(mysql.dialect())): + eq_(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS DECIMAL(10, 2)) AS anon_1 \nFROM casttest") + else: + eq_(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS NUMERIC(10, 2)) AS anon_1 \nFROM casttest") + + # first test with Postgres engine + check_results(postgres.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%(param_1)s') + + # then the Oracle engine + check_results(oracle.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'CLOB', 'VARCHAR(20)'], ':param_1') + + # then the sqlite engine + check_results(sqlite.dialect(), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '?') + + # then the MySQL engine + check_results(mysql.dialect(), ['DECIMAL(10, 2)', 'DECIMAL(12, 9)', 'DATE', 'CHAR', 'CHAR(20)'], '%s') + + self.assert_compile(cast(text('NULL'), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) + self.assert_compile(cast(null(), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) + self.assert_compile(cast(literal_column('NULL'), Integer), "CAST(NULL AS INTEGER)", dialect=sqlite.dialect()) + + def test_date_between(self): + import datetime + table = Table('dt', metadata, + Column('date', Date)) + self.assert_compile(table.select(table.c.date.between(datetime.date(2006,6,1), datetime.date(2006,6,5))), + "SELECT dt.date FROM dt WHERE dt.date BETWEEN :date_1 AND :date_2", checkparams={'date_1':datetime.date(2006,6,1), 'date_2':datetime.date(2006,6,5)}) + + self.assert_compile(table.select(sql.between(table.c.date, datetime.date(2006,6,1), datetime.date(2006,6,5))), + "SELECT dt.date FROM dt WHERE dt.date BETWEEN :param_1 AND :param_2", checkparams={'param_1':datetime.date(2006,6,1), 'param_2':datetime.date(2006,6,5)}) + + def test_operator_precedence(self): + table = Table('op', metadata, + Column('field', Integer)) + self.assert_compile(table.select((table.c.field == 5) == None), + "SELECT op.field FROM op WHERE (op.field = :field_1) IS NULL") + self.assert_compile(table.select((table.c.field + 5) == table.c.field), + "SELECT op.field FROM op WHERE op.field + :field_1 = op.field") + self.assert_compile(table.select((table.c.field + 5) * 6), + "SELECT op.field FROM op WHERE (op.field + :field_1) * :param_1") + self.assert_compile(table.select((table.c.field * 5) + 6), + "SELECT op.field FROM op WHERE op.field * :field_1 + :param_1") + self.assert_compile(table.select(5 + table.c.field.in_([5,6])), + "SELECT op.field FROM op WHERE :param_1 + (op.field IN (:field_1, :field_2))") + self.assert_compile(table.select((5 + table.c.field).in_([5,6])), + "SELECT op.field FROM op WHERE :field_1 + op.field IN (:param_1, :param_2)") + self.assert_compile(table.select(not_(and_(table.c.field == 5, table.c.field == 7))), + "SELECT op.field FROM op WHERE NOT (op.field = :field_1 AND op.field = :field_2)") + self.assert_compile(table.select(not_(table.c.field == 5)), + "SELECT op.field FROM op WHERE op.field != :field_1") + self.assert_compile(table.select(not_(table.c.field.between(5, 6))), + "SELECT op.field FROM op WHERE NOT (op.field BETWEEN :field_1 AND :field_2)") + self.assert_compile(table.select(not_(table.c.field) == 5), + "SELECT op.field FROM op WHERE (NOT op.field) = :param_1") + self.assert_compile(table.select((table.c.field == table.c.field).between(False, True)), + "SELECT op.field FROM op WHERE (op.field = op.field) BETWEEN :param_1 AND :param_2") + self.assert_compile(table.select(between((table.c.field == table.c.field), False, True)), + "SELECT op.field FROM op WHERE (op.field = op.field) BETWEEN :param_1 AND :param_2") + + def test_naming(self): + s1 = select([table1.c.myid, table1.c.myid.label('foobar'), func.hoho(table1.c.name), func.lala(table1.c.name).label('gg')]) + assert s1.c.keys() == ['myid', 'foobar', 'hoho(mytable.name)', 'gg'] + + from sqlalchemy.databases.sqlite import SLNumeric + meta = MetaData() + t1 = Table('mytable', meta, Column('col1', Integer)) + + for col, key, expr, label in ( + (table1.c.name, 'name', 'mytable.name', None), + (table1.c.myid==12, 'mytable.myid = :myid_1', 'mytable.myid = :myid_1', 'anon_1'), + (func.hoho(table1.c.myid), 'hoho(mytable.myid)', 'hoho(mytable.myid)', 'hoho_1'), + (cast(table1.c.name, SLNumeric), 'CAST(mytable.name AS NUMERIC(10, 2))', 'CAST(mytable.name AS NUMERIC(10, 2))', 'anon_1'), + (t1.c.col1, 'col1', 'mytable.col1', None), + (column('some wacky thing'), 'some wacky thing', '"some wacky thing"', '') + ): + s1 = select([col], from_obj=getattr(col, 'table', None) or table1) + assert s1.c.keys() == [key], s1.c.keys() + + if label: + self.assert_compile(s1, "SELECT %s AS %s FROM mytable" % (expr, label)) + else: + self.assert_compile(s1, "SELECT %s FROM mytable" % (expr,)) + + s1 = select([s1]) + if label: + self.assert_compile(s1, "SELECT %s FROM (SELECT %s AS %s FROM mytable)" % (label, expr, label)) + elif col.table is not None: + # sqlite rule labels subquery columns + self.assert_compile(s1, "SELECT %s FROM (SELECT %s AS %s FROM mytable)" % (key,expr, key)) + else: + self.assert_compile(s1, "SELECT %s FROM (SELECT %s FROM mytable)" % (expr,expr)) + +class CRUDTest(TestBase, AssertsCompiledSQL): + def test_insert(self): + # generic insert, will create bind params for all columns + self.assert_compile(insert(table1), "INSERT INTO mytable (myid, name, description) VALUES (:myid, :name, :description)") + + # insert with user-supplied bind params for specific columns, + # cols provided literally + self.assert_compile( + insert(table1, {table1.c.myid : bindparam('userid'), table1.c.name : bindparam('username')}), + "INSERT INTO mytable (myid, name) VALUES (:userid, :username)") + + # insert with user-supplied bind params for specific columns, cols + # provided as strings + self.assert_compile( + insert(table1, dict(myid = 3, name = 'jack')), + "INSERT INTO mytable (myid, name) VALUES (:myid, :name)" + ) + + # test with a tuple of params instead of named + self.assert_compile( + insert(table1, (3, 'jack', 'mydescription')), + "INSERT INTO mytable (myid, name, description) VALUES (:myid, :name, :description)", + checkparams = {'myid':3, 'name':'jack', 'description':'mydescription'} + ) + + self.assert_compile( + insert(table1, values={table1.c.myid : bindparam('userid')}).values({table1.c.name : bindparam('username')}), + "INSERT INTO mytable (myid, name) VALUES (:userid, :username)" + ) + + self.assert_compile(insert(table1, values=dict(myid=func.lala())), "INSERT INTO mytable (myid) VALUES (lala())") + + def test_inline_insert(self): + metadata = MetaData() + table = Table('sometable', metadata, + Column('id', Integer, primary_key=True), + Column('foo', Integer, default=func.foobar())) + self.assert_compile(table.insert(values={}, inline=True), "INSERT INTO sometable (foo) VALUES (foobar())") + self.assert_compile(table.insert(inline=True), "INSERT INTO sometable (foo) VALUES (foobar())", params={}) + + def test_update(self): + self.assert_compile(update(table1, table1.c.myid == 7), "UPDATE mytable SET name=:name WHERE mytable.myid = :myid_1", params = {table1.c.name:'fred'}) + self.assert_compile(table1.update().where(table1.c.myid==7).values({table1.c.myid:5}), "UPDATE mytable SET myid=:myid WHERE mytable.myid = :myid_1", checkparams={'myid':5, 'myid_1':7}) + self.assert_compile(update(table1, table1.c.myid == 7), "UPDATE mytable SET name=:name WHERE mytable.myid = :myid_1", params = {'name':'fred'}) + self.assert_compile(update(table1, values = {table1.c.name : table1.c.myid}), "UPDATE mytable SET name=mytable.myid") + self.assert_compile(update(table1, whereclause = table1.c.name == bindparam('crit'), values = {table1.c.name : 'hi'}), "UPDATE mytable SET name=:name WHERE mytable.name = :crit", params = {'crit' : 'notthere'}, checkparams={'crit':'notthere', 'name':'hi'}) + self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.name : table1.c.myid}), "UPDATE mytable SET name=mytable.myid, description=:description WHERE mytable.myid = :myid_1", params = {'description':'test'}, checkparams={'description':'test', 'myid_1':12}) + self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.myid : 9}), "UPDATE mytable SET myid=:myid, description=:description WHERE mytable.myid = :myid_1", params = {'myid_1': 12, 'myid': 9, 'description': 'test'}) + self.assert_compile(update(table1, table1.c.myid ==12), "UPDATE mytable SET myid=:myid WHERE mytable.myid = :myid_1", params={'myid':18}, checkparams={'myid':18, 'myid_1':12}) + s = table1.update(table1.c.myid == 12, values = {table1.c.name : 'lala'}) + c = s.compile(column_keys=['id', 'name']) + self.assert_compile(update(table1, table1.c.myid == 12, values = {table1.c.name : table1.c.myid}).values({table1.c.name:table1.c.name + 'foo'}), "UPDATE mytable SET name=(mytable.name || :name_1), description=:description WHERE mytable.myid = :myid_1", params = {'description':'test'}) + self.assert_(str(s) == str(c)) + + self.assert_compile(update(table1, + (table1.c.myid == func.hoho(4)) & + (table1.c.name == literal('foo') + table1.c.name + literal('lala')), + values = { + table1.c.name : table1.c.name + "lala", + table1.c.myid : func.do_stuff(table1.c.myid, literal('hoho')) + }), "UPDATE mytable SET myid=do_stuff(mytable.myid, :param_1), name=(mytable.name || :name_1) " + "WHERE mytable.myid = hoho(:hoho_1) AND mytable.name = :param_2 || mytable.name || :param_3") + + def test_correlated_update(self): + # test against a straight text subquery + u = update(table1, values = {table1.c.name : text("(select name from mytable where id=mytable.id)")}) + self.assert_compile(u, "UPDATE mytable SET name=(select name from mytable where id=mytable.id)") + + mt = table1.alias() + u = update(table1, values = {table1.c.name : select([mt.c.name], mt.c.myid==table1.c.myid)}) + self.assert_compile(u, "UPDATE mytable SET name=(SELECT mytable_1.name FROM mytable AS mytable_1 WHERE mytable_1.myid = mytable.myid)") + + # test against a regular constructed subquery + s = select([table2], table2.c.otherid == table1.c.myid) + u = update(table1, table1.c.name == 'jack', values = {table1.c.name : s}) + self.assert_compile(u, "UPDATE mytable SET name=(SELECT myothertable.otherid, myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid) WHERE mytable.name = :name_1") + + # test a non-correlated WHERE clause + s = select([table2.c.othername], table2.c.otherid == 7) + u = update(table1, table1.c.name==s) + self.assert_compile(u, "UPDATE mytable SET myid=:myid, name=:name, description=:description WHERE mytable.name = "\ + "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = :otherid_1)") + + # test one that is actually correlated... + s = select([table2.c.othername], table2.c.otherid == table1.c.myid) + u = table1.update(table1.c.name==s) + self.assert_compile(u, "UPDATE mytable SET myid=:myid, name=:name, description=:description WHERE mytable.name = "\ + "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid)") + + def test_delete(self): + self.assert_compile(delete(table1, table1.c.myid == 7), "DELETE FROM mytable WHERE mytable.myid = :myid_1") + self.assert_compile(table1.delete().where(table1.c.myid == 7), "DELETE FROM mytable WHERE mytable.myid = :myid_1") + self.assert_compile(table1.delete().where(table1.c.myid == 7).where(table1.c.name=='somename'), "DELETE FROM mytable WHERE mytable.myid = :myid_1 AND mytable.name = :name_1") + + def test_correlated_delete(self): + # test a non-correlated WHERE clause + s = select([table2.c.othername], table2.c.otherid == 7) + u = delete(table1, table1.c.name==s) + self.assert_compile(u, "DELETE FROM mytable WHERE mytable.name = "\ + "(SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = :otherid_1)") + + # test one that is actually correlated... + s = select([table2.c.othername], table2.c.otherid == table1.c.myid) + u = table1.delete(table1.c.name==s) + self.assert_compile(u, "DELETE FROM mytable WHERE mytable.name = (SELECT myothertable.othername FROM myothertable WHERE myothertable.otherid = mytable.myid)") + +class InlineDefaultTest(TestBase, AssertsCompiledSQL): + def test_insert(self): + m = MetaData() + foo = Table('foo', m, + Column('id', Integer)) + + t = Table('test', m, + Column('col1', Integer, default=func.foo(1)), + Column('col2', Integer, default=select([func.coalesce(func.max(foo.c.id))])), + ) + + self.assert_compile(t.insert(inline=True, values={}), "INSERT INTO test (col1, col2) VALUES (foo(:foo_1), (SELECT coalesce(max(foo.id)) AS coalesce_1 FROM foo))") + + def test_update(self): + m = MetaData() + foo = Table('foo', m, + Column('id', Integer)) + + t = Table('test', m, + Column('col1', Integer, onupdate=func.foo(1)), + Column('col2', Integer, onupdate=select([func.coalesce(func.max(foo.c.id))])), + Column('col3', String(30)) + ) + + self.assert_compile(t.update(inline=True, values={'col3':'foo'}), "UPDATE test SET col1=foo(:foo_1), col2=(SELECT coalesce(max(foo.id)) AS coalesce_1 FROM foo), col3=:col3") + +class SchemaTest(TestBase, AssertsCompiledSQL): + def test_select(self): + # these tests will fail with the MS-SQL compiler since it will alias schema-qualified tables + self.assert_compile(table4.select(), "SELECT remote_owner.remotetable.rem_id, remote_owner.remotetable.datatype_id, remote_owner.remotetable.value FROM remote_owner.remotetable") + self.assert_compile(table4.select(and_(table4.c.datatype_id==7, table4.c.value=='hi')), + "SELECT remote_owner.remotetable.rem_id, remote_owner.remotetable.datatype_id, remote_owner.remotetable.value FROM remote_owner.remotetable WHERE "\ + "remote_owner.remotetable.datatype_id = :datatype_id_1 AND remote_owner.remotetable.value = :value_1") + + s = table4.select(and_(table4.c.datatype_id==7, table4.c.value=='hi')) + s.use_labels = True + self.assert_compile(s, "SELECT remote_owner.remotetable.rem_id AS remote_owner_remotetable_rem_id, remote_owner.remotetable.datatype_id AS remote_owner_remotetable_datatype_id, remote_owner.remotetable.value "\ + "AS remote_owner_remotetable_value FROM remote_owner.remotetable WHERE "\ + "remote_owner.remotetable.datatype_id = :datatype_id_1 AND remote_owner.remotetable.value = :value_1") + + def test_alias(self): + a = alias(table4, 'remtable') + self.assert_compile(a.select(a.c.datatype_id==7), "SELECT remtable.rem_id, remtable.datatype_id, remtable.value FROM remote_owner.remotetable AS remtable "\ + "WHERE remtable.datatype_id = :datatype_id_1") + + def test_update(self): + self.assert_compile(table4.update(table4.c.value=='test', values={table4.c.datatype_id:12}), "UPDATE remote_owner.remotetable SET datatype_id=:datatype_id "\ + "WHERE remote_owner.remotetable.value = :value_1") + + def test_insert(self): + self.assert_compile(table4.insert(values=(2, 5, 'test')), "INSERT INTO remote_owner.remotetable (rem_id, datatype_id, value) VALUES "\ + "(:rem_id, :datatype_id, :value)") + diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py new file mode 100644 index 000000000..a172eb452 --- /dev/null +++ b/test/sql/test_selectable.py @@ -0,0 +1,524 @@ +"""Test various algorithmic properties of selectables.""" + +from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message +from sqlalchemy import * +from sqlalchemy.test import * +from sqlalchemy.sql import util as sql_util, visitors +from sqlalchemy import exc +from sqlalchemy.sql import table, column +from sqlalchemy import util + +metadata = MetaData() +table1 = Table('table1', metadata, + Column('col1', Integer, primary_key=True), + Column('col2', String(20)), + Column('col3', Integer), + Column('colx', Integer), + +) + +table2 = Table('table2', metadata, + Column('col1', Integer, primary_key=True), + Column('col2', Integer, ForeignKey('table1.col1')), + Column('col3', String(20)), + Column('coly', Integer), +) + +class SelectableTest(TestBase, AssertsExecutionResults): + def test_distance_on_labels(self): + # same column three times + s = select([table1.c.col1.label('c2'), table1.c.col1, table1.c.col1.label('c1')]) + + # didnt do this yet...col.label().make_proxy() has same "distance" as col.make_proxy() so far + #assert s.corresponding_column(table1.c.col1) is s.c.col1 + assert s.corresponding_column(s.c.col1) is s.c.col1 + assert s.corresponding_column(s.c.c1) is s.c.c1 + + def test_distance_on_aliases(self): + a1 = table1.alias('a1') + + for s in ( + select([a1, table1], use_labels=True), + select([table1, a1], use_labels=True) + ): + assert s.corresponding_column(table1.c.col1) is s.c.table1_col1 + assert s.corresponding_column(a1.c.col1) is s.c.a1_col1 + + + def test_join_against_self(self): + jj = select([table1.c.col1.label('bar_col1')]) + jjj = join(table1, jj, table1.c.col1==jj.c.bar_col1) + + # test column directly agaisnt itself + assert jjj.corresponding_column(jjj.c.table1_col1) is jjj.c.table1_col1 + + assert jjj.corresponding_column(jj.c.bar_col1) is jjj.c.bar_col1 + + # test alias of the join + j2 = jjj.alias('foo') + assert j2.corresponding_column(table1.c.col1) is j2.c.table1_col1 + + def test_select_on_table(self): + sel = select([table1, table2], use_labels=True) + assert sel.corresponding_column(table1.c.col1) is sel.c.table1_col1 + assert sel.corresponding_column(table1.c.col1, require_embedded=True) is sel.c.table1_col1 + assert table1.corresponding_column(sel.c.table1_col1) is table1.c.col1 + assert table1.corresponding_column(sel.c.table1_col1, require_embedded=True) is None + + def test_join_against_join(self): + j = outerjoin(table1, table2, table1.c.col1==table2.c.col2) + jj = select([ table1.c.col1.label('bar_col1')],from_obj=[j]).alias('foo') + jjj = join(table1, jj, table1.c.col1==jj.c.bar_col1) + assert jjj.corresponding_column(jjj.c.table1_col1) is jjj.c.table1_col1 + + j2 = jjj.alias('foo') + assert j2.corresponding_column(jjj.c.table1_col1) is j2.c.table1_col1 + + assert jjj.corresponding_column(jj.c.bar_col1) is jj.c.bar_col1 + + def test_table_alias(self): + a = table1.alias('a') + + j = join(a, table2) + + criterion = a.c.col1 == table2.c.col2 + self.assert_(criterion.compare(j.onclause)) + + def test_union(self): + # tests that we can correspond a column in a Select statement with a certain Table, against + # a column in a Union where one of its underlying Selects matches to that same Table + u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( + select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) + ) + s1 = table1.select(use_labels=True) + s2 = table2.select(use_labels=True) + c = u.corresponding_column(s1.c.table1_col2) + assert u.corresponding_column(s1.c.table1_col2) is u.c.col2 + assert u.corresponding_column(s2.c.table2_col2) is u.c.col2 + + def test_union_precedence(self): + # conflicting column correspondence should be resolved based on + # the order of the select()s in the union + + s1 = select([table1.c.col1, table1.c.col2]) + s2 = select([table1.c.col2, table1.c.col1]) + s3 = select([table1.c.col3, table1.c.colx]) + s4 = select([table1.c.colx, table1.c.col3]) + + u1 = union(s1, s2) + assert u1.corresponding_column(table1.c.col1) is u1.c.col1 + assert u1.corresponding_column(table1.c.col2) is u1.c.col2 + + u1 = union(s1, s2, s3, s4) + assert u1.corresponding_column(table1.c.col1) is u1.c.col1 + assert u1.corresponding_column(table1.c.col2) is u1.c.col2 + assert u1.corresponding_column(table1.c.colx) is u1.c.col2 + assert u1.corresponding_column(table1.c.col3) is u1.c.col1 + + def test_singular_union(self): + u = union(select([table1.c.col1, table1.c.col2, table1.c.col3]), select([table1.c.col1, table1.c.col2, table1.c.col3])) + + u = union(select([table1.c.col1, table1.c.col2, table1.c.col3])) + assert u.c.col1 + assert u.c.col2 + assert u.c.col3 + + def test_alias_union(self): + # same as testunion, except its an alias of the union + u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( + select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) + ).alias('analias') + s1 = table1.select(use_labels=True) + s2 = table2.select(use_labels=True) + assert u.corresponding_column(s1.c.table1_col2) is u.c.col2 + assert u.corresponding_column(s2.c.table2_col2) is u.c.col2 + assert u.corresponding_column(s2.c.table2_coly) is u.c.coly + assert s2.corresponding_column(u.c.coly) is s2.c.table2_coly + + def test_select_union(self): + # like testaliasunion, but off a Select off the union. + u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( + select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) + ).alias('analias') + s = select([u]) + s1 = table1.select(use_labels=True) + s2 = table2.select(use_labels=True) + assert s.corresponding_column(s1.c.table1_col2) is s.c.col2 + assert s.corresponding_column(s2.c.table2_col2) is s.c.col2 + + def test_union_against_join(self): + # same as testunion, except its an alias of the union + u = select([table1.c.col1, table1.c.col2, table1.c.col3, table1.c.colx, null().label('coly')]).union( + select([table2.c.col1, table2.c.col2, table2.c.col3, null().label('colx'), table2.c.coly]) + ).alias('analias') + j1 = table1.join(table2) + assert u.corresponding_column(j1.c.table1_colx) is u.c.colx + assert j1.corresponding_column(u.c.colx) is j1.c.table1_colx + + def test_join(self): + a = join(table1, table2) + print str(a.select(use_labels=True)) + b = table2.alias('b') + j = join(a, b) + print str(j) + criterion = a.c.table1_col1 == b.c.col2 + self.assert_(criterion.compare(j.onclause)) + + def test_select_alias(self): + a = table1.select().alias('a') + j = join(a, table2) + + criterion = a.c.col1 == table2.c.col2 + self.assert_(criterion.compare(j.onclause)) + + def test_select_labels(self): + a = table1.select(use_labels=True) + j = join(a, table2) + + criterion = a.c.table1_col1 == table2.c.col2 + self.assert_(criterion.compare(j.onclause)) + + def test_column_labels(self): + a = select([table1.c.col1.label('acol1'), table1.c.col2.label('acol2'), table1.c.col3.label('acol3')]) + j = join(a, table2) + criterion = a.c.acol1 == table2.c.col2 + self.assert_(criterion.compare(j.onclause)) + + def test_labeled_select_correspoinding(self): + l1 = select([func.max(table1.c.col1)]).label('foo') + + s = select([l1]) + assert s.corresponding_column(l1).name == s.c.foo + + s = select([table1.c.col1, l1]) + assert s.corresponding_column(l1).name == s.c.foo + + def test_select_alias_labels(self): + a = table2.select(use_labels=True).alias('a') + j = join(a, table1) + + criterion = table1.c.col1 == a.c.table2_col2 + self.assert_(criterion.compare(j.onclause)) + + def test_table_joined_to_select_of_table(self): + metadata = MetaData() + a = Table('a', metadata, + Column('id', Integer, primary_key=True)) + b = Table('b', metadata, + Column('id', Integer, primary_key=True), + Column('aid', Integer, ForeignKey('a.id')), + ) + + j1 = a.outerjoin(b) + j2 = select([a.c.id.label('aid')]).alias('bar') + + j3 = a.join(j2, j2.c.aid==a.c.id) + + j4 = select([j3]).alias('foo') + assert j4.corresponding_column(j2.c.aid) is j4.c.aid + assert j4.corresponding_column(a.c.id) is j4.c.id + + def test_two_metadata_join_raises(self): + m = MetaData() + m2 = MetaData() + + t1 = Table('t1', m, Column('id', Integer), Column('id2', Integer)) + t2 = Table('t2', m, Column('id', Integer, ForeignKey('t1.id'))) + t3 = Table('t3', m2, Column('id', Integer, ForeignKey('t1.id2'))) + + s = select([t2, t3], use_labels=True) + + assert_raises(exc.NoReferencedTableError, s.join, t1) + +class PrimaryKeyTest(TestBase, AssertsExecutionResults): + def test_join_pk_collapse_implicit(self): + """test that redundant columns in a join get 'collapsed' into a minimal primary key, + which is the root column along a chain of foreign key relationships.""" + + meta = MetaData() + a = Table('a', meta, Column('id', Integer, primary_key=True)) + b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True)) + c = Table('c', meta, Column('id', Integer, ForeignKey('b.id'), primary_key=True)) + d = Table('d', meta, Column('id', Integer, ForeignKey('c.id'), primary_key=True)) + + assert c.c.id.references(b.c.id) + assert not d.c.id.references(a.c.id) + + assert list(a.join(b).primary_key) == [a.c.id] + assert list(b.join(c).primary_key) == [b.c.id] + assert list(a.join(b).join(c).primary_key) == [a.c.id] + assert list(b.join(c).join(d).primary_key) == [b.c.id] + assert list(d.join(c).join(b).primary_key) == [b.c.id] + assert list(a.join(b).join(c).join(d).primary_key) == [a.c.id] + + def test_join_pk_collapse_explicit(self): + """test that redundant columns in a join get 'collapsed' into a minimal primary key, + which is the root column along a chain of explicit join conditions.""" + + meta = MetaData() + a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) + b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer)) + c = Table('c', meta, Column('id', Integer, ForeignKey('b.id'), primary_key=True), Column('x', Integer)) + d = Table('d', meta, Column('id', Integer, ForeignKey('c.id'), primary_key=True), Column('x', Integer)) + + print list(a.join(b, a.c.x==b.c.id).primary_key) + assert list(a.join(b, a.c.x==b.c.id).primary_key) == [b.c.id] + assert list(b.join(c, b.c.x==c.c.id).primary_key) == [b.c.id] + assert list(a.join(b).join(c, c.c.id==b.c.x).primary_key) == [a.c.id] + assert list(b.join(c, c.c.x==b.c.id).join(d).primary_key) == [c.c.id] + assert list(b.join(c, c.c.id==b.c.x).join(d).primary_key) == [b.c.id] + assert list(d.join(b, d.c.id==b.c.id).join(c, b.c.id==c.c.x).primary_key) == [c.c.id] + assert list(a.join(b).join(c, c.c.id==b.c.x).join(d).primary_key) == [a.c.id] + + assert list(a.join(b, and_(a.c.id==b.c.id, a.c.x==b.c.id)).primary_key) == [a.c.id] + + def test_init_doesnt_blowitaway(self): + meta = MetaData() + a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) + b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer)) + + j = a.join(b) + assert list(j.primary_key) == [a.c.id] + + j.foreign_keys + assert list(j.primary_key) == [a.c.id] + + def test_non_column_clause(self): + meta = MetaData() + a = Table('a', meta, Column('id', Integer, primary_key=True), Column('x', Integer)) + b = Table('b', meta, Column('id', Integer, ForeignKey('a.id'), primary_key=True), Column('x', Integer, primary_key=True)) + + j = a.join(b, and_(a.c.id==b.c.id, b.c.x==5)) + assert str(j) == "a JOIN b ON a.id = b.id AND b.x = :x_1", str(j) + assert list(j.primary_key) == [a.c.id, b.c.x] + + def test_onclause_direction(self): + metadata = MetaData() + + employee = Table( 'Employee', metadata, + Column('name', String(100)), + Column('id', Integer, primary_key= True), + ) + + engineer = Table( 'Engineer', metadata, + Column('id', Integer, ForeignKey( 'Employee.id', ), primary_key=True), + ) + + eq_( + util.column_set(employee.join(engineer, employee.c.id==engineer.c.id).primary_key), + util.column_set([employee.c.id]) + ) + + eq_( + util.column_set(employee.join(engineer, engineer.c.id==employee.c.id).primary_key), + util.column_set([employee.c.id]) + ) + + +class ReduceTest(TestBase, AssertsExecutionResults): + def test_reduce(self): + meta = MetaData() + t1 = Table('t1', meta, + Column('t1id', Integer, primary_key=True), + Column('t1data', String(30))) + t2 = Table('t2', meta, + Column('t2id', Integer, ForeignKey('t1.t1id'), primary_key=True), + Column('t2data', String(30))) + t3 = Table('t3', meta, + Column('t3id', Integer, ForeignKey('t2.t2id'), primary_key=True), + Column('t3data', String(30))) + + + eq_( + util.column_set(sql_util.reduce_columns([t1.c.t1id, t1.c.t1data, t2.c.t2id, t2.c.t2data, t3.c.t3id, t3.c.t3data])), + util.column_set([t1.c.t1id, t1.c.t1data, t2.c.t2data, t3.c.t3data]) + ) + + def test_reduce_selectable(self): + metadata = MetaData() + + engineers = Table('engineers', metadata, + Column('engineer_id', Integer, primary_key=True), + Column('engineer_name', String(50)), + ) + + managers = Table('managers', metadata, + Column('manager_id', Integer, primary_key=True), + Column('manager_name', String(50)) + ) + + s = select([engineers, managers]).where(engineers.c.engineer_name==managers.c.manager_name) + + eq_(util.column_set(sql_util.reduce_columns(list(s.c), s)), + util.column_set([s.c.engineer_id, s.c.engineer_name, s.c.manager_id]) + ) + + def test_reduce_aliased_join(self): + metadata = MetaData() + people = Table('people', metadata, + Column('person_id', Integer, Sequence('person_id_seq', optional=True), primary_key=True), + Column('name', String(50)), + Column('type', String(30))) + + engineers = Table('engineers', metadata, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('engineer_name', String(50)), + Column('primary_language', String(50)), + ) + + managers = Table('managers', metadata, + Column('person_id', Integer, ForeignKey('people.person_id'), primary_key=True), + Column('status', String(30)), + Column('manager_name', String(50)) + ) + + pjoin = people.outerjoin(engineers).outerjoin(managers).select(use_labels=True).alias('pjoin') + eq_( + util.column_set(sql_util.reduce_columns([pjoin.c.people_person_id, pjoin.c.engineers_person_id, pjoin.c.managers_person_id])), + util.column_set([pjoin.c.people_person_id]) + ) + + def test_reduce_aliased_union(self): + metadata = MetaData() + item_table = Table( + 'item', metadata, + Column('id', Integer, ForeignKey('base_item.id'), primary_key=True), + Column('dummy', Integer, default=0)) + + base_item_table = Table( + 'base_item', metadata, + Column('id', Integer, primary_key=True), + Column('child_name', String(255), default=None)) + + from sqlalchemy.orm.util import polymorphic_union + + item_join = polymorphic_union( { + 'BaseItem':base_item_table.select(base_item_table.c.child_name=='BaseItem'), + 'Item':base_item_table.join(item_table), + }, None, 'item_join') + + eq_( + util.column_set(sql_util.reduce_columns([item_join.c.id, item_join.c.dummy, item_join.c.child_name])), + util.column_set([item_join.c.id, item_join.c.dummy, item_join.c.child_name]) + ) + + def test_reduce_aliased_union_2(self): + metadata = MetaData() + + page_table = Table('page', metadata, + Column('id', Integer, primary_key=True), + ) + magazine_page_table = Table('magazine_page', metadata, + Column('page_id', Integer, ForeignKey('page.id'), primary_key=True), + ) + classified_page_table = Table('classified_page', metadata, + Column('magazine_page_id', Integer, ForeignKey('magazine_page.page_id'), primary_key=True), + ) + + from sqlalchemy.orm.util import polymorphic_union + pjoin = polymorphic_union( + { + 'm': page_table.join(magazine_page_table), + 'c': page_table.join(magazine_page_table).join(classified_page_table), + }, None, 'page_join') + + eq_( + util.column_set(sql_util.reduce_columns([pjoin.c.id, pjoin.c.page_id, pjoin.c.magazine_page_id])), + util.column_set([pjoin.c.id]) + ) + + +class DerivedTest(TestBase, AssertsExecutionResults): + def test_table(self): + meta = MetaData() + t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + + assert t1.is_derived_from(t1) + assert not t2.is_derived_from(t1) + + def test_alias(self): + meta = MetaData() + t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + + assert t1.alias().is_derived_from(t1) + assert not t2.alias().is_derived_from(t1) + assert not t1.is_derived_from(t1.alias()) + assert not t1.is_derived_from(t2.alias()) + + def test_select(self): + meta = MetaData() + t1 = Table('t1', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + t2 = Table('t2', meta, Column('c1', Integer, primary_key=True), Column('c2', String(30))) + + assert t1.select().is_derived_from(t1) + assert not t2.select().is_derived_from(t1) + + assert select([t1, t2]).is_derived_from(t1) + + assert t1.select().alias('foo').is_derived_from(t1) + assert select([t1, t2]).alias('foo').is_derived_from(t1) + assert not t2.select().alias('foo').is_derived_from(t1) + +class AnnotationsTest(TestBase): + def test_annotated_corresponding_column(self): + table1 = table('table1', column("col1")) + + s1 = select([table1.c.col1]) + t1 = s1._annotate({}) + t2 = s1 + + # t1 needs to share the same _make_proxy() columns as t2, even though it's + # annotated. otherwise paths will diverge once they are corresponded against "inner" below. + assert t1.c is t2.c + assert t1.c.col1 is t2.c.col1 + + inner = select([s1]) + assert inner.corresponding_column(t2.c.col1, require_embedded=False) is inner.corresponding_column(t2.c.col1, require_embedded=True) is inner.c.col1 + assert inner.corresponding_column(t1.c.col1, require_embedded=False) is inner.corresponding_column(t1.c.col1, require_embedded=True) is inner.c.col1 + + def test_annotated_visit(self): + table1 = table('table1', column("col1"), column("col2")) + + bin = table1.c.col1 == bindparam('foo', value=None) + assert str(bin) == "table1.col1 = :foo" + def visit_binary(b): + b.right = table1.c.col2 + + b2 = visitors.cloned_traverse(bin, {}, {'binary':visit_binary}) + assert str(b2) == "table1.col1 = table1.col2" + + b3 = visitors.cloned_traverse(bin._annotate({}), {}, {'binary':visit_binary}) + assert str(b3) == "table1.col1 = table1.col2" + + def visit_binary(b): + b.left = bindparam('bar') + + b4 = visitors.cloned_traverse(b2, {}, {'binary':visit_binary}) + assert str(b4) == ":bar = table1.col2" + + b5 = visitors.cloned_traverse(b3, {}, {'binary':visit_binary}) + assert str(b5) == ":bar = table1.col2" + + def test_deannotate(self): + table1 = table('table1', column("col1"), column("col2")) + + bin = table1.c.col1 == bindparam('foo', value=None) + + b2 = sql_util._deep_annotate(bin, {'_orm_adapt':True}) + b3 = sql_util._deep_deannotate(b2) + b4 = sql_util._deep_deannotate(bin) + + for elem in (b2._annotations, b2.left._annotations): + assert '_orm_adapt' in elem + + for elem in (b3._annotations, b3.left._annotations, b4._annotations, b4.left._annotations): + assert elem == {} + + assert b2.left is not bin.left + assert b3.left is not b2.left is not bin.left + assert b4.left is bin.left # since column is immutable + assert b4.right is not bin.right is not b2.right is not b3.right + diff --git a/test/sql/test_types.py b/test/sql/test_types.py new file mode 100644 index 000000000..13b6d0954 --- /dev/null +++ b/test/sql/test_types.py @@ -0,0 +1,891 @@ +from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message +import decimal +import datetime, os, re +from sqlalchemy import * +from sqlalchemy import exc, types, util +from sqlalchemy.sql import operators +from sqlalchemy.test.testing import eq_ +import sqlalchemy.engine.url as url +from sqlalchemy.databases import mssql, oracle, mysql, postgres, firebird +from sqlalchemy.test import * + + +class AdaptTest(TestBase): + def testadapt(self): + e1 = url.URL('postgres').get_dialect()() + e2 = url.URL('mysql').get_dialect()() + e3 = url.URL('sqlite').get_dialect()() + e4 = url.URL('firebird').get_dialect()() + + type = String(40) + + t1 = type.dialect_impl(e1) + t2 = type.dialect_impl(e2) + t3 = type.dialect_impl(e3) + t4 = type.dialect_impl(e4) + + impls = [t1, t2, t3, t4] + for i,ta in enumerate(impls): + for j,tb in enumerate(impls): + if i == j: + assert ta == tb # call me paranoid... :) + else: + assert ta != tb + + def testmsnvarchar(self): + dialect = mssql.MSSQLDialect() + # run the test twice to ensure the caching step works too + for x in range(0, 1): + col = Column('', Unicode(length=10)) + dialect_type = col.type.dialect_impl(dialect) + assert isinstance(dialect_type, mssql.MSNVarchar) + assert dialect_type.get_col_spec() == 'NVARCHAR(10)' + + + def testoracletimestamp(self): + dialect = oracle.OracleDialect() + t1 = oracle.OracleTimestamp + t2 = oracle.OracleTimestamp() + t3 = types.TIMESTAMP + assert isinstance(dialect.type_descriptor(t1), oracle.OracleTimestamp) + assert isinstance(dialect.type_descriptor(t2), oracle.OracleTimestamp) + assert isinstance(dialect.type_descriptor(t3), oracle.OracleTimestamp) + + def testmysqlbinary(self): + dialect = mysql.MySQLDialect() + t1 = mysql.MSVarBinary + t2 = mysql.MSVarBinary() + assert isinstance(dialect.type_descriptor(t1), mysql.MSVarBinary) + assert isinstance(dialect.type_descriptor(t2), mysql.MSVarBinary) + + def teststringadapt(self): + """test that String with no size becomes TEXT, *all* others stay as varchar/String""" + + oracle_dialect = oracle.OracleDialect() + mysql_dialect = mysql.MySQLDialect() + postgres_dialect = postgres.PGDialect() + firebird_dialect = firebird.FBDialect() + + for dialect, start, test in [ + (oracle_dialect, String(), oracle.OracleString), + (oracle_dialect, VARCHAR(), oracle.OracleString), + (oracle_dialect, String(50), oracle.OracleString), + (oracle_dialect, Unicode(), oracle.OracleString), + (oracle_dialect, UnicodeText(), oracle.OracleText), + (oracle_dialect, NCHAR(), oracle.OracleString), + (oracle_dialect, oracle.OracleRaw(50), oracle.OracleRaw), + (mysql_dialect, String(), mysql.MSString), + (mysql_dialect, VARCHAR(), mysql.MSString), + (mysql_dialect, String(50), mysql.MSString), + (mysql_dialect, Unicode(), mysql.MSString), + (mysql_dialect, UnicodeText(), mysql.MSText), + (mysql_dialect, NCHAR(), mysql.MSNChar), + (postgres_dialect, String(), postgres.PGString), + (postgres_dialect, VARCHAR(), postgres.PGString), + (postgres_dialect, String(50), postgres.PGString), + (postgres_dialect, Unicode(), postgres.PGString), + (postgres_dialect, UnicodeText(), postgres.PGText), + (postgres_dialect, NCHAR(), postgres.PGString), + (firebird_dialect, String(), firebird.FBString), + (firebird_dialect, VARCHAR(), firebird.FBString), + (firebird_dialect, String(50), firebird.FBString), + (firebird_dialect, Unicode(), firebird.FBString), + (firebird_dialect, UnicodeText(), firebird.FBText), + (firebird_dialect, NCHAR(), firebird.FBString), + ]: + assert isinstance(start.dialect_impl(dialect), test), "wanted %r got %r" % (test, start.dialect_impl(dialect)) + + + +class UserDefinedTest(TestBase): + """tests user-defined types.""" + + def testprocessing(self): + + global users + users.insert().execute( + user_id=2, goofy='jack', goofy2='jack', goofy4=u'jack', + goofy7=u'jack', goofy8=12, goofy9=12) + users.insert().execute( + user_id=3, goofy='lala', goofy2='lala', goofy4=u'lala', + goofy7=u'lala', goofy8=15, goofy9=15) + users.insert().execute( + user_id=4, goofy='fred', goofy2='fred', goofy4=u'fred', + goofy7=u'fred', goofy8=9, goofy9=9) + + l = users.select().execute().fetchall() + for assertstr, assertint, assertint2, row in zip( + ["BIND_INjackBIND_OUT", "BIND_INlalaBIND_OUT", "BIND_INfredBIND_OUT"], + [1200, 1500, 900], + [1800, 2250, 1350], + l + ): + for col in row[1:5]: + eq_(col, assertstr) + eq_(row[5], assertint) + eq_(row[6], assertint2) + for col in row[3], row[4]: + assert isinstance(col, unicode) + + @classmethod + def setup_class(cls): + global users, metadata + + class MyType(types.TypeEngine): + def get_col_spec(self): + return "VARCHAR(100)" + def bind_processor(self, dialect): + def process(value): + return "BIND_IN"+ value + return process + def result_processor(self, dialect): + def process(value): + return value + "BIND_OUT" + return process + def adapt(self, typeobj): + return typeobj() + + class MyDecoratedType(types.TypeDecorator): + impl = String + def bind_processor(self, dialect): + impl_processor = super(MyDecoratedType, self).bind_processor(dialect) or (lambda value:value) + def process(value): + return "BIND_IN"+ impl_processor(value) + return process + def result_processor(self, dialect): + impl_processor = super(MyDecoratedType, self).result_processor(dialect) or (lambda value:value) + def process(value): + return impl_processor(value) + "BIND_OUT" + return process + def copy(self): + return MyDecoratedType() + + class MyNewUnicodeType(types.TypeDecorator): + impl = Unicode + + def process_bind_param(self, value, dialect): + return "BIND_IN" + value + + def process_result_value(self, value, dialect): + return value + "BIND_OUT" + + def copy(self): + return MyNewUnicodeType(self.impl.length) + + class MyNewIntType(types.TypeDecorator): + impl = Integer + + def process_bind_param(self, value, dialect): + return value * 10 + + def process_result_value(self, value, dialect): + return value * 10 + + def copy(self): + return MyNewIntType() + + class MyNewIntSubClass(MyNewIntType): + def process_result_value(self, value, dialect): + return value * 15 + + def copy(self): + return MyNewIntSubClass() + + class MyUnicodeType(types.TypeDecorator): + impl = Unicode + + def bind_processor(self, dialect): + impl_processor = super(MyUnicodeType, self).bind_processor(dialect) or (lambda value:value) + + def process(value): + return "BIND_IN"+ impl_processor(value) + return process + + def result_processor(self, dialect): + impl_processor = super(MyUnicodeType, self).result_processor(dialect) or (lambda value:value) + def process(value): + return impl_processor(value) + "BIND_OUT" + return process + + def copy(self): + return MyUnicodeType(self.impl.length) + + metadata = MetaData(testing.db) + users = Table('type_users', metadata, + Column('user_id', Integer, primary_key = True), + # totall custom type + Column('goofy', MyType, nullable = False), + + # decorated type with an argument, so its a String + Column('goofy2', MyDecoratedType(50), nullable = False), + + Column('goofy4', MyUnicodeType(50), nullable = False), + Column('goofy7', MyNewUnicodeType(50), nullable = False), + Column('goofy8', MyNewIntType, nullable = False), + Column('goofy9', MyNewIntSubClass, nullable = False), + ) + + metadata.create_all() + + @classmethod + def teardown_class(cls): + metadata.drop_all() + +class ColumnsTest(TestBase, AssertsExecutionResults): + + def testcolumns(self): + expectedResults = { 'int_column': 'int_column INTEGER', + 'smallint_column': 'smallint_column SMALLINT', + 'varchar_column': 'varchar_column VARCHAR(20)', + 'numeric_column': 'numeric_column NUMERIC(12, 3)', + 'float_column': 'float_column FLOAT(25)', + } + + db = testing.db + if testing.against('oracle'): + expectedResults['float_column'] = 'float_column NUMERIC(25, 2)' + + if testing.against('sqlite'): + expectedResults['float_column'] = 'float_column FLOAT' + + if testing.against('maxdb'): + expectedResults['numeric_column'] = ( + expectedResults['numeric_column'].replace('NUMERIC', 'FIXED')) + + if testing.against('mssql'): + for key, value in expectedResults.items(): + expectedResults[key] = '%s NULL' % value + + testTable = Table('testColumns', MetaData(db), + Column('int_column', Integer), + Column('smallint_column', SmallInteger), + Column('varchar_column', String(20)), + Column('numeric_column', Numeric(12,3)), + Column('float_column', Float(25)), + ) + + for aCol in testTable.c: + eq_( + expectedResults[aCol.name], + db.dialect.schemagenerator(db.dialect, db, None, None).\ + get_column_specification(aCol)) + +class UnicodeTest(TestBase, AssertsExecutionResults): + """tests the Unicode type. also tests the TypeDecorator with instances in the types package.""" + @classmethod + def setup_class(cls): + global unicode_table + metadata = MetaData(testing.db) + unicode_table = Table('unicode_table', metadata, + Column('id', Integer, Sequence('uni_id_seq', optional=True), primary_key=True), + Column('unicode_varchar', Unicode(250)), + Column('unicode_text', UnicodeText), + Column('plain_varchar', String(250)) + ) + unicode_table.create() + @classmethod + def teardown_class(cls): + unicode_table.drop() + + def teardown(self): + unicode_table.delete().execute() + + def test_round_trip(self): + assert unicode_table.c.unicode_varchar.type.length == 250 + rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' + unicodedata = rawdata.decode('utf-8') + if testing.against('sqlite'): + rawdata = "something" + + unicode_table.insert().execute(unicode_varchar=unicodedata, + unicode_text=unicodedata, + plain_varchar=rawdata) + x = unicode_table.select().execute().fetchone() + self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) + self.assert_(isinstance(x['unicode_text'], unicode) and x['unicode_text'] == unicodedata) + if isinstance(x['plain_varchar'], unicode): + # SQLLite and MSSQL return non-unicode data as unicode + self.assert_(testing.against('sqlite', 'mssql')) + if not testing.against('sqlite'): + self.assert_(x['plain_varchar'] == unicodedata) + else: + self.assert_(not isinstance(x['plain_varchar'], unicode) and x['plain_varchar'] == rawdata) + + def test_union(self): + """ensure compiler processing works for UNIONs""" + + rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' + unicodedata = rawdata.decode('utf-8') + if testing.against('sqlite'): + rawdata = "something" + unicode_table.insert().execute(unicode_varchar=unicodedata, + unicode_text=unicodedata, + plain_varchar=rawdata) + + x = union(select([unicode_table.c.unicode_varchar]), select([unicode_table.c.unicode_varchar])).execute().fetchone() + self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) + + def test_assertions(self): + try: + unicode_table.insert().execute(unicode_varchar='not unicode') + assert False + except exc.SAWarning, e: + assert str(e) == "Unicode type received non-unicode bind param value 'not unicode'", str(e) + + unicode_engine = engines.utf8_engine(options={'convert_unicode':True, + 'assert_unicode':True}) + try: + try: + unicode_engine.execute(unicode_table.insert(), plain_varchar='im not unicode') + assert False + except exc.InvalidRequestError, e: + assert str(e) == "Unicode type received non-unicode bind param value 'im not unicode'" + + @testing.emits_warning('.*non-unicode bind') + def warns(): + # test that data still goes in if warning is emitted.... + unicode_table.insert().execute(unicode_varchar='not unicode') + assert (select([unicode_table.c.unicode_varchar]).execute().fetchall() == [('not unicode', )]) + warns() + + finally: + unicode_engine.dispose() + + @testing.fails_on('oracle', 'FIXME: unknown') + def test_blank_strings(self): + unicode_table.insert().execute(unicode_varchar=u'') + assert select([unicode_table.c.unicode_varchar]).scalar() == u'' + + def test_engine_parameter(self): + """tests engine-wide unicode conversion""" + prev_unicode = testing.db.engine.dialect.convert_unicode + prev_assert = testing.db.engine.dialect.assert_unicode + try: + testing.db.engine.dialect.convert_unicode = True + testing.db.engine.dialect.assert_unicode = False + rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' + unicodedata = rawdata.decode('utf-8') + if testing.against('sqlite', 'mssql'): + rawdata = "something" + unicode_table.insert().execute(unicode_varchar=unicodedata, + unicode_text=unicodedata, + plain_varchar=rawdata) + x = unicode_table.select().execute().fetchone() + self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) + self.assert_(isinstance(x['unicode_text'], unicode) and x['unicode_text'] == unicodedata) + if not testing.against('sqlite', 'mssql'): + self.assert_(isinstance(x['plain_varchar'], unicode) and x['plain_varchar'] == unicodedata) + finally: + testing.db.engine.dialect.convert_unicode = prev_unicode + testing.db.engine.dialect.convert_unicode = prev_assert + + @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') + @testing.fails_on('firebird', 'Data type unknown') + def test_length_function(self): + """checks the database correctly understands the length of a unicode string""" + teststr = u'aaa\x1234' + self.assert_(testing.db.func.length(teststr).scalar() == len(teststr)) + +class BinaryTest(TestBase, AssertsExecutionResults): + __excluded_on__ = ( + ('mysql', '<', (4, 1, 1)), # screwy varbinary types + ) + + @classmethod + def setup_class(cls): + global binary_table, MyPickleType + + class MyPickleType(types.TypeDecorator): + impl = PickleType + + def process_bind_param(self, value, dialect): + if value: + value.stuff = 'this is modified stuff' + return value + + def process_result_value(self, value, dialect): + if value: + value.stuff = 'this is the right stuff' + return value + + binary_table = Table('binary_table', MetaData(testing.db), + Column('primary_id', Integer, Sequence('binary_id_seq', optional=True), primary_key=True), + Column('data', Binary), + Column('data_slice', Binary(100)), + Column('misc', String(30)), + # construct PickleType with non-native pickle module, since cPickle uses relative module + # loading and confuses this test's parent package 'sql' with the 'sqlalchemy.sql' package relative + # to the 'types' module + Column('pickled', PickleType), + Column('mypickle', MyPickleType) + ) + binary_table.create() + + def teardown(self): + binary_table.delete().execute() + + @classmethod + def teardown_class(cls): + binary_table.drop() + + @testing.fails_on('mssql', 'MSSQl BINARY type right pads the fixed length with \x00') + def testbinary(self): + testobj1 = pickleable.Foo('im foo 1') + testobj2 = pickleable.Foo('im foo 2') + testobj3 = pickleable.Foo('im foo 3') + + stream1 =self.load_stream('binary_data_one.dat') + stream2 =self.load_stream('binary_data_two.dat') + binary_table.insert().execute(primary_id=1, misc='binary_data_one.dat', data=stream1, data_slice=stream1[0:100], pickled=testobj1, mypickle=testobj3) + binary_table.insert().execute(primary_id=2, misc='binary_data_two.dat', data=stream2, data_slice=stream2[0:99], pickled=testobj2) + binary_table.insert().execute(primary_id=3, misc='binary_data_two.dat', data=None, data_slice=stream2[0:99], pickled=None) + + for stmt in ( + binary_table.select(order_by=binary_table.c.primary_id), + text("select * from binary_table order by binary_table.primary_id", typemap={'pickled':PickleType, 'mypickle':MyPickleType}, bind=testing.db) + ): + l = stmt.execute().fetchall() + eq_(list(stream1), list(l[0]['data'])) + eq_(list(stream1[0:100]), list(l[0]['data_slice'])) + eq_(list(stream2), list(l[1]['data'])) + eq_(testobj1, l[0]['pickled']) + eq_(testobj2, l[1]['pickled']) + eq_(testobj3.moredata, l[0]['mypickle'].moredata) + eq_(l[0]['mypickle'].stuff, 'this is the right stuff') + + def load_stream(self, name, len=12579): + f = os.path.join(os.path.dirname(__file__), "..", name) + # put a number less than the typical MySQL default BLOB size + return file(f).read(len) + +class ExpressionTest(TestBase, AssertsExecutionResults): + @classmethod + def setup_class(cls): + global test_table, meta + + class MyCustomType(types.TypeEngine): + def get_col_spec(self): + return "INT" + def bind_processor(self, dialect): + def process(value): + return value * 10 + return process + def result_processor(self, dialect): + def process(value): + return value / 10 + return process + def adapt_operator(self, op): + return {operators.add:operators.sub, operators.sub:operators.add}.get(op, op) + + meta = MetaData(testing.db) + test_table = Table('test', meta, + Column('id', Integer, primary_key=True), + Column('data', String(30)), + Column('atimestamp', Date), + Column('avalue', MyCustomType)) + + meta.create_all() + + test_table.insert().execute({'id':1, 'data':'somedata', 'atimestamp':datetime.date(2007, 10, 15), 'avalue':25}) + + @classmethod + def teardown_class(cls): + meta.drop_all() + + def test_control(self): + assert testing.db.execute("select avalue from test").scalar() == 250 + + assert test_table.select().execute().fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] + + def test_bind_adapt(self): + expr = test_table.c.atimestamp == bindparam("thedate") + assert expr.right.type.__class__ == test_table.c.atimestamp.type.__class__ + + assert testing.db.execute(test_table.select().where(expr), {"thedate":datetime.date(2007, 10, 15)}).fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] + + expr = test_table.c.avalue == bindparam("somevalue") + assert expr.right.type.__class__ == test_table.c.avalue.type.__class__ + assert testing.db.execute(test_table.select().where(expr), {"somevalue":25}).fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] + + @testing.fails_on('firebird', 'Data type unknown on the parameter') + def test_operator_adapt(self): + """test type-based overloading of operators""" + + # test string concatenation + expr = test_table.c.data + "somedata" + assert testing.db.execute(select([expr])).scalar() == "somedatasomedata" + + expr = test_table.c.id + 15 + assert testing.db.execute(select([expr])).scalar() == 16 + + # test custom operator conversion + expr = test_table.c.avalue + 40 + assert expr.type.__class__ is test_table.c.avalue.type.__class__ + + # + operator converted to - + # value is calculated as: (250 - (40 * 10)) / 10 == -15 + assert testing.db.execute(select([expr.label('foo')])).scalar() == -15 + + # this one relies upon anonymous labeling to assemble result + # processing rules on the column. + assert testing.db.execute(select([expr])).scalar() == -15 + +class DateTest(TestBase, AssertsExecutionResults): + @classmethod + def setup_class(cls): + global users_with_date, insert_data + + db = testing.db + if testing.against('oracle'): + import sqlalchemy.databases.oracle as oracle + insert_data = [ + (7, 'jack', + datetime.datetime(2005, 11, 10, 0, 0), + datetime.date(2005,11,10), + datetime.datetime(2005, 11, 10, 0, 0, 0, 29384)), + (8, 'roy', + datetime.datetime(2005, 11, 10, 11, 52, 35), + datetime.date(2005,10,10), + datetime.datetime(2006, 5, 10, 15, 32, 47, 6754)), + (9, 'foo', + datetime.datetime(2006, 11, 10, 11, 52, 35), + datetime.date(1970,4,1), + datetime.datetime(2004, 9, 18, 4, 0, 52, 1043)), + (10, 'colber', None, None, None), + ] + fnames = ['user_id', 'user_name', 'user_datetime', + 'user_date', 'user_time'] + + collist = [Column('user_id', INT, primary_key=True), + Column('user_name', VARCHAR(20)), + Column('user_datetime', DateTime), + Column('user_date', Date), + Column('user_time', TIMESTAMP)] + else: + datetime_micro = 54839 + time_micro = 999 + + # Missing or poor microsecond support: + if testing.against('mssql', 'mysql', 'firebird'): + datetime_micro, time_micro = 0, 0 + # No microseconds for TIME + elif testing.against('maxdb'): + time_micro = 0 + + insert_data = [ + (7, 'jack', + datetime.datetime(2005, 11, 10, 0, 0), + datetime.date(2005, 11, 10), + datetime.time(12, 20, 2)), + (8, 'roy', + datetime.datetime(2005, 11, 10, 11, 52, 35), + datetime.date(2005, 10, 10), + datetime.time(0, 0, 0)), + (9, 'foo', + datetime.datetime(2005, 11, 10, 11, 52, 35, datetime_micro), + datetime.date(1970, 4, 1), + datetime.time(23, 59, 59, time_micro)), + (10, 'colber', None, None, None), + ] + + + fnames = ['user_id', 'user_name', 'user_datetime', + 'user_date', 'user_time'] + + collist = [Column('user_id', INT, primary_key=True), + Column('user_name', VARCHAR(20)), + Column('user_datetime', DateTime(timezone=False)), + Column('user_date', Date), + Column('user_time', Time)] + + if testing.against('sqlite', 'postgres'): + insert_data.append( + (11, 'historic', + datetime.datetime(1850, 11, 10, 11, 52, 35, datetime_micro), + datetime.date(1727,4,1), + None), + ) + + users_with_date = Table('query_users_with_date', + MetaData(testing.db), *collist) + users_with_date.create() + insert_dicts = [dict(zip(fnames, d)) for d in insert_data] + + for idict in insert_dicts: + users_with_date.insert().execute(**idict) + + @classmethod + def teardown_class(cls): + users_with_date.drop() + + def testdate(self): + global insert_data + + l = map(tuple, users_with_date.select().execute().fetchall()) + self.assert_(l == insert_data, + 'DateTest mismatch: got:%s expected:%s' % (l, insert_data)) + + def testtextdate(self): + x = testing.db.text( + "select user_datetime from query_users_with_date", + typemap={'user_datetime':DateTime}).execute().fetchall() + + self.assert_(isinstance(x[0][0], datetime.datetime)) + + x = testing.db.text( + "select * from query_users_with_date where user_datetime=:somedate", + bindparams=[bindparam('somedate', type_=types.DateTime)]).execute( + somedate=datetime.datetime(2005, 11, 10, 11, 52, 35)).fetchall() + + def testdate2(self): + meta = MetaData(testing.db) + t = Table('testdate', meta, + Column('id', Integer, + Sequence('datetest_id_seq', optional=True), + primary_key=True), + Column('adate', Date), Column('adatetime', DateTime)) + t.create(checkfirst=True) + try: + d1 = datetime.date(2007, 10, 30) + t.insert().execute(adate=d1, adatetime=d1) + d2 = datetime.datetime(2007, 10, 30) + t.insert().execute(adate=d2, adatetime=d2) + + x = t.select().execute().fetchall()[0] + self.assert_(x.adate.__class__ == datetime.date) + self.assert_(x.adatetime.__class__ == datetime.datetime) + + t.delete().execute() + + # test mismatched date/datetime + t.insert().execute(adate=d2, adatetime=d2) + eq_(select([t.c.adate, t.c.adatetime], t.c.adate==d1).execute().fetchall(), [(d1, d2)]) + eq_(select([t.c.adate, t.c.adatetime], t.c.adate==d1).execute().fetchall(), [(d1, d2)]) + + finally: + t.drop(checkfirst=True) + +class StringTest(TestBase, AssertsExecutionResults): + @testing.fails_on('mysql', 'FIXME: unknown') + @testing.fails_on('oracle', 'FIXME: unknown') + def test_nolength_string(self): + metadata = MetaData(testing.db) + foo = Table('foo', metadata, Column('one', String)) + + foo.create() + foo.drop() + +def _missing_decimal(): + """Python implementation supports decimals""" + try: + import decimal + return False + except ImportError: + return True + +class NumericTest(TestBase, AssertsExecutionResults): + @classmethod + def setup_class(cls): + global numeric_table, metadata + metadata = MetaData(testing.db) + numeric_table = Table('numeric_table', metadata, + Column('id', Integer, Sequence('numeric_id_seq', optional=True), primary_key=True), + Column('numericcol', Numeric(asdecimal=False)), + Column('floatcol', Float), + Column('ncasdec', Numeric), + Column('fcasdec', Float(asdecimal=True)) + ) + metadata.create_all() + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def teardown(self): + numeric_table.delete().execute() + + @testing.fails_if(_missing_decimal) + def test_decimal(self): + from decimal import Decimal + numeric_table.insert().execute( + numericcol=3.5, floatcol=5.6, ncasdec=12.4, fcasdec=15.75) + numeric_table.insert().execute( + numericcol=Decimal("3.5"), floatcol=Decimal("5.6"), + ncasdec=Decimal("12.4"), fcasdec=Decimal("15.75")) + + l = numeric_table.select().execute().fetchall() + rounded = [ + (l[0][0], l[0][1], round(l[0][2], 5), l[0][3], l[0][4]), + (l[1][0], l[1][1], round(l[1][2], 5), l[1][3], l[1][4]), + ] + testing.eq_(rounded, [ + (1, 3.5, 5.6, Decimal("12.4"), Decimal("15.75")), + (2, 3.5, 5.6, Decimal("12.4"), Decimal("15.75")), + ]) + + def test_decimal_fallback(self): + from decimal import Decimal + + numeric_table.insert().execute(ncasdec=12.4, fcasdec=15.75) + numeric_table.insert().execute(ncasdec=Decimal("12.4"), + fcasdec=Decimal("15.75")) + + for row in numeric_table.select().execute().fetchall(): + assert isinstance(row['ncasdec'], decimal.Decimal) + assert isinstance(row['fcasdec'], decimal.Decimal) + + def test_length_deprecation(self): + assert_raises(exc.SADeprecationWarning, Numeric, length=8) + + @testing.uses_deprecated(".*is deprecated for Numeric") + def go(): + n = Numeric(length=12) + assert n.scale == 12 + go() + + n = Numeric(scale=12) + for dialect in engines.all_dialects(): + n2 = dialect.type_descriptor(n) + eq_(n2.scale, 12, dialect.name) + + # test colspec generates successfully using 'scale' + assert n2.get_col_spec() + + # test constructor of the dialect-specific type + n3 = n2.__class__(scale=5) + eq_(n3.scale, 5, dialect.name) + + @testing.uses_deprecated(".*is deprecated for Numeric") + def go(): + n3 = n2.__class__(length=6) + eq_(n3.scale, 6, dialect.name) + go() + + +class IntervalTest(TestBase, AssertsExecutionResults): + @classmethod + def setup_class(cls): + global interval_table, metadata + metadata = MetaData(testing.db) + interval_table = Table("intervaltable", metadata, + Column("id", Integer, Sequence('interval_id_seq', optional=True), primary_key=True), + Column("interval", Interval), + ) + metadata.create_all() + + def teardown(self): + interval_table.delete().execute() + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def test_roundtrip(self): + delta = datetime.datetime(2006, 10, 5) - datetime.datetime(2005, 8, 17) + interval_table.insert().execute(interval=delta) + assert interval_table.select().execute().fetchone()['interval'] == delta + + def test_null(self): + interval_table.insert().execute(id=1, inverval=None) + assert interval_table.select().execute().fetchone()['interval'] is None + +class BooleanTest(TestBase, AssertsExecutionResults): + @classmethod + def setup_class(cls): + global bool_table + metadata = MetaData(testing.db) + bool_table = Table('booltest', metadata, + Column('id', Integer, primary_key=True), + Column('value', Boolean)) + bool_table.create() + @classmethod + def teardown_class(cls): + bool_table.drop() + def testbasic(self): + bool_table.insert().execute(id=1, value=True) + bool_table.insert().execute(id=2, value=False) + bool_table.insert().execute(id=3, value=True) + bool_table.insert().execute(id=4, value=True) + bool_table.insert().execute(id=5, value=True) + + res = bool_table.select(bool_table.c.value==True).execute().fetchall() + assert(res==[(1, True),(3, True),(4, True),(5, True)]) + + res2 = bool_table.select(bool_table.c.value==False).execute().fetchall() + assert(res2==[(2, False)]) + +class PickleTest(TestBase): + def test_noeq_deprecation(self): + p1 = PickleType() + + assert_raises(DeprecationWarning, + p1.compare_values, pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2) + ) + + assert_raises(DeprecationWarning, + p1.compare_values, pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2) + ) + + @testing.uses_deprecated() + def go(): + # test actual dumps comparison + assert p1.compare_values(pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2)) + assert p1.compare_values(pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2)) + go() + + assert p1.compare_values({1:2, 3:4}, {3:4, 1:2}) + + p2 = PickleType(mutable=False) + assert not p2.compare_values(pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2)) + assert not p2.compare_values(pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2)) + + def test_eq_comparison(self): + p1 = PickleType() + + for obj in ( + {'1':'2'}, + pickleable.Bar(5, 6), + pickleable.OldSchool(10, 11) + ): + assert p1.compare_values(p1.copy_value(obj), obj) + + assert_raises(NotImplementedError, p1.compare_values, pickleable.BrokenComparable('foo'),pickleable.BrokenComparable('foo')) + + def test_nonmutable_comparison(self): + p1 = PickleType() + + for obj in ( + {'1':'2'}, + pickleable.Bar(5, 6), + pickleable.OldSchool(10, 11) + ): + assert p1.compare_values(p1.copy_value(obj), obj) + +class CallableTest(TestBase): + @classmethod + def setup_class(cls): + global meta + meta = MetaData(testing.db) + + @classmethod + def teardown_class(cls): + meta.drop_all() + + def test_callable_as_arg(self): + ucode = util.partial(Unicode, assert_unicode=None) + + thing_table = Table('thing', meta, + Column('name', ucode(20)) + ) + assert isinstance(thing_table.c.name.type, Unicode) + thing_table.create() + + def test_callable_as_kwarg(self): + ucode = util.partial(Unicode, assert_unicode=None) + + thang_table = Table('thang', meta, + Column('name', type_=ucode(20), primary_key=True) + ) + assert isinstance(thang_table.c.name.type, Unicode) + thang_table.create() + diff --git a/test/sql/test_unicode.py b/test/sql/test_unicode.py new file mode 100644 index 000000000..d75913267 --- /dev/null +++ b/test/sql/test_unicode.py @@ -0,0 +1,138 @@ +# coding: utf-8 +"""verrrrry basic unicode column name testing""" + +from sqlalchemy import * +from sqlalchemy.test import * +from sqlalchemy.test.engines import utf8_engine +from sqlalchemy.sql import column + +class UnicodeSchemaTest(TestBase): + __requires__ = ('unicode_ddl',) + + @classmethod + def setup_class(cls): + global unicode_bind, metadata, t1, t2, t3 + + unicode_bind = utf8_engine() + + metadata = MetaData(unicode_bind) + t1 = Table('unitable1', metadata, + Column(u'méil', Integer, primary_key=True), + Column(u'\u6e2c\u8a66', Integer), + test_needs_fk=True, + ) + t2 = Table(u'Unitéble2', metadata, + Column(u'méil', Integer, primary_key=True, key="a"), + Column(u'\u6e2c\u8a66', Integer, ForeignKey(u'unitable1.méil'), + key="b" + ), + test_needs_fk=True, + ) + + # Few DBs support Unicode foreign keys + if testing.against('sqlite'): + t3 = Table(u'\u6e2c\u8a66', metadata, + Column(u'\u6e2c\u8a66_id', Integer, primary_key=True, + autoincrement=False), + Column(u'unitable1_\u6e2c\u8a66', Integer, + ForeignKey(u'unitable1.\u6e2c\u8a66') + ), + Column(u'Unitéble2_b', Integer, + ForeignKey(u'Unitéble2.b') + ), + Column(u'\u6e2c\u8a66_self', Integer, + ForeignKey(u'\u6e2c\u8a66.\u6e2c\u8a66_id') + ), + test_needs_fk=True, + ) + else: + t3 = Table(u'\u6e2c\u8a66', metadata, + Column(u'\u6e2c\u8a66_id', Integer, primary_key=True, + autoincrement=False), + Column(u'unitable1_\u6e2c\u8a66', Integer), + Column(u'Unitéble2_b', Integer), + Column(u'\u6e2c\u8a66_self', Integer), + test_needs_fk=True, + ) + metadata.create_all() + + def teardown(self): + if metadata.tables: + t3.delete().execute() + t2.delete().execute() + t1.delete().execute() + + @classmethod + def teardown_class(cls): + global unicode_bind + metadata.drop_all() + del unicode_bind + + def test_insert(self): + t1.insert().execute({u'méil':1, u'\u6e2c\u8a66':5}) + t2.insert().execute({'a':1, 'b':1}) + t3.insert().execute({u'\u6e2c\u8a66_id': 1, + u'unitable1_\u6e2c\u8a66': 5, + u'Unitéble2_b': 1, + u'\u6e2c\u8a66_self': 1}) + + assert t1.select().execute().fetchall() == [(1, 5)] + assert t2.select().execute().fetchall() == [(1, 1)] + assert t3.select().execute().fetchall() == [(1, 5, 1, 1)] + + def test_reflect(self): + t1.insert().execute({u'méil':2, u'\u6e2c\u8a66':7}) + t2.insert().execute({'a':2, 'b':2}) + t3.insert().execute({u'\u6e2c\u8a66_id': 2, + u'unitable1_\u6e2c\u8a66': 7, + u'Unitéble2_b': 2, + u'\u6e2c\u8a66_self': 2}) + + meta = MetaData(unicode_bind) + tt1 = Table(t1.name, meta, autoload=True) + tt2 = Table(t2.name, meta, autoload=True) + tt3 = Table(t3.name, meta, autoload=True) + + tt1.insert().execute({u'méil':1, u'\u6e2c\u8a66':5}) + tt2.insert().execute({u'méil':1, u'\u6e2c\u8a66':1}) + tt3.insert().execute({u'\u6e2c\u8a66_id': 1, + u'unitable1_\u6e2c\u8a66': 5, + u'Unitéble2_b': 1, + u'\u6e2c\u8a66_self': 1}) + + self.assert_(tt1.select(order_by=desc(u'méil')).execute().fetchall() == + [(2, 7), (1, 5)]) + self.assert_(tt2.select(order_by=desc(u'méil')).execute().fetchall() == + [(2, 2), (1, 1)]) + self.assert_(tt3.select(order_by=desc(u'\u6e2c\u8a66_id')). + execute().fetchall() == + [(2, 7, 2, 2), (1, 5, 1, 1)]) + meta.drop_all() + metadata.create_all() + +class EscapesDefaultsTest(testing.TestBase): + def test_default_exec(self): + metadata = MetaData(testing.db) + t1 = Table('t1', metadata, + Column(u'special_col', Integer, Sequence('special_col'), primary_key=True), + Column('data', String(50)) # to appease SQLite without DEFAULT VALUES + ) + t1.create() + + try: + engine = metadata.bind + + # reset the identifier preparer, so that we can force it to cache + # a unicode identifier + engine.dialect.identifier_preparer = engine.dialect.preparer(engine.dialect) + select([column(u'special_col')]).select_from(t1).execute() + assert isinstance(engine.dialect.identifier_preparer.format_sequence(Sequence('special_col')), unicode) + + # now execute, run the sequence. it should run in u"Special_col.nextid" or similar as + # a unicode object; cx_oracle asserts that this is None or a String (postgres lets it pass thru). + # ensure that base.DefaultRunner is encoding. + t1.insert().execute(data='foo') + finally: + t1.drop() + + diff --git a/test/sql/testtypes.py b/test/sql/testtypes.py deleted file mode 100644 index e5cffe328..000000000 --- a/test/sql/testtypes.py +++ /dev/null @@ -1,875 +0,0 @@ -import decimal -import testenv; testenv.configure_for_tests() -import datetime, os, pickleable, re -from sqlalchemy import * -from sqlalchemy import exc, types, util -from sqlalchemy.sql import operators -from testlib.testing import eq_ -import sqlalchemy.engine.url as url -from sqlalchemy.databases import mssql, oracle, mysql, postgres, firebird -from testlib import * - - -class AdaptTest(TestBase): - def testadapt(self): - e1 = url.URL('postgres').get_dialect()() - e2 = url.URL('mysql').get_dialect()() - e3 = url.URL('sqlite').get_dialect()() - e4 = url.URL('firebird').get_dialect()() - - type = String(40) - - t1 = type.dialect_impl(e1) - t2 = type.dialect_impl(e2) - t3 = type.dialect_impl(e3) - t4 = type.dialect_impl(e4) - - impls = [t1, t2, t3, t4] - for i,ta in enumerate(impls): - for j,tb in enumerate(impls): - if i == j: - assert ta == tb # call me paranoid... :) - else: - assert ta != tb - - def testmsnvarchar(self): - dialect = mssql.MSSQLDialect() - # run the test twice to ensure the caching step works too - for x in range(0, 1): - col = Column('', Unicode(length=10)) - dialect_type = col.type.dialect_impl(dialect) - assert isinstance(dialect_type, mssql.MSNVarchar) - assert dialect_type.get_col_spec() == 'NVARCHAR(10)' - - - def testoracletimestamp(self): - dialect = oracle.OracleDialect() - t1 = oracle.OracleTimestamp - t2 = oracle.OracleTimestamp() - t3 = types.TIMESTAMP - assert isinstance(dialect.type_descriptor(t1), oracle.OracleTimestamp) - assert isinstance(dialect.type_descriptor(t2), oracle.OracleTimestamp) - assert isinstance(dialect.type_descriptor(t3), oracle.OracleTimestamp) - - def testmysqlbinary(self): - dialect = mysql.MySQLDialect() - t1 = mysql.MSVarBinary - t2 = mysql.MSVarBinary() - assert isinstance(dialect.type_descriptor(t1), mysql.MSVarBinary) - assert isinstance(dialect.type_descriptor(t2), mysql.MSVarBinary) - - def teststringadapt(self): - """test that String with no size becomes TEXT, *all* others stay as varchar/String""" - - oracle_dialect = oracle.OracleDialect() - mysql_dialect = mysql.MySQLDialect() - postgres_dialect = postgres.PGDialect() - firebird_dialect = firebird.FBDialect() - - for dialect, start, test in [ - (oracle_dialect, String(), oracle.OracleString), - (oracle_dialect, VARCHAR(), oracle.OracleString), - (oracle_dialect, String(50), oracle.OracleString), - (oracle_dialect, Unicode(), oracle.OracleString), - (oracle_dialect, UnicodeText(), oracle.OracleText), - (oracle_dialect, NCHAR(), oracle.OracleString), - (oracle_dialect, oracle.OracleRaw(50), oracle.OracleRaw), - (mysql_dialect, String(), mysql.MSString), - (mysql_dialect, VARCHAR(), mysql.MSString), - (mysql_dialect, String(50), mysql.MSString), - (mysql_dialect, Unicode(), mysql.MSString), - (mysql_dialect, UnicodeText(), mysql.MSText), - (mysql_dialect, NCHAR(), mysql.MSNChar), - (postgres_dialect, String(), postgres.PGString), - (postgres_dialect, VARCHAR(), postgres.PGString), - (postgres_dialect, String(50), postgres.PGString), - (postgres_dialect, Unicode(), postgres.PGString), - (postgres_dialect, UnicodeText(), postgres.PGText), - (postgres_dialect, NCHAR(), postgres.PGString), - (firebird_dialect, String(), firebird.FBString), - (firebird_dialect, VARCHAR(), firebird.FBString), - (firebird_dialect, String(50), firebird.FBString), - (firebird_dialect, Unicode(), firebird.FBString), - (firebird_dialect, UnicodeText(), firebird.FBText), - (firebird_dialect, NCHAR(), firebird.FBString), - ]: - assert isinstance(start.dialect_impl(dialect), test), "wanted %r got %r" % (test, start.dialect_impl(dialect)) - - - -class UserDefinedTest(TestBase): - """tests user-defined types.""" - - def testprocessing(self): - - global users - users.insert().execute( - user_id=2, goofy='jack', goofy2='jack', goofy4=u'jack', - goofy7=u'jack', goofy8=12, goofy9=12) - users.insert().execute( - user_id=3, goofy='lala', goofy2='lala', goofy4=u'lala', - goofy7=u'lala', goofy8=15, goofy9=15) - users.insert().execute( - user_id=4, goofy='fred', goofy2='fred', goofy4=u'fred', - goofy7=u'fred', goofy8=9, goofy9=9) - - l = users.select().execute().fetchall() - for assertstr, assertint, assertint2, row in zip( - ["BIND_INjackBIND_OUT", "BIND_INlalaBIND_OUT", "BIND_INfredBIND_OUT"], - [1200, 1500, 900], - [1800, 2250, 1350], - l - ): - for col in row[1:5]: - self.assertEquals(col, assertstr) - self.assertEquals(row[5], assertint) - self.assertEquals(row[6], assertint2) - for col in row[3], row[4]: - assert isinstance(col, unicode) - - def setUpAll(self): - global users, metadata - - class MyType(types.TypeEngine): - def get_col_spec(self): - return "VARCHAR(100)" - def bind_processor(self, dialect): - def process(value): - return "BIND_IN"+ value - return process - def result_processor(self, dialect): - def process(value): - return value + "BIND_OUT" - return process - def adapt(self, typeobj): - return typeobj() - - class MyDecoratedType(types.TypeDecorator): - impl = String - def bind_processor(self, dialect): - impl_processor = super(MyDecoratedType, self).bind_processor(dialect) or (lambda value:value) - def process(value): - return "BIND_IN"+ impl_processor(value) - return process - def result_processor(self, dialect): - impl_processor = super(MyDecoratedType, self).result_processor(dialect) or (lambda value:value) - def process(value): - return impl_processor(value) + "BIND_OUT" - return process - def copy(self): - return MyDecoratedType() - - class MyNewUnicodeType(types.TypeDecorator): - impl = Unicode - - def process_bind_param(self, value, dialect): - return "BIND_IN" + value - - def process_result_value(self, value, dialect): - return value + "BIND_OUT" - - def copy(self): - return MyNewUnicodeType(self.impl.length) - - class MyNewIntType(types.TypeDecorator): - impl = Integer - - def process_bind_param(self, value, dialect): - return value * 10 - - def process_result_value(self, value, dialect): - return value * 10 - - def copy(self): - return MyNewIntType() - - class MyNewIntSubClass(MyNewIntType): - def process_result_value(self, value, dialect): - return value * 15 - - def copy(self): - return MyNewIntSubClass() - - class MyUnicodeType(types.TypeDecorator): - impl = Unicode - - def bind_processor(self, dialect): - impl_processor = super(MyUnicodeType, self).bind_processor(dialect) or (lambda value:value) - - def process(value): - return "BIND_IN"+ impl_processor(value) - return process - - def result_processor(self, dialect): - impl_processor = super(MyUnicodeType, self).result_processor(dialect) or (lambda value:value) - def process(value): - return impl_processor(value) + "BIND_OUT" - return process - - def copy(self): - return MyUnicodeType(self.impl.length) - - metadata = MetaData(testing.db) - users = Table('type_users', metadata, - Column('user_id', Integer, primary_key = True), - # totall custom type - Column('goofy', MyType, nullable = False), - - # decorated type with an argument, so its a String - Column('goofy2', MyDecoratedType(50), nullable = False), - - Column('goofy4', MyUnicodeType(50), nullable = False), - Column('goofy7', MyNewUnicodeType(50), nullable = False), - Column('goofy8', MyNewIntType, nullable = False), - Column('goofy9', MyNewIntSubClass, nullable = False), - ) - - metadata.create_all() - - def tearDownAll(self): - metadata.drop_all() - -class ColumnsTest(TestBase, AssertsExecutionResults): - - def testcolumns(self): - expectedResults = { 'int_column': 'int_column INTEGER', - 'smallint_column': 'smallint_column SMALLINT', - 'varchar_column': 'varchar_column VARCHAR(20)', - 'numeric_column': 'numeric_column NUMERIC(12, 3)', - 'float_column': 'float_column FLOAT(25)', - } - - db = testing.db - if testing.against('oracle'): - expectedResults['float_column'] = 'float_column NUMERIC(25, 2)' - - if testing.against('sqlite'): - expectedResults['float_column'] = 'float_column FLOAT' - - if testing.against('maxdb'): - expectedResults['numeric_column'] = ( - expectedResults['numeric_column'].replace('NUMERIC', 'FIXED')) - - if testing.against('mssql'): - for key, value in expectedResults.items(): - expectedResults[key] = '%s NULL' % value - - testTable = Table('testColumns', MetaData(db), - Column('int_column', Integer), - Column('smallint_column', SmallInteger), - Column('varchar_column', String(20)), - Column('numeric_column', Numeric(12,3)), - Column('float_column', Float(25)), - ) - - for aCol in testTable.c: - self.assertEquals( - expectedResults[aCol.name], - db.dialect.schemagenerator(db.dialect, db, None, None).\ - get_column_specification(aCol)) - -class UnicodeTest(TestBase, AssertsExecutionResults): - """tests the Unicode type. also tests the TypeDecorator with instances in the types package.""" - def setUpAll(self): - global unicode_table - metadata = MetaData(testing.db) - unicode_table = Table('unicode_table', metadata, - Column('id', Integer, Sequence('uni_id_seq', optional=True), primary_key=True), - Column('unicode_varchar', Unicode(250)), - Column('unicode_text', UnicodeText), - Column('plain_varchar', String(250)) - ) - unicode_table.create() - def tearDownAll(self): - unicode_table.drop() - - def tearDown(self): - unicode_table.delete().execute() - - def test_round_trip(self): - assert unicode_table.c.unicode_varchar.type.length == 250 - rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' - unicodedata = rawdata.decode('utf-8') - if testing.against('sqlite'): - rawdata = "something" - - unicode_table.insert().execute(unicode_varchar=unicodedata, - unicode_text=unicodedata, - plain_varchar=rawdata) - x = unicode_table.select().execute().fetchone() - self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) - self.assert_(isinstance(x['unicode_text'], unicode) and x['unicode_text'] == unicodedata) - if isinstance(x['plain_varchar'], unicode): - # SQLLite and MSSQL return non-unicode data as unicode - self.assert_(testing.against('sqlite', 'mssql')) - if not testing.against('sqlite'): - self.assert_(x['plain_varchar'] == unicodedata) - else: - self.assert_(not isinstance(x['plain_varchar'], unicode) and x['plain_varchar'] == rawdata) - - def test_union(self): - """ensure compiler processing works for UNIONs""" - - rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' - unicodedata = rawdata.decode('utf-8') - if testing.against('sqlite'): - rawdata = "something" - unicode_table.insert().execute(unicode_varchar=unicodedata, - unicode_text=unicodedata, - plain_varchar=rawdata) - - x = union(select([unicode_table.c.unicode_varchar]), select([unicode_table.c.unicode_varchar])).execute().fetchone() - self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) - - def test_assertions(self): - try: - unicode_table.insert().execute(unicode_varchar='not unicode') - assert False - except exc.SAWarning, e: - assert str(e) == "Unicode type received non-unicode bind param value 'not unicode'", str(e) - - unicode_engine = engines.utf8_engine(options={'convert_unicode':True, - 'assert_unicode':True}) - try: - try: - unicode_engine.execute(unicode_table.insert(), plain_varchar='im not unicode') - assert False - except exc.InvalidRequestError, e: - assert str(e) == "Unicode type received non-unicode bind param value 'im not unicode'" - - @testing.emits_warning('.*non-unicode bind') - def warns(): - # test that data still goes in if warning is emitted.... - unicode_table.insert().execute(unicode_varchar='not unicode') - assert (select([unicode_table.c.unicode_varchar]).execute().fetchall() == [('not unicode', )]) - warns() - - finally: - unicode_engine.dispose() - - @testing.fails_on('oracle', 'FIXME: unknown') - def test_blank_strings(self): - unicode_table.insert().execute(unicode_varchar=u'') - assert select([unicode_table.c.unicode_varchar]).scalar() == u'' - - def test_engine_parameter(self): - """tests engine-wide unicode conversion""" - prev_unicode = testing.db.engine.dialect.convert_unicode - prev_assert = testing.db.engine.dialect.assert_unicode - try: - testing.db.engine.dialect.convert_unicode = True - testing.db.engine.dialect.assert_unicode = False - rawdata = 'Alors vous imaginez ma surprise, au lever du jour, quand une dr\xc3\xb4le de petit voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9. Elle disait: \xc2\xab S\xe2\x80\x99il vous pla\xc3\xaet\xe2\x80\xa6 dessine-moi un mouton! \xc2\xbb\n' - unicodedata = rawdata.decode('utf-8') - if testing.against('sqlite', 'mssql'): - rawdata = "something" - unicode_table.insert().execute(unicode_varchar=unicodedata, - unicode_text=unicodedata, - plain_varchar=rawdata) - x = unicode_table.select().execute().fetchone() - self.assert_(isinstance(x['unicode_varchar'], unicode) and x['unicode_varchar'] == unicodedata) - self.assert_(isinstance(x['unicode_text'], unicode) and x['unicode_text'] == unicodedata) - if not testing.against('sqlite', 'mssql'): - self.assert_(isinstance(x['plain_varchar'], unicode) and x['plain_varchar'] == unicodedata) - finally: - testing.db.engine.dialect.convert_unicode = prev_unicode - testing.db.engine.dialect.convert_unicode = prev_assert - - @testing.crashes('oracle', 'FIXME: unknown, verify not fails_on') - @testing.fails_on('firebird', 'Data type unknown') - def test_length_function(self): - """checks the database correctly understands the length of a unicode string""" - teststr = u'aaa\x1234' - self.assert_(testing.db.func.length(teststr).scalar() == len(teststr)) - -class BinaryTest(TestBase, AssertsExecutionResults): - __excluded_on__ = ( - ('mysql', '<', (4, 1, 1)), # screwy varbinary types - ) - - def setUpAll(self): - global binary_table, MyPickleType - - class MyPickleType(types.TypeDecorator): - impl = PickleType - - def process_bind_param(self, value, dialect): - if value: - value.stuff = 'this is modified stuff' - return value - - def process_result_value(self, value, dialect): - if value: - value.stuff = 'this is the right stuff' - return value - - binary_table = Table('binary_table', MetaData(testing.db), - Column('primary_id', Integer, Sequence('binary_id_seq', optional=True), primary_key=True), - Column('data', Binary), - Column('data_slice', Binary(100)), - Column('misc', String(30)), - # construct PickleType with non-native pickle module, since cPickle uses relative module - # loading and confuses this test's parent package 'sql' with the 'sqlalchemy.sql' package relative - # to the 'types' module - Column('pickled', PickleType), - Column('mypickle', MyPickleType) - ) - binary_table.create() - - def tearDown(self): - binary_table.delete().execute() - - def tearDownAll(self): - binary_table.drop() - - @testing.fails_on('mssql', 'MSSQl BINARY type right pads the fixed length with \x00') - def testbinary(self): - testobj1 = pickleable.Foo('im foo 1') - testobj2 = pickleable.Foo('im foo 2') - testobj3 = pickleable.Foo('im foo 3') - - stream1 =self.load_stream('binary_data_one.dat') - stream2 =self.load_stream('binary_data_two.dat') - binary_table.insert().execute(primary_id=1, misc='binary_data_one.dat', data=stream1, data_slice=stream1[0:100], pickled=testobj1, mypickle=testobj3) - binary_table.insert().execute(primary_id=2, misc='binary_data_two.dat', data=stream2, data_slice=stream2[0:99], pickled=testobj2) - binary_table.insert().execute(primary_id=3, misc='binary_data_two.dat', data=None, data_slice=stream2[0:99], pickled=None) - - for stmt in ( - binary_table.select(order_by=binary_table.c.primary_id), - text("select * from binary_table order by binary_table.primary_id", typemap={'pickled':PickleType, 'mypickle':MyPickleType}, bind=testing.db) - ): - l = stmt.execute().fetchall() - self.assertEquals(list(stream1), list(l[0]['data'])) - self.assertEquals(list(stream1[0:100]), list(l[0]['data_slice'])) - self.assertEquals(list(stream2), list(l[1]['data'])) - self.assertEquals(testobj1, l[0]['pickled']) - self.assertEquals(testobj2, l[1]['pickled']) - self.assertEquals(testobj3.moredata, l[0]['mypickle'].moredata) - self.assertEquals(l[0]['mypickle'].stuff, 'this is the right stuff') - - def load_stream(self, name, len=12579): - f = os.path.join(os.path.dirname(testenv.__file__), name) - # put a number less than the typical MySQL default BLOB size - return file(f).read(len) - -class ExpressionTest(TestBase, AssertsExecutionResults): - def setUpAll(self): - global test_table, meta - - class MyCustomType(types.TypeEngine): - def get_col_spec(self): - return "INT" - def bind_processor(self, dialect): - def process(value): - return value * 10 - return process - def result_processor(self, dialect): - def process(value): - return value / 10 - return process - def adapt_operator(self, op): - return {operators.add:operators.sub, operators.sub:operators.add}.get(op, op) - - meta = MetaData(testing.db) - test_table = Table('test', meta, - Column('id', Integer, primary_key=True), - Column('data', String(30)), - Column('atimestamp', Date), - Column('avalue', MyCustomType)) - - meta.create_all() - - test_table.insert().execute({'id':1, 'data':'somedata', 'atimestamp':datetime.date(2007, 10, 15), 'avalue':25}) - - def tearDownAll(self): - meta.drop_all() - - def test_control(self): - assert testing.db.execute("select avalue from test").scalar() == 250 - - assert test_table.select().execute().fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] - - def test_bind_adapt(self): - expr = test_table.c.atimestamp == bindparam("thedate") - assert expr.right.type.__class__ == test_table.c.atimestamp.type.__class__ - - assert testing.db.execute(test_table.select().where(expr), {"thedate":datetime.date(2007, 10, 15)}).fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] - - expr = test_table.c.avalue == bindparam("somevalue") - assert expr.right.type.__class__ == test_table.c.avalue.type.__class__ - assert testing.db.execute(test_table.select().where(expr), {"somevalue":25}).fetchall() == [(1, 'somedata', datetime.date(2007, 10, 15), 25)] - - @testing.fails_on('firebird', 'Data type unknown on the parameter') - def test_operator_adapt(self): - """test type-based overloading of operators""" - - # test string concatenation - expr = test_table.c.data + "somedata" - assert testing.db.execute(select([expr])).scalar() == "somedatasomedata" - - expr = test_table.c.id + 15 - assert testing.db.execute(select([expr])).scalar() == 16 - - # test custom operator conversion - expr = test_table.c.avalue + 40 - assert expr.type.__class__ is test_table.c.avalue.type.__class__ - - # + operator converted to - - # value is calculated as: (250 - (40 * 10)) / 10 == -15 - assert testing.db.execute(select([expr.label('foo')])).scalar() == -15 - - # this one relies upon anonymous labeling to assemble result - # processing rules on the column. - assert testing.db.execute(select([expr])).scalar() == -15 - -class DateTest(TestBase, AssertsExecutionResults): - def setUpAll(self): - global users_with_date, insert_data - - db = testing.db - if testing.against('oracle'): - import sqlalchemy.databases.oracle as oracle - insert_data = [ - (7, 'jack', - datetime.datetime(2005, 11, 10, 0, 0), - datetime.date(2005,11,10), - datetime.datetime(2005, 11, 10, 0, 0, 0, 29384)), - (8, 'roy', - datetime.datetime(2005, 11, 10, 11, 52, 35), - datetime.date(2005,10,10), - datetime.datetime(2006, 5, 10, 15, 32, 47, 6754)), - (9, 'foo', - datetime.datetime(2006, 11, 10, 11, 52, 35), - datetime.date(1970,4,1), - datetime.datetime(2004, 9, 18, 4, 0, 52, 1043)), - (10, 'colber', None, None, None), - ] - fnames = ['user_id', 'user_name', 'user_datetime', - 'user_date', 'user_time'] - - collist = [Column('user_id', INT, primary_key=True), - Column('user_name', VARCHAR(20)), - Column('user_datetime', DateTime), - Column('user_date', Date), - Column('user_time', TIMESTAMP)] - else: - datetime_micro = 54839 - time_micro = 999 - - # Missing or poor microsecond support: - if testing.against('mssql', 'mysql', 'firebird'): - datetime_micro, time_micro = 0, 0 - # No microseconds for TIME - elif testing.against('maxdb'): - time_micro = 0 - - insert_data = [ - (7, 'jack', - datetime.datetime(2005, 11, 10, 0, 0), - datetime.date(2005, 11, 10), - datetime.time(12, 20, 2)), - (8, 'roy', - datetime.datetime(2005, 11, 10, 11, 52, 35), - datetime.date(2005, 10, 10), - datetime.time(0, 0, 0)), - (9, 'foo', - datetime.datetime(2005, 11, 10, 11, 52, 35, datetime_micro), - datetime.date(1970, 4, 1), - datetime.time(23, 59, 59, time_micro)), - (10, 'colber', None, None, None), - ] - - - fnames = ['user_id', 'user_name', 'user_datetime', - 'user_date', 'user_time'] - - collist = [Column('user_id', INT, primary_key=True), - Column('user_name', VARCHAR(20)), - Column('user_datetime', DateTime(timezone=False)), - Column('user_date', Date), - Column('user_time', Time)] - - if testing.against('sqlite', 'postgres'): - insert_data.append( - (11, 'historic', - datetime.datetime(1850, 11, 10, 11, 52, 35, datetime_micro), - datetime.date(1727,4,1), - None), - ) - - users_with_date = Table('query_users_with_date', - MetaData(testing.db), *collist) - users_with_date.create() - insert_dicts = [dict(zip(fnames, d)) for d in insert_data] - - for idict in insert_dicts: - users_with_date.insert().execute(**idict) - - def tearDownAll(self): - users_with_date.drop() - - def testdate(self): - global insert_data - - l = map(tuple, users_with_date.select().execute().fetchall()) - self.assert_(l == insert_data, - 'DateTest mismatch: got:%s expected:%s' % (l, insert_data)) - - def testtextdate(self): - x = testing.db.text( - "select user_datetime from query_users_with_date", - typemap={'user_datetime':DateTime}).execute().fetchall() - - self.assert_(isinstance(x[0][0], datetime.datetime)) - - x = testing.db.text( - "select * from query_users_with_date where user_datetime=:somedate", - bindparams=[bindparam('somedate', type_=types.DateTime)]).execute( - somedate=datetime.datetime(2005, 11, 10, 11, 52, 35)).fetchall() - - def testdate2(self): - meta = MetaData(testing.db) - t = Table('testdate', meta, - Column('id', Integer, - Sequence('datetest_id_seq', optional=True), - primary_key=True), - Column('adate', Date), Column('adatetime', DateTime)) - t.create(checkfirst=True) - try: - d1 = datetime.date(2007, 10, 30) - t.insert().execute(adate=d1, adatetime=d1) - d2 = datetime.datetime(2007, 10, 30) - t.insert().execute(adate=d2, adatetime=d2) - - x = t.select().execute().fetchall()[0] - self.assert_(x.adate.__class__ == datetime.date) - self.assert_(x.adatetime.__class__ == datetime.datetime) - - t.delete().execute() - - # test mismatched date/datetime - t.insert().execute(adate=d2, adatetime=d2) - self.assertEquals(select([t.c.adate, t.c.adatetime], t.c.adate==d1).execute().fetchall(), [(d1, d2)]) - self.assertEquals(select([t.c.adate, t.c.adatetime], t.c.adate==d1).execute().fetchall(), [(d1, d2)]) - - finally: - t.drop(checkfirst=True) - -class StringTest(TestBase, AssertsExecutionResults): - @testing.fails_on('mysql', 'FIXME: unknown') - @testing.fails_on('oracle', 'FIXME: unknown') - def test_nolength_string(self): - metadata = MetaData(testing.db) - foo = Table('foo', metadata, Column('one', String)) - - foo.create() - foo.drop() - -def _missing_decimal(): - """Python implementation supports decimals""" - try: - import decimal - return False - except ImportError: - return True - -class NumericTest(TestBase, AssertsExecutionResults): - def setUpAll(self): - global numeric_table, metadata - metadata = MetaData(testing.db) - numeric_table = Table('numeric_table', metadata, - Column('id', Integer, Sequence('numeric_id_seq', optional=True), primary_key=True), - Column('numericcol', Numeric(asdecimal=False)), - Column('floatcol', Float), - Column('ncasdec', Numeric), - Column('fcasdec', Float(asdecimal=True)) - ) - metadata.create_all() - - def tearDownAll(self): - metadata.drop_all() - - def tearDown(self): - numeric_table.delete().execute() - - @testing.fails_if(_missing_decimal) - def test_decimal(self): - from decimal import Decimal - numeric_table.insert().execute( - numericcol=3.5, floatcol=5.6, ncasdec=12.4, fcasdec=15.75) - numeric_table.insert().execute( - numericcol=Decimal("3.5"), floatcol=Decimal("5.6"), - ncasdec=Decimal("12.4"), fcasdec=Decimal("15.75")) - - l = numeric_table.select().execute().fetchall() - rounded = [ - (l[0][0], l[0][1], round(l[0][2], 5), l[0][3], l[0][4]), - (l[1][0], l[1][1], round(l[1][2], 5), l[1][3], l[1][4]), - ] - testing.eq_(rounded, [ - (1, 3.5, 5.6, Decimal("12.4"), Decimal("15.75")), - (2, 3.5, 5.6, Decimal("12.4"), Decimal("15.75")), - ]) - - def test_decimal_fallback(self): - from decimal import Decimal - - numeric_table.insert().execute(ncasdec=12.4, fcasdec=15.75) - numeric_table.insert().execute(ncasdec=Decimal("12.4"), - fcasdec=Decimal("15.75")) - - for row in numeric_table.select().execute().fetchall(): - assert isinstance(row['ncasdec'], decimal.Decimal) - assert isinstance(row['fcasdec'], decimal.Decimal) - - def test_length_deprecation(self): - self.assertRaises(exc.SADeprecationWarning, Numeric, length=8) - - @testing.uses_deprecated(".*is deprecated for Numeric") - def go(): - n = Numeric(length=12) - assert n.scale == 12 - go() - - n = Numeric(scale=12) - for dialect in engines.all_dialects(): - n2 = dialect.type_descriptor(n) - eq_(n2.scale, 12, dialect.name) - - # test colspec generates successfully using 'scale' - assert n2.get_col_spec() - - # test constructor of the dialect-specific type - n3 = n2.__class__(scale=5) - eq_(n3.scale, 5, dialect.name) - - @testing.uses_deprecated(".*is deprecated for Numeric") - def go(): - n3 = n2.__class__(length=6) - eq_(n3.scale, 6, dialect.name) - go() - - -class IntervalTest(TestBase, AssertsExecutionResults): - def setUpAll(self): - global interval_table, metadata - metadata = MetaData(testing.db) - interval_table = Table("intervaltable", metadata, - Column("id", Integer, Sequence('interval_id_seq', optional=True), primary_key=True), - Column("interval", Interval), - ) - metadata.create_all() - - def tearDown(self): - interval_table.delete().execute() - - def tearDownAll(self): - metadata.drop_all() - - def test_roundtrip(self): - delta = datetime.datetime(2006, 10, 5) - datetime.datetime(2005, 8, 17) - interval_table.insert().execute(interval=delta) - assert interval_table.select().execute().fetchone()['interval'] == delta - - def test_null(self): - interval_table.insert().execute(id=1, inverval=None) - assert interval_table.select().execute().fetchone()['interval'] is None - -class BooleanTest(TestBase, AssertsExecutionResults): - def setUpAll(self): - global bool_table - metadata = MetaData(testing.db) - bool_table = Table('booltest', metadata, - Column('id', Integer, primary_key=True), - Column('value', Boolean)) - bool_table.create() - def tearDownAll(self): - bool_table.drop() - def testbasic(self): - bool_table.insert().execute(id=1, value=True) - bool_table.insert().execute(id=2, value=False) - bool_table.insert().execute(id=3, value=True) - bool_table.insert().execute(id=4, value=True) - bool_table.insert().execute(id=5, value=True) - - res = bool_table.select(bool_table.c.value==True).execute().fetchall() - assert(res==[(1, True),(3, True),(4, True),(5, True)]) - - res2 = bool_table.select(bool_table.c.value==False).execute().fetchall() - assert(res2==[(2, False)]) - -class PickleTest(TestBase): - def test_noeq_deprecation(self): - p1 = PickleType() - - self.assertRaises(DeprecationWarning, - p1.compare_values, pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2) - ) - - self.assertRaises(DeprecationWarning, - p1.compare_values, pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2) - ) - - @testing.uses_deprecated() - def go(): - # test actual dumps comparison - assert p1.compare_values(pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2)) - assert p1.compare_values(pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2)) - go() - - assert p1.compare_values({1:2, 3:4}, {3:4, 1:2}) - - p2 = PickleType(mutable=False) - assert not p2.compare_values(pickleable.BarWithoutCompare(1, 2), pickleable.BarWithoutCompare(1, 2)) - assert not p2.compare_values(pickleable.OldSchoolWithoutCompare(1, 2), pickleable.OldSchoolWithoutCompare(1, 2)) - - def test_eq_comparison(self): - p1 = PickleType() - - for obj in ( - {'1':'2'}, - pickleable.Bar(5, 6), - pickleable.OldSchool(10, 11) - ): - assert p1.compare_values(p1.copy_value(obj), obj) - - self.assertRaises(NotImplementedError, p1.compare_values, pickleable.BrokenComparable('foo'),pickleable.BrokenComparable('foo')) - - def test_nonmutable_comparison(self): - p1 = PickleType() - - for obj in ( - {'1':'2'}, - pickleable.Bar(5, 6), - pickleable.OldSchool(10, 11) - ): - assert p1.compare_values(p1.copy_value(obj), obj) - -class CallableTest(TestBase): - def setUpAll(self): - global meta - meta = MetaData(testing.db) - - def tearDownAll(self): - meta.drop_all() - - def test_callable_as_arg(self): - ucode = util.partial(Unicode, assert_unicode=None) - - thing_table = Table('thing', meta, - Column('name', ucode(20)) - ) - assert isinstance(thing_table.c.name.type, Unicode) - thing_table.create() - - def test_callable_as_kwarg(self): - ucode = util.partial(Unicode, assert_unicode=None) - - thang_table = Table('thang', meta, - Column('name', type_=ucode(20), primary_key=True) - ) - assert isinstance(thang_table.c.name.type, Unicode) - thang_table.create() - -if __name__ == "__main__": - testenv.main() diff --git a/test/sql/unicode.py b/test/sql/unicode.py deleted file mode 100644 index c5002aaff..000000000 --- a/test/sql/unicode.py +++ /dev/null @@ -1,139 +0,0 @@ -# coding: utf-8 -"""verrrrry basic unicode column name testing""" - -import testenv; testenv.configure_for_tests() -from sqlalchemy import * -from testlib import * -from testlib.engines import utf8_engine -from sqlalchemy.sql import column - -class UnicodeSchemaTest(TestBase): - __requires__ = ('unicode_ddl',) - - def setUpAll(self): - global unicode_bind, metadata, t1, t2, t3 - - unicode_bind = utf8_engine() - - metadata = MetaData(unicode_bind) - t1 = Table('unitable1', metadata, - Column(u'méil', Integer, primary_key=True), - Column(u'\u6e2c\u8a66', Integer), - test_needs_fk=True, - ) - t2 = Table(u'Unitéble2', metadata, - Column(u'méil', Integer, primary_key=True, key="a"), - Column(u'\u6e2c\u8a66', Integer, ForeignKey(u'unitable1.méil'), - key="b" - ), - test_needs_fk=True, - ) - - # Few DBs support Unicode foreign keys - if testing.against('sqlite'): - t3 = Table(u'\u6e2c\u8a66', metadata, - Column(u'\u6e2c\u8a66_id', Integer, primary_key=True, - autoincrement=False), - Column(u'unitable1_\u6e2c\u8a66', Integer, - ForeignKey(u'unitable1.\u6e2c\u8a66') - ), - Column(u'Unitéble2_b', Integer, - ForeignKey(u'Unitéble2.b') - ), - Column(u'\u6e2c\u8a66_self', Integer, - ForeignKey(u'\u6e2c\u8a66.\u6e2c\u8a66_id') - ), - test_needs_fk=True, - ) - else: - t3 = Table(u'\u6e2c\u8a66', metadata, - Column(u'\u6e2c\u8a66_id', Integer, primary_key=True, - autoincrement=False), - Column(u'unitable1_\u6e2c\u8a66', Integer), - Column(u'Unitéble2_b', Integer), - Column(u'\u6e2c\u8a66_self', Integer), - test_needs_fk=True, - ) - metadata.create_all() - - def tearDown(self): - if metadata.tables: - t3.delete().execute() - t2.delete().execute() - t1.delete().execute() - - def tearDownAll(self): - global unicode_bind - metadata.drop_all() - del unicode_bind - - def test_insert(self): - t1.insert().execute({u'méil':1, u'\u6e2c\u8a66':5}) - t2.insert().execute({'a':1, 'b':1}) - t3.insert().execute({u'\u6e2c\u8a66_id': 1, - u'unitable1_\u6e2c\u8a66': 5, - u'Unitéble2_b': 1, - u'\u6e2c\u8a66_self': 1}) - - assert t1.select().execute().fetchall() == [(1, 5)] - assert t2.select().execute().fetchall() == [(1, 1)] - assert t3.select().execute().fetchall() == [(1, 5, 1, 1)] - - def test_reflect(self): - t1.insert().execute({u'méil':2, u'\u6e2c\u8a66':7}) - t2.insert().execute({'a':2, 'b':2}) - t3.insert().execute({u'\u6e2c\u8a66_id': 2, - u'unitable1_\u6e2c\u8a66': 7, - u'Unitéble2_b': 2, - u'\u6e2c\u8a66_self': 2}) - - meta = MetaData(unicode_bind) - tt1 = Table(t1.name, meta, autoload=True) - tt2 = Table(t2.name, meta, autoload=True) - tt3 = Table(t3.name, meta, autoload=True) - - tt1.insert().execute({u'méil':1, u'\u6e2c\u8a66':5}) - tt2.insert().execute({u'méil':1, u'\u6e2c\u8a66':1}) - tt3.insert().execute({u'\u6e2c\u8a66_id': 1, - u'unitable1_\u6e2c\u8a66': 5, - u'Unitéble2_b': 1, - u'\u6e2c\u8a66_self': 1}) - - self.assert_(tt1.select(order_by=desc(u'méil')).execute().fetchall() == - [(2, 7), (1, 5)]) - self.assert_(tt2.select(order_by=desc(u'méil')).execute().fetchall() == - [(2, 2), (1, 1)]) - self.assert_(tt3.select(order_by=desc(u'\u6e2c\u8a66_id')). - execute().fetchall() == - [(2, 7, 2, 2), (1, 5, 1, 1)]) - meta.drop_all() - metadata.create_all() - -class EscapesDefaultsTest(testing.TestBase): - def test_default_exec(self): - metadata = MetaData(testing.db) - t1 = Table('t1', metadata, - Column(u'special_col', Integer, Sequence('special_col'), primary_key=True), - Column('data', String(50)) # to appease SQLite without DEFAULT VALUES - ) - t1.create() - - try: - engine = metadata.bind - - # reset the identifier preparer, so that we can force it to cache - # a unicode identifier - engine.dialect.identifier_preparer = engine.dialect.preparer(engine.dialect) - select([column(u'special_col')]).select_from(t1).execute() - assert isinstance(engine.dialect.identifier_preparer.format_sequence(Sequence('special_col')), unicode) - - # now execute, run the sequence. it should run in u"Special_col.nextid" or similar as - # a unicode object; cx_oracle asserts that this is None or a String (postgres lets it pass thru). - # ensure that base.DefaultRunner is encoding. - t1.insert().execute(data='foo') - finally: - t1.drop() - - -if __name__ == '__main__': - testenv.main() -- cgit v1.2.1