Периодически возникает желание положить в базу «неположимое» — например, засунуть очень длинную строку. Нет, записать ее в поле таблицы — для PostgreSQL проблем нет, но вот в индекс…
Проблема в том, что вся строка (ROW) индекса целиком должна полностью умещаться на одной странице данных (8KB), иначе вас ждет примерно такая ошибка:
Можно, конечно, сделать функциональный индекс по substring, но тогда это же выражение придется протаскивать и во все запросы, что совсем не добавляет счастья.
Аналогичные грабли ждут нас и при попытке отправить слишком большой (больше 8000 байт) payload в NOTIFY.
Вроде бы все должно быть предельно просто — возьми да отрежь (или порежь дольками, если надо) строку по 8K. Только вот по 8K — байт, а строки-то у нас на JavaScript (подставить язык по вкусу) все сплошь из unicode-символов, которые еще и длину могут иметь в байтах переменную. А заносить в базу «полувалидную» строку как-то совсем нехорошо.
А что будет, если мы строку все-таки порежем «посередине» символа? Тут нам на помощь приходит стандарт UNICODE:
Итого, если нам целиком вся-вся строка не очень дорога (например, с текстом ошибки, от которого нам важно только начало), подрежем ее до последнего целого символа под требуемый размер в байтах:
Проблема в том, что вся строка (ROW) индекса целиком должна полностью умещаться на одной странице данных (8KB), иначе вас ждет примерно такая ошибка:
ERROR: index row size… exceeds maximum… for index ...То есть даже в простейшем случае индекса из единственной строки — можно наступить на грабли. Как с ними бороться?
Можно, конечно, сделать функциональный индекс по substring, но тогда это же выражение придется протаскивать и во все запросы, что совсем не добавляет счастья.
Аналогичные грабли ждут нас и при попытке отправить слишком большой (больше 8000 байт) payload в NOTIFY.
Вроде бы все должно быть предельно просто — возьми да отрежь (или порежь дольками, если надо) строку по 8K. Только вот по 8K — байт, а строки-то у нас на JavaScript (подставить язык по вкусу) все сплошь из unicode-символов, которые еще и длину могут иметь в байтах переменную. А заносить в базу «полувалидную» строку как-то совсем нехорошо.
А что будет, если мы строку все-таки порежем «посередине» символа? Тут нам на помощь приходит стандарт UNICODE:
U+FFFD � REPLACEMENT CHARACTER used to replace an unknown, unrecognized or unrepresentable characterОказывается, если мы попытаемся преобразовать такую «порезаную побайтово» строку обратно, на конце мы получим символ с кодом FFFD:
Итого, если нам целиком вся-вся строка не очень дорога (например, с текстом ошибки, от которого нам важно только начало), подрежем ее до последнего целого символа под требуемый размер в байтах:
const MAX_BYTE_LEN = 8191;
let buf = Buffer.from(errorString);
if (buf.length > MAX_BYTE_LEN) {
errorString = buf.slice(0, MAX_BYTE_LEN).toString().replace(/\uFFFD$/, '');
}