Managing PostgreSQL Transactions from Python using Psycopg2
If you’re working with the PostgreSQL database using Python, you’ll likely need to manage transactions. Transactions are sequences of database operations that are executed as a single unit of work.
In this article, we will explore how to manage PostgreSQL transactions from Python using the popular Psycopg2 library.
Enabling and Disabling auto-commit Mode
By default, Psycopg2 is in auto-commit mode. This means that each SQL statement sent to PostgreSQL is automatically committed to the database.
While this mode is convenient for running one-off queries, it’s not appropriate for managing transactions. To enable transaction management, we need to disable auto-commit mode.
We can do this by setting the autocommit property of the connection object to False:
import psycopg2
conn = psycopg2.connect(database="my_database", user="my_user", password="my_password", host="my_host", port="my_port")
conn.autocommit = False
By disabling autocommit, we tell Psycopg2 that we want to manage transactions manually.
Committing Transactions
Once we have disabled autocommit mode, we can begin managing transactions. After executing a sequence of SQL statements, we need to commit the transaction to the database:
conn.commit()
The commit()
method sends all SQL statements that were executed as part of the transaction to the database.
It also frees up any resources that were allocated during the transaction. Committing the transaction ensures that any changes made to the database are persisted, meaning they’re made permanent.
Maintaining ACID properties of Transactions
When managing transactions, it’s important to maintain the ACID properties of the database. ACID stands for Atomicity, Consistency, Isolation, and Durability.
- Atomicity means that a transaction is treated as a single, indivisible operation.
- Consistency means that a transaction leaves the database in a valid state.
- Isolation means that multiple transactions can safely execute concurrently without interfering with each other.
- Durability means that once a transaction is committed, its changes are permanent and won’t be lost in the event of a power outage or other failure.
Rolling Back Transactions
Sometimes a transaction may fail, and we need to revert any changes made to the database. We can do this by rolling back the transaction:
conn.rollback()
The rollback()
method undoes any changes made to the database as part of the transaction.
It also frees up any resources that were allocated during the transaction. Rolling back a transaction ensures that any changes made to the database are discarded, meaning they’re not made permanent.
Psycopg2 Transactions Control
Now that we know how to manage transactions, let’s look at how we can control them in Psycopg2.
Connection Object and Transaction Handling
The connection object is the key to managing transactions in Psycopg2. We use the connection object to execute SQL statements, manage transactions, and interact with the PostgreSQL server.
When we disable autocommit mode, we’re telling the connection object that we want to manage transactions manually. We can then use the commit()
and rollback()
methods to commit or roll back transactions.
Cursor Object and Database Operations
In addition to the connection object, we also use the cursor object to execute SQL statements and interact with the database. The cursor object maintains state information about the current transaction and the current result set.
We can create a cursor object using the cursor()
method of the connection object:
cur = conn.cursor()
Once we have a cursor object, we can execute SQL statements using the execute()
method:
cur.execute("SELECT * FROM my_table")
The cursor object is used to execute SQL statements within the context of a transaction. We can also use the cursor object to fetch results from the database using the fetchall()
and fetchone()
methods:
rows = cur.fetchall()
row = cur.fetchone()
Commit and Rollback Methods
We’ve already discussed the commit()
and rollback()
methods, but it’s worth reiterating how they work in Psycopg2. The commit()
method sends all SQL statements executed as part of the transaction to the database and frees up any resources that were allocated during the transaction.
The rollback()
method undoes any changes made to the database as part of the transaction and frees up any resources that were allocated during the transaction. These methods are critical to managing transactions in Psycopg2.
Autocommit
As mentioned earlier, Psycopg2 is in auto-commit mode by default. While this mode is convenient for one-off queries, it’s not appropriate for managing transactions.
When we disable autocommit mode, we can begin managing transactions manually. This allows us to maintain the ACID properties of the database and ensure that changes to the database are persisted only when we want them to be.
Conclusion
In conclusion, managing transactions in PostgreSQL from Python using Psycopg2 is a key skill for any developer working with databases. By learning how to disable autocommit mode, manage transactions, maintain ACID properties, and control transactions using the connection and cursor objects, we can write robust and reliable code that interacts safely with the database.
3) Python Example to Manage PostgreSQL Transactions
Using the Psycopg2 library, Python developers can manage PostgreSQL transactions. The following code snippet shows how to create a connection to a PostgreSQL database, disable autocommit mode, and manage transactions manually:
import psycopg2
# create a connection to the PostgreSQL database
conn = psycopg2.connect(database="my_database", user="my_user", password="my_password", host="my_host", port="my_port")
# disable autocommit mode
conn.autocommit = False
# create a cursor object
cur = conn.cursor()
try:
# begin a transaction
cur.execute("BEGIN;")
# execute some SQL statements
cur.execute("INSERT INTO my_table (col1, col2) VALUES (%s, %s);", (val1, val2))
cur.execute("UPDATE my_table SET col1 = %s WHERE col2 = %s;", (new_val1, val2))
# commit the transaction
conn.commit()
except Exception as e:
# rollback the transaction
conn.rollback()
print("Error: ", e)
finally:
# close the cursor and connection objects
cur.close()
conn.close()
In this example, we use the BEGIN;
statement to start a new transaction. We then execute some SQL statements using the cursor object, and if all of them complete successfully, we commit the transaction using the commit()
method.
If an exception is raised during the transaction, we use the rollback()
method to undo any changes made to the database. Finally, we close the cursor and connection objects to free up any resources that were allocated during the transaction.
4) Using the With Statement to Control Transactions
While the above example works, it can be cumbersome and error-prone to manually manage transactions in this way. Fortunately, we can use Python’s with
statement and context managers to make transaction management more convenient and less error-prone.
Context Managers and Psycopg2
Context managers are Python objects that provide a convenient way to manage resources. They ensure that resources are properly acquired and released, even in the face of exceptions or other errors.
With the with
statement, we can ensure that context managers are used properly, regardless of what happens in the code block. In the case of Psycopg2, we can use the connection and cursor objects as context managers to ensure that they’re properly closed when we’re done using them.
This has the added benefit of automatically managing transactions for us. To use the connection object as a context manager, we can do the following:
import psycopg2
with psycopg2.connect(database="my_database", user="my_user", password="my_password", host="my_host", port="my_port") as conn:
with conn.cursor() as cur:
# execute some SQL statements
cur.execute("INSERT INTO my_table (col1, col2) VALUES (%s, %s);", (val1, val2))
cur.execute("UPDATE my_table SET col1 = %s WHERE col2 = %s;", (new_val1, val2))
In this example, we use the with
statement to create a connection to the PostgreSQL database and a cursor object. We then execute some SQL statements using the cursor object.
When the with
block is exited, the cursor and connection objects are automatically closed, and any changes made to the database during the transaction are committed.
Advantages of With Statement
Using the with
statement to manage transactions has several advantages over manually managing transactions:
- It ensures that the connection and cursor objects are properly closed, regardless of what happens in the code block.
- It automatically manages transactions for us, so we don’t have to worry about committing or rolling back transactions manually.
- It reduces the amount of boilerplate code needed to manage transactions, making our code simpler and easier to read.
Demo of Online Shopping Transaction
Let’s say we want to implement an online shopping transaction using PostgreSQL and Psycopg2. We want to add some items to a customer’s cart, update their order history, and deduct the price of the items from their account balance.
We can use the with
statement to make this transaction safe and reliable, even in the face of exceptions or other errors. Here’s an example:
import psycopg2
def add_to_cart(customer_id, item_id):
with psycopg2.connect(database="my_database", user="my_user", password="my_password", host="my_host", port="my_port") as conn:
with conn.cursor() as cur:
try:
# begin transaction
cur.execute("BEGIN;")
# add item to cart
cur.execute("INSERT INTO cart (customer_id, item_id) VALUES (%s, %s);", (customer_id, item_id))
# update order history
cur.execute("INSERT INTO order_history (customer_id, item_id, order_date) VALUES (%s, %s, NOW());", (customer_id, item_id))
# deduct item price from account balance
cur.execute("UPDATE customers SET account_balance = account_balance - (SELECT price FROM items WHERE id = %s) WHERE id = %s;", (item_id, customer_id))
# commit transaction
conn.commit()
print("Item added to cart successfully!")
except Exception as e:
# rollback transaction
conn.rollback()
print("Error: ", e)
In this example, we use the with
statement to manage the transaction. We begin by adding the item to the customer’s cart, then update their order history, and finally deduct the price of the item from their account balance.
If any of these steps fail, we use the rollback()
method to undo any changes made to the database.
Conclusion
Using Psycopg2, Python developers can manage PostgreSQL transactions easily and efficiently. We can control transactions manually using the connection and cursor objects, or we can use the with
statement to manage transactions automatically.
Regardless of which approach we take, it’s important to maintain the ACID properties of the database to ensure data integrity, consistency, and durability. 5) Psycopg2’s Isolation Level Constants
Isolation Levels in Database Systems
An isolation level is a property of transactions in database systems that defines how changes made by one transaction are visible to other transactions. Isolation levels are important for maintaining transaction integrity and protecting against data inconsistencies.
The four standard isolation levels in database systems are:
- READ UNCOMMITTED: This level allows dirty reads, meaning that a transaction can read data that has been modified by another transaction but not yet committed. This level has the lowest level of isolation and can lead to data inconsistencies.
- READ COMMITTED: This level allows transactions to see only those changes that have been committed by other transactions. This level is safer than READ UNCOMMITTED but can still lead to non-repeatable reads and phantom reads.
- REPEATABLE READ: This level ensures that, within a transaction, a row is read the same way multiple times. This level uses shared locks to prevent concurrent modifications or deletions of data.
- SERIALIZABLE: This level provides the highest level of isolation and guarantees that transactions execute as if they were executed sequentially. This level uses strict locking to ensure that concurrent access to data does not produce non-repeatable reads or phantom reads.
Available Isolation Levels in Psycopg2
Psycopg2 provides constants for the four standard isolation levels and an additional level, SNAPSHOT. SNAPSHOT is a vendor-specific isolation level supported by PostgreSQL that provides a consistent snapshot view of the database for all transactions.
The constants for the standard isolation levels are:
psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED
psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE
The constant for the SNAPSHOT isolation level is:
psycopg2.extensions.ISOLATION_LEVEL_SNAPSHOT
Setting Isolation Levels
To set the isolation level for a Psycopg2 connection, we can use the connection’s set_session
method. Here’s how:
import psycopg2
# create a connection to the PostgreSQL database
conn = psycopg2.connect(database="my_database", user="my_user", password="my_password", host="my_host", port="my_port")
# set the isolation level to REPEATABLE READ
conn.set_session(isolation_level=psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ)
# create a cursor object
cur = conn.cursor()
# execute some SQL statements
cur.execute("SELECT * FROM my_table")
# close the cursor and connection objects
cur.close()
conn.close()
In this example, we create a connection to a PostgreSQL database and set the isolation level to REPEATABLE READ using the set_session
method. We then create a cursor object and execute some SQL statements using the cursor.
When we’re done, we close the cursor and connection objects to free up any resources that were allocated during the transaction. Note that the set_session
method is part of the connection class, so we must have a connection object before we can set the isolation level.
Conclusion
Managing isolation levels is an important aspect of managing database transactions. Psycopg2 provides constants for the standard isolation levels and a vendor-specific level supported by PostgreSQL.
By setting the isolation level using the connection’s set_session
method, we can ensure that our transactions maintain the appropriate level of isolation and preserve transaction integrity. In conclusion, managing transactions and isolation levels in PostgreSQL from Python using Psycopg2 is crucial for ensuring transaction integrity, data consistency, and durability in database systems.
With the ability to enable, disable, commit, roll back and maintain ACID properties of transactions, developers can create robust and reliable code that interacts safely with the database. Additionally, by using the with
statement and setting isolation level constants, Python developers can improve transaction management and maintain the appropriate level of isolation for their transactions.
Overall, it is essential to have a solid understanding of Psycopg2 library and PostgreSQL database in order to manage transactions properly and ensure data consistency.