From 4d2b87dbfb8c34f39a294fb470884dc5feb29b05 Mon Sep 17 00:00:00 2001
From: Michael Davis <michael.davis@cern.ch>
Date: Tue, 21 May 2019 16:39:19 +0200
Subject: [PATCH] [catalogue] Postgres escapes ByteA type before insertion into
 DB

Binary blobs containing zeros are now handled correctly.
---
 catalogue/CatalogueTest.cpp      |  2 +-
 catalogue/PostgresCatalogue.cpp  |  2 +-
 rdbms/Conn.hpp                   |  9 +++++++++
 rdbms/wrapper/PostgresColumn.cpp | 17 +++++++++++++++++
 rdbms/wrapper/PostgresColumn.hpp | 11 +++++++++++
 rdbms/wrapper/PostgresConn.hpp   |  3 ++-
 6 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index bd1607cd5f..50bb22beab 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -3465,7 +3465,7 @@ TEST_P(cta_catalogue_CatalogueTest, createTape_1_tape_with_write_log_1_tape_with
     file1Written.diskFileOwnerUid     = PUBLIC_DISK_USER;
     file1Written.diskFileGid          = PUBLIC_DISK_GROUP;
     file1Written.size                 = fileSize;
-    file1Written.checksumBlob.insert(checksum::ADLER32, "1234");
+    file1Written.checksumBlob.insert(checksum::ADLER32, 0x1000); // tests checksum with embedded zeros
     file1Written.storageClassName     = storageClass.name;
     file1Written.vid                  = vid1;
     file1Written.fSeq                 = 1;
diff --git a/catalogue/PostgresCatalogue.cpp b/catalogue/PostgresCatalogue.cpp
index e3793c1a2f..dc5a8ded68 100644
--- a/catalogue/PostgresCatalogue.cpp
+++ b/catalogue/PostgresCatalogue.cpp
@@ -491,7 +491,7 @@ void PostgresCatalogue::idempotentBatchInsertArchiveFiles(rdbms::Conn &conn,
       archiveFileBatch.diskFileUser.setFieldValue(i, event.diskFileOwnerUid);
       archiveFileBatch.diskFileGroup.setFieldValue(i, event.diskFileGid);
       archiveFileBatch.size.setFieldValue(i, event.size);
-      archiveFileBatch.checksumBlob.setFieldValue(i, event.checksumBlob.serialize());
+      archiveFileBatch.checksumBlob.setFieldByteA(conn, i, event.checksumBlob.serialize());
       archiveFileBatch.storageClassName.setFieldValue(i, event.storageClassName);
       archiveFileBatch.creationTime.setFieldValue(i, now);
       archiveFileBatch.reconciliationTime.setFieldValue(i, now);
diff --git a/rdbms/Conn.hpp b/rdbms/Conn.hpp
index b9849a9b45..4d3f6b140c 100644
--- a/rdbms/Conn.hpp
+++ b/rdbms/Conn.hpp
@@ -209,6 +209,15 @@ public:
    */
   std::list<std::string> getTriggerNames();
 
+  /**
+   * Get a pointer to the connection wrapper implementation
+   *
+   * Required for Postgres PQescapeByteaConn()
+   */
+  wrapper::ConnWrapper *getConnWrapperPtr() {
+    return m_connAndStmts->conn.get();
+  }
+
 private:
 
   /**
diff --git a/rdbms/wrapper/PostgresColumn.cpp b/rdbms/wrapper/PostgresColumn.cpp
index 90636678b2..7e58d0032e 100644
--- a/rdbms/wrapper/PostgresColumn.cpp
+++ b/rdbms/wrapper/PostgresColumn.cpp
@@ -18,6 +18,7 @@
 
 #include "common/exception/Exception.hpp"
 #include "rdbms/wrapper/PostgresColumn.hpp"
+#include "rdbms/wrapper/PostgresConn.hpp"
 
 namespace cta {
 namespace rdbms {
@@ -46,6 +47,22 @@ size_t PostgresColumn::getNbRows() const {
   return m_nbRows;
 }
 
+//------------------------------------------------------------------------------
+// setFieldByteA
+//------------------------------------------------------------------------------
+void PostgresColumn::setFieldByteA(rdbms::Conn &conn, const size_t index, const std::string &value) {
+  auto pgconn_ptr = dynamic_cast<PostgresConn*>(conn.getConnWrapperPtr());
+  auto pgconn = pgconn_ptr->get();
+
+  size_t escaped_length;
+  auto escapedByteA = PQescapeByteaConn(pgconn, reinterpret_cast<const unsigned char*>(value.c_str()),
+    value.length(), &escaped_length);
+  std::string escapedStr(reinterpret_cast<const char*>(escapedByteA), escaped_length);
+  PQfreemem(escapedByteA);
+
+  copyStrIntoField(index, escapedStr);
+}
+
 //------------------------------------------------------------------------------
 // getValue
 //------------------------------------------------------------------------------
diff --git a/rdbms/wrapper/PostgresColumn.hpp b/rdbms/wrapper/PostgresColumn.hpp
index 741f9d4348..64f1679d16 100644
--- a/rdbms/wrapper/PostgresColumn.hpp
+++ b/rdbms/wrapper/PostgresColumn.hpp
@@ -22,6 +22,8 @@
 #include <string.h>
 #include <typeinfo>
 #include <vector>
+#include <libpq-fe.h>
+#include <rdbms/Conn.hpp>
 
 namespace cta {
 namespace rdbms {
@@ -73,6 +75,15 @@ public:
     setFieldValue(index, value, std::is_integral<T>());
   }
 
+  /**
+   * Sets the BYTEA field at the specified index to the value of a byte array
+   *
+   * @param conn  The connection to the Postgres database
+   * @param index The index of the field
+   * @param value The value of the field expressed as a byte array
+   */
+  void setFieldByteA(rdbms::Conn &conn, const size_t index, const std::string &value);
+
 private:
 
   /**
diff --git a/rdbms/wrapper/PostgresConn.hpp b/rdbms/wrapper/PostgresConn.hpp
index 53ef5facb6..ce67db9c29 100644
--- a/rdbms/wrapper/PostgresConn.hpp
+++ b/rdbms/wrapper/PostgresConn.hpp
@@ -33,6 +33,7 @@ namespace wrapper {
 
 class PostgresStmt;
 class PostgresRset;
+class PostgresColumn;
 
 class PostgresConn: public ConnWrapper {
 public:
@@ -42,7 +43,7 @@ public:
    */
   friend PostgresStmt;
   friend PostgresRset;
-
+  friend PostgresColumn;
 
   /**
    * Constructor.
-- 
GitLab