You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make sure you're familiar with the `Asyncio approach <https://docs.python.org/3/library/asyncio.html>`_
581
584
before using asynchronous Python SDK, as it requires special async/await syntax.
582
585
586
+
.. note::
587
+
588
+
**Transaction behavior with async operations**: The asynchronous SDK supports the same transaction
589
+
behavior as the synchronous version. However, be careful when running concurrent queries within
590
+
the same connection as they will share the same transaction context. For parallel processing,
591
+
consider using separate connections for independent operations.
592
+
583
593
584
594
Simple asynchronous example
585
595
---------------------------
@@ -936,3 +946,206 @@ function will raise a ``QueryTimeoutError`` exception.
936
946
)
937
947
938
948
**Warning**: If running multiple queries, and one of queries times out, all the previous queries will not be rolled back and their result will persist. All the remaining queries will be cancelled.
949
+
950
+
Transaction support
951
+
==============================
952
+
953
+
The Firebolt Python SDK provides comprehensive transaction support for both explicit transaction management and automatic transaction handling through the autocommit feature. Transactions ensure data integrity by grouping multiple SQL statements into atomic operations.
954
+
955
+
Understanding autocommit behavior
956
+
----------------------------------
957
+
958
+
The SDK supports two transaction modes controlled by the ``autocommit`` parameter:
959
+
960
+
**Autocommit enabled (default behavior)**
961
+
When ``autocommit=True`` (the default), each SQL statement is automatically committed immediately after execution. This provides immediate data visibility but doesn't allow for multi-statement transactions.
962
+
963
+
**Autocommit disabled**
964
+
When ``autocommit=False``, statements are executed within transactions that must be explicitly committed or rolled back. This enables multi-statement atomic operations.
965
+
966
+
.. _autocommit_example:
967
+
968
+
::
969
+
970
+
from firebolt.db import connect
971
+
972
+
# Default behavior: autocommit enabled
973
+
with connect(
974
+
auth=auth,
975
+
account_name="your_account",
976
+
database="your_database"
977
+
) as connection:
978
+
cursor = connection.cursor()
979
+
cursor.execute("INSERT INTO test_table VALUES (1, 'data')")
980
+
# Data is immediately visible to other connections
981
+
982
+
# Autocommit disabled for transaction control
983
+
with connect(
984
+
auth=auth,
985
+
account_name="your_account",
986
+
database="your_database",
987
+
autocommit=False
988
+
) as connection:
989
+
cursor = connection.cursor()
990
+
cursor.execute("INSERT INTO test_table VALUES (2, 'transactional')")
991
+
# Data is not visible to other connections until committed
992
+
993
+
Explicit transaction control
994
+
-----------------------------
995
+
996
+
When ``autocommit=False``, you can use explicit transaction statements to control when changes are committed or rolled back. The SDK supports the standard SQL transaction commands.
997
+
998
+
**Important**: ``BEGIN`` and ``BEGIN TRANSACTION`` statements are **not supported** when ``autocommit=False`` because transactions are started implicitly with the first SQL statement. Attempting to execute ``BEGIN`` will result in an error. With ``autocommit=True`` executing ``BEGIN`` has no effect.
999
+
1000
+
.. _explicit_transaction_example:
1001
+
1002
+
**Committing transactions**
1003
+
1004
+
::
1005
+
1006
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1007
+
cursor = connection.cursor()
1008
+
1009
+
# Transaction starts implicitly with first statement
1010
+
cursor.execute("INSERT INTO accounts VALUES ('A123', 1000.00)")
1011
+
cursor.execute("INSERT INTO accounts VALUES ('B456', 500.00)")
1012
+
1013
+
# Explicitly commit the transaction
1014
+
cursor.execute("COMMIT TRANSACTION")
1015
+
# or use the short form
1016
+
# cursor.execute("COMMIT")
1017
+
1018
+
**Rolling back transactions**
1019
+
1020
+
::
1021
+
1022
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1023
+
cursor = connection.cursor()
1024
+
1025
+
cursor.execute("INSERT INTO accounts VALUES ('A123', 1000.00)")
1026
+
cursor.execute("UPDATE accounts SET balance = balance - 200 WHERE id = 'A123'")
1027
+
1028
+
# Something went wrong, rollback the transaction
1029
+
cursor.execute("ROLLBACK TRANSACTION")
1030
+
# or use the short form
1031
+
cursor.execute("ROLLBACK")
1032
+
1033
+
Connection-level transaction methods
1034
+
------------------------------------
1035
+
1036
+
The ``Connection`` object provides convenient methods for transaction control that work across all cursors created from the same connection.
1037
+
1038
+
.. _connection_transaction_methods_example:
1039
+
1040
+
::
1041
+
1042
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1043
+
cursor1 = connection.cursor()
1044
+
cursor2 = connection.cursor()
1045
+
1046
+
# Both cursors share the same transaction
1047
+
cursor1.execute("INSERT INTO table1 VALUES (1, 'data1')")
1048
+
cursor2.execute("INSERT INTO table2 VALUES (2, 'data2')")
1049
+
1050
+
# Commit using connection method (affects both cursors)
1051
+
connection.commit()
1052
+
1053
+
# Or rollback using connection method
1054
+
# connection.rollback()
1055
+
1056
+
Context manager behavior
1057
+
------------------------
1058
+
1059
+
When using connections as context managers, the SDK provides automatic transaction handling:
1060
+
1061
+
**With autocommit=False**: The connection automatically commits the transaction when exiting normally, or rolls back on exceptions.
1062
+
1063
+
**With autocommit=True**: No special behavior since each statement is already committed immediately.
1064
+
1065
+
.. _context_manager_transaction_example:
1066
+
1067
+
::
1068
+
1069
+
# Automatic commit on successful completion
1070
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1071
+
cursor = connection.cursor()
1072
+
cursor.execute("INSERT INTO test_table VALUES (1, 'auto-commit')")
1073
+
# Transaction is automatically committed when context exits normally
1074
+
1075
+
# Automatic rollback on exception
1076
+
try:
1077
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1078
+
cursor = connection.cursor()
1079
+
cursor.execute("INSERT INTO test_table VALUES (2, 'will-rollback')")
1080
+
raise ValueError("Something went wrong")
1081
+
except ValueError:
1082
+
pass
1083
+
# Transaction is automatically rolled back due to exception
1084
+
1085
+
Transaction isolation and cursor sharing
1086
+
-----------------------------------------
1087
+
1088
+
**Important**: All cursors created from the same connection share the same transaction context. Changes made through one cursor are immediately visible to other cursors from the same connection, but not to external connections until the transaction is committed.
1089
+
1090
+
.. _cursor_isolation_example:
1091
+
1092
+
::
1093
+
1094
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1095
+
cursor1 = connection.cursor()
1096
+
cursor2 = connection.cursor()
1097
+
1098
+
# Insert data using cursor1
1099
+
cursor1.execute("INSERT INTO test_table VALUES (1, 'shared_data')")
1100
+
1101
+
# cursor2 can immediately see cursor1's changes within the same transaction
1102
+
cursor2.execute("SELECT * FROM test_table WHERE id = 1")
1103
+
data = cursor2.fetchall() # Will return the inserted row
1104
+
1105
+
# But external connections cannot see the data until commit
1106
+
cursor1.execute("COMMIT")
1107
+
1108
+
Error handling and recovery
1109
+
---------------------------
1110
+
1111
+
When a statement fails within a transaction, the transaction enters an aborted state. All subsequent statements will fail until you explicitly execute ``ROLLBACK`` to clear the aborted state.
1112
+
1113
+
.. _error_handling_example:
1114
+
1115
+
::
1116
+
1117
+
with connect(auth=auth, account_name="account", database="db", autocommit=False) as connection:
1118
+
cursor = connection.cursor()
1119
+
1120
+
try:
1121
+
cursor.execute("INSERT INTO test_table VALUES (1, 'data')")
1122
+
cursor.execute("INSERT INTO test_table VALUES ('invalid', 123)") # This will fail
1123
+
cursor.execute("COMMIT") # This will fail due to aborted transaction
1124
+
except Exception:
1125
+
# Must explicitly rollback to clear aborted state
1126
+
cursor.execute("ROLLBACK")
1127
+
print("Transaction aborted, rolled back successfully")
1128
+
1129
+
Best practices
1130
+
--------------
1131
+
1132
+
1. **Use autocommit=False for multi-statement operations** that need to be atomic
1133
+
2. **Always handle exceptions** when using transactions and include proper rollback logic
1134
+
3. **Keep transactions short** to minimize lock contention and improve performance
1135
+
4. **Leverage context managers** for automatic transaction cleanup
1136
+
1137
+
Transaction limitations and notes
1138
+
---------------------------------
1139
+
1140
+
**SDK-specific behavior:**
1141
+
1142
+
* ``BEGIN`` and ``BEGIN TRANSACTION`` statements are **not supported** when ``autocommit=False`` because transactions start implicitly with the first SQL statement
1143
+
* When ``autocommit=True``, executing ``BEGIN`` is a no-op and does not start a transaction
1144
+
* All cursors from the same connection share transaction state - there is no cursor-level isolation
1145
+
* Closing connection with ``close`` automatically rolls back uncommitted transactions when ``autocommit=False``
1146
+
1147
+
**Firebolt platform limitations:**
1148
+
1149
+
For complete details on transaction capabilities and limitations, see the
0 commit comments