From: Mike Bayer Date: Mon, 1 Oct 2012 05:59:59 +0000 (-0400) Subject: - fix the fixture here that wasn't creating consistently X-Git-Tag: rel_0_8_0b1~87 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0bb5a9eab829f9a4cfda3c37cdf2202d84e55f3f;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - fix the fixture here that wasn't creating consistently - rewrite --dropfirst to be more industrial strength, includes views - fix order_by="foreign_key" to maintain the same ordering as metadata.sorted_tables. Not ideal that this was the other way throughout 0.7 but this is still a little-used method, in contrast to metadata.sorted_tables. --- diff --git a/CHANGES b/CHANGES index f546bef60a..470c9f96f2 100644 --- a/CHANGES +++ b/CHANGES @@ -364,6 +364,11 @@ underneath "0.7.xx". where specific capabilities and features can be enabled or disabled for testing. + - [bug] The Inspector.get_table_names() + order_by="foreign_key" feature now sorts + tables by dependee first, to be consistent + with util.sort_tables and metadata.sorted_tables. + - [bug] Fixed bug whereby if a database restart affected multiple connections, each connection would individually invoke a new diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py index 6373e8f103..4505aa18a1 100644 --- a/lib/sqlalchemy/engine/reflection.py +++ b/lib/sqlalchemy/engine/reflection.py @@ -151,14 +151,32 @@ class Inspector(object): return [] def get_table_names(self, schema=None, order_by=None): - """Return all table names in `schema`. + """Return all table names in referred to within a particular schema. + + The names are expected to be real tables only, not views. + Views are instead returned using the :meth:`.get_view_names` + method. + + + :param schema: Schema name. If ``schema`` is left at ``None``, the + database's default schema is + used, else the named schema is searched. If the database does not + support named schemas, behavior is undefined if ``schema`` is not + passed as ``None``. - :param schema: Optional, retrieve names from a non-default schema. :param order_by: Optional, may be the string "foreign_key" to sort - the result on foreign key dependencies. + the result on foreign key dependencies. + + .. versionchanged:: 0.8 the "foreign_key" sorting sorts tables + in order of dependee to dependent; that is, in creation + order, rather than in drop order. This is to maintain + consistency with similar features such as + :attr:`.MetaData.sorted_tables` and :func:`.util.sort_tables`. + + .. seealso:: + + :attr:`.MetaData.sorted_tables` - This should probably not return view names or maybe it should return - them with an indicator t or v. """ if hasattr(self.dialect, 'get_table_names'): @@ -167,14 +185,11 @@ class Inspector(object): else: tnames = self.engine.table_names(schema) if order_by == 'foreign_key': - import random - random.shuffle(tnames) - tuples = [] for tname in tnames: for fkey in self.get_foreign_keys(tname, schema): if tname != fkey['referred_table']: - tuples.append((tname, fkey['referred_table'])) + tuples.append((fkey['referred_table'], tname)) tnames = list(topological.sort(tuples, tnames)) return tnames diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index d7720a8676..859ccd99b1 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -2536,8 +2536,18 @@ class MetaData(SchemaItem): @property def sorted_tables(self): - """Returns a list of ``Table`` objects sorted in order of - dependency. + """Returns a list of :class:`.Table` objects sorted in order of + foreign key dependency. + + The sorting will place :class:`.Table` objects that have dependencies + first, before the dependencies themselves, representing the + order in which they can be created. To get the order in which + the tables would be dropped, use the ``reversed()`` Python built-in. + + .. seealso:: + + :meth:`.Inspector.sorted_tables` + """ return sqlutil.sort_tables(self.tables.itervalues()) @@ -3220,6 +3230,16 @@ class CreateTable(_CreateDropBase): for column in element.columns ] + +class _DropView(_CreateDropBase): + """Semi-public 'DROP VIEW' construct. + + Used by the test suite for dialect-agnostic drops of views. + This object will eventually be part of a public "view" API. + + """ + __visit_name__ = "drop_view" + class CreateColumn(visitors.Visitable): """Represent a :class:`.Column` as rendered in a CREATE TABLE statement, via the :class:`.CreateTable` construct. diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index bb1d757bec..d3a4a64a20 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -1854,6 +1854,9 @@ class DDLCompiler(engine.Compiled): def visit_drop_table(self, drop): return "\nDROP TABLE " + self.preparer.format_table(drop.element) + def visit_drop_view(self, drop): + return "\nDROP VIEW " + self.preparer.format_table(drop.element) + def _index_identifier(self, ident): if isinstance(ident, sql._truncated_label): max = self.dialect.max_index_name_length or \ diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 70ea4c751a..28c13398f4 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -13,7 +13,8 @@ from collections import deque """Utility functions that build upon SQL and Schema constructs.""" def sort_tables(tables, skip_fn=None): - """sort a collection of Table objects in order of their foreign-key dependency.""" + """sort a collection of Table objects in order of + their foreign-key dependency.""" tables = list(tables) tuples = [] @@ -30,8 +31,8 @@ def sort_tables(tables, skip_fn=None): for table in tables: visitors.traverse(table, - {'schema_visitor':True}, - {'foreign_key':visit_foreign_key}) + {'schema_visitor': True}, + {'foreign_key': visit_foreign_key}) tuples.extend( [parent, table] for parent in table._extra_dependencies diff --git a/lib/sqlalchemy/testing/plugin/noseplugin.py b/lib/sqlalchemy/testing/plugin/noseplugin.py index cf132198f8..1034749e70 100644 --- a/lib/sqlalchemy/testing/plugin/noseplugin.py +++ b/lib/sqlalchemy/testing/plugin/noseplugin.py @@ -148,22 +148,29 @@ def _create_testing_engine(options, file_config): @post def _prep_testing_database(options, file_config): from sqlalchemy.testing import engines - from sqlalchemy import schema + from sqlalchemy import schema, inspect # also create alt schemas etc. here? if options.dropfirst: e = engines.utf8_engine() - existing = e.table_names() - if existing: - print "Dropping existing tables in database: " + db_url - try: - print "Tables: %s" % ', '.join(existing) - except: - pass - print "Abort within 5 seconds..." - time.sleep(5) - md = schema.MetaData(e, reflect=True) - md.drop_all() + inspector = inspect(e) + + for vname in inspector.get_view_names(): + e.execute(schema._DropView(schema.Table(vname, schema.MetaData()))) + + for vname in inspector.get_view_names(schema="test_schema"): + e.execute(schema._DropView( + schema.Table(vname, + schema.MetaData(), schema="test_schema"))) + + for tname in reversed(inspector.get_table_names(order_by="foreign_key")): + e.execute(schema.DropTable(schema.Table(tname, schema.MetaData()))) + + for tname in reversed(inspector.get_table_names( + order_by="foreign_key", schema="test_schema")): + e.execute(schema.DropTable( + schema.Table(tname, schema.MetaData(), schema="test_schema"))) + e.dispose() diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index a02ee479e9..6d36e4e568 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -116,7 +116,7 @@ class ComponentReflectionTest(fixtures.TablesTest): ) cls.define_index(metadata, users) - cls.define_views(metadata, schema=None) + cls.define_views(metadata, schema) @classmethod def define_index(cls, metadata, users): @@ -131,6 +131,7 @@ class ComponentReflectionTest(fixtures.TablesTest): view_name = fullname + '_v' query = "CREATE VIEW %s AS SELECT * FROM %s" % ( view_name, fullname) + event.listen( metadata, "after_create", @@ -173,7 +174,7 @@ class ComponentReflectionTest(fixtures.TablesTest): table_names = insp.get_table_names(schema, order_by=order_by) if order_by == 'foreign_key': - answer = ['dingalings', 'email_addresses', 'users'] + answer = ['users', 'email_addresses', 'dingalings'] eq_(table_names, answer) else: answer = ['dingalings', 'email_addresses', 'users']