a very brief example of this, which if you keep digging in you can see how
tricky it gets (fast), is like:
from sqlalchemy.sql import column, table
t1 = table('t1', column('x'), column('y'))
t2 = table('t1', column('x'), column('y'))
t3 = table('t2', column('p'), column('r'))
t4 = table('t2', column('r'), column('p'))
assert t1._hash == t2._hash
assert t3._hash != t4._hash
the patch to produce the above is below. Note that Table/Column are easier to
hash than table()/column(), since we treat the upper class versions as
singletons. There is a lot more state that needs to be taken into account
though, like the _annotations dictionary on every ClauseElement. In the case
where an element doesn't define a fixed _hash, the usage of a new instance of
that element in an ad-hoc Query means that whole Query can't be cached, because
the element would have a different "id" each time (though dangerously, that
id() can be reused when the original is garbage collected...that's an issue
actually, we might instead need to use a counter for that case).
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 5820cb1..d5de299 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -1669,6 +1669,7 @@ class ClauseElement(Visitable):
"""
c = self.__class__.__new__(self.__class__)
c.__dict__ = self.__dict__.copy()
+ c.__dict__.pop('_hash', None)
ClauseElement._cloned_set._reset(c)
ColumnElement.comparator._reset(c)
@@ -1681,6 +1682,10 @@ class ClauseElement(Visitable):
return c
+ @util.memoized_property
+ def _hash(self):
+ return id(self)
+
@property
def _constructor(self):
"""return the 'constructor' for this ClauseElement.
@@ -2421,6 +2426,10 @@ class ColumnCollection(util.OrderedProperties):
self._data.update((c.key, c) for c in cols)
self.__dict__['_all_cols'] = util.column_set(self)
+ @util.memoized_property
+ def _hash(self):
+ return hash(tuple(c._hash for c in self))
+
def __str__(self):
return repr([str(c) for c in self])
@@ -4432,6 +4441,17 @@ class ColumnClause(Immutable, ColumnElement):
self.type = sqltypes.to_instance(type_)
self.is_literal = is_literal
+ @util.memoized_property
+ def _hash(self):
+ return hash(
+ (
+ hash(self.key),
+ hash(self.table.name), # note using "self.table" here
causes an endless loop
+ self.type._hash,
+ hash(self.is_literal)
+ )
+ )
+
def _compare_name_for_result(self, other):
if self.is_literal or \
self.table is None or \
@@ -4586,6 +4606,15 @@ class TableClause(Immutable, FromClause):
for c in columns:
self.append_column(c)
+ @util.memoized_property
+ def _hash(self):
+ return hash(
+ (
+ hash(self.name),
+ self._columns._hash,
+ )
+ )
+
def _init_collections(self):
pass
diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py
index bfff053..16834d1 100644
--- a/lib/sqlalchemy/types.py
+++ b/lib/sqlalchemy/types.py
@@ -59,6 +59,10 @@ class TypeEngine(AbstractType):
def __reduce__(self):
return _reconstitute_comparator, (self.expr, )
+ @property
+ def _hash(self):
+ return id(self) # default to the same value as __hash__() if a
specific hash is not defined
+
hashable = True
"""Flag, if False, means values from this type aren't hashable.
On May 30, 2013, at 3:10 PM, Michael Bayer <[email protected]> wrote:
> my first 25 seconds of looking at this reveals that if you want to be able to
> generate a hash, this has to go all the way down to everything.
> query.filter(X == Y) means you need a hash for X == Y too. These hashes
> are definitely going to be determined using a traversal scheme for sure:
>
> q = X == Y
>
> q._magic_hash_value_()
>
> will ask "X", operator.eq, "Y", for their hash values ("X" and "Y" assuming
> they are Column objects are considered to be "immutable", even though they
> can be copies of "X" and "Y" sometimes with different semantics), and combine
> them together.
>
> So some_select_statement._magic_hash_value_() would traverse all the way down
> as well.
>
> This is why object identity was a lot easier to work with.
>
>
>
>
> On May 30, 2013, at 3:05 PM, Michael Bayer <[email protected]> wrote:
>
>>
>> On May 30, 2013, at 2:28 PM, Claudio Freire <[email protected]> wrote:
>>
>>> On Thu, May 30, 2013 at 2:25 PM, Michael Bayer <[email protected]>
>>> wrote:
>>>
>>>> If you want to work on a feature that is actually going to change
>>>> SQLAlchemy, (and would that be before or after you finish #2720? :) ), it
>>>> would be:
>>>
>>> After, I didn't forget, just real life real work priorities made me
>>> veer away from it. Since it was for 0.9, I judged I could safely delay
>>> 2720 a bit while I take care of work related priorities ;-)
>>
>> also, I find an overhaul to Query such that it's self-hashing a lot more
>> interesting than #2720. It would be a much bigger performance savings and
>> it would apply to other interpreters like pypy too. Replacements of tiny
>> sections of code with C, not that interesting :) (redoing all the C in pyrex
>> is more interesting but not necessarily a priority).
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "sqlalchemy" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To post to this group, send email to [email protected].
>> Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.