.. changelog::
:version: 1.1.6
+ .. change:: 3909
+ :tags: bug, orm
+ :tickets: 3909
+
+ Fixed a major inefficiency in the "eager_defaults" feature whereby
+ an unnecessary SELECT would be emitted for column values where the
+ ORM had explicitly inserted NULL, corresponding to attributes that
+ were unset on the object but did not have any server default
+ specified, as well as expired attributes on update that nevertheless
+ had no server onupdate set up. As these columns are not part of the
+ RETURNING that eager_defaults tries to use, they should not be
+ post-SELECTed either.
+
.. change:: 3905
:tags: bug, sql
:tickets: 3905
for table, columns in self._cols_by_table.items()
)
+ @_memoized_configured_property
+ def _server_default_plus_onupdate_propkeys(self):
+ result = set()
+
+ for table, columns in self._cols_by_table.items():
+ for col in columns:
+ if (
+ (
+ col.server_default is not None or
+ col.server_onupdate is not None
+ ) and col in self._columntoproperty
+ ):
+ result.add(self._columntoproperty[col].key)
+
+ return result
+
@_memoized_configured_property
def _server_onupdate_default_cols(self):
return dict(
toload_now = []
if base_mapper.eager_defaults:
- toload_now.extend(state._unloaded_non_object)
- elif mapper.version_id_col is not None and \
+ toload_now.extend(
+ state._unloaded_non_object.intersection(
+ mapper._server_default_plus_onupdate_propkeys)
+ )
+
+ if mapper.version_id_col is not None and \
mapper.version_id_generator is False:
if mapper._version_id_prop.key in state.unloaded:
toload_now.extend([mapper._version_id_prop.key])
)
)
+ def test_insert_dont_fetch_nondefaults(self):
+ Thing2 = self.classes.Thing2
+ s = Session()
+
+ t1 = Thing2(id=1, bar=2)
+
+ s.add(t1)
+
+ self.assert_sql_execution(
+ testing.db,
+ s.flush,
+ CompiledSQL(
+ "INSERT INTO test2 (id, foo, bar) "
+ "VALUES (:id, :foo, :bar)",
+ [{'id': 1, 'foo': None, 'bar': 2}]
+ )
+ )
+
+ def test_update_dont_fetch_nondefaults(self):
+ Thing2 = self.classes.Thing2
+ s = Session()
+
+ t1 = Thing2(id=1, bar=2)
+
+ s.add(t1)
+ s.flush()
+
+ s.expire(t1, ['foo'])
+
+ t1.bar = 3
+
+ self.assert_sql_execution(
+ testing.db,
+ s.flush,
+ CompiledSQL(
+ "UPDATE test2 SET bar=:bar WHERE test2.id = :test2_id",
+ [{'bar': 3, 'test2_id': 1}]
+ )
+ )
+
class TypeWoBoolTest(fixtures.MappedTest, testing.AssertsExecutionResults):
"""test support for custom datatypes that return a non-__bool__ value