问题
I didn't find any documentation describing how to work with binary arrays in PostgreSQL using libpq. So you have table:
CREATE TABLE IF NOT EXISTS test_array ( array_column text[] )
You want to insert array in binary format using PQexecParams
How exactly one does this?
回答1:
Everything described bellow is tested on PostgreSQL 9.5.
PQexecParams has the following prototype:
PGresult *PQexecParams(PGconn *conn,
const char *command,
int nParams,
const Oid *paramTypes,
const char * const *paramValues,
const int *paramLengths,
const int *paramFormats,
int resultFormat);
https://www.postgresql.org/docs/current/static/libpq-exec.html
Oid
for text array and other types can be found in
/postgresql_sources_folder/src/include/catalog/pg_type.h
In our case we need 2 of them:
#define TEXTARRAYOID 1009
#define TEXTOID 25
The exact format of external PostgreSQL binary arrays can be deduced from function:
Datum array_send(PG_FUNCTION_ARGS);
Defined here:
/postgresql_sources_folder/src/backend/utils/adt/arrayfuncs.c
I describe format in structure:
struct pgValsT {
/* number of array dimensions */
int32_t ndims;
/* flag describing if array has NULL values */
int32_t hasNull;
/* Oid of data stored in array. In our case is 25 for TEXT */
Oid oidType;
/* Number of elements in array */
int32_t totalLen;
/* Not sure for this one.
I think it describes dimensions of elements in case of arrays storing arrays */
int32_t subDims;
/* Here our data begins */
char dataBegin[];
}__attribute__ ((__packed__));
Array elements are stored as structures:
struct varlena
{
/* -1 if data = NULL */
int32_t vl_len;
/* our data */
char vl_dat[];
}__attribute__ ((__packed__));
Same can be applied for retrieving arrays in binary format.
Full example for insertion text array in binary format to table described in question:
#include <postgresql/libpq-fe.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <cstring>
#define TEXTOID 25
#define TEXTARRAYOID 1009
int main(){
PGconn* conn = PQsetdbLogin("localhost", "5432",
nullptr, nullptr, "my_database", "my_user",
"my_password");
PGresult* res = NULL;
/* our strings to pass as array. Sizes should exclude terminating '\0'
and be presented in network byte order */
const char aStr1[] = "Stackoverflow";
uint32_t pgSizeStr1 = ntohl(sizeof(aStr1) - 1);
const char aStr2[] = "is";
uint32_t pgSizeStr2 = ntohl(sizeof(aStr2) - 1);
const char aStr3[] = "awesome!";
uint32_t pgSizeStr3 = ntohl(sizeof(aStr3) - 1);
/* number of parameters for PQexecParams. We need 1 for sending 1 text array */
int nParams = 1;
/* number of elements in array */
int nElems = 3;
/* our type is text array */
Oid paramTypes[] = { TEXTARRAYOID };
/* Allocating memory for our pgValsT structure + our data. Don't allocate memory for '\0' at the end of a string */
uint32_t dataSize = sizeof(pgValsT) + sizeof(int32_t) * nElems + sizeof(aStr1) + sizeof(aStr2) + sizeof(aStr3) - nElems;
void* vData = malloc(dataSize);
pgValsT* pDataStruct = (pgValsT*)vData;
/* setting up pointer to data to send to PostgreSQL */
char* paramValues[] = { (char*)vData };
/* setting up our element size */
int paramLengths[] = { dataSize };
/* setting binary format for our data */
int paramFormats[] = { 1 };
/* our array has one dimension */
pDataStruct->ndims = ntohl(1);
/* our array has no NULL elements */
pDataStruct->hasNull = ntohl(0);
/* type of our elements is text */
pDataStruct->oidType = ntohl(TEXTOID);
/* our array has 3 elements */
pDataStruct->totalLen = ntohl(nElems);
pDataStruct->subDims = ntohl(1);
/* copy our strings and sizes to data structure excluding terminating '\0' */
size_t byteOffset = 0;
memcpy(pDataStruct->dataBegin, &pgSizeStr1, sizeof(pgSizeStr1));
memcpy(pDataStruct->dataBegin + sizeof(pgSizeStr1), aStr1, sizeof(aStr1) - 1);
byteOffset += sizeof(pgSizeStr1) + sizeof(aStr1) - 1;
memcpy(pDataStruct->dataBegin + byteOffset, &pgSizeStr2, sizeof(pgSizeStr2));
memcpy(pDataStruct->dataBegin + byteOffset + sizeof(pgSizeStr2), aStr2, sizeof(aStr2) - 1);
byteOffset += sizeof(pgSizeStr2) + sizeof(aStr2) - 1;
memcpy(pDataStruct->dataBegin + byteOffset, &pgSizeStr3, sizeof(pgSizeStr3));
memcpy(pDataStruct->dataBegin + byteOffset + sizeof(pgSizeStr3), aStr3, sizeof(aStr3) - 1);
/* executing query */
res = PQexecParams(conn, "INSERT INTO test_array (array_column) values ($1)", 1, paramTypes, paramValues, paramLengths, paramFormats, 1);
PQfinish(conn);
PQclear(res);
free(vData);
}
来源:https://stackoverflow.com/questions/49156762/how-to-insert-text-array-in-postgresql-table-in-binary-format-using-libpq