Расширение libpqxx C Aggregate возвращает неверные данные?

Я изучаю, как создавать совокупные расширения C и использовать libpqxx с C++ на стороне клиента для обработки данных.

У моего игрушечного агрегатного расширения есть один аргумент типа bytea, а состояние также имеет тип bytea. Ниже приведен самый простой пример моей проблемы:

Сторона сервера:

PG_FUNCTION_INFO_V1( simple_func );

Datum simple_func( PG_FUNCTION_ARGS ){

    bytea *new_state   = (bytea *) palloc( 128 + VARHDRSZ );
    memset(new_state, 0, 128 + VARHDRSZ );
    SET_VARSIZE( new_state,128 + VARHDRSZ );

    PG_RETURN_BYTEA_P( new_state );

}

Сторона клиента:

std::basic_string< std::byte > buffer;

pqxx::connection c{"postgresql://user:simplepassword@localhost/contrib_regression"};
pqxx::work w(c);
c.prepare( "simple_func", "SELECT simple_func( $1 )  FROM table" );
pqxx::result r = w.exec_prepared( "simple_func", buffer );

for (auto row: r){ 
        cout << "  Result Size: " << row[ "simple_func" ].size() << endl;
        cout << "Raw Result Data: ";
        for( int jj=0; jj < row[ "simple_func" ].size(); jj++ ) printf( "%02" PRIx8,   (uint8_t) row[ "simple_func" ].c_str()[jj] )  ;
        cout << endl;
}

Результат на стороне клиента печатает:

Result Size: 258
Raw Result Data: 5c783030303030303030303030303030...

Где шаблон 30 повторяется до конца строки, а напечатанная строка в шестнадцатеричном формате составляет 512 байт.

Я ожидал получить массив длиной 128 байт, где каждый байт равен нулю. Что я делаю не так?

Версия libpqxx — 7.2, а PostgreSQL 12 — в Ubuntu 20.04.

Дополнение

Установка оператора расширения sql;

CREATE OR REPLACE FUNCTION agg_simple_func( state bytea, arg1 bytea)
RETURNS bytea
AS '$libdir/agg_simple_func'
LANGUAGE C IMMUTABLE STRICT;

CREATE OR REPLACE AGGREGATE simple_func( arg1 bytea)  
(
    sfunc = agg_simple_func,
    stype = bytea,
    initcond = "\xFFFF" 
);

person ReverseFlow    schedule 02.10.2020    source источник


Ответы (2)


Похоже, что ответ заключается в том, что данные типа bytea на стороне клиента должны быть получены следующим образом в библиотеке libpqxx версии 7.0 (не тестировалось в более ранних версиях):

row[ "simple_func" ].as<std::basic_string<std::byte>>()

Это извлекает правильные данные bytea без каких-либо преобразований, особенностей строк или неожиданного поведения, как я видел.

person ReverseFlow    schedule 02.10.2020
comment
Хорошо, что вы решили проблему - я мог помочь только с серверной частью. Но вы видите, что есть смысл исследовать обе стороны независимо друг от друга, если это возможно. - person Laurenz Albe; 05.10.2020
comment
@LaurenzAlbe Да. Спасибо. - person ReverseFlow; 06.10.2020

Рекомендую заниматься этими вещами по порядку: сначала заставить функцию работать, тестируя ее с помощью psql в интерактивных запросах, затем писать клиентский код (или наоборот).

Я не могу говорить о libpqxx, но я должен пожаловаться на вашу функцию: то, что вы представили, даже не скомпилируется, потому что вы написали DATUM в верхнем регистре и забыли заголовки и другие важные вещи.

Эта функция будет скомпилирована и запущена, как вы ожидаете:

#include "postgres.h"
#include "fmgr.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(simplest_func);

Datum simplest_func(PG_FUNCTION_ARGS) {
    bytea *new_state = (bytea *) palloc(128 + VARHDRSZ);
    memset(new_state, 0, 128 + VARHDRSZ);
    SET_VARSIZE(new_state, 128 + VARHDRSZ);

    PG_RETURN_BYTEA_P(new_state);
}

memset будет работать таким образом, но лучший, более идиоматический и надежный способ установить значение varlena это

    memset(VARDATA(new_state), 0, 128);

Я понятия не имею, как вы получили свой результат, но поскольку представленный вами код не компилируется, я не знаю, как на самом деле выглядит ваша функция.

person Laurenz Albe    schedule 02.10.2020
comment
Заглавная дата была опечаткой с моей стороны. Я должен был включить шаблонную пластину для полного расширения, но тогда есть часть psql и т. Д. ... Где-то нужно остановиться. Но это моя функция, я тестирую точно такой же код. Я удалил все остальное, сократил его до этого примера и протестировал его, и я получил результат, о котором я упоминал, с libpqxx. Я подозреваю, что проблема может быть в libpqxx, но я не знаю, что не так. Что касается memset, я хотел обнулить все, включая заголовок, чтобы знать, чего ожидать на стороне клиента. Возможно, это было лишним. - person ReverseFlow; 02.10.2020
comment
Возможно, вам следует поделиться утверждением CREATE FUNCTION. - person Laurenz Albe; 02.10.2020
comment
Я добавил оператор sql для создания совокупного расширения. - person ReverseFlow; 02.10.2020
comment
Это вызовет другую функцию! Должно быть CREATE FUNCTION ... AS '$libdir/agg_simple_func', 'simplest_func';. - person Laurenz Albe; 02.10.2020
comment
Нет, я уверен, что понял правильно. Это единственные расширения, которые я определил, и я не получаю ошибок при их вызове. Кроме того, \da показывает мои агрегатные функции, и появляется определенная. - person ReverseFlow; 02.10.2020
comment
В документации сказано: AS obj_file, link_symbol: Строка obj_file — это имя файла разделяемой библиотеки, содержащего скомпилированную функцию C, и интерпретируется как команда LOAD. Строка link_symbol — это символ ссылки на функцию, то есть имя функции в исходном коде на языке C. Если символ ссылки опущен, предполагается, что он совпадает с именем определяемой функции SQL.. Но это simple_func в вашем случае. - person Laurenz Albe; 02.10.2020
comment
Я исправил опечатку, везде должно быть simple_func, а не simple_func. Кроме того, я думаю, что это в случае, если obj_file и link_symbol разные, что полезно знать :). В моем случае они оба одинаковы. - person ReverseFlow; 02.10.2020