Program Listing for File cql.hpp
↰ Return to documentation for file (cif++/cql.hpp)
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. 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.
*
* 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 OWNER 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.
*/
#pragma once
#include "cif++/category.hpp"
#include "cif++/item.hpp"
#include "cif++/iterator.hpp"
#include "cif++/row.hpp"
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
// --------------------------------------------------------------------
namespace cif::cql
{
class connection;
struct result_impl;
// --------------------------------------------------------------------
class field_ref final
{
public:
[[nodiscard]] std::string_view name() const &
{
return m_row.get_category().get_item_name(m_index);
}
[[nodiscard]] constexpr size_t num() const noexcept
{
return m_index;
}
template <typename T = std::string>
[[nodiscard]] auto get() const -> T
{
return m_row[m_index].get<T>();
}
[[nodiscard]] bool is_null() const
{
return m_row[m_index].is_null();
}
template <typename T>
auto value_or(const T &dv) const
{
return m_row[m_index].value_or(dv);
}
field_ref(const_row_handle rh, uint16_t col, std::shared_ptr<result_impl> result_impl)
: m_row(std::move(rh))
, m_index(col)
, m_result_impl(std::move(result_impl))
{
}
field_ref(const field_ref &) = default;
field_ref(field_ref &&) = default;
field_ref &operator=(const field_ref &) = default;
field_ref &operator=(field_ref &&) = default;
private:
const_row_handle m_row;
uint16_t m_index;
std::shared_ptr<result_impl> m_result_impl;
};
// --------------------------------------------------------------------
class row_ref final
{
public:
class const_field_iterator
{
public:
friend class result;
using iterator_category = std::forward_iterator_tag;
using value_type = const field_ref;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
const_field_iterator(const const_field_iterator &) = default;
const_field_iterator(const_field_iterator &&) = default;
const_field_iterator &operator=(const const_field_iterator &) = default;
const_field_iterator &operator=(const_field_iterator &&) = default;
reference operator*()
{
return m_current;
}
pointer operator->()
{
return &m_current;
}
const_field_iterator &operator++()
{
if (m_row)
{
++m_col;
m_current = field_ref(m_row, m_col, m_result_impl);
}
return *this;
}
const_field_iterator operator++(int)
{
const_field_iterator result(*this);
this->operator++();
return result;
}
bool operator==(const const_field_iterator &rhs) const
{
return m_row == rhs.m_row and m_col == rhs.m_col;
}
bool operator!=(const const_field_iterator &rhs) const
{
return m_row != rhs.m_row or m_col != rhs.m_col;
}
private:
friend class row_ref;
const_field_iterator(const_row_handle row, uint16_t column, std::shared_ptr<result_impl> result_impl)
: m_row(std::move(row))
, m_col(column)
, m_current(m_row, m_col, result_impl)
, m_result_impl(result_impl)
{
}
const_row_handle m_row;
uint16_t m_col;
field_ref m_current;
std::shared_ptr<result_impl> m_result_impl;
};
// --------------------------------------------------------------------
row_ref() = default;
row_ref(const_row_handle rh, std::shared_ptr<result_impl> result_impl)
: m_row(std::move(rh))
, m_result_impl(std::move(result_impl))
{
}
row_ref(const row_ref &) = default;
row_ref &operator=(const row_ref &) = default;
// --------------------------------------------------------------------
[[nodiscard]] const_field_iterator begin() const noexcept { return { m_row, 0, m_result_impl }; }
[[nodiscard]] const_field_iterator cbegin() const noexcept { return { m_row, 0, m_result_impl }; }
[[nodiscard]] const_field_iterator end() const noexcept { return { m_row, static_cast<uint16_t>(size()), m_result_impl }; }
[[nodiscard]] const_field_iterator cend() const noexcept { return { m_row, static_cast<uint16_t>(size()), m_result_impl }; }
[[nodiscard]] field_ref front() const noexcept { return { m_row, 0, m_result_impl }; }
[[nodiscard]] field_ref back() const noexcept { return { m_row, static_cast<uint16_t>(size() - 1), m_result_impl }; }
[[nodiscard]] size_t size() const noexcept;
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
[[nodiscard]] field_ref operator[](uint16_t index) const noexcept { return { m_row, index, m_result_impl }; }
[[nodiscard]] field_ref operator[](std::string_view name) const;
// --------------------------------------------------------------------
bool operator==(const row_ref &rhs) const { return m_row == rhs.m_row; }
bool operator!=(const row_ref &rhs) const { return m_row != rhs.m_row; }
private:
const_row_handle m_row;
std::shared_ptr<result_impl> m_result_impl;
};
// --------------------------------------------------------------------
class result
{
public:
// --------------------------------------------------------------------
class iterator
{
public:
friend class view;
using iterator_category = std::forward_iterator_tag;
using value_type = const row_ref;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
// const_row_iterator() = default;
iterator(std::shared_ptr<result_impl> result_impl, category::const_iterator cat_iter)
: m_iter(std::move(cat_iter))
, m_current(*m_iter, result_impl)
, m_result_impl(result_impl)
{
}
iterator(const iterator &) = default;
iterator(iterator &&) = default;
// const_row_iterator &operator=(const const_row_iterator &) = default;
// const_row_iterator &operator=(const_row_iterator &&) = default;
reference operator*()
{
return m_current;
}
pointer operator->()
{
return &m_current;
}
iterator &operator++()
{
++m_iter;
m_current = { *m_iter, m_result_impl };
return *this;
}
iterator operator++(int)
{
iterator result(*this);
this->operator++();
return result;
}
bool operator==(const iterator &rhs) const
{
return m_result_impl == rhs.m_result_impl and m_iter == rhs.m_iter;
}
bool operator!=(const iterator &rhs) const
{
return m_result_impl != rhs.m_result_impl or m_iter != rhs.m_iter;
}
private:
category::const_iterator m_iter;
row_ref m_current;
std::shared_ptr<result_impl> m_result_impl;
};
// --------------------------------------------------------------------
result() = delete;
result(result const &rhs) noexcept = default;
result(result &&rhs) noexcept = default;
result &operator=(result const &rhs) noexcept = default;
result &operator=(result &&rhs) noexcept = default;
result(category &&data, const std::string &query = "");
~result() = default;
[[nodiscard]] row_ref one_row() const
{
if (size() != 1)
throw std::runtime_error("Expected one row");
return front();
}
[[nodiscard]] field_ref one_field() const
{
expect_columns(1);
if (size() != 1)
throw std::runtime_error("Expected one row");
return one_row().front();
}
// --------------------------------------------------------------------
[[nodiscard]] iterator begin() const noexcept;
[[nodiscard]] iterator cbegin() const noexcept;
[[nodiscard]] iterator end() const noexcept;
[[nodiscard]] iterator cend() const noexcept;
[[nodiscard]] row_ref front() const;
[[nodiscard]] row_ref back() const;
[[nodiscard]] size_t size() const noexcept;
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
[[nodiscard]] size_t column_count() const;
[[nodiscard]] category &get_category() const;
void expect_columns(size_t cols) const
{
if (auto actual = column_count(); size() > 0 and cols != actual)
throw std::runtime_error("Unexpected number of columns");
}
// --------------------------------------------------------------------
friend std::ostream &operator<<(std::ostream &os, const result &r)
{
os << r.get_category();
return os;
}
private:
friend class transaction;
friend class SelectStatement;
std::shared_ptr<result_impl> m_impl;
};
// --------------------------------------------------------------------
template <typename... Ts>
class cql_iterator_proxy : public cif::iterator_proxy<Ts...>
{
public:
cql_iterator_proxy(result &&res)
: cif::iterator_proxy<Ts...>(res.get_category())
, m_result(std::forward<result>(res))
{
m_result.expect_columns(cif::iterator_proxy<Ts...>::N);
}
private:
result m_result;
};
// --------------------------------------------------------------------
class transaction final
{
public:
transaction(connection &conn);
~transaction();
transaction(const transaction &) = delete;
transaction &operator=(const transaction &) = delete;
result exec(std::string query);
result exec(std::string query, std::string &tail);
template <typename... Ts>
cql_iterator_proxy<Ts...> stream(const std::string &sql)
{
return cql_iterator_proxy<Ts...>{ exec(sql) };
}
void commit();
void rollback();
private:
connection &m_conn;
bool m_transaction_active = false;
};
// --------------------------------------------------------------------
class connection final
{
public:
connection(datablock &db);
~connection();
friend class transaction;
[[nodiscard]] bool is_complete_statement(const std::string &sql) const;
result exec(std::string query);
result exec(std::string query, std::string &tail);
[[nodiscard]] bool is_modified() const;
private:
struct connection_impl *m_impl;
};
} // namespace cif::cql