Program Listing for File variant.hpp

Return to documentation for file (/tmp/ws/src/warehouse_ros_sqlite/include/warehouse_ros_sqlite/impl/variant.hpp)

// Copyright 2020 Bjarne von Horn
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the copyright holder nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// SPDX-License-Identifier: BSD-3-Clause

#ifndef WAREHOUSE_ROS_SQLITE__IMPL__VARIANT_HPP_
#define WAREHOUSE_ROS_SQLITE__IMPL__VARIANT_HPP_

#include <sqlite3.h>

#include <boost/variant.hpp>

#include <warehouse_ros_sqlite/utils.hpp>
#include <warehouse_ros_sqlite/exceptions.hpp>

#include <string>
#include <sstream>
#include <utility>

namespace warehouse_ros_sqlite
{
class BindVisitor : boost::static_visitor<int>
{
  sqlite3_stmt * stmt_;
  int idx_;

public:
  explicit BindVisitor(sqlite3_stmt * stmt, int start_idx = 1)
  : stmt_(stmt), idx_(start_idx)
  {
  }
  int operator()(int i)
  {
    return sqlite3_bind_int64(stmt_, idx_++, i);
  }
  int operator()(double d)
  {
    return sqlite3_bind_double(stmt_, idx_++, d);
  }
  int operator()(const std::string & s)
  {
    return sqlite3_bind_blob64(stmt_, idx_++, s.data(), s.size(), SQLITE_STATIC);
  }
  int operator()(NullValue /* unused */)
  {
    return sqlite3_bind_null(stmt_, idx_++);
  }
  int getTotalBinds() const
  {
    return idx_ - 1;
  }
};

class EnsureColumnVisitor : boost::static_visitor<>
{
  sqlite3 * db_;
  std::string unescaped_tablename_;
  schema::escaped_tablename escaped_tablename_;
  std::string unescaped_colname_;
  bool columnExists()
  {
    const std::string colname(schema::METADATA_COLUMN_PREFIX + unescaped_colname_);
    return sqlite3_table_column_metadata(
      db_, schema::DB_NAME, unescaped_tablename_.c_str(), colname.c_str(), nullptr,
      nullptr, nullptr, nullptr, nullptr) == SQLITE_OK;
  }
  void addColumn(const char * datatype)
  {
    std::ostringstream query_builder;
    query_builder << "ALTER TABLE " << escaped_tablename_ << " ADD " <<
      schema::escape_columnname_with_prefix(unescaped_colname_) << " " << datatype << ";";
    if (sqlite3_exec(db_, query_builder.str().c_str(), nullptr, nullptr, nullptr) != SQLITE_OK) {
      throw InternalError("could not create column", db_);
    }
  }

public:
  EnsureColumnVisitor(sqlite3 * db, const std::string & unescaped_tablename)
  : db_(db),
    unescaped_tablename_(unescaped_tablename),
    escaped_tablename_(schema::escape_identifier(unescaped_tablename))
  {
  }
  void operator()(int /*unused*/)  // NOLINT
  {
    if (!columnExists()) {
      addColumn("INTEGER");
    }
  }
  void operator()(double /*unused*/)  // NOLINT
  {
    if (!columnExists()) {
      addColumn("FLOAT");
    }
  }
  void operator()(const std::string & /*unused*/)
  {
    if (!columnExists()) {
      addColumn("BLOB");
    }
  }
  void operator()(NullValue /* unused */)
  {
    if (!columnExists()) {
      throw std::runtime_error("not implemented");
    }
  }
  EnsureColumnVisitor & setColumnName(const std::string & unescaped_column)
  {
    unescaped_colname_ = unescaped_column;
    return *this;
  }
};

namespace detail
{
template<typename R, typename T>
struct NullValueGet
{
  static R get(T /* unused */)
  {
    throw boost::bad_get();
  }
};

template<typename R>
struct NullValueGet<R, typename std::enable_if<!std::is_same<R, NullValue>::value, R>::type>
{
  static R get(R r)
  {
    return std::forward<R>(r);
  }
};

template<typename R>
struct NullValueGet<R, NullValue>
{
  static R get(NullValue /* unused */)
  {
    return R();
  }
};

}  // namespace detail

template<typename R>
struct NullValueVisitor : boost::static_visitor<R>
{
  template<typename T>
  R operator()(T t) const
  {
    return detail::NullValueGet<R, T>::get(std::forward<T>(t));
  }
};
}  // namespace warehouse_ros_sqlite

#endif  // WAREHOUSE_ROS_SQLITE__IMPL__VARIANT_HPP_