Lars Kakavandi-Nielsen
Lars Kakavandi-Nielsen

Reputation: 2198

Storing std::vector in postgresql with pqxx and retrieving it from the database.

I need to store the content of an std::vector<uint8_t> in a postgresql database and be able to get it out again. I am using the library pqxx as the "wrapper" for database connection. For storing I do this:

      std::string query = "INSERT INTO file_pivot(file_id, p_id, deviation) VALUES($1, $2, $3) RETURNING id";

      pqxx::binarystring deviation((void*)&(data), data.size());

      conn.prepare("file_insert", query);
      pqxx::result res = worker.prepared("file_insert")(1)(pivot_id)(deviation).exec();

      worker.commit();

Which does work and store it in the data base, from what I can tell, the issue then getting the damn vector out again.

What I tried to do is this:

      pqxx::work sworker(conn);
      std::string squery = "SELECT * FROM file_pivot WHERE file_id = $1";

      conn.prepare("select_file", squery);
      pqxx::result sres = sworker.prepared("select_file")(file_id).exec();

      std::vector<uint8_t> rdata;
      if(sres.size() > 0)
      {
          pqxx::binarystring sblob(res[0][3]);
          std::vector<uint8_t>*rrdata = (std::vector<uint8_t>*) sblob.data();
          rdata = *rrdata;
      }

But when I then compare the original vector data to rdata like this:

      assert(std::equal(data.begin(), data.end(), rdata.begin()));

I get an segmentation fault, I have tried by using rrdata->begin() as well same problem. Can someone help with this, it is starting to get on my nerves.

Upvotes: 0

Views: 1466

Answers (1)

Thomas
Thomas

Reputation: 181825

pqxx::binarystring::data does not return a pointer to a vector, so you can't just treat it as one like you're doing here:

          std::vector<uint8_t>*rrdata = (std::vector<uint8_t>*) sblob.data();

Rather, the data() function returns a pointer to the raw data. To construct a vector out of this, the simplest way is to use the iterators provided by pqxx::binarystring, like so:

std::vector<uint8_t> rdata(sblob.begin(), sblob.end());

Because the iterators are typedef'd as plain old pointers, the std::vector constructor will be able to subtract them to allocate the right size for the vector in one go.

Note that this does make a copy of the data; there is no way to construct a vector from existing memory, because a vector always¹ owns the memory it points to.


Instead of using a std::vector<uint8_t>, it might be easier to use std::string instead. A string in C++ is just a series of chars (bytes), not characters, and pqxx::binarystring has built-in conversions from and to std::string.


¹ Nitpicker's corner: unless you use a suitable custom allocator. Not recommended.

Upvotes: 1

Related Questions