How to insert text array in PostgreSQL table in binary format using libpq?

社会主义新天地 提交于 2019-12-11 16:21:03

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!