
.. _program_listing_file_cif++_cql.hpp:

Program Listing for File cql.hpp
================================

|exhale_lsh| :ref:`Return to documentation for file <file_cif++_cql.hpp>` (``cif++/cql.hpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   /*-
    * 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
