-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for SQLAlchemy 1.4 #391
Conversation
SQLAlchemy 1.4 dropped support for Python 3.5, see sqlalchemy/sqlalchemy@d0b5ce2. However, Python 2.7 is still supported. |
f598944
to
8ba3b99
Compare
ObservationsTests fail with:
-- https://github.com/crate/crate-python/pull/391/checks?check_run_id=1406879590#step:5:139 Root causeThe signature of
Background
|
78f15ec
to
0b1d8cb
Compare
Dear @chaudum and @m-kharbat, I've tried to apply some minor updates from SQLAlchemy 1.4 to our crate-python/src/crate/client/sqlalchemy/compiler.py Lines 187 to 193 in 7e12a49
crate-python/src/crate/client/sqlalchemy/compiler.py Lines 285 to 289 in 7e12a49
However, after I didn't have quick success when making a few adjustments and after reading the comments above, I wanted to check back with you about the further plan. Regarding the conform with earlier versions of CreateDB comment: Maybe we don't need any amendments coming from these functions anymore and drop support for CrateDB 0.x releases? Apart from that, I also found that some bits within these methods are apparently related to still support SQLAlchemy 1.0 and 1.1. Maybe we also want to revise this and drop respective support? With kind regards, |
5d8a543
to
320d369
Compare
66e3308
to
8726c13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
39d0101, after some other minor adjustments, magically made the test suite succeed, by adjusting the parameter mangling to/from crate_before_execute
.
Other than the inline comments below, some other items could/should be addressed before finalizing this patch. Please let me know about your suggestions and if you can spot other shortcomings.
-
Special attention should be given to the new SQL Compilation Caching feature [1,2]. The
CrateDialect
shouldsupports_statement_cache = True
, see [3]. Also, there is a newcache_ok
attribute on custom types [4], which should probably be applied to all CrateDB SQLAlchemy types listed incrate.client.sqlalchemy.types
. [5] might also be related here.Currently, we observe all those warnings referenced at SA's identify what constructs are blocking caching from being enabled documentation:
SAWarning: Dialect crate:crate-python will not make use of SQL compilation caching as it does not set the
supports_statement_cache
attribute toTrue
. This can have significant performance implications including some performance degradations in comparison to prior SQLAlchemy versions. Dialect maintainers should seek to set this attribute to True after appropriate development and testing for SQLAlchemy 1.4 caching support. Alternatively, this attribute may be set toFalse
which will disable this warning. (Background on this error at: https://sqlalche.me/e/14/cprf)SAWarning: UserDefinedType
{_Craty(),_ObjectArray(),Geopoint(),Geoshape()}
will not produce a cache key because thecache_ok
attribute is not set toTrue
. This can have significant performance implications including some performance degradations in comparison to prior SQLAlchemy versions. Set this attribute toTrue
if this type object's state is safe to use in a cache key, orFalse
to disable this warning. (Background on this error at: https://sqlalche.me/e/14/cprf)SAWarning:
{Class Any,Class Match}
will not make use of SQL compilation caching as it does not set theinherit_cache
attribute toTrue
. This can have significant performance implications including some performance degradations in comparison to prior SQLAlchemy versions. Set this attribute toTrue
if this object can make use of the cache key generated by the superclass. Alternatively, this attribute may be set toFalse
which will disable this warning. (Background on this error at: https://sqlalche.me/e/14/cprf) -
Go through the list of significant changes [6] thoroughly and summarize/compile relevant details regarding CrateDB into the changelog file.
-
Test Asynchronous I/O with
asyncpg
, see [7].engine = create_async_engine( "postgresql+asyncpg://scott:tiger@localhost/test" )
[1] https://docs.sqlalchemy.org/en/14/core/connections.html#sql-compilation-caching
[2] https://docs.sqlalchemy.org/en/14/faq/performance.html#faq-new-caching
[3] https://docs.sqlalchemy.org/en/14/core/connections.html#caching-for-third-party-dialects
[4] https://docs.sqlalchemy.org/en/14/core/custom_types.html
[5] https://docs.sqlalchemy.org/en/14/errors.html#caching-caveats
[6] https://docs.sqlalchemy.org/en/14/changelog/migration_14.html
[7] https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html
@@ -74,7 +79,24 @@ def rewrite_update(clauseelement, multiparams, params): | |||
def crate_before_execute(conn, clauseelement, multiparams, params): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to properly rewrite parameters for update statements
On the topic of using events for rewriting update statements, @zzzeek suggested at sqlalchemy/sqlalchemy#5915 (comment):
For the
before_execute()
thing, if you want to rewrite paramaters, a much better place for a dialect to do that is indialect.do_execute()
anddialect.do_executemany()
(will work in 1.3 also). See for example psycopg2do_executemany()
that does a bunch of rewriting. https://github.com/sqlalchemy/sqlalchemy/blob/master/lib/sqlalchemy/dialects/postgresql/psycopg2.py#L905
Also at sqlalchemy/sqlalchemy#5915 (comment):
You should not have to use any events, that is, no DialectEvents or ConnectionEvents at all; the code you are writing is a dialect. You would implement simple methods
do_execute()
anddo_executemany()
as methods of your class CrateDialect right here: https://github.com/crate/crate-python/blob/master/src/crate/client/sqlalchemy/dialect.py#L160 ; these override the default methods that are on DefaultDialect. dialects shouldnt use events for these kinds of things, the dialect implements the actual way that these things happen without any events riding on them.
Thank you again!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't followed Mike's suggestion yet, after I was able to make the patch work with 39d0101. We will probably have to decide whether to drop support for SQLAlchemy 1.1 and 1.2? What are your opinions about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At sqlalchemy/sqlalchemy#5915 (reply in thread), I shared further thoughts and asked for guidance how to improve that specific topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't followed Mike's suggestion yet, after I was able to make the patch work with 39d0101. We will probably have to decide whether to drop support for SQLAlchemy 1.1 and 1.2? What are your opinions about this?
As Mike already pointed out, there are 1) not much users using these old version 2) if the do, they probably use an older version of our driver as well.
So +1 for dropping the support of SQLAlchemy < 1.3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, let's do it. I would prefer to bring in corresponding cleanups and improvements on behalf of the next iteration, if you don't have any objections. Otherwise, please let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#424 implemented the update to drop support for corresponding EOL software components, i.e. SQLAlchemy 1.1 and 1.2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About the original topic in this thread, the migration to hook into ExecutionContext.pre_exec()
for rewriting UPDATE
statements instead of using engine events, see #425.
# The rewriting logic in `rewrite_update` and `visit_update` needs | ||
# adjustments here in order to prevent `sqlalchemy.exc.CompileError: | ||
# Unconsumed column names: characters_name, data['nested']` | ||
""" | ||
if parameters and stmt_parameter_tuples: | ||
check = ( | ||
set(parameters) | ||
.intersection(_column_as_key(k) for k, v in stmt_parameter_tuples) | ||
.difference(check_columns) | ||
) | ||
if check: | ||
raise exc.CompileError( | ||
"Unconsumed column names: %s" | ||
% (", ".join("%s" % (c,) for c in check)) | ||
) | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment does not really help me in understanding the issue like why are there unconsumed column names.
Is it still valid? If so can you please comment more about the why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for asking. It looks like through the special rewriting logic of the CrateDB driver, this sanity check by SQLAlchemy croaks. I don't actually know the "why", but I tried to improve the inline comment with 57d5941 and also added a backlog item to investigate further, see also #425.
# special logic that only occurs for multi-table UPDATE | ||
# statements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code speaks for itself here, I don't think the comment adds much value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is part of the code which was vendored from upstream SA14. I didn't want to introduce any deviances to code which was copied over verbatim.
In order to test cases when views are actually _present_, some would have to be created. This is out of scope for this patch. - https://github.com/sqlalchemy/sqlalchemy/wiki/Views - https://github.com/jklukas/sqlalchemy-views - https://stackoverflow.com/questions/9766940/how-to-create-an-sql-view-with-sqlalchemy
This patch follows the documentation at [1] and adds the attributes `supports_statement_cache`, `cache_ok` and `inherit_cache` to the corresponding classes of `CrateDialect`, `UserDefinedType` and `ColumnElement`. The test suite thoroughly accepted this change of setting all attributes to `True`, effectively fully supporting SQL compilation caching. [1] https://docs.sqlalchemy.org/en/14/faq/performance.html
Beforehand, after observing errors like:: RuntimeError: number of values in row (1) differ from number of column processors (0) we decided to take the `resultproxy` out of the equation in order to work on the real gist of SQLAlchemy 1.4 compatibility. When looking at this again, it turned out that the root cause have been inaccuracies when setting up fake cursors within the test cases. SQLAlchemy <1.4 might have been more forgiving on that, but >=1.4 isn't.
SADeprecationWarning: The argument signature for the `ConnectionEvents.before_execute` event listener has changed as of version 1.4, and conversion for the old argument signature will be removed in a future release. The new signature is `def before_execute(conn, clauseelement, multiparams, params, execution_options)`.
This pull request introduces 1 alert when merging 8a817fe into 67ec8e3 - view on LGTM.com new alerts:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Maybe wait for @mfussenegger to have a final look at it?
|
||
# A module is imported with the "import" and "import from" statements. | ||
# https://lgtm.com/rules/1818040193/ | ||
- exclude: py/import-and-import-from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this work? #391 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it doesn't ;[. At least, it does not work while not being merged. I hope that the additional rule will be recognized after the merge. However, it is weird because the other rule about py/not-named-self
apparently seems to work well.
This pull request introduces 1 alert when merging 00d016e into 67ec8e3 - view on LGTM.com new alerts:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The low coverage + duplication of the visit_update_14
still doesn't feel right to me, but I guess it's good enough to move forward
CHANGES.txt
Outdated
SQLAlchemy 1.4 became stricter on some details. Where it was ok to use a | ||
textual column expression in *plain text* beforehand, a | ||
`SQLAlchemy literal_column`_ type should be used now. This specifically | ||
applies to `CrateDB system columns`_ like ``_score``. | ||
|
||
For example, when a query might have looked like this beforehand:: | ||
|
||
session.query(Character.name, '_score') | ||
|
||
it should now be written like:: | ||
|
||
session.query(Character.name, sa.literal_column('_score')) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
```suggestion
SQLAlchemy 1.4 requires to wrap system columns like ``_score`` in a `SQLAlchemy literal_column`_ type. Before it was possible to use a query like this::
session.query(Character.name, '_score')
It must now be written like::
session.query(Character.name, sa.literal_column('_score'))
The text before used should but it's not an optional thing, users must change this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
187a11d improves the wording, following your suggestion. Thank you again!
Thank you, I added your suggestion to crate/sqlalchemy-cratedb#74. |
lgtm.com issued an alert like:: Module 'sqlalchemy' is imported with both 'import' and 'import from' See: https://lgtm.com/projects/g/crate/crate-python/rev/pr-8ea3b257a334a41cdb531d681072752e662eee56. The corresponding rule in lgtm.yml introduced by bcf9fff does not seem to work.
This pull request introduces 1 alert when merging aad1458 into 67ec8e3 - view on LGTM.com new alerts:
|
Summary of the changes / Why this is an improvement
SQLAlchemy 1.4.0 is about to be released.SQLAlchemy 1.4.37 has been released. People are eagerly waiting for corresponding support.