diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-04-01 13:37:35 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-04-01 13:37:35 -0400 |
| commit | 82b6e074920cb972a569db4d2d395c8949868a31 (patch) | |
| tree | d18df886d56a5c9873011bd3b4c84f057ee6bf8a /lib/sqlalchemy/orm | |
| parent | 7dff6f6d490528f5e88493cdf4b14d3136b40d3c (diff) | |
| download | sqlalchemy-82b6e074920cb972a569db4d2d395c8949868a31.tar.gz | |
- Fixed bug in unit of work whereby a joined-inheritance
subclass could insert the row for the "sub" table
before the parent table, if the two tables had no
ForeignKey constraints set up between them.
Also in 0.7.11. [ticket:2689]
- fix a glitch in the assertsql.CompiledSQL fixture regarding
when a multiparam compiledSQL is used within an AllOf
- add a new utility function randomize_unitofwork() which
does the function of --reversetop
Diffstat (limited to 'lib/sqlalchemy/orm')
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 16 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/util.py | 37 |
2 files changed, 51 insertions, 2 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 2d7f62153..d258a20b6 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2002,10 +2002,20 @@ class Mapper(_InspectionAttr): @_memoized_configured_property def _sorted_tables(self): table_to_mapper = {} + for mapper in self.base_mapper.self_and_descendants: for t in mapper.tables: table_to_mapper.setdefault(t, mapper) + extra_dependencies = [] + for table, mapper in table_to_mapper.items(): + super_ = mapper.inherits + if super_: + extra_dependencies.extend([ + (super_table, table) + for super_table in super_.tables + ]) + def skip(fk): # attempt to skip dependencies that are not # significant to the inheritance chain @@ -2017,7 +2027,7 @@ class Mapper(_InspectionAttr): if parent is not None and \ dep is not None and \ dep is not parent and \ - dep.inherit_condition is not None: + dep.inherit_condition is not None: cols = set(sql_util.find_columns(dep.inherit_condition)) if parent.inherit_condition is not None: cols = cols.union(sql_util.find_columns( @@ -2028,7 +2038,9 @@ class Mapper(_InspectionAttr): return False sorted_ = sql_util.sort_tables(table_to_mapper.iterkeys(), - skip_fn=skip) + skip_fn=skip, + extra_dependencies=extra_dependencies) + ret = util.OrderedDict() for t in sorted_: ret[t] = table_to_mapper[t] diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index cc9dd6ba5..f3b8e271d 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -1265,3 +1265,40 @@ def attribute_str(instance, attribute): def state_attribute_str(state, attribute): return state_str(state) + "." + attribute + + +def randomize_unitofwork(): + """Use random-ordering sets within the unit of work in order + to detect unit of work sorting issues. + + This is a utility function that can be used to help reproduce + inconsistent unit of work sorting issues. For example, + if two kinds of objects A and B are being inserted, and + B has a foreign key reference to A - the A must be inserted first. + However, if there is no relationship between A and B, the unit of work + won't know to perform this sorting, and an operation may or may not + fail, depending on how the ordering works out. Since Python sets + and dictionaries have non-deterministic ordering, such an issue may + occur on some runs and not on others, and in practice it tends to + have a great dependence on the state of the interpreter. This leads + to so-called "heisenbugs" where changing entirely irrelevant aspects + of the test program still cause the failure behavior to change. + + By calling ``randomize_unitofwork()`` when a script first runs, the + ordering of a key series of sets within the unit of work implementation + are randomized, so that the script can be minimized down to the fundamental + mapping and operation that's failing, while still reproducing the issue + on at least some runs. + + This utility is also available when running the test suite via the + ``--reversetop`` flag. + + .. versionadded:: 0.8.1 created a standalone version of the + ``--reversetop`` feature. + + """ + from sqlalchemy.orm import unitofwork, session, mapper, dependency + from sqlalchemy.util import topological + from sqlalchemy.testing.util import RandomSet + topological.set = unitofwork.set = session.set = mapper.set = \ + dependency.set = RandomSet |
