]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Deannotate ORM columns in ColumnEntity
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Nov 2018 20:40:59 +0000 (16:40 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 2 Nov 2018 20:40:59 +0000 (16:40 -0400)
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

doc/build/changelog/unreleased_12/4347.rst [new file with mode: 0644]
lib/sqlalchemy/orm/query.py
test/aaa_profiling/test_orm.py
test/profiles.txt

diff --git a/doc/build/changelog/unreleased_12/4347.rst b/doc/build/changelog/unreleased_12/4347.rst
new file mode 100644 (file)
index 0000000..c24f87b
--- /dev/null
@@ -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.
index bfddb5cfec3392614953bc7d77084e3fe1f0dc0e..cace2e54afe69cdcbbd95878bbb47b1ff54a09e3 100644 (file)
@@ -4243,6 +4243,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]
 
@@ -4251,6 +4256,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)
 
index ae0dd657d4d4860744849cf2db467fd2200d75ae..18434b9a4e1deaba5c753608e47cefb1c4c89c26 100644 (file)
@@ -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()
index ae7b5746ab7d7006711102f1d277988ad9e7f6e4..ef13151bcf0651501b3c99cd4aa4fadd2f5a75ed 100644 (file)
@@ -1,15 +1,15 @@
 # /home/classic/dev/sqlalchemy/test/profiles.txt
 # This file is written out on a per-environment basis.
-# For each test in aaa_profiling, the corresponding function and
+# For each test in aaa_profiling, the corresponding function and 
 # environment is located within this file.  If it doesn't exist,
 # the test is skipped.
-# If a callcount does exist, it is compared to what we received.
+# If a callcount does exist, it is compared to what we received. 
 # assertions are raised if the counts do not match.
-#
-# To add a new callcount test, apply the function_call_count
-# decorator and re-run the tests using the --write-profiles
+# 
+# To add a new callcount test, apply the function_call_count 
+# decorator and re-run the tests using the --write-profiles 
 # option - this file will be rewritten including the new count.
-#
+# 
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert
 
@@ -196,6 +196,69 @@ test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgre
 test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 158
 test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 158
 
+# 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_orm.AttributeOverheadTest.test_attribute_set
 
 test.aaa_profiling.test_orm.AttributeOverheadTest.test_attribute_set 2.7_mssql_pyodbc_dbapiunicode_cextensions 4256
@@ -475,7 +538,6 @@ test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_postgresql_
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_cextensions 455708
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 455708
 
-
 # TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results
 
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_mssql_pyodbc_dbapiunicode_cextensions 466544