9.3. Математические функции и операторы
Математические операторы определены для множества типов Postgres Pro. Как работают эти операции с типами, для которых нет стандартных соглашений о математических действиях (например, с типами даты/времени), мы опишем в последующих разделах.
В Таблице 9.4 перечислены все доступные математические операторы.
Таблица 9.4. Математические операторы
Оператор | Описание | Пример | Результат |
---|---|---|---|
+ | сложение | 2 + 3 | 5 |
- | вычитание | 2 - 3 | -1 |
* | умножение | 2 * 3 | 6 |
/ | деление (при целочисленном делении остаток отбрасывается) | 4 / 2 | 2 |
% | остаток от деления | 5 % 4 | 1 |
^ | возведение в степень (вычисляется слева направо) | 2.0 ^ 3.0 | 8 |
|/ | квадратный корень | |/ 25.0 | 5 |
||/ | кубический корень | ||/ 27.0 | 3 |
! | факториал (устаревший оператор, его заменяет функция factorial() ) | 5 ! | 120 |
!! | факториал в префиксной форме (устаревший оператор, его заменяет функция factorial() ) | !! 5 | 120 |
@ | модуль числа (абсолютное значение) | @ -5.0 | 5 |
& | битовый AND | 91 & 15 | 11 |
| | битовый OR | 32 | 3 | 35 |
# | битовый XOR | 17 # 5 | 20 |
~ | битовый NOT | ~1 | -2 |
<< | битовый сдвиг влево | 1 << 4 | 16 |
>> | битовый сдвиг вправо | 8 >> 2 | 2 |
Битовые операторы работают только с целочисленными типами данных и с битовыми строками bit
и bit varying
, как показано в Таблице 9.13.
В Таблице 9.5 перечислены все существующие математические функции. Сокращение dp
в ней обозначает тип double precision
(плавающее с двойной точностью). Многие из этих функций имеют несколько форм с разными типами аргументов. За исключением случаев, где это указано явно, любая форма функции возвращает результат того же типа, что и аргумент. Функции, работающие с данными double precision
, в массе своей используют реализации из системных библиотек сервера, поэтому точность и поведение в граничных случаях может зависеть от системы сервера.
Таблица 9.5. Математические функции
В Таблице 9.6 перечислены все функции для генерации случайных чисел.
Таблица 9.6. Случайные функции
Характеристики значений, возвращаемых функцией
зависят от системы. Для применения в криптографии они непригодны; альтернативы описаны в pgcrypto.random()
Наконец, в Таблице 9.7 перечислены все имеющиеся тригонометрические функции. Все эти функции принимают аргументы и возвращают значения типа double precision
. У каждой функции имеются две вариации: одна измеряет углы в радианах, а вторая — в градусах.
Таблица 9.7. Тригонометрические функции
Примечание
Также можно работать с углами в градусах, применяя вышеупомянутые функции преобразования единиц
и radians()
. Однако предпочтительнее использовать тригонометрические функции с градусами, так как это позволяет избежать ошибок округления в особых случаях, например, при вычислении degrees()
sind(30)
.
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;