diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp
index d5635847eb838973992ff79af13e046e6d664878..1d01bd6fb85357133471700947d9d6065d8734fd 100644
--- a/catalogue/OracleCatalogue.cpp
+++ b/catalogue/OracleCatalogue.cpp
@@ -293,6 +293,7 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event
     checkTapeFileWrittenFieldsAreSet(firstEvent);
     const time_t now = time(nullptr);
     const std::string nowStr = std::to_string(now);
+    const uint32_t creationTimeMaxFieldSize = nowStr.length() + 1;
     std::lock_guard<std::mutex> m_lock(m_mutex);
     auto conn = m_connPool.getConn();
     rdbms::AutoRollback autoRollback(conn);
@@ -301,23 +302,9 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event
     uint64_t expectedFSeq = tape.lastFSeq + 1;
     uint64_t totalCompressedBytesWritten = 0;
 
-    uint32_t vidMaxFieldSize = 0;
-    uint32_t fSeqMaxFieldSize = 0;
-    uint32_t blockIdMaxFieldSize = 0;
-    uint32_t compressedSizeMaxFieldSize = 0;
-    uint32_t copyNbMaxFieldSize = 0;
-    const uint32_t creationTimeMaxFieldSize = nowStr.length() + 1;
-    uint32_t archiveFileIdMaxFieldSize = 0;
-
-    std::unique_ptr<ub2[]> vidFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> fSeqFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> blockIdFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> compressedSizeFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> copyNbFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> creationTimeFieldSizeArray(new ub2[events.size()]);
-    std::unique_ptr<ub2[]> archiveFileIdFieldSizeArray(new ub2[events.size()]);
-
     uint32_t i = 0;
+    TapeFileBatch tapeFileBatch(events.size());
+
     for (const auto &event: events) {
       checkTapeFileWrittenFieldsAreSet(firstEvent);
 
@@ -328,49 +315,20 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event
       if (expectedFSeq != event.fSeq) {
         exception::Exception ex;
         ex.getMessage() << "FSeq mismatch for tape " << firstEvent.vid << ": expected=" << expectedFSeq << " actual=" <<
-                        firstEvent.fSeq;
+          firstEvent.fSeq;
         throw ex;
       }
 
       expectedFSeq++;
       totalCompressedBytesWritten += event.compressedSize;
 
-      vidFieldSizeArray[i] = event.vid.length() + 1;
-      if (vidFieldSizeArray[i] > vidMaxFieldSize) {
-        vidMaxFieldSize = vidFieldSizeArray[i];
-      }
-
-      const std::string fSeqStr = std::to_string(event.fSeq);
-      fSeqFieldSizeArray[i] = fSeqStr.length() + 1;
-      if (fSeqFieldSizeArray[i] > fSeqMaxFieldSize) {
-        fSeqMaxFieldSize = fSeqFieldSizeArray[i];
-      }
-
-      const std::string blockIdStr = std::to_string(event.blockId);
-      blockIdFieldSizeArray[i] = blockIdStr.length() + 1;
-      if (blockIdFieldSizeArray[i] > blockIdMaxFieldSize) {
-        blockIdMaxFieldSize = blockIdFieldSizeArray[i];
-      }
-
-      const std::string compressedSizeStr = std::to_string(event.compressedSize);
-      compressedSizeFieldSizeArray[i] = compressedSizeStr.length() + 1;
-      if (compressedSizeFieldSizeArray[i] > compressedSizeMaxFieldSize) {
-        compressedSizeMaxFieldSize = compressedSizeFieldSizeArray[i];
-      }
-
-      const std::string copyNbStr = std::to_string(event.copyNb);
-      copyNbFieldSizeArray[i] = copyNbStr.length() + 1;
-      if (copyNbFieldSizeArray[i] > copyNbMaxFieldSize) {
-        copyNbMaxFieldSize = copyNbFieldSizeArray[i];
-      }
-
-      creationTimeFieldSizeArray[i] = creationTimeMaxFieldSize;
-
-      const std::string archiveFileIdStr = std::to_string(event.archiveFileId);
-      archiveFileIdFieldSizeArray[i] = archiveFileIdStr.length() + 1;
-      if (archiveFileIdFieldSizeArray[i] > archiveFileIdMaxFieldSize) {
-        archiveFileIdMaxFieldSize = archiveFileIdFieldSizeArray[i];
-      }
+      tapeFileBatch.vid.setFieldLenToValueLen(i, event.vid);
+      tapeFileBatch.fSeq.setFieldLenToValueLen(i, event.fSeq);
+      tapeFileBatch.blockId.setFieldLenToValueLen(i, event.blockId);
+      tapeFileBatch.compressedSize.setFieldLenToValueLen(i, event.compressedSize);
+      tapeFileBatch.copyNb.setFieldLenToValueLen(i, event.copyNb);
+      tapeFileBatch.creationTime.setFieldLen(i, creationTimeMaxFieldSize);
+      tapeFileBatch.archiveFileId.setFieldLenToValueLen(i, event.archiveFileId);
 
       i++;
     }
