我没有找到任何描述如何使用libpq在PostgreSQL中使用二进制数组的文档。 所以你有桌子:
CREATE TABLE IF NOT EXISTS test_array ( array_column text[] )
您希望使用PQexecParams
以二进制格式插入数组
这是怎么做到的?
答案 0 :(得分:0)
下面描述的所有内容都在PostgreSQL 9.5上进行了测试。
PQexecParams有以下原型:
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
可以在
/postgresql_sources_folder/src/include/catalog/pg_type.h
在我们的案例中,我们需要其中两个:
#define TEXTARRAYOID 1009
#define TEXTOID 25
外部PostgreSQL二进制数组的确切格式可以从函数推导出来:
Datum array_send(PG_FUNCTION_ARGS);
在此定义:
/postgresql_sources_folder/src/backend/utils/adt/arrayfuncs.c
我在结构中描述格式:
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__));
数组元素存储为结构:
struct varlena
{
/* -1 if data = NULL */
int32_t vl_len;
/* our data */
char vl_dat[];
}__attribute__ ((__packed__));
同样可以应用于以二进制格式检索数组。
以二进制格式插入文本数组到完整示例:
#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);
}