IMHIRED Telegram 393
API курильщика или «Хорошо, что я не психопат»

СУБД Pangolin, в которой я теперь работаю, основана на open-source'ном код PostgreSQL. А значит, наша кодовая база является бесконечным источником говнокода поучительных примеров 😁

Я вчера два часа жизни потратил на отлавливание проезда по памяти. Написал код, который создаёт новый кортеж для последующей вставки его в таблицу. И там segfault 💣 А Postgres — это всякие межпроцессные взаимодействия, синхронизация через общую память и прочие места, где легко накосячить. Ну вот я копал в эту сторону, обложившись GDB, breakpoint'ами и отладочным выводом в логи.

Какого же было моё возмущение, когда я нашёл причину 😬🤬

В Postgres есть довольно простая функция создания нового кортежа — heap_form_tuple:
HeapTuple heap_form_tuple(
TupleDesc tupleDescriptor,
const Datum *values,
const bool *isnull);


Она принимает некое описание кортежа и два массива:
- значения его колонок
- булевский массив, который задаёт, какие колонки имеют значение NULL
Размер этих массивов записан в tupleDescriptor.

А ещё для удобства есть константы, которые задают номера колонок системных таблиц. То есть массив values можно заполнять вот так:
Datum *values = ...;
bool *isnulls = ...;

values[Anum_pg_authid_oid] = ObjectIdGetDatum(roleid);
values[Anum_pg_authid_rolname] = CStringGetDatum("user");
...
isnulls[Anum_pg_authid_rolvaliduntil] = true; // Говорим, что эта колонка имеет значение NULL

HeapTuple tuple = heap_form_tuple(desc, values, isnulls);



И какого же было моё изумление, что нумерация констант Anum_pg_authid_oid, Anum_pg_authid_rolname и т.п. начинается с единицы! С единицы, Карл! В языке, где вся индексация везде осуществляется с нуля! 🤯

И проезд по памяти у меня происходил в строке isnulls[Anum_pg_authid_rolvaliduntil] = true, потому что Anum_pg_authid_rolvaliduntil имеет значение 12, а в массиве isnulls как раз было выделено 12 элементов 🤦‍♂️

А значит, чтобы корректно использовать удобные константы, нужно каждый раз вычитать из них 1 при заполнении кортежа (код ниже взят отсюда):
new_record[Anum_pg_authid_rolname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);


Хороший API легко использовать правильно и тяжело — неправильно. API выше — плохой. Гораздо лучше было бы пронумеровать константы с нуля, а внутри функций, которым нужна нумерация с единицы, делать +1. А так получилось, что деталь реализации пролезла в интерфейс, и отобрала у меня 2 часа на дебаг 😔

Как говорится, пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете. Хорошо, что я не психопат и, разобравшись с этим, продолжил заниматься рабочими задачами. А то ж ведь мог и в git blame полезть 😆



tgoop.com/imhired/393
Create:
Last Update:

API курильщика или «Хорошо, что я не психопат»

СУБД Pangolin, в которой я теперь работаю, основана на open-source'ном код PostgreSQL. А значит, наша кодовая база является бесконечным источником говнокода поучительных примеров 😁

Я вчера два часа жизни потратил на отлавливание проезда по памяти. Написал код, который создаёт новый кортеж для последующей вставки его в таблицу. И там segfault 💣 А Postgres — это всякие межпроцессные взаимодействия, синхронизация через общую память и прочие места, где легко накосячить. Ну вот я копал в эту сторону, обложившись GDB, breakpoint'ами и отладочным выводом в логи.

Какого же было моё возмущение, когда я нашёл причину 😬🤬

В Postgres есть довольно простая функция создания нового кортежа — heap_form_tuple:

HeapTuple heap_form_tuple(
TupleDesc tupleDescriptor,
const Datum *values,
const bool *isnull);


Она принимает некое описание кортежа и два массива:
- значения его колонок
- булевский массив, который задаёт, какие колонки имеют значение NULL
Размер этих массивов записан в tupleDescriptor.

А ещё для удобства есть константы, которые задают номера колонок системных таблиц. То есть массив values можно заполнять вот так:
Datum *values = ...;
bool *isnulls = ...;

values[Anum_pg_authid_oid] = ObjectIdGetDatum(roleid);
values[Anum_pg_authid_rolname] = CStringGetDatum("user");
...
isnulls[Anum_pg_authid_rolvaliduntil] = true; // Говорим, что эта колонка имеет значение NULL

HeapTuple tuple = heap_form_tuple(desc, values, isnulls);



И какого же было моё изумление, что нумерация констант Anum_pg_authid_oid, Anum_pg_authid_rolname и т.п. начинается с единицы! С единицы, Карл! В языке, где вся индексация везде осуществляется с нуля! 🤯

И проезд по памяти у меня происходил в строке isnulls[Anum_pg_authid_rolvaliduntil] = true, потому что Anum_pg_authid_rolvaliduntil имеет значение 12, а в массиве isnulls как раз было выделено 12 элементов 🤦‍♂️

А значит, чтобы корректно использовать удобные константы, нужно каждый раз вычитать из них 1 при заполнении кортежа (код ниже взят отсюда):
new_record[Anum_pg_authid_rolname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);


Хороший API легко использовать правильно и тяжело — неправильно. API выше — плохой. Гораздо лучше было бы пронумеровать константы с нуля, а внутри функций, которым нужна нумерация с единицы, делать +1. А так получилось, что деталь реализации пролезла в интерфейс, и отобрала у меня 2 часа на дебаг 😔

Как говорится, пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете. Хорошо, что я не психопат и, разобравшись с этим, продолжил заниматься рабочими задачами. А то ж ведь мог и в git blame полезть 😆

BY Илья Шишков: код, собесы, IT


Share with your friend now:
tgoop.com/imhired/393

View MORE
Open in Telegram


Telegram News

Date: |

Ng Man-ho, a 27-year-old computer technician, was convicted last month of seven counts of incitement charges after he made use of the 100,000-member Chinese-language channel that he runs and manages to post "seditious messages," which had been shut down since August 2020. In handing down the sentence yesterday, deputy judge Peter Hui Shiu-keung of the district court said that even if Ng did not post the messages, he cannot shirk responsibility as the owner and administrator of such a big group for allowing these messages that incite illegal behaviors to exist. Members can post their voice notes of themselves screaming. Interestingly, the group doesn’t allow to post anything else which might lead to an instant ban. As of now, there are more than 330 members in the group. How to create a business channel on Telegram? (Tutorial) Hashtags are a fast way to find the correct information on social media. To put your content out there, be sure to add hashtags to each post. We have two intelligent tips to give you:
from us


Telegram Илья Шишков: код, собесы, IT
FROM American