@@ -404,52 +362,15 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event
       }
     }
 
-    std::unique_ptr<char[]> vidCStrArray(new char[events.size() * vidMaxFieldSize]);
-    std::unique_ptr<char[]> fSeqCStrArray(new char[events.size() * fSeqMaxFieldSize]);
-    std::unique_ptr<char[]> blockIdCStrArray(new char[events.size() * blockIdMaxFieldSize]);
-    std::unique_ptr<char[]> compressedSizeCStrArray(new char[events.size() * compressedSizeMaxFieldSize]);
-    std::unique_ptr<char[]> copyNbCStrArray(new char[events.size() * copyNbMaxFieldSize]);
-    std::unique_ptr<char[]> creationTimeCStrArray(new char[events.size() * creationTimeMaxFieldSize]);
-    std::unique_ptr<char[]> archiveFileIdCStrArray(new char[events.size() * archiveFileIdMaxFieldSize]);
-
     i = 0;
     for (const auto &event: events) {
-      {
-        char *const element = vidCStrArray.get() + i * vidMaxFieldSize;
-        strncpy(element, event.vid.c_str(), vidMaxFieldSize);
-        element[vidMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = fSeqCStrArray.get() + i * fSeqMaxFieldSize;
-        strncpy(element, std::to_string(event.fSeq).c_str(), fSeqMaxFieldSize);
-        element[fSeqMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = blockIdCStrArray.get() + i * blockIdMaxFieldSize;
-        strncpy(element, std::to_string(event.blockId).c_str(), blockIdMaxFieldSize);
-        element[blockIdMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = compressedSizeCStrArray.get() + i * compressedSizeMaxFieldSize;
-        strncpy(element, std::to_string(event.compressedSize).c_str(), compressedSizeMaxFieldSize);
-        element[compressedSizeMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = copyNbCStrArray.get() + i * copyNbMaxFieldSize;
-        strncpy(element, std::to_string(event.copyNb).c_str(), copyNbMaxFieldSize);
-        element[copyNbMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = creationTimeCStrArray.get() + i * creationTimeMaxFieldSize;
-        strncpy(element, nowStr.c_str(), creationTimeMaxFieldSize);
-        element[creationTimeMaxFieldSize - 1] = '\0';
-      }
-      {
-        char *const element = archiveFileIdCStrArray.get() + i * archiveFileIdMaxFieldSize;
-        strncpy(element, std::to_string(event.archiveFileId).c_str(), archiveFileIdMaxFieldSize);
-        element[archiveFileIdMaxFieldSize - 1] = '\0';
-      }
-
+      tapeFileBatch.vid.copyStrIntoField(i, event.vid.c_str());
+      tapeFileBatch.fSeq.copyStrIntoField(i, std::to_string(event.fSeq));
+      tapeFileBatch.blockId.copyStrIntoField(i, std::to_string(event.blockId));
+      tapeFileBatch.compressedSize.copyStrIntoField(i, std::to_string(event.compressedSize));
+      tapeFileBatch.copyNb.copyStrIntoField(i, std::to_string(event.copyNb));
+      tapeFileBatch.creationTime.copyStrIntoField(i, nowStr);
+      tapeFileBatch.archiveFileId.copyStrIntoField(i, std::to_string(event.archiveFileId));
       i++;
     }
 
@@ -472,21 +393,14 @@ void OracleCatalogue::filesWrittenToTape(const std::list<TapeFileWritten> &event
         ":ARCHIVE_FILE_ID)";
     auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF);
     rdbms::OcciStmt &occiStmt = dynamic_cast<rdbms::OcciStmt &>(*stmt);
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":VID"), vidCStrArray.get(), oracle::occi::OCCI_SQLT_STR,
-      vidMaxFieldSize, vidFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":FSEQ"), fSeqCStrArray.get(), oracle::occi::OCCI_SQLT_STR,
-      fSeqMaxFieldSize, fSeqFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":BLOCK_ID"), blockIdCStrArray.get(), oracle::occi::OCCI_SQLT_STR,
-      blockIdMaxFieldSize, blockIdFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":COMPRESSED_SIZE_IN_BYTES"), compressedSizeCStrArray.get(),
-      oracle::occi::OCCI_SQLT_STR, compressedSizeMaxFieldSize, compressedSizeFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":COPY_NB"), copyNbCStrArray.get(), oracle::occi::OCCI_SQLT_STR,
-      copyNbMaxFieldSize, copyNbFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":CREATION_TIME"), creationTimeCStrArray.get(),
-      oracle::occi::OCCI_SQLT_STR, creationTimeMaxFieldSize, creationTimeFieldSizeArray.get());
-    occiStmt->setDataBuffer(occiStmt.getParamIdx(":ARCHIVE_FILE_ID"), archiveFileIdCStrArray.get(),
-      oracle::occi::OCCI_SQLT_STR, archiveFileIdMaxFieldSize, archiveFileIdFieldSizeArray.get());
-    occiStmt->executeArrayUpdate(events.size());
+    occiStmt.setColumn(tapeFileBatch.vid);
+    occiStmt.setColumn(tapeFileBatch.fSeq);
+    occiStmt.setColumn(tapeFileBatch.blockId);
+    occiStmt.setColumn(tapeFileBatch.compressedSize);
+    occiStmt.setColumn(tapeFileBatch.copyNb);
+    occiStmt.setColumn(tapeFileBatch.creationTime);
+    occiStmt.setColumn(tapeFileBatch.archiveFileId);
+    occiStmt->executeArrayUpdate(tapeFileBatch.nbRows);
 
     conn.commit();
 
diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp
index f2b39843ed72fbb2a73751128cabebe91c07baa8..f2a5089223f6d7b9d7172e27cfaedfb3a0ed672f 100644
--- a/catalogue/OracleCatalogue.hpp
+++ b/catalogue/OracleCatalogue.hpp
@@ -19,6 +19,10 @@
 #pragma once
 
 #include "catalogue/RdbmsCatalogue.hpp"
+#include "rdbms/OcciColumn.hpp"
+
+#include <occi.h>
+#include <string.h>
 
 namespace cta {
 namespace catalogue {
@@ -98,6 +102,28 @@ private:
    */
   common::dataStructures::Tape selectTapeForUpdate(rdbms::PooledConn &conn, const std::string &vid);
 
+  struct TapeFileBatch {
+    size_t nbRows;
+    rdbms::OcciColumn vid;
+    rdbms::OcciColumn fSeq;
+    rdbms::OcciColumn blockId;
+    rdbms::OcciColumn compressedSize;
+    rdbms::OcciColumn copyNb;
+    rdbms::OcciColumn creationTime;
+    rdbms::OcciColumn archiveFileId;
+
+    TapeFileBatch(const size_t nbRowsValue):
+      nbRows(nbRowsValue),
+      vid("VID", nbRows),
+      fSeq("FSEQ", nbRows),
+      blockId("BLOCK_ID", nbRows),
+      compressedSize("COMPRESSED_SIZE_IN_BYTES", nbRows),
+      copyNb("COPY_NB", nbRows),
+      creationTime("CREATION_TIME", nbRows),
+      archiveFileId("ARCHIVE_FILE_ID", nbRows) {
+    }
+  };
+
 }; // class OracleCatalogue
 
 } // namespace catalogue
