From 77d6d31542ab2365f218d7305309d6c3468a36dc Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 1 Mar 2009 20:24:02 +0000 Subject: [PATCH] - Added PGUuid and PGBit types to sqlalchemy.databases.postgres. [ticket:1327] - Refection of unknown PG types won't crash when those types are specified within a domain. [ticket:1327] - executemany() in conjunction with INSERT..RETURNING is documented as undefined by psycopg2. --- CHANGES | 6 +++ lib/sqlalchemy/databases/postgres.py | 13 +++++- test/dialect/postgres.py | 63 +++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index f6fc99271b..f47e3ebcb8 100644 --- a/CHANGES +++ b/CHANGES @@ -89,6 +89,12 @@ CHANGES - postgres - Index reflection won't fail when an index with multiple expressions is encountered. + + - Added PGUuid and PGBit types to + sqlalchemy.databases.postgres. [ticket:1327] + + - Refection of unknown PG types won't crash when those + types are specified within a domain. [ticket:1327] - mssql - Preliminary support for pymssql 1.0.1 diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index 8caadfaf8b..038a9e8df4 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -193,6 +193,14 @@ class PGBoolean(sqltypes.Boolean): def get_col_spec(self): return "BOOLEAN" +class PGBit(sqltypes.TypeEngine): + def get_col_spec(self): + return "BIT" + +class PGUuid(sqltypes.TypeEngine): + def get_col_spec(self): + return "UUID" + class PGArray(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine): def __init__(self, item_type, mutable=True): if isinstance(item_type, type): @@ -283,6 +291,8 @@ ischema_names = { 'real' : PGFloat, 'inet': PGInet, 'cidr': PGCidr, + 'uuid':PGUuid, + 'bit':PGBit, 'macaddr': PGMacAddr, 'double precision' : PGFloat, 'timestamp' : PGDateTime, @@ -527,6 +537,7 @@ class PGDialect(default.DefaultDialect): elif attype == 'timestamp without time zone': kwargs['timezone'] = False + coltype = None if attype in ischema_names: coltype = ischema_names[attype] else: @@ -540,8 +551,6 @@ class PGDialect(default.DefaultDialect): # It can, however, override the default value, but can't set it to null. default = domain['default'] coltype = ischema_names[domain['attype']] - else: - coltype = None if coltype: coltype = coltype(*args, **kwargs) diff --git a/test/dialect/postgres.py b/test/dialect/postgres.py index 4b1ab91788..3867d1b019 100644 --- a/test/dialect/postgres.py +++ b/test/dialect/postgres.py @@ -95,11 +95,15 @@ class ReturningTest(TestBase, AssertsExecutionResults): self.assertEqual(result.fetchall(), [(1,)]) - # Multiple inserts only return the last row - result2 = table.insert(postgres_returning=[table]).execute( - [{'persons': 2, 'full': False}, {'persons': 3, 'full': True}]) - self.assertEqual(result2.fetchall(), [(3,3,True)]) - + @testing.fails_on('postgres', 'Known limitation of psycopg2') + def test_executemany(): + # return value is documented as failing with psycopg2/executemany + result2 = table.insert(postgres_returning=[table]).execute( + [{'persons': 2, 'full': False}, {'persons': 3, 'full': True}]) + self.assertEqual(result2.fetchall(), [(2, 2, False), (3,3,True)]) + + test_executemany() + result3 = table.insert(postgres_returning=[(table.c.id*2).label('double_id')]).execute({'persons': 4, 'full': False}) self.assertEqual([dict(row) for row in result3], [{'double_id':8}]) @@ -432,6 +436,25 @@ class DomainReflectionTest(TestBase, AssertsExecutionResults): self.assertEquals(str(table.columns.answer.server_default.arg), '0', "Reflected default value didn't equal expected value") self.assertTrue(table.columns.answer.nullable, "Expected reflected column to be nullable.") + def test_unknown_types(self): + from sqlalchemy.databases import postgres + + ischema_names = postgres.ischema_names + postgres.ischema_names = {} + try: + m2 = MetaData(testing.db) + self.assertRaises(exc.SAWarning, Table, "testtable", m2, autoload=True) + + @testing.emits_warning('Did not recognize type') + def warns(): + m3 = MetaData(testing.db) + t3 = Table("testtable", m3, autoload=True) + assert t3.c.answer.type.__class__ == sa.types.NullType + + finally: + postgres.ischema_names = ischema_names + + class MiscTest(TestBase, AssertsExecutionResults): __only_on__ = 'postgres' @@ -878,6 +901,36 @@ class ServerSideCursorsTest(TestBase, AssertsExecutionResults): finally: test_table.drop(checkfirst=True) +class SpecialTypesTest(TestBase, ComparesTables): + """test DDL and reflection of PG-specific types """ + + __only_on__ = 'postgres' + __excluded_on__ = (('postgres', '<', (8, 3, 0)),) + + def setUpAll(self): + global metadata, table + metadata = MetaData(testing.db) + + table = Table('sometable', metadata, + Column('id', postgres.PGUuid, primary_key=True), + Column('flag', postgres.PGBit), + Column('addr', postgres.PGInet), + Column('addr2', postgres.PGMacAddr), + Column('addr3', postgres.PGCidr) + ) + + metadata.create_all() + + def tearDownAll(self): + metadata.drop_all() + + def test_reflection(self): + m = MetaData(testing.db) + t = Table('sometable', m, autoload=True) + + self.assert_tables_equal(table, t) + + class MatchTest(TestBase, AssertsCompiledSQL): __only_on__ = 'postgres' __excluded_on__ = (('postgres', '<', (8, 3, 0)),) -- 2.47.2