to transient objects with attributes unset would leak NEVER_SET,
and negated_contains_or_equals would do so for any transient
object as the comparison used only the committed value.
Repaired the NEVER_SET cases, fixes#3371, and also made
negated_contains_or_equals() use state_attr_by_column() just
like a non-negated comparison, fixes#3374
in callcounts specific to the "expiration" of objects, as in
the "auto expire" feature of :meth:`.Session.commit` and
for :meth:`.Session.expire_all`, as well as in the "cleanup" step
which occurs when object states are garbage collected.
fixes#3307
as that of 🎫`3199`, when the ``named=True`` parameter
would be used. Some events would fail to register, and others
would not invoke the event arguments correctly, generally in the
case of when an event was "wrapped" for adaption in some other way.
The "named" mechanics have been rearranged to not interfere with
the argument signature expected by internal wrapper functions.
fixes#3197
and :meth:`.AttributeEvents.dispose_collection`, which track when
a collection is first associated with an instance and when it is
replaced. These handlers supersede the :meth:`.collection.linker`
annotation. The old hook remains supported through an event adapter.
when we do a get; we return the None as always but we leave the dict blank
and the loader callable still in place. The case for this implicit get on a pending object is not
super common and there really should be no change in state at all when this
operation proceeds. This change is more dramatic as it reverses
a behavior SQLA has had since the first release.
fixes#3061
implicitly initialized to None via first access; this action,
which has always resulted in a population of the attribute,
now emits an attribute event just like any other attribute set
operation and generates the same kind of history as one. Additionally,
many mapper internal operations will no longer implicitly generate
these "None" values when various never-set attributes are checked.
These are subtle behavioral fixes to attribute mechanics which provide
a better solution to the problem of 🎫`3060`, which also
involves recognition of attributes explicitly set to ``None``
vs. attributes that were never set.
fixes#3061
scenario, where an INSERT/DELETE can be turned into an UPDATE.
In this situation, a many-to-one relationship set to None, or
in some cases a scalar attribute set to None, may not be detected
as a net change in value, and therefore the UPDATE would not reset
what was on the previous row. This is due to some as-yet
unresovled side effects of the way attribute history works in terms
of implicitly assuming None isn't really a "change" for a previously
un-set attribute. See also 🎫`3061`. fixes#3060
attribute will now honor the "passive" flag
passed to it; as this defaults to ``PASSIVE_OFF``, the function will
by default query the database if the value is not present.
This is a behavioral change vs. 0.8. [ticket:2787]
- Added new method :meth:`.AttributeState.load_history`, works like
:attr:`.AttributeState.history` but also fires loader callables.
:class:`.AttributeImpl` as an "initiator" token has been changed;
the object is now an event-specific object called :class:`.attributes.Event`.
Additionally, the attribute system no longer halts events based
on a matching "initiator" token; this logic has been moved to be
specific to ORM backref event handlers, which are the typical source
of the re-propagation of an attribute event onto subsequent append/set/remove
operations. End user code which emulates the behavior of backrefs
must now ensure that recursive event propagation schemes are halted,
if the scheme does not use the backref handlers. Using this new system,
backref handlers can now peform a
"two-hop" operation when an object is appended to a collection,
associated with a new many-to-one, de-associated with the previous
many-to-one, and then removed from a previous collection. Before this
change, the last step of removal from the previous collection would
not occur.
[ticket:2789]
when loading mapped entities. The function overhead of applying
a per-object deferred callable to an instance at load time was
significantly higher than that of just loading the data from the row
(note that ``defer()`` is meant to reduce DB/network overhead, not
necessarily function call count); the function call overhead is now
less than that of loading data from the column in all cases. There
is also a reduction in the number of "lazy callable" objects created
per load from N (total deferred values in the result) to 1 (total
number of deferred cols).
[ticket:2778]
when an object we moved from "persistent" to "pending"
using the :func:`.make_transient` function, for operations
involving collection-based backrefs.
[ticket:2773]
mapper configuration; will now test for name conflicts on
superclasses and subclasses, in addition to the current mapper,
as these conflicts break things just as much. This is new for
0.8, but see below for a warning that will also be triggered
in 0.7.11.
- Improved the error message emitted when a "backref loop" is detected,
that is when an attribute event triggers a bidirectional
assignment between two other attributes with no end.
This condition can occur not just when an object of the wrong
type is assigned, but also when an attribute is mis-configured
to backref into an existing backref pair. Also in 0.7.11.
- A warning is emitted when a MapperProperty is assigned to a mapper
that replaces an existing property, if the properties in question
aren't plain column-based properties. Replacement of relationship
properties is rarely (ever?) what is intended and usually refers to a
mapper mis-configuration. Also in 0.7.11.
[ticket:2674]
on a never-set collection would fail; made this act just like
scalars for now and added tests. I would think that HISTORY_BLANK
would be more appropriate here but it's too late in the game
to mess with that.
become an externally usable package but still remains within the main sqlalchemy parent package.
in this system, we use kind of an ugly hack to get the noseplugin imported outside of the
"sqlalchemy" package, while still making it available within sqlalchemy for usage by
third party libraries.
now generates attribute events. Previously, a None
append would be ignored in some cases. Related
to [ticket:2229].
- [feature] The presence of None in a mapped collection
now raises an error during flush. Previously,
None values in collections would be silently ignored.
[ticket:2229]
assignment could cause recursion overflow if the
assignment triggered a backref of the same name
as a bi-directional attribute on the incorrect
class to the same target. An informative
error is raised now.
to an instrumented collection is no longer
associated with the parent class due to
expiration/attribute refresh/collection
replacement, but an append
or remove operation is received on the
now-detached collection. [ticket:2476]
ORM, including the MutableType class as well
as the mutable=True flag on PickleType
and postgresql.ARRAY has been removed.
In-place mutations are detected by the ORM
using the sqlalchemy.ext.mutable extension,
introduced in 0.7. The removal of MutableType
and associated constructs removes a great
deal of complexity from SQLAlchemy's internals.
The approach performed poorly as it would incur
a scan of the full contents of the Session
when in use. [ticket:2442]
- remove() on a scalar object will raise if the object
removed is not what was present.
- InstanceState can be pickled if obj() is None; this
to support the other changes in this commit
- only use trackparent flag on attributes if
single_parent or ONETOMANY; otherwise we can skip this
overhead
- attribute hasparent()/sethasparent() check that trackparent
is set, else their usage is invalid
- [bug] Fixed backref behavior when "popping" the
value off of a many-to-one in response to
a removal from a stale one-to-many - the operation
is skipped, since the many-to-one has since
been updated. [ticket:2315]
- [bug] After some years of not doing this, added
more granularity to the "is X a parent of Y"
functionality, which is used when determining
if the FK on "Y" needs to be "nulled out" as well
as if "Y" should be deleted with delete-orphan
cascade. The test now takes into account the
Python identity of the parent as well its identity
key, to see if the last known parent of Y is
definitely X. If a decision
can't be made, a StaleDataError is raised. The
conditions where this error is raised are fairly
rare, requiring that the previous parent was
garbage collected, and previously
could very well inappropriately update/delete
a record that's since moved onto a new parent,
though there may be some cases where
"silent success" occurred previously that will now
raise in the face of ambiguity.
Expiring "Y" resets the "parent" tracker, meaning
X.remove(Y) could then end up deleting Y even
if X is stale, but this is the same behavior
as before; it's advised to expire X also in that
case. [ticket:2264]
operation on some relationship() based attributes
would fail when a lazyload would emit; this could
trigger within a flush() under certain conditions.
[ticket:2224] Thanks to the user who submitted
the great test for this.
condition of being asked to UPDATE or DELETE
on a primary key value that contains NULL
in it. [ticket:2127]
- Some refinements to attribute history. More
changes are pending possibly in 0.8, but
for now history has been modified such that
scalar history doesn't have a "side effect"
of populating None for a non-present value.
This allows a slightly better ability to
distinguish between a None set and no actual
change, affects [ticket:2127] as well.
- rewriting the history tests in test_attributes to be
individual per operation/assertion. its a huge job
so this is partial for the moment.
backrefs, typically when autoflush=False, where
the back-referenced collection wouldn't
properly handle add/removes with no net
change. Thanks to Richard Murri for the
test case + patch. [ticket:2123]
(also in 0.6.7).
append or similar event on a collection occurs after
the parent object has been dereferenced, which
prevents the parent from being marked as "dirty"
in the session. Will commit as a warning in 0.6.
[ticket:2046]
these are back to being part of LoaderStrategy
- simplify attribute.get()
- inline the dict get inside of attribute.__get__()
- revert some memoized attrs from InstanceState which are called in almost
all cases regardless
- inlining
outside of "sqlalchemy" and under "test/".
Rationale:
- coverage plugin works without issue, without need for an awkward
additional package install
- command line for "nosetests" isn't polluted with SQLAlchemy options
[ticket:1949]