diff --git a/rdbms/CMakeLists.txt b/rdbms/CMakeLists.txt
index 2211e1211d863e23fb19876b1b9e5cdd761c3322..d96294db9bd6bb9756f3fbd54cc98a2d6b63ea6d 100644
--- a/rdbms/CMakeLists.txt
+++ b/rdbms/CMakeLists.txt
@@ -59,6 +59,7 @@ if (OCCI_SUPPORT)
   set (RDBMS_LIB_SRC_FILES
     ${RDBMS_LIB_SRC_FILES}
     ConnFactoryFactory.cpp
+    OcciColumn.cpp
     OcciConn.cpp
     OcciConnFactory.cpp
     OcciEnv.cpp
@@ -89,11 +90,20 @@ target_link_libraries (ctardbms
 
 install (TARGETS ctardbms DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
 
-set(RDBMS_UNIT_TESTS_LIB_SRC_FILES
-  ConnPoolTest.cpp
-  LoginTest.cpp
-  ParamNameToIdxTest.cpp
-  SqliteStmtTest.cpp)
+if (OCCI_SUPPORT)
+  set(RDBMS_UNIT_TESTS_LIB_SRC_FILES
+    ConnPoolTest.cpp
+    LoginTest.cpp
+    OcciColumnTest.cpp
+    ParamNameToIdxTest.cpp
+    SqliteStmtTest.cpp)
+else (OCCI_SUPPORT)
+  set(RDBMS_UNIT_TESTS_LIB_SRC_FILES
+    ConnPoolTest.cpp
+    LoginTest.cpp
+    ParamNameToIdxTest.cpp
+    SqliteStmtTest.cpp)
+endif (OCCI_SUPPORT)
 
 add_library (ctardbmsunittests SHARED
   ${RDBMS_UNIT_TESTS_LIB_SRC_FILES})
diff --git a/rdbms/OcciColumn.cpp b/rdbms/OcciColumn.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e29e1193857497356916e7ee28d6a7dd9bbf649
--- /dev/null
+++ b/rdbms/OcciColumn.cpp
@@ -0,0 +1,153 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/exception/Exception.hpp"
+#include "rdbms/OcciColumn.hpp"
+
+namespace cta {
+namespace rdbms {
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+OcciColumn::OcciColumn(const std::string &colName, const size_t nbRows):
+  m_colName(colName),
+  m_nbRows(nbRows),
+  m_maxFieldLength(0) {
+  m_fieldLengths.reset(new ub2[m_nbRows]);
+  ub2 *fieldLengths = m_fieldLengths.get();
+  if (nullptr == fieldLengths) {
+    exception::Exception ex;
+    ex.getMessage() << __FUNCTION__ << " failed: colName=" << m_colName << ": Failed to allocate array of field"
+      " lengths for database column: nbRows=" << nbRows;
+    throw ex;
+  }
+  for(size_t i = 0; i < m_nbRows; i++) {
+    fieldLengths[i] = 0;
+  }
+}
+
+//------------------------------------------------------------------------------
+// getColName
+//------------------------------------------------------------------------------
+const std::string &OcciColumn::getColName() const {
+  return m_colName;
+}
+
+//------------------------------------------------------------------------------
+// getNbRows
+//------------------------------------------------------------------------------
+size_t OcciColumn::getNbRows() const {
+  return m_nbRows;
+}
+
+//------------------------------------------------------------------------------
+// setFieldLen
+//------------------------------------------------------------------------------
+void OcciColumn::setFieldLen(const size_t index, const ub2 length) {
+  try {
+    if (nullptr != m_buffer.get()) {
+      exception::Exception ex;
+      ex.getMessage() << "Failed to set length at index " << index << " to value " << length << ": "
+        "A field length cannot be set after the column buffer has been allocated";
+      throw ex;
+    }
+    if (index > m_nbRows - 1) {
+      exception::Exception ex;
+      ex.getMessage() << "Failed to set length at index " << index << " to value " << length << ": "
+        "Index is greater than permitted maximum of " << (m_nbRows - 1);
+      throw ex;
+    }
+    m_fieldLengths[index] = length;
+    if (length > m_maxFieldLength) {
+      m_maxFieldLength = length;
+    }
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " +
+      ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// getFieldLength
+//------------------------------------------------------------------------------
+ub2 *OcciColumn::getFieldLengths() {
+  return m_fieldLengths.get();
+}
+
+//------------------------------------------------------------------------------
+// getBuffer
+//------------------------------------------------------------------------------
+char *OcciColumn::getBuffer() {
+  try {
+    if (nullptr == m_buffer.get()) {
+      const size_t bufSize = m_nbRows * m_maxFieldLength;
+
+      if (0 == bufSize) {
+        exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << " failed: Failed to allocate buffer for database column:"
+          " The size of the buffer to be allocated is zero which is invalid";
+        throw ex;
+      }
+
+      m_buffer.reset(new char[bufSize]);
+      if (nullptr == m_buffer.get()) {
+          exception::Exception ex;
+        ex.getMessage() << __FUNCTION__ << " failed: Failed to allocate buffer for database column:"
+          " bufSize=" << bufSize;
+        throw ex;
+      }
+    }
+    return m_buffer.get();
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " +
+      ex.getMessage().str());
+  }
+}
+
+//------------------------------------------------------------------------------
+// getMaxFieldLength
+//------------------------------------------------------------------------------
+ub2 OcciColumn::getMaxFieldLength() const {
+  return m_maxFieldLength;
+}
+
+//------------------------------------------------------------------------------
+// copyStrIntoField
+//------------------------------------------------------------------------------
+void OcciColumn::copyStrIntoField(const size_t index, const std::string &str) {
+  try {
+    const size_t strLenIncludingNull = str.length() + 1;
+    if(strLenIncludingNull > m_maxFieldLength) {
+      exception::Exception ex;
+      ex.getMessage() << "String length including the null terminator is greater than the maximum field length:"
+        " strLenIncludingNull=" << strLenIncludingNull << " maxFieldLength=" << m_maxFieldLength;
+      throw ex;
+    }
+    char *const buf = getBuffer();
+    char *const element = buf + index * m_maxFieldLength;
+    strncpy(element, str.c_str(), m_maxFieldLength);
+    element[m_maxFieldLength - 1] = '\0';
+  } catch(exception::Exception &ex) {
+    throw exception::Exception(std::string(__FUNCTION__) + " failed: colName=" + m_colName + ": " +
+      ex.getMessage().str());
+  }
+}
+
+} // namespace rdbms
+} // namespace cta
diff --git a/rdbms/OcciColumn.hpp b/rdbms/OcciColumn.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..934892694ebcbf4ef69c6ef95536b127591fa6ea
--- /dev/null
+++ b/rdbms/OcciColumn.hpp
@@ -0,0 +1,169 @@
+/*
+ * The CERN Tape Archive(CTA) project
+ * Copyright(C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "catalogue/RdbmsCatalogue.hpp"
+
+#include <occi.h>
+#include <string.h>
+#include <typeinfo>
+
+namespace cta {
+namespace rdbms {
+
+/**
+ * A class to help with preparing batch inserts and updatesi with the OCCI
+ * interface.
+ */
+class OcciColumn {
+public:
+  /**
+   * Constructor.
+   *
+   * @param colName The name of the column.
+   * @param nbRows The number of rows in the column.
+   */
+  OcciColumn(const std::string &colName, const size_t nbRows);
+
+  /**
+   * Returns the name of the column.
+   *
+   * @return The name of the column.
+   */
+  const std::string &getColName() const;
+
+  /**
+   * Returns the number of rows in the column.
+   *
+   * @return The number of rows in the column.
+   */
+  size_t getNbRows() const;
+
+  /**
+   * Sets the length of the field at the specified index to the length of the
+   * specified value.
+   *
+   * Please not that this method does not record the specified value.  This
+   * method only stores the length of the value.
+   *
+   * @param index The index of the field.
+   * @param value The value whose length is to be recorded.
+   */
+  template<typename T> void setFieldLenToValueLen(const size_t index, const T &value) {
+    setFieldLenToValueLen(index, value, std::is_integral<T>());
+  }
+
+private:
+  /**
+   * Sets the length of the field at the specified index to the length of the
+   * specified value.
+   *
+   * Please not that this method does not record the specified value.  This
+   * method only stores the length of the value.
+   *
+   * @param index The index of the field.
+   * @param value The value whose length is to be recorded.
+   */
+  template<typename T> void setFieldLenToValueLen(const size_t index, const T &value, std::false_type) {
+    setFieldLen(index, value.length() + 1);
+  }
+
+  /**
+   * Sets the length of the field at the specified index to the length of the
+   * specified value.
+   *
+   * Please not that this method does not record the specified value.  This
+   * method only stores the length of the value.
+   *
+   * @param index The index of the field.
+   * @param value The value whose length is to be recorded.
+   */
+  template<typename T> void setFieldLenToValueLen(const size_t index, const T &value, std::true_type) {
+    setFieldLen(index, std::to_string(value).length() + 1);
+  }
+
+public:
+  /**
+   * Sets the length of the field at the specified index.
+   *
+   * @param index The index of the field.
+   * @param length The length of the field.
+   */
+  void setFieldLen(const size_t index, const ub2 length);
+
+  /**
+   * Returns the array of field lengths.
+   *
+   * @return The array of field lengths.
+   */
+  ub2 *getFieldLengths();
+
+  /**
+   * Returns the buffer of column field values.
+   *
+   * @return The buffer of column field values.
+   */
+  char *getBuffer();
+
+  /**
+   * Returns the maximum of the field lengths.
+   *
+   * @return the maximum of the field lengths.
+   */
+  ub2 getMaxFieldLength() const;
+
+  /**
+   * Copies the specified string value into the field at the specified index.
+   *
+   * @param index The index of the field.
+   * @param str The string value.
+   */
+  void copyStrIntoField(const size_t index, const std::string &str);
+
+private:
+
+  /**
+   * The name of the column.
+   */
+  std::string m_colName;
+
+  /**
+   * The number of rows in the column.
+   */
+  size_t m_nbRows;
+
+  /**
+   * The buffer of column field values.
+   */
+  std::unique_ptr<char[]> m_buffer;
+
+  /**
+   * The array of field lengths.
+   */
+  std::unique_ptr<ub2[]> m_fieldLengths;
+
+  /**
+   * The maximum of all the field lengths.
+   */
+  ub2 m_maxFieldLength;
+
+}; // OcciColumn
+
+} // namespace rdbms
+} // namespace cta
diff --git a/rdbms/OcciColumnTest.cpp b/rdbms/OcciColumnTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9ea054c3334bb0b0baaac69db9c4134d5fdbdea
--- /dev/null
+++ b/rdbms/OcciColumnTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "OcciColumn.hpp"
+
+#include <gtest/gtest.h>
+
+namespace unitTests {
+
+class cta_rdbms_OcciColumnTest : public ::testing::Test {
+protected:
+
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+};
+
+TEST_F(cta_rdbms_OcciColumnTest, getColName) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_EQ(colName, col.getColName());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, getNbRows) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_EQ(nbRows, col.getNbRows());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, setFieldLen) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_EQ(0, col.getMaxFieldLength());
+
+  const ub2 field0Len = 1234;
+  col.setFieldLen(0, field0Len);
+  ASSERT_EQ(field0Len, col.getMaxFieldLength());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, setFieldLenToValueLen_stringValue) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_EQ(0, col.getMaxFieldLength());
+
+  const std::string field0Value = "field0Value";
+  col.setFieldLenToValueLen(0, field0Value);
+  ASSERT_EQ(field0Value.length() + 1, col.getMaxFieldLength());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, setFieldLenToValueLen_uint64_tValue) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_EQ(0, col.getMaxFieldLength());
+
+  const uint64_t field0Value = 1234;
+  col.setFieldLenToValueLen(0, field0Value);
+  ASSERT_EQ(5, col.getMaxFieldLength());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, setFieldLen_tooLate) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 2;
+  OcciColumn col(colName, nbRows);
+
+  const ub2 field0Len = 1234;
+  const ub2 field1Len = 5678;
+  col.setFieldLen(0, field0Len);
+
+  col.getBuffer();
+
+  ASSERT_THROW(col.setFieldLen(1, field1Len), exception::Exception);
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, setFieldLen_invalidIndex) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  const ub2 field1Len = 5678;
+  ASSERT_THROW(col.setFieldLen(1, field1Len), exception::Exception);
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, getFieldLengths) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 3;
+  OcciColumn col(colName, nbRows);
+
+  const ub2 field0Len = 1234;
+  const ub2 field1Len = 5678;
+  const ub2 field2Len = 9012;
+  col.setFieldLen(0, field0Len);
+  col.setFieldLen(1, field1Len);
+  col.setFieldLen(2, field2Len);
+
+  ub2 *const fieldLens = col.getFieldLengths();
+
+  ASSERT_EQ(field0Len, fieldLens[0]);
+  ASSERT_EQ(field1Len, fieldLens[1]);
+  ASSERT_EQ(field2Len, fieldLens[2]);
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, getBuffer) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  const ub2 field0Len = 1234;
+  col.setFieldLen(0, field0Len);
+
+  char *const buf = col.getBuffer();
+  ASSERT_NE(nullptr, buf);
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, getBuffer_tooEarly) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  ASSERT_THROW(col.getBuffer(), exception::Exception);
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, getMaxFieldLength) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 4;
+  OcciColumn col(colName, nbRows);
+
+  const ub2 field0Len = 1234;
+  const ub2 field1Len = 5678;
+  const ub2 field2Len = 9012;
+  const ub2 field3Len = 3456;
+  col.setFieldLen(0, field0Len);
+  col.setFieldLen(1, field1Len);
+  col.setFieldLen(2, field2Len);
+  col.setFieldLen(3, field3Len);
+
+  ASSERT_EQ(field2Len, col.getMaxFieldLength());
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_1_oneField) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  const std::string field0Value = "FIELD 0 VALUE";
+  col.setFieldLen(0, field0Value.length() + 1);
+  col.copyStrIntoField(0, field0Value);
+
+  char *const buf = col.getBuffer();
+  ASSERT_EQ(field0Value, std::string(buf));
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_twoFields) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 2;
+  OcciColumn col(colName, nbRows);
+
+  const std::string field0Value = "FIELD 0 VALUE";
+  const std::string field1Value = "FIELD 1 VALUE";
+  col.setFieldLen(0, field0Value.length() + 1);
+  col.setFieldLen(1, field1Value.length() + 1);
+  col.copyStrIntoField(0, field0Value);
+  col.copyStrIntoField(1, field1Value);
+
+  char *const buf = col.getBuffer();
+  const char *const bufField0 = buf;
+  const char *const bufField1 = buf + col.getMaxFieldLength();
+  ASSERT_EQ(field0Value, std::string(bufField0));
+  ASSERT_EQ(field1Value, std::string(bufField1));
+}
+
+TEST_F(cta_rdbms_OcciColumnTest, copyStrIntoField_tooLong) {
+  using namespace cta;
+  using namespace cta::rdbms;
+
+  const std::string colName = "TEST_COLUMN";
+  const size_t nbRows = 1;
+  OcciColumn col(colName, nbRows);
+
+  const std::string field0Value = "FIELD 0 VALUE";
+  col.setFieldLen(0, 1);
+  ASSERT_THROW(col.copyStrIntoField(0, field0Value), exception::Exception);
+}
+
+} // namespace unitTests
diff --git a/rdbms/OcciStmt.cpp b/rdbms/OcciStmt.cpp
index e89df1844105f69cf26a0907caaac58b81785bb8..fe5227145f1ed428b8a0ed35b9b2f04ffbcd4a1b 100644
--- a/rdbms/OcciStmt.cpp
+++ b/rdbms/OcciStmt.cpp
@@ -18,6 +18,7 @@
 
 #include "common/exception/Exception.hpp"
 #include "common/make_unique.hpp"
