From: Mike Bayer Date: Fri, 2 Nov 2018 20:40:59 +0000 (-0400) Subject: Deannotate ORM columns in ColumnEntity X-Git-Tag: rel_1_2_14~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e9467b050d0c818b6d356df7760cc2e8e54a1f7;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Deannotate ORM columns in ColumnEntity Fixed a minor performance issue which could in some cases add unnecessary overhead to result fetching, involving the use of ORM columns and entities that include those same columns at the same time within a query. The issue has to do with hash / eq overhead when referring to the column in different ways. Fixes: #4347 Change-Id: I191d4d1b1623898060a9accdfd186de16f89a6b7 (cherry picked from commit 88bfa1b89c5b3b2290fa266c53322f003833af40) --- diff --git a/doc/build/changelog/unreleased_12/4347.rst b/doc/build/changelog/unreleased_12/4347.rst new file mode 100644 index 0000000000..c24f87b0d8 --- /dev/null +++ b/doc/build/changelog/unreleased_12/4347.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, orm + :tickets: 4347 + + Fixed a minor performance issue which could in some cases add unnecessary + overhead to result fetching, involving the use of ORM columns and entities + that include those same columns at the same time within a query. The issue + has to do with hash / eq overhead when referring to the column in different + ways. diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 30c683f8e2..d14e34a4f3 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -4226,6 +4226,11 @@ class _ColumnEntity(_QueryEntity): else: column = query._adapt_clause(self.column, False, True) + if column._annotations: + # annotated columns perform more slowly in compiler and + # result due to the __eq__() method, so use deannotated + column = column._deannotate() + if context.adapter: column = context.adapter.columns[column] @@ -4234,6 +4239,12 @@ class _ColumnEntity(_QueryEntity): def setup_context(self, query, context): column = query._adapt_clause(self.column, False, True) + + if column._annotations: + # annotated columns perform more slowly in compiler and + # result due to the __eq__() method, so use deannotated + column = column._deannotate() + context.froms += tuple(self.froms) context.primary_columns.append(column) diff --git a/test/aaa_profiling/test_orm.py b/test/aaa_profiling/test_orm.py index ae0dd657d4..18434b9a4e 100644 --- a/test/aaa_profiling/test_orm.py +++ b/test/aaa_profiling/test_orm.py @@ -1,13 +1,14 @@ from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.orm import mapper, relationship, \ sessionmaker, Session, defer, joinedload, defaultload, selectinload, \ - Load, configure_mappers + Load, configure_mappers, Bundle from sqlalchemy import testing from sqlalchemy.testing import profiling from sqlalchemy.testing import fixtures from sqlalchemy.testing.schema import Table, Column from sqlalchemy import inspect + class MergeTest(fixtures.MappedTest): @classmethod @@ -954,3 +955,149 @@ class BranchedOptionTest(fixtures.MappedTest): q.options(*opts) go() + +class AnnotatedOverheadTest(fixtures.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table( + 'a', + metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + @classmethod + def setup_classes(cls): + class A(cls.Basic): + pass + + @classmethod + def setup_mappers(cls): + A = cls.classes.A + a = cls.tables.a + + mapper(A, a) + + @classmethod + def insert_data(cls): + A = cls.classes.A + s = Session() + s.add_all([A(data='asdf') for i in range(5)]) + s.commit() + + def test_no_bundle(self): + A = self.classes.A + s = Session() + + q = s.query(A).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_no_entity_wo_annotations(self): + A = self.classes.A + a = self.tables.a + s = Session() + + q = s.query(a.c.data).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_no_entity_w_annotations(self): + A = self.classes.A + s = Session() + q = s.query(A.data).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_entity_w_annotations(self): + A = self.classes.A + s = Session() + q = s.query( + A, A.data + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_entity_wo_annotations(self): + A = self.classes.A + a = self.tables.a + s = Session() + q = s.query( + A, a.c.data + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_no_bundle_wo_annotations(self): + A = self.classes.A + a = self.tables.a + s = Session() + q = s.query( + a.c.data, A + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_no_bundle_w_annotations(self): + A = self.classes.A + s = Session() + q = s.query( + A.data, A + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_bundle_wo_annotation(self): + A = self.classes.A + a = self.tables.a + s = Session() + q = s.query( + Bundle("ASdf", a.c.data), A + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() + + def test_bundle_w_annotation(self): + A = self.classes.A + s = Session() + q = s.query( + Bundle("ASdf", A.data), A + ).select_from(A) + + @profiling.function_call_count() + def go(): + for i in range(100): + q.all() + go() diff --git a/test/profiles.txt b/test/profiles.txt index 7ae8011d41..af67a04512 100644 --- a/test/profiles.txt +++ b/test/profiles.txt @@ -11,6 +11,69 @@ # option - this file will be rewritten including the new count. # +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 48456,48400 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51456,51400 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 50867,50800 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 54067,54000 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 48480,48480 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51480,51480 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 50880,50880 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 54080,54080 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46205,46200 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 49205,49200 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48008,48000 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 51208,51200 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46280,46280 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 49280,49280 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48080,48080 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 51280,51280 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 38500,38500 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 41000,41000 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_cextensions 41200,41200 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 43900,43900 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46205,46200 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 49205,49200 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48008,48000 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 51208,51200 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46280,46280 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 49280,49280 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48080,48080 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 51280,51280 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 25437,25437 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 26937,26937 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 27535,27535 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 29235,29235 + +# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations + +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 25517,25517 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 27017,27017 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 27615,27615 +test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 29315,29315 + # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_cextensions 75