]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commit
ORM bulk insert via execute
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 7 Aug 2022 16:14:19 +0000 (12:14 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 24 Sep 2022 15:18:01 +0000 (11:18 -0400)
commita8029f5a7e3e376ec57f1614ab0294b717d53c05
tree84b1a3b3a6d3f4c9d6e8054f9cdfa190344436cb
parent2bcc97da424eef7db9a5d02f81d02344925415ee
ORM bulk insert via execute

* ORM Insert now includes "bulk" mode that will run
  essentially the same process as session.bulk_insert_mappings;
  interprets the given list of values as ORM attributes for
  key names
* ORM UPDATE has a similar feature, without RETURNING support,
  for session.bulk_update_mappings
* Added support for upserts to do RETURNING ORM objects as well
* ORM UPDATE/DELETE with list of parameters + WHERE criteria
  is a not implemented; use connection
* ORM UPDATE/DELETE defaults to "auto" synchronize_session;
  use fetch if RETURNING is present, evaluate if not, as
  "fetch" is much more efficient (no expired object SELECT problem)
  and less error prone if RETURNING is available
  UPDATE: howver this is inefficient!   please continue to
  use evaluate for simple cases, auto can move to fetch
  if criteria not evaluable
* "Evaluate" criteria will now not preemptively
  unexpire and SELECT attributes that were individually
  expired. Instead, if evaluation of the criteria indicates that
  the necessary attrs were expired, we expire the object
  completely (delete) or expire the SET attrs unconditionally
  (update). This keeps the object in the same unloaded state
  where it will refresh those attrs on the next pass, for
  this generally unusual case.  (originally #5664)
* Core change! update/delete rowcount comes from len(rows)
  if RETURNING was used.  SQLite at least otherwise did not
  support this.  adjusted test_rowcount accordingly
* ORM DELETE with a list of parameters at all is also a not
  implemented as this would imply "bulk", and there is no
  bulk_delete_mappings (could be, but we dont have that)
* ORM insert().values() with single or multi-values translates
  key names based on ORM attribute names
* ORM returning() implemented for insert, update, delete;
  explcit returning clauses now interpret rows in an ORM
  context, with support for qualifying loader options as well
* session.bulk_insert_mappings() assigns polymorphic identity
  if not set.
* explicit RETURNING + synchronize_session='fetch' is now
  supported with UPDATE and DELETE.
* expanded return_defaults() to work with DELETE also.
* added support for composite attributes to be present
  in the dictionaries used by bulk_insert_mappings and
  bulk_update_mappings, which is also the new ORM bulk
  insert/update feature, that will expand the composite
  values into their individual mapped attributes the way they'd
  be on a mapped instance.
* bulk UPDATE supports "synchronize_session=evaluate", is the
  default.  this does not apply to session.bulk_update_mappings,
  just the new version
* both bulk UPDATE and bulk INSERT, the latter with or without
  RETURNING, support *heterogenous* parameter sets.
  session.bulk_insert/update_mappings did this, so this feature
  is maintained.  now cursor result can be both horizontally
  and vertically spliced :)

This is now a long story with a lot of options, which in
itself is a problem to be able to document all of this
in some way that makes sense.  raising exceptions for
use cases we haven't supported is pretty important here
too, the tradition of letting unsupported things just not work
is likely not a good idea at this point, though there
are still many cases that aren't easily avoidable

Fixes: #8360
Fixes: #7864
Fixes: #7865
Change-Id: Idf28379f8705e403a3c6a937f6a798a042ef2540
45 files changed:
doc/build/orm/session_basics.rst
lib/sqlalchemy/dialects/mssql/pyodbc.py
lib/sqlalchemy/dialects/postgresql/provision.py
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/engine/cursor.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/engine/result.py
lib/sqlalchemy/orm/bulk_persistence.py
lib/sqlalchemy/orm/context.py
lib/sqlalchemy/orm/descriptor_props.py
lib/sqlalchemy/orm/evaluator.py
lib/sqlalchemy/orm/identity.py
lib/sqlalchemy/orm/loading.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/persistence.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/sql/annotation.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/crud.py
lib/sqlalchemy/sql/dml.py
lib/sqlalchemy/testing/assertsql.py
lib/sqlalchemy/testing/fixtures.py
lib/sqlalchemy/testing/suite/test_rowcount.py
lib/sqlalchemy/util/_py_collections.py
test/ext/test_horizontal_shard.py
test/ext/test_hybrid.py
test/orm/dml/__init__.py [new file with mode: 0644]
test/orm/dml/test_bulk.py [moved from test/orm/test_bulk.py with 80% similarity]
test/orm/dml/test_bulk_statements.py [new file with mode: 0644]
test/orm/dml/test_evaluator.py [moved from test/orm/test_evaluator.py with 99% similarity]
test/orm/dml/test_update_delete_where.py [moved from test/orm/test_update_delete.py with 83% similarity]
test/orm/inheritance/test_basic.py
test/orm/test_bind.py
test/orm/test_composites.py
test/orm/test_cycles.py
test/orm/test_defaults.py
test/orm/test_events.py
test/orm/test_unitofwork.py
test/orm/test_unitofworkv2.py
test/orm/test_versioning.py
test/sql/test_resultset.py
test/sql/test_returning.py
test/sql/test_selectable.py