如何使用libpq以二进制格式在PostgreSQL表中插入文本数组?

时间:2018-03-07 16:25:49

标签: c++ c database postgresql libpq

我没有找到任何描述如何使用libpq在PostgreSQL中使用二进制数组的文档。 所以你有桌子:

CREATE TABLE IF NOT EXISTS test_array ( array_column text[] )

您希望使用PQexecParams以二进制格式插入数组 这是怎么做到的?

1 个答案:

答案 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);
}