blob: 7ae1ed7c984c30ea82c608e6b32a45ac94f6d58b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "ignite/common/ignite_error.h"
#include "ignite/common/primitive.h"
#include <initializer_list>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
namespace ignite {
// Forward declaration.
class ignite_tuple;
namespace detail {
ignite_tuple concat(const ignite_tuple &left, const ignite_tuple &right);
}
/**
* Ignite tuple.
*/
class ignite_tuple {
friend ignite_tuple detail::concat(const ignite_tuple &left, const ignite_tuple &right);
public:
// Default
ignite_tuple() = default;
/**
* Constructor.
*
* @param capacity Capacity.
*/
explicit ignite_tuple(size_t capacity) {
m_pairs.reserve(capacity);
m_indices.reserve(capacity);
}
/**
* Constructor.
*
* @param pairs Pairs.
*/
ignite_tuple(std::initializer_list<std::pair<std::string, primitive>> pairs)
: m_pairs(pairs)
, m_indices() {
m_indices.reserve(pairs.size());
for (size_t i = 0; i < m_pairs.size(); ++i)
m_indices.emplace(std::make_pair(parse_name(m_pairs[i].first), i));
}
/**
* Gets a number of columns in the tuple.
*
* @return Number of columns in the tuple.
*/
[[nodiscard]] std::int32_t column_count() const noexcept { return std::int32_t(m_pairs.size()); }
/**
* Gets the value of the specified column.
*
* @param idx The column index.
* @return Column value.
*/
[[nodiscard]] const primitive &get(uint32_t idx) const {
if (idx > m_pairs.size()) {
throw ignite_error(error::code::ILLEGAL_ARGUMENT,
"Index is too large: idx=" + std::to_string(idx) + ", columns_num=" + std::to_string(m_pairs.size()));
}
return m_pairs[idx].second;
}
/**
* Gets the value of the specified column.
*
* @tparam T Column type.
* @param idx The column index.
* @return Column value.
*/
template<typename T>
[[nodiscard]] T get(uint32_t idx) const {
return get(idx).template get<T>();
}
/**
* Sets the value of the specified column.
*
* @tparam T Column type.
* @param idx The column index.
* @param value Value.
*/
template<typename T>
void set(uint32_t idx, T &&value) {
if (idx > m_pairs.size()) {
throw ignite_error(error::code::ILLEGAL_ARGUMENT,
"Index is too large: idx=" + std::to_string(idx) + ", columns_num=" + std::to_string(m_pairs.size()));
}
m_pairs[idx].second = std::forward<T>(value);
}
/**
* Gets the value of the specified column.
*
* @param name The column name.
* @return Column value.
*/
[[nodiscard]] const primitive &get(std::string_view name) const {
auto it = m_indices.find(parse_name(name));
if (it == m_indices.end())
throw ignite_error(error::code::ILLEGAL_ARGUMENT,
"Can not find column with the name '" + std::string(name) + "' in the tuple");
auto idx = it->second;
return m_pairs[idx].second;
}
/**
* Gets the value of the specified column.
*
* @tparam T Column type.
* @param name The column name.
* @return Column value.
*/
template<typename T>
[[nodiscard]] T get(std::string_view name) const {
return get(name).template get<T>();
}
/**
* Sets the value of the specified column.
*
* @tparam T Column type.
* @param name The column name.
* @param value Value.
*/
template<typename T>
void set(std::string_view name, T &&value) {
auto parsed_name = parse_name(name);
auto it = m_indices.find(parsed_name);
if (it != m_indices.end()) {
auto idx = it->second;
m_pairs[idx].second = std::forward<T>(value);
return;
}
m_pairs.emplace_back(name, std::forward<T>(value));
m_indices.emplace(parsed_name, m_pairs.size() - 1);
}
/**
* Gets the name of the column, given the zero-based column ordinal.
*
* @param idx The column index.
* @return Column name.
*/
[[nodiscard]] const std::string &column_name(uint32_t idx) const {
if (idx > m_pairs.size()) {
throw ignite_error(error::code::ILLEGAL_ARGUMENT,
"Index is too large: idx=" + std::to_string(idx) + ", columns_num=" + std::to_string(m_pairs.size()));
}
return m_pairs[idx].first;
}
/**
* Gets the column ordinal given the name of the column, or -1 when
* the column with the given name does not exist.
*
* @param name The column name.
* @return Column index.
*/
[[nodiscard]] std::int32_t column_ordinal(std::string_view name) const {
auto it = m_indices.find(parse_name(name));
if (it == m_indices.end())
return -1;
return std::int32_t(it->second);
}
private:
/**
* Constructor.
*
* @param pairs Pairs.
* @param indices Indices.
*/
ignite_tuple(
std::vector<std::pair<std::string, primitive>> &&pairs, std::unordered_map<std::string, size_t> indices)
: m_pairs(std::move(pairs))
, m_indices(std::move(indices)) {}
/**
* Normalize column name.
*
* @param name The column name.
* @return Normalized column name.
*/
[[nodiscard]] static std::string parse_name(std::string_view name) {
if (name.size() >= 2 && name.front() == '"' && name.back() == '"')
name = name.substr(1, name.size() - 2);
if (name.empty())
throw ignite_error("Column name can not be an empty string");
std::string res;
res.reserve(name.size());
for (auto c : name) {
res.push_back(char(std::toupper(c)));
}
return res;
}
/** Pairs of column names and values. */
std::vector<std::pair<std::string, primitive>> m_pairs;
/** Indices of the columns corresponding to their names. */
std::unordered_map<std::string, std::size_t> m_indices;
};
} // namespace ignite