diff options
author | mike bayer <mike_mp@zzzcomputing.com> | 2013-06-23 08:31:57 -0700 |
---|---|---|
committer | mike bayer <mike_mp@zzzcomputing.com> | 2013-06-23 08:31:57 -0700 |
commit | 907b5f72dd784f737eda0c6698732de96763a170 (patch) | |
tree | f5a35cabd718917a60fb131ea7c846d74639cfb0 | |
parent | c890ce89133675c2ba90bdcf5cbf9f3a0a0a1337 (diff) | |
parent | 3a80bf0d504ccc1e198249be27d87a6045c39ee4 (diff) | |
download | sqlalchemy-907b5f72dd784f737eda0c6698732de96763a170.tar.gz |
Merge pull request #11 from malor/fix_uc_reflection
Fix unique constraints reflection in SQLite and PostgreSQL
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 25 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/suite/test_reflection.py | 11 |
3 files changed, 26 insertions, 13 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 16ace0583..9d89fe160 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -205,6 +205,7 @@ underlying CREATE INDEX command, so it *must* be a valid index type for your version of PostgreSQL. """ +from collections import defaultdict import re from ... import sql, schema, exc, util @@ -2010,25 +2011,31 @@ class PGDialect(default.DefaultDialect): UNIQUE_SQL = """ SELECT cons.conname as name, - ARRAY_AGG(a.attname) as column_names + cons.conkey as key, + a.attnum as col_num, + a.attname as col_name FROM pg_catalog.pg_constraint cons - left outer join pg_attribute a - on cons.conrelid = a.attrelid and a.attnum = ANY(cons.conkey) + join pg_attribute a + on cons.conrelid = a.attrelid AND a.attnum = ANY(cons.conkey) WHERE cons.conrelid = :table_oid AND cons.contype = 'u' - GROUP BY - cons.conname """ - t = sql.text(UNIQUE_SQL, - typemap={'column_names': ARRAY(sqltypes.Unicode)}) + t = sql.text(UNIQUE_SQL, typemap={'col_name': sqltypes.Unicode}) c = connection.execute(t, table_oid=table_oid) + uniques = defaultdict(lambda: defaultdict(dict)) + for row in c.fetchall(): + uc = uniques[row.name] + uc["key"] = row.key + uc["cols"][row.col_num] = row.col_name + return [ - {'name': row.name, 'column_names': row.column_names} - for row in c.fetchall() + {'name': name, + 'column_names': [uc["cols"][i] for i in uc["key"]]} + for name, uc in uniques.items() ] def _load_enums(self, connection): diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 3e2a158a0..787fdec17 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -933,7 +933,8 @@ class SQLiteDialect(default.DefaultDialect): UNIQUE_PATTERN = 'CONSTRAINT (\w+) UNIQUE \(([^\)]+)\)' return [ - {'name': name, 'column_names': [c.strip() for c in cols.split(',')]} + {'name': name, + 'column_names': [col.strip(' "') for col in cols.split(',')]} for name, cols in re.findall(UNIQUE_PATTERN, table_data) ] diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index 858ca8a77..255fd5595 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -370,9 +370,10 @@ class ComponentReflectionTest(fixtures.TablesTest): def _test_get_unique_constraints(self, schema=None): uniques = sorted( [ + {'name': 'unique_a', 'column_names': ['a']}, {'name': 'unique_a_b_c', 'column_names': ['a', 'b', 'c']}, - {'name': 'unique_a_c', 'column_names': ['a', 'c']}, - {'name': 'unique_b_c', 'column_names': ['b', 'c']}, + {'name': 'unique_c_a_b', 'column_names': ['c', 'a', 'b']}, + {'name': 'unique_asc_key', 'column_names': ['asc', 'key']}, ], key=operator.itemgetter('name') ) @@ -382,6 +383,9 @@ class ComponentReflectionTest(fixtures.TablesTest): Column('a', sa.String(20)), Column('b', sa.String(30)), Column('c', sa.Integer), + # reserved identifiers + Column('asc', sa.String(30)), + Column('key', sa.String(30)), schema=schema ) for uc in uniques: @@ -396,7 +400,8 @@ class ComponentReflectionTest(fixtures.TablesTest): key=operator.itemgetter('name') ) - eq_(uniques, reflected) + for orig, refl in zip(uniques, reflected): + eq_(orig, refl) @testing.provide_metadata |