51.30. pg_largeobject
В каталоге pg_largeobject
содержатся данные, образующие «большие объекты». Большой объект идентифицируется по OID, назначаемому при его создании. Каждый большой объект разделяется на сегменты или «страницы», достаточно небольшие для удобного размещения в строках таблицы pg_largeobject
. Объём данных на странице определяется как LOBLKSIZE
(что в настоящее время составляет BLCKSZ/4
, то есть обычно 2 Кб).
До PostgreSQL 9.0 большие объекты не были связаны с механизмом разрешений. В результате таблица pg_largeobject
была доступна на чтение для всех и через неё можно было получить OID (и содержимое) всех больших объектов в системе. Теперь это не так; для получения списка OID больших объектов нужно обратиться к pg_largeobject_metadata
.
Таблица 51.30. Столбцы pg_largeobject
Тип столбца Описание |
---|
Идентификатор большого объекта, включающего эту страницу |
Номер этой страницы в большом объекте (начиная с нуля) |
Собственно данные, хранящиеся в большом объекте. Их объём не может превышать |
В каждой строке pg_largeobject
содержатся данные для одной строки большого объекта, начиная со смещения (pageno * LOBLKSIZE
) внутри него (в байтах). Эта реализация допускает разреженное хранилище: страницы могут отсутствовать и могут быть короче LOBLKSIZE
, даже если это не последние страницы объектов. Пропущенные области в большом объекте будут считываться как нулевые.
45.8. Explicit Subtransactions
Recovering from errors caused by database access as described in Section 45.7.2 can lead to an undesirable situation where some operations succeed before one of them fails, and after recovering from that error the data is left in an inconsistent state. PL/Python offers a solution to this problem in the form of explicit subtransactions.
45.8.1. Subtransaction Context Managers
Consider a function that implements a transfer between two accounts:
CREATE FUNCTION transfer_funds() RETURNS void AS $$ try: plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") except plpy.SPIError as e: result = "error transferring funds: %s" % e.args else: result = "funds transferred correctly" plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) plpy.execute(plan, [result]) $$ LANGUAGE plpythonu;
If the second UPDATE
statement results in an exception being raised, this function will report the error, but the result of the first UPDATE
will nevertheless be committed. In other words, the funds will be withdrawn from Joe's account, but will not be transferred to Mary's account.
To avoid such issues, you can wrap your plpy.execute
calls in an explicit subtransaction. The plpy
module provides a helper object to manage explicit subtransactions that gets created with the plpy.subtransaction()
function. Objects created by this function implement the context manager interface. Using explicit subtransactions we can rewrite our function as:
CREATE FUNCTION transfer_funds2() RETURNS void AS $$ try: with plpy.subtransaction(): plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") except plpy.SPIError as e: result = "error transferring funds: %s" % e.args else: result = "funds transferred correctly" plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) plpy.execute(plan, [result]) $$ LANGUAGE plpythonu;
Note that the use of try
/except
is still required. Otherwise the exception would propagate to the top of the Python stack and would cause the whole function to abort with a PostgreSQL error, so that the operations
table would not have any row inserted into it. The subtransaction context manager does not trap errors, it only assures that all database operations executed inside its scope will be atomically committed or rolled back. A rollback of the subtransaction block occurs on any kind of exception exit, not only ones caused by errors originating from database access. A regular Python exception raised inside an explicit subtransaction block would also cause the subtransaction to be rolled back.
45.8.2. Older Python Versions
Context managers syntax using the with
keyword is available by default in Python 2.6. For compatibility with older Python versions, you can call the subtransaction manager's __enter__
and __exit__
functions using the enter
and exit
convenience aliases. The example function that transfers funds could be written as:
CREATE FUNCTION transfer_funds_old() RETURNS void AS $$ try: subxact = plpy.subtransaction() subxact.enter() try: plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") except: import sys subxact.exit(*sys.exc_info()) raise else: subxact.exit(None, None, None) except plpy.SPIError as e: result = "error transferring funds: %s" % e.args else: result = "funds transferred correctly" plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) plpy.execute(plan, [result]) $$ LANGUAGE plpythonu;