Skip to content

Commit 7e3dc97

Browse files
authored
Add autocommit (#717)
* Add autocommit to features
1 parent 4513d62 commit 7e3dc97

File tree

6 files changed

+223
-1
lines changed

6 files changed

+223
-1
lines changed

doc/changes/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
* #706: Added more single and multiple entry ORM examples
1919
* #712: Added to features information on: autoincremented columns, foreign keys, & automatic indexes
2020
* #714: Added to features information on: caching + more non-ORM examples
21+
* #716: Added to features information on: autocommit

doc/user_guide/examples/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Examples
1111
testing_connection
1212
non_orm/index.rst
1313
orm/index.rst
14+
specific_focuses/index.rst
1415

1516
SQLAlchemy-Exasol contains the following examples which are regularly verified in the CI to be correct.
1617
For a broader introduction and an overview, please see:
@@ -49,7 +50,7 @@ Some basic preparation steps are required to see the examples in action:
4950
- `exasol/docker-db <https://hub.docker.com/r/exasol/docker-db/tags>`__, which can be easily accessed by using ``poetry run -- nox -s db:start``
5051
- the free `Exasol Community Edition <https://www.exasol.com/free-signup-community-edition/>`__
5152

52-
#. Open the ``/examples/`` directory and edit the file configuration file, as described in :ref:`example_configuration`.
53+
#. Open the ``/examples/`` directory and edit the configuration file, as described in :ref:`example_configuration`.
5354
#. To verify that your connection configuration is correct, run the code, as described in :ref:`example_connection`.
5455
#. Now, you may run whatever examples you would like:
5556

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
.. _autocommit:
2+
3+
Autocommit
4+
==========
5+
6+
These examples are meant to highlight best practices and allow users to explore the
7+
difference between having ``AUTOCOMMIT`` enabled versus disabled.
8+
9+
Throughout our examples, we use ``engine.begin()``. Code inside of a with-block using
10+
``engine.begin()`` will behave the same whether ``AUTOCOMMIT``
11+
is enabled (``y``) or disabled (``n``). This is because ``engine.begin()`` automatically
12+
issues a ``COMMIT`` when the block successfully finished or a ``ROLLBACK`` if it fails.
13+
For any basic database modifications, the recommended best practice is to use
14+
``engine.begin()``. This is to ensure that the objects are in the desired state before
15+
performing further manipulation steps. While it is possible to use ``engine.connect()``
16+
instead, when ``AUTOCOMMIT`` is disabled, you must manually, inside of the with-block,
17+
call ``commit()``. For a more nuanced discussion, see :ref:`begin_or_connect`.
18+
19+
To explore what changes when ``AUTOCOMMIT`` is set to ``n``, you can:
20+
21+
1. Modify your configuration file, as described on :ref:`example_configuration`, to set
22+
``"AUTOCOMMIT":"n"``.
23+
2. Modify the code in the examples to use ``engine.connect()`` instead of
24+
``engine.begin()``.
25+
3. Try to execute the examples and see what happens. You should get failures, as
26+
``engine.connect()`` without ``AUTOCOMMIT`` requires you to manually call
27+
``conn.commit()`` as needed.
28+
4. Add ``conn.commit()`` to the examples and try to execute them. They should now pass.
29+
30+
.. note::
31+
32+
The examples on this page are presented in sections, but they are all part
33+
of the same module ``examples/features/specific_focuses/_1_autocommit.py``.
34+
They are broken up here to provide further information and context. Please
35+
ensure that you read and execute the parts in order.
36+
37+
38+
.. _ddl:
39+
40+
Data Definition Language (DDL)
41+
------------------------------
42+
43+
.. literalinclude:: ../../../../examples/features/specific_focuses/_1_autocommit.py
44+
:language: python3
45+
:caption: examples/features/specific_focuses/autocommit.py
46+
:end-before: # 2. Data Query Language (DQL)
47+
48+
49+
It is recommended to use ``engine.begin()`` for DDL statements.
50+
51+
.. note::
52+
53+
Note that ``IDENTITY``
54+
(`an Exasol Database keyword <https://docs.exasol.com/db/latest/sql_references/data_types/identitycolumns.htm?Highlight=identity>`__)
55+
**must** be included for the ID to autoincrement. This is not required for the
56+
examples given in :ref:`examples_non_orm` or :ref:`examples_orm`, where SQLAlchemy
57+
generates the required SQL statement for the Exasol database instance.
58+
59+
Data Query Language (DQL)
60+
-------------------------
61+
62+
.. literalinclude:: ../../../../examples/features/specific_focuses/_1_autocommit.py
63+
:language: python3
64+
:caption: examples/features/specific_focuses/autocommit.py
65+
:start-at: # 2. Data Query Language (DQL)
66+
:end-before: # 3. Data Manipulation Language (DML)
67+
68+
Typically, ``SELECT`` is considered the only member of DQL. In such use cases, no data
69+
manipulations are occurring, so no manual commits need to be made. As such,
70+
this code works and should stay the same regardless of whether ``AUTOCOMMIT`` is
71+
enabled or disabled.
72+
73+
Data Manipulation Language (DML)
74+
--------------------------------
75+
76+
.. literalinclude:: ../../../../examples/features/specific_focuses/_1_autocommit.py
77+
:language: python3
78+
:caption: examples/features/specific_focuses/autocommit.py
79+
:start-at: # 3. Data Manipulation Language (DML)
80+
81+
Like the :ref:`ddl` example, it is preferred here to use ``begin()`` in the with-block.
82+
This behaves the same whether ``AUTOCOMMIT`` is enabled or disabled. For a
83+
more nuanced discussion, see :ref:`begin_or_connect`.
84+
85+
.. _begin_or_connect:
86+
87+
88+
Connection Management in SQLAlchemy
89+
-----------------------------------
90+
91+
.. _begin-method:
92+
93+
The ``begin()`` Method
94+
^^^^^^^^^^^^^^^^^^^^^^
95+
96+
The ``begin()`` method provides a **transactional boundary**. It is the safest choice
97+
for an operation that modifies the database.
98+
99+
.. list-table:: When to use ``begin()``
100+
:widths: 30 70
101+
:header-rows: 1
102+
103+
* - Scenario
104+
- Description
105+
* - DDL
106+
- Use for ``CREATE``, ``ALTER``, or ``DROP`` commands to ensure schema changes are committed.
107+
* - DML
108+
- Ideal for ``INSERT``, ``UPDATE``, and ``DELETE`` where you want an "all-or-nothing" result.
109+
* - Auto-Rollback
110+
- If your code fails mid-block, SQLAlchemy automatically reverts any partial changes.
111+
112+
.. _connect-method:
113+
114+
The ``connect()`` Method
115+
^^^^^^^^^^^^^^^^^^^^^^^^
116+
117+
The ``connect()`` method provides a **raw connection**. It offers granular control but
118+
places the responsibility of transaction management on the user.
119+
120+
When ``AUTOCOMMIT`` is enabled, using ``connect()`` ensures that each statement is
121+
finalised by the database as soon as it is executed. If ``AUTOCOMMIT`` is disabled,
122+
then you must manually use ``commit``.
123+
124+
.. list-table:: When to use ``connect()``
125+
:widths: 30 70
126+
:header-rows: 1
127+
128+
* - Scenario
129+
- Description
130+
* - DQL
131+
- Best for ``SELECT`` statements where no data is being changed.
132+
* - DML
133+
- Use if you need to commit specific parts of a long-running script at different times.
134+
* - Performance Tuning
135+
- Avoids the overhead of starting a transaction block when only reading data.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Specific Focuses
2+
================
3+
4+
.. toctree::
5+
:maxdepth: 2
6+
7+
autocommit

doc/user_guide/features/index.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,24 @@
33
Features
44
========
55

6+
Autocommit
7+
----------
8+
9+
By default, SQLAlchemy-Exasol has ``AUTOCOMMIT`` turned on, as mentioned in
10+
:ref:`dialect_specific_params`. This parameter is only relevant when using
11+
``engine.connect()``. When using ``engine.begin()``, the transaction, if the inner steps
12+
are successful, is always automatically committed at the end of the with-block.
13+
14+
Committing or using autocommit ensures that any changes made during a transaction — such
15+
as inserts or updates — are permanently saved to the database. For more information on these
16+
patterns, please see :ref:`begin_or_connect` and these other resources:
17+
18+
19+
20+
* Our :ref:`Autocommit Examples <autocommit>`
21+
* SQLAlchemy's `Working with Engines and Connections <https://docs.sqlalchemy.org/en/20/core/connections.html>`__
22+
* SQLAlchemy's `Transaction & Connection Management <https://docs.sqlalchemy.org/en/20/orm/session_transaction.html>`__
23+
624
Autoincremented Columns
725
-----------------------
826

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from sqlalchemy import text
2+
3+
from examples.config import (
4+
DEFAULT_SCHEMA_NAME,
5+
ENGINE,
6+
SQL_ALCHEMY,
7+
)
8+
9+
# 0. Ensure that the schema exists
10+
SQL_ALCHEMY.create_schema(engine=ENGINE, schema=DEFAULT_SCHEMA_NAME)
11+
12+
# 1. Data Definition Language (DDL)
13+
TABLE_NAME = "HEX_LOOKUP"
14+
15+
create_hex_table = f"""
16+
CREATE OR REPLACE TABLE {DEFAULT_SCHEMA_NAME}.{TABLE_NAME} (
17+
id INTEGER IDENTITY PRIMARY KEY,
18+
hex_code CHAR(6) NOT NULL
19+
);
20+
"""
21+
22+
with ENGINE.begin() as conn:
23+
conn.execute(text(create_hex_table))
24+
25+
# 2. Data Query Language (DQL)
26+
query = f"""
27+
SELECT *
28+
FROM SYS.EXA_ALL_TABLES
29+
WHERE TABLE_SCHEMA = '{DEFAULT_SCHEMA_NAME}'
30+
AND TABLE_NAME = '{TABLE_NAME}'
31+
"""
32+
33+
with ENGINE.connect() as conn:
34+
results = conn.execute(text(query)).fetchall()
35+
# conn.commit() is never needed for DQL
36+
37+
print(f"Table search result: {results}")
38+
39+
# 3. Data Manipulation Language (DML)
40+
hex_data = [{"hex_code": "FF5733"}, {"hex_code": "33FF57"}, {"hex_code": "3357FF"}]
41+
42+
insert_statement = (
43+
f"INSERT INTO {DEFAULT_SCHEMA_NAME}.{TABLE_NAME} " f"(hex_code) VALUES (:hex_code)"
44+
)
45+
with ENGINE.begin() as conn:
46+
conn.execute(text(insert_statement), hex_data)
47+
# if `ENGINE.connect()` were used instead of `ENGINE.begin()` and
48+
# "AUTOCOMMIT" were disabled, you MUST include a `conn.commit()`
49+
# for the change to be persisted; otherwise, no results will be
50+
# found in #4,
51+
52+
# 4. DQL
53+
select_statement = f"SELECT * FROM {DEFAULT_SCHEMA_NAME}.{TABLE_NAME}"
54+
55+
with ENGINE.connect() as conn:
56+
result = conn.execute(text(select_statement)).fetchall()
57+
58+
print(f"Number of entries: {len(result)}")
59+
for row in result:
60+
print(row)

0 commit comments

Comments
 (0)