Closed MikhailZhukov closed 4 years ago
hi there -
thanks for the clear test case.
unfortunately this issue is an illustration of a problem in the "serializer" extension that isn't really solvable in a generalized way. The issue here is that the query against TestTable, when deserialized, uses the TestTable class that's in the script as well as the TestTable.some_primary_id object when it renders columns clause of the query. However, the WHERE clause is using the deserialized version of TestTable.some_primary_id. The bound parameter, when presented with itself as well as it's cloned self, causes a conflict because they have the same name.
The bound parameter object could try to work around this by changing its "unique" name when deserialized, i'll probably just commit that, which does fix this specific issue and is:
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 422eb6220..54981375a 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -1386,6 +1386,13 @@ class BindParameter(roles.InElementRole, ColumnElement):
d["value"] = v
return d
+ def __setstate__(self, state):
+ if state.get('unique', False):
+ state['key'] = _anonymous_label(
+ "%%(%d %s)s" % (id(self), state.get('_orig_key', 'param'))
+ )
+ self.__dict__.update(state)
+
def __repr__(self):
return "BindParameter(%r, %r, type_=%r)" % (
self.key,
I can't be sure what other unwanted effects this may have in other areas that deal with serialization but I will have to hope that there is very little serialize/deserialize of queries going on in real world applications in general.
In fact I hope to deprecate and remove the "serializer" extension in any case as there are a lot more things it doesn't really do correctly. Can I ask what your use case for the "serializer" is? AFAIK there aren't really any good uses for it and it should not have been added.
Mike Bayer has proposed a fix for this issue in the master branch:
Alter unique bound parameter key on deserialize https://gerrit.sqlalchemy.org/1660
Mike Bayer has proposed a fix for this issue in the rel_1_3 branch:
Alter unique bound parameter key on deserialize https://gerrit.sqlalchemy.org/1661
I used serializer for implementing pagination for REST API. First request arrives, I build query, then serialize and cache it. When client want next chunk, I deserialize query, adjust limit/offset and execute it.
The reason why I chosen this way is that queries can be very complicated, and context (attributes and values, joins, etc) will be lost when requesting next chunk.
Hi.
Thanks a lot for helping.
In fact I hope to deprecate and remove the "serializer" extension in any case as there are a lot more things it doesn't really do correctly. Can I ask what your use case for the "serializer" is? AFAIK there aren't really any good uses for it and it should not have been added.
Thanks for information about deprecating serializer. We will think about replacement it in our pagination implementation.
is it possible that instaed of caching the completed query, you cache the information from the REST request that is used to build up that query? this would be much more portable.
yes, i think we will do that in nearest future. I replaced all column_property
to hybrid_property
now and it look like it work good enough while we will refactor code base
Deserialization of query with column_property fail with error
I test this error with Python3.7 + SQLAlchemy 1.3.12 In Python2.7 + SQLAlchemy 1.1.18 deserialization does not work too, but with other exception
For reproducing this bug I make simple test file: