Add a new SQLExecutor class for managing all SQL operations.

Review Request #11242 — Created Oct. 24, 2020 and submitted — Latest diff uploaded


Django Evolution


SQLExecutor is a new class intended for use whenever SQL statements
need to be executed. It's responsible for transaction management, SQL
statement preparation, and execution, and replaces the old run_sql(),
write_sql(), and execute_sql() functions.

The main thing this brings to the table is improved transaction
management. Normally with Django, you either use low-level transaction
commands (such as transaction.commit()) and juggle the various error
handling and savepoint situations yourself, or you make use of
transaction.atomic() to manage all this.

The problem with transaction.atomic() is that, being a context
manager, you can't easily start one, let it sit around outside of that
context, and then later stop it without some ugly code.

SQLExecutor simplifies this by turning transaction management and SQL
executions into just separate instructions that can be executed in
sequence. A caller can start a transaction, execute SQL, start a new one
(which will commit the previous), execute more SQL, commit, execute new
SQL outside a transaction, etc.

This change lays the ground work for this. The next change will allow
generated SQL to be marked as requiring an independent transaction or
requiring being run outside of a transaction, which will be factored in
during the SQL preparation step.

Unit tests passed.

Tested creating and upgrading databases successfully.