Обсуждение: Returning Vector of Pairs with a PostgreSQL C Extension Function
Hello, I want to return an vector of pairs using a C extension. This is a simple code I have: extern "C" { Datum pair(PG_FUNCTION_ARGS){ // For text aka. character varying parameter text *t1 = PG_GETARG_TEXT_PP(0); text *t2 = PG_GETARG_TEXT_PP(1); std::string localT1 = text_to_cstring(t1); std::string localT2 = text_to_cstring(t2); /* Construct the return value of the C extention to PostgreSQl */ // Returns Text ponter of casted cstrings to text //PG_RETURN_TEXT_P(cstring_to_text_with_len(localT1.c_str(), localT1.size())); //PG_RETURN_TEXT_P(cstring_to_text_with_len(encodedLocalT1.c_str(), encodedLocalT1.size())); //PG_RETURN_TEXT_P(cstring_to_text_with_len(outputParam.c_str(), outputParam.size())); // Return vector of pairs std::vector<std::pair<std::string, std::string>> ret; ret.emplace_back(encodedLocalT1, encodedLocalT2); PG_RETURN_ARRAYTYPE_P(ret); }; PG_FUNCTION_INFO_V1(pair); } But it doesn't work like it. Even using this doesn't work: ArrayType *array; std::vector<std::pair<std::string, std::string>> ret; ret.emplace_back(encodedLocalT1, encodedLocalT2); array = ret; PG_RETURN_POINTER(array); Is it possible to return a vector of pairs? If not then how can I return 2 strings in an Array? Best regards, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
TalGloz <glozmantal@gmail.com> writes: > I want to return an vector of pairs using a C extension. You're going to have to work a lot harder than this: > // Return vector of pairs > std::vector<std::pair<std::string, std::string>> ret; > ret.emplace_back(encodedLocalT1, encodedLocalT2); > PG_RETURN_ARRAYTYPE_P(ret); I do not know what the internal representation of std::vector is in your C++ library, but it seems highly unlikely that it exactly matches what Postgres arrays look like. Nor has Postgres ever heard of a std::pair. You'd need to either create a composite type of two text columns and then build an array of those, or else decide to return a two-dimensional array to represent this case. In any case, you must use Postgres functions to construct the return value; the C++ library is completely useless for that. regards, tom lane
OK so I think I'm on the right way with this code and now my goal is to return a text array (text[ ]) with 2 fields extern "C" { Datum text_array(PG_FUNCTION_ARGS){ // For text aka. character varying parameter text *t1 = PG_GETARG_TEXT_PP(0); text *t2 = PG_GETARG_TEXT_PP(1); std::string localT1 = text_to_cstring(t1); std::string localT2 = text_to_cstring(t2); ArrayType *array; Datum* elements[2]; int16 typlen; bool typbyval; char typalign; elements[0] = CStringGetDatum(localT1.c_str()); elements[1] = CStringGetDatum(localT2.c_str()); get_typlenbyvalalign(TEXTOID, &typlen, &typbyval, &typalign); array = construct_array(elements, 2, TEXTOID, typlen, typbyval, typalign); PG_RETURN_ARRAYTYPE_P(array); }; PG_FUNCTION_INFO_V1(text_array); } But I'm getting this: g++ -std=c++17 -fPIC -Wall -Werror -g -O0 -pthread -I/usr/pgsql-10/include/server -I/usr/local/include -I/usr/local/include/cppcodec -o seal_diff_cpp.o -c seal_diff_cpp.cpp In file included from seal_diff_cpp.cpp:2: seal_diff_cpp.cpp: In function ‘Datum seal_diff_cpp(FunctionCallInfo)’: /usr/pgsql-10/include/server/postgres.h:562:29: error: invalid conversion from ‘Datum’ {aka ‘long unsigned int’} to ‘Datum*’ {aka ‘long unsigned int*’} [-fpermissive] #define PointerGetDatum(X) ((Datum) (X)) ~^~~~~~~~~~~~ /usr/pgsql-10/include/server/postgres.h:584:28: note: in expansion of macro ‘PointerGetDatum’ #define CStringGetDatum(X) PointerGetDatum(X) ^~~~~~~~~~~~~~~ seal_diff_cpp.cpp:168:23: note: in expansion of macro ‘CStringGetDatum’ elements[0] = CStringGetDatum(localT1.c_str()); ^~~~~~~~~~~~~~~ /usr/pgsql-10/include/server/postgres.h:562:29: error: invalid conversion from ‘Datum’ {aka ‘long unsigned int’} to ‘Datum*’ {aka ‘long unsigned int*’} [-fpermissive] #define PointerGetDatum(X) ((Datum) (X)) ~^~~~~~~~~~~~ /usr/pgsql-10/include/server/postgres.h:584:28: note: in expansion of macro ‘PointerGetDatum’ #define CStringGetDatum(X) PointerGetDatum(X) ^~~~~~~~~~~~~~~ seal_diff_cpp.cpp:169:16: note: in expansion of macro ‘CStringGetDatum’ elements[1] = CStringGetDatum(localT2.c_str()); ^~~~~~~~~~~~~~~ seal_diff_cpp.cpp:171:26: error: cannot convert ‘Datum**’ {aka ‘long unsigned int**’} to ‘Datum*’ {aka ‘long unsigned int*’} array = construct_array(elements, 2, TEXTOID, typlen, typbyval, typalign); ^~~~~~~~ In file included from /usr/pgsql-10/include/server/utils/acl.h:38, from /usr/pgsql-10/include/server/catalog/objectaddress.h:18, from /usr/pgsql-10/include/server/catalog/pg_publication.h:21, from /usr/pgsql-10/include/server/utils/rel.h:21, from seal_diff_cpp.cpp:6: /usr/pgsql-10/include/server/utils/array.h:370:42: note: initializing argument 1 of ‘ArrayType* construct_array(Datum*, int, Oid, int, bool, char)’ extern ArrayType *construct_array(Datum *elems, int nelems, ~~~~~~~^~~~~ make: *** [Makefile:19: seal_diff_cpp.o] Error 1 Best regards, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
TalGloz <glozmantal@gmail.com> writes: > Datum* elements[2]; Datum, not Datum*. regards, tom lane
Yes, that was the problem and now everything works. Thanks, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
Hello, I'm having a small problem. The code works but the array cells don't hold the whole length of *localT1* and *localT2* texts, I think it has something to do with *int16 typlen* in my set parameters: ArrayType *array; Datum elements[2]; int16 typlen; bool typbyval; char typalign; elements[0] = CStringGetDatum(localT1.c_str()); elements[1] = CStringGetDatum(localT2.c_str()); get_typlenbyvalalign(TEXTOID, &typlen, &typbyval, &typalign); array = construct_array(elements, 2, TEXTOID, typlen, typbyval, typalign); I can't change the int16 to int32 or int64 because the get_typlenbyvalalign() function takes an int16. Is there a way to change my settings so that each input (in this example the *localT1* and *localT2*) fits fully in each sell? Best regards, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
It looks like changing the elements[0] = CStringGetDatum(localT1.c_str()); elements[1] = CStringGetDatum(localT2.c_str()); to: elements[0] = PointerGetDatum(cstring_to_text(localT1.c_str())); elements[1] = PointerGetDatum(cstring_to_text(localT2.c_str())); Solved the problem. If anyone thinks that there is a better way please tell me. Best regards, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
2018-08-27 14:40 GMT+02:00 TalGloz <glozmantal@gmail.com>:
It looks like changing the
elements[0] = CStringGetDatum(localT1.c_str());
elements[1] = CStringGetDatum(localT2.c_str());
to:
elements[0] = PointerGetDatum(cstring_to_text(localT1.c_str()));
elements[1] = PointerGetDatum(cstring_to_text(localT2.c_str()));
Solved the problem. If anyone thinks that there is a better way please tell
me.
There is not better or worst way - you have to use just correct way, that is one
There are already prepared macros
#define CStringGetTextDatum(s) PointerGetDatum(cstring_to_text(s))
#define TextDatumGetCString(d) text_to_cstring((text *) DatumGetPointer(d))
#define TextDatumGetCString(d) text_to_cstring((text *) DatumGetPointer(d))
you can use it.
Regards
Pavel
Pavel Stehule wrote > There are already prepared macros > > #define CStringGetTextDatum(s) PointerGetDatum(cstring_to_text(s)) > #define TextDatumGetCString(d) text_to_cstring((text *) > DatumGetPointer(d)) Thanks for pointing out the macros, for some reason I've missed them. Best regards, Tal -- Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html