From: Mike Bayer Date: Thu, 18 Nov 2010 23:44:35 +0000 (-0500) Subject: - Added as_uuid=True flag to the UUID type, will receive X-Git-Tag: rel_0_7b1~258 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2336b1cebfcb2f304e09cbc2a0e8bb3fb3a9ceeb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Added as_uuid=True flag to the UUID type, will receive and return values as Python UUID() objects rather than strings. Currently, the UUID type is only known to work with psycopg2. [ticket:1956] --- diff --git a/CHANGES b/CHANGES index 4f65434559..86fac65a2a 100644 --- a/CHANGES +++ b/CHANGES @@ -57,7 +57,12 @@ CHANGES - Ensured every numeric, float, int code, scalar + array, are recognized by psycopg2 and pg8000's "numeric" base type. [ticket:1955] - + + - Added as_uuid=True flag to the UUID type, will receive + and return values as Python UUID() objects rather than + strings. Currently, the UUID type is only known to + work with psycopg2. [ticket:1956] + - mysql - Fixed error handling for Jython + zxjdbc, such that has_table() property works again. Regression from diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 6eec651b1b..7b1a97c325 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -94,6 +94,11 @@ from sqlalchemy.sql import compiler, expression, util as sql_util from sqlalchemy.sql import operators as sql_operators from sqlalchemy import types as sqltypes +try: + from uuid import UUID as _python_UUID +except ImportError: + _python_UUID = None + from sqlalchemy.types import INTEGER, BIGINT, SMALLINT, VARCHAR, \ CHAR, TEXT, FLOAT, NUMERIC, \ DATE, BOOLEAN @@ -134,6 +139,12 @@ class TIME(sqltypes.TIME): self.precision = precision class INTERVAL(sqltypes.TypeEngine): + """Postgresql INTERVAL type. + + The INTERVAL type may not be supported on all DBAPIs. + It is known to work on psycopg2 and not pg8000 or zxjdbc. + + """ __visit_name__ = 'INTERVAL' def __init__(self, precision=None): self.precision = precision @@ -156,17 +167,67 @@ class BIT(sqltypes.TypeEngine): PGBit = BIT class UUID(sqltypes.TypeEngine): + """Postgresql UUID type. + + Represents the UUID column type, interpreting + data either as natively returned by the DBAPI + or as Python uuid objects. + + The UUID type may not be supported on all DBAPIs. + It is known to work on psycopg2 and not pg8000. + + """ __visit_name__ = 'UUID' + + def __init__(self, as_uuid=False): + """Construct a UUID type. + + + :param as_uuid=False: if True, values will be interpreted + as Python uuid objects, converting to/from string via the + DBAPI. + + """ + if as_uuid and _python_UUID is None: + raise NotImplementedError( + "This version of Python does not support the native UUID type." + ) + self.as_uuid = as_uuid + + def bind_processor(self, dialect): + if self.as_uuid: + def process(value): + if value is not None: + value = str(value) + return value + return process + else: + return None + + def result_processor(self, dialect, coltype): + if self.as_uuid: + def process(value): + if value is not None: + value = _python_UUID(value) + return value + return process + else: + return None + PGUuid = UUID class ARRAY(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine): """Postgresql ARRAY type. Represents values as Python lists. + + The ARRAY type may not be supported on all DBAPIs. + It is known to work on psycopg2 and not pg8000. **Note:** be sure to read the notes for - :class:`~sqlalchemy.types.MutableType` regarding ORM - performance implications. + :class:`.MutableType` regarding ORM + performance implications. The :class:`.ARRAY` type's + mutability can be disabled using the "mutable" flag. """ __visit_name__ = 'ARRAY' diff --git a/test/dialect/test_postgresql.py b/test/dialect/test_postgresql.py index 54127cd986..f3eb91ef26 100644 --- a/test/dialect/test_postgresql.py +++ b/test/dialect/test_postgresql.py @@ -1884,6 +1884,64 @@ class SpecialTypesTest(TestBase, ComparesTables): assert t.c.plain_interval.type.precision is None assert t.c.precision_interval.type.precision == 3 +class UUIDTest(TestBase): + """Test the bind/return values of the UUID type.""" + + __only_on__ = 'postgresql' + + @testing.fails_on('postgresql+pg8000', 'No support for UUID type') + def test_uuid_string(self): + import uuid + self._test_round_trip( + Table('utable', MetaData(), + Column('data', postgresql.UUID()) + ), + str(uuid.uuid4()), + str(uuid.uuid4()) + ) + + @testing.fails_on('postgresql+pg8000', 'No support for UUID type') + def test_uuid_uuid(self): + import uuid + self._test_round_trip( + Table('utable', MetaData(), + Column('data', postgresql.UUID(as_uuid=True)) + ), + uuid.uuid4(), + uuid.uuid4() + ) + + def test_no_uuid_available(self): + from sqlalchemy.dialects.postgresql import base + uuid_type = base._python_UUID + base._python_UUID = None + try: + assert_raises( + NotImplementedError, + postgresql.UUID, as_uuid=True + ) + finally: + base._python_UUID = uuid_type + + def setup(self): + self.conn = testing.db.connect() + trans = self.conn.begin() + + def teardown(self): + self.conn.close() + + def _test_round_trip(self, utable, value1, value2): + utable.create(self.conn) + self.conn.execute(utable.insert(), {'data':value1}) + self.conn.execute(utable.insert(), {'data':value2}) + r = self.conn.execute( + select([utable.c.data]). + where(utable.c.data != value1) + ) + eq_(r.fetchone()[0], value2) + eq_(r.fetchone(), None) + + class MatchTest(TestBase, AssertsCompiledSQL): __only_on__ = 'postgresql'