1

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 Answer 1

2

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);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.