+#include "rdbms/OcciColumn.hpp"
 #include "rdbms/OcciConn.hpp"
 #include "rdbms/OcciRset.hpp"
 #include "rdbms/OcciStmt.hpp"
@@ -238,6 +239,16 @@ oracle::occi::Statement *OcciStmt::operator->() const {
   return get();
 }
 
+//------------------------------------------------------------------------------
+// setColumn
+//------------------------------------------------------------------------------
+void OcciStmt::setColumn(OcciColumn &col) {
+  const std::string paramName = std::string(":") + col.getColName();
+  const auto paramIdx = getParamIdx(paramName);
+  m_stmt->setDataBuffer(paramIdx, col.getBuffer(), oracle::occi::OCCI_SQLT_STR,
+    col.getMaxFieldLength(), col.getFieldLengths());
+}
+
 //------------------------------------------------------------------------------
 // connShouldBeClosed
 //------------------------------------------------------------------------------
diff --git a/rdbms/OcciStmt.hpp b/rdbms/OcciStmt.hpp
index bbfdfc05cfdec49189885a2ecd458e97ac0812b5..7508628f32be64cb44cff6bff678c49df6ccdcf5 100644
--- a/rdbms/OcciStmt.hpp
+++ b/rdbms/OcciStmt.hpp
@@ -40,6 +40,8 @@ class OcciConn;
  */
 class OcciRset;
 
+class OcciColumn;
+
 /**
  * A convenience wrapper around an OCCI prepared statement.
  */
@@ -150,6 +152,13 @@ public:
    */
   oracle::occi::Statement *operator->() const;
 
+  /**
+   * Sets the specified column data for a batch-based database access.
+   *
+   * @param The column data.
+   */
+  void setColumn(OcciColumn &col);
+
 private:
 
   /**