From 5d741c16662cde063e6930e7d3fe87b00ae9b8bb Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 28 May 2014 19:56:19 -0700 Subject: [PATCH] Various fixes for bugs found in use of psycopg2. --- sqlalchemy_utils/functions/database.py | 29 ++++++++++++++++++++++---- sqlalchemy_utils/types/password.py | 2 +- sqlalchemy_utils/types/uuid.py | 9 ++++---- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/sqlalchemy_utils/functions/database.py b/sqlalchemy_utils/functions/database.py index 5986cc3..0cfbc6a 100644 --- a/sqlalchemy_utils/functions/database.py +++ b/sqlalchemy_utils/functions/database.py @@ -76,7 +76,10 @@ def database_exists(url): url = copy(make_url(url)) database = url.database - url.database = None + if url.drivername.startswith('postgresql'): + url.database = 'template1' + else: + url.database = None engine = sa.create_engine(url) @@ -126,7 +129,10 @@ def create_database(url, encoding='utf8'): url = copy(make_url(url)) database = url.database - if not url.drivername.startswith('sqlite'): + + if url.drivername.startswith('postgresql'): + url.database = 'template1' + elif not url.drivername.startswith('sqlite'): url.database = None engine = sa.create_engine(url) @@ -137,7 +143,8 @@ def create_database(url, encoding='utf8'): engine.raw_connection().set_isolation_level( ISOLATION_LEVEL_AUTOCOMMIT) - text = "CREATE DATABASE %s ENCODING = '%s'" % (database, encoding) + text = "CREATE DATABASE %s ENCODING '%s' TEMPLATE template0" % ( + database, encoding) engine.execute(text) elif engine.dialect.name == 'mysql': @@ -168,7 +175,10 @@ def drop_database(url): url = copy(make_url(url)) database = url.database - if not url.drivername.startswith('sqlite'): + + if url.drivername.startswith('postgresql'): + url.database = 'template1' + elif not url.drivername.startswith('sqlite'): url.database = None engine = sa.create_engine(url) @@ -179,6 +189,17 @@ def drop_database(url): elif engine.dialect.name == 'postgresql' and engine.driver == 'psycopg2': from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT engine.raw_connection().set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) + + # Disconnect all users from the database we are dropping. + text = ''' + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '%s' + AND pid <> pg_backend_pid() + ''' % database + engine.execute(text) + + # Drop the database. text = "DROP DATABASE %s" % database engine.execute(text) diff --git a/sqlalchemy_utils/types/password.py b/sqlalchemy_utils/types/password.py index 6820d24..d8034e4 100644 --- a/sqlalchemy_utils/types/password.py +++ b/sqlalchemy_utils/types/password.py @@ -143,7 +143,7 @@ class PasswordType(types.TypeDecorator, ScalarCoercible): scheme = getattr(__import__('passlib.hash').hash, name) length = 4 + len(scheme.name) length += len(str(getattr(scheme, 'max_rounds', ''))) - length += getattr(scheme, 'max_salt_size', 0) + length += (getattr(scheme, 'max_salt_size', 0) or 0) length += getattr( scheme, 'encoded_checksum_size', diff --git a/sqlalchemy_utils/types/uuid.py b/sqlalchemy_utils/types/uuid.py index 331eda6..59d29e3 100644 --- a/sqlalchemy_utils/types/uuid.py +++ b/sqlalchemy_utils/types/uuid.py @@ -25,14 +25,15 @@ class UUIDType(types.TypeDecorator, ScalarCoercible): python_type = uuid.UUID - def __init__(self, binary=True): + def __init__(self, binary=True, native=True): """ :param binary: Whether to use a BINARY(16) or CHAR(32) fallback. """ self.binary = binary + self.native = native def load_dialect_impl(self, dialect): - if dialect.name == 'postgresql': + if dialect.name == 'postgresql' and self.native: # Use the native UUID type. return dialect.type_descriptor(postgresql.UUID()) @@ -59,7 +60,7 @@ class UUIDType(types.TypeDecorator, ScalarCoercible): if not isinstance(value, uuid.UUID): value = self._coerce(value) - if dialect.name == 'postgresql': + if self.native and dialect.name == 'postgresql': return str(value) return value.bytes if self.binary else value.hex @@ -68,7 +69,7 @@ class UUIDType(types.TypeDecorator, ScalarCoercible): if value is None: return value - if dialect.name == 'postgresql': + if self.native and dialect.name == 'postgresql': return uuid.UUID(value) return uuid.UUID(bytes=value) if self.binary else uuid.UUID(value)