372 lines
10 KiB
C++
372 lines
10 KiB
C++
#include "table.h"
|
|
#include "csvreader.h"
|
|
#include "ml_string.h"
|
|
|
|
#include <charconv>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
|
|
namespace usql {
|
|
|
|
Table::Table(const std::string& name, const std::vector<ColDefNode>& columns) {
|
|
m_name = name;
|
|
m_col_defs = columns;
|
|
m_rows.reserve(256);
|
|
}
|
|
|
|
Table::Table(const Table &other) {
|
|
m_name = other.m_name;
|
|
m_col_defs = other.m_col_defs;
|
|
m_rows.reserve(other.m_rows.size());
|
|
|
|
for(const Row& orig_row : other.m_rows)
|
|
if (orig_row.is_visible())
|
|
commit_copy_of_row((Row&)orig_row);
|
|
}
|
|
|
|
ColDefNode Table::get_column_def(const std::string &col_name) {
|
|
auto name_cmp = [col_name](const ColDefNode& cd) { return cd.name == col_name; };
|
|
|
|
auto col_def = std::find_if(std::begin(m_col_defs), std::end(m_col_defs), name_cmp);
|
|
if (col_def != std::end(m_col_defs)) {
|
|
return *col_def;
|
|
} else {
|
|
throw Exception("column does not exist (" + col_name + ")");
|
|
}
|
|
}
|
|
|
|
ColDefNode Table::get_column_def(int col_index) {
|
|
if (col_index >= 0 && col_index < columns_count()) {
|
|
return m_col_defs[col_index];
|
|
} else {
|
|
throw Exception("column with this m_index does not exists (" + std::to_string(col_index) + ")");
|
|
}
|
|
}
|
|
|
|
Row& Table::create_empty_row() {
|
|
std::unique_lock guard(m_insert_guard);
|
|
|
|
m_rows.emplace_back(columns_count(), false);
|
|
return m_rows.back();
|
|
}
|
|
|
|
void Table::create_row_from_vector(const std::vector<ColDefNode> &colDefs, const std::vector<std::string> &csv_line) {
|
|
// prepare empty new_row
|
|
Row& new_row = create_empty_row();
|
|
|
|
// copy values
|
|
for (size_t i = 0; i < std::min<size_t>(columns_count(), csv_line.size()); i++) {
|
|
const ColDefNode & col_def = colDefs[i];
|
|
|
|
if (csv_line[i].empty()) {
|
|
new_row.setColumnNull(col_def.order);
|
|
} else if (col_def.type == ColumnType::integer_type) {
|
|
new_row.setIntColumnValue(col_def.order, Settings::string_to_long(csv_line[i]));
|
|
} else if (col_def.type == ColumnType::float_type) {
|
|
new_row.setFloatColumnValue(col_def.order, Settings::string_to_double(csv_line[i]));
|
|
} else if (col_def.type == ColumnType::varchar_type) {
|
|
new_row.setStringColumnValue(col_def.order, csv_line[i]);
|
|
} else if (col_def.type == ColumnType::date_type) {
|
|
new_row.setDateColumnValue(col_def.order, csv_line[i]);
|
|
} else if (col_def.type == ColumnType::bool_type) {
|
|
new_row.setBoolColumnValue(col_def.order, csv_line[i]);
|
|
} else
|
|
throw Exception("unsupported column type");
|
|
}
|
|
|
|
// append new_row
|
|
commit_row(new_row);
|
|
}
|
|
|
|
std::string Table::csv_string() {
|
|
const size_t k_row_size_est = m_col_defs.size() * 16;
|
|
|
|
std::string out_string;
|
|
out_string.reserve(m_rows.size() * k_row_size_est);
|
|
|
|
// header
|
|
for(size_t i = 0; i < m_col_defs.size(); i++) {
|
|
if (i > 0) out_string += ',';
|
|
out_string += m_col_defs[i].name;
|
|
}
|
|
|
|
// rows
|
|
for (auto & row : m_rows) {
|
|
if (row.is_visible()) {
|
|
std::string csv_line{"\n"};
|
|
csv_line.reserve(k_row_size_est);
|
|
|
|
for (size_t i = 0; i < m_col_defs.size(); i++) {
|
|
if (i > 0) csv_line += ',';
|
|
|
|
auto &col = row[i];
|
|
if (!col.isNull()) {
|
|
csv_line += col.getCsvStringValue();
|
|
}
|
|
}
|
|
out_string += csv_line;
|
|
}
|
|
}
|
|
|
|
return out_string;
|
|
}
|
|
|
|
size_t Table::load_csv_string(const std::string &content) {
|
|
std::vector<ColDefNode> &colDefs = m_col_defs;
|
|
|
|
CsvReader csvparser{};
|
|
auto row_cnt = csvparser.parseCSVString(content, colDefs, *this);
|
|
|
|
return row_cnt;
|
|
}
|
|
|
|
size_t Table::load_csv_file(const std::string &filename) {
|
|
std::vector<ColDefNode> &colDefs = m_col_defs;
|
|
|
|
// allocate enough space
|
|
int line_size = 256;
|
|
|
|
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
|
|
auto file_size = in.tellg();
|
|
|
|
std::ifstream infile(filename);
|
|
if (infile.good()) {
|
|
std::string sLine;
|
|
std::getline(infile, sLine);
|
|
line_size = (int)sLine.size() + 1;
|
|
}
|
|
infile.close();
|
|
|
|
if (file_size > 0) {
|
|
auto new_size = m_rows.size() + int((file_size / line_size) * 1.20);
|
|
m_rows.reserve(new_size);
|
|
}
|
|
|
|
// load rows
|
|
CsvReader csvparser{};
|
|
auto row_cnt = csvparser.parseCSVFile(filename, colDefs, *this);
|
|
|
|
return row_cnt;
|
|
}
|
|
|
|
void Table::print() {
|
|
std::string out{"| "};
|
|
std::string out2{"+-"};
|
|
|
|
for(const auto& col_def : m_col_defs) {
|
|
int col_size = Row::print_get_column_size(col_def);
|
|
|
|
if (col_def.type==ColumnType::integer_type || col_def.type==ColumnType::float_type || col_def.type==ColumnType::bool_type)
|
|
out.append(string_padd(col_def.name, col_size, ' ', false) + " | ");
|
|
else
|
|
out.append(string_padd(col_def.name, col_size, ' ', true) + " | ");
|
|
|
|
out2.append(string_padd("-", col_size, '-', true) + "-+ ");
|
|
}
|
|
|
|
// std::cout << "** " << m_name << " **" << std::endl;
|
|
std::cout << out << std::endl;
|
|
std::cout << out2 << std::endl;
|
|
|
|
for(auto& row : m_rows) {
|
|
row.print(m_col_defs);
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
size_t Table::get_rowid(const Row &row) const {
|
|
const Row* row_addr = (Row*)&row;
|
|
const Row* begin_addr = &(*m_rows.begin());
|
|
|
|
return row_addr - begin_addr;
|
|
}
|
|
|
|
void Table::commit_row(Row &row) {
|
|
try {
|
|
validate_row(row);
|
|
index_row(row);
|
|
} catch (const Exception &e) {
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
void Table::commit_copy_of_row(Row &row) {
|
|
Row& new_row = create_empty_row();
|
|
|
|
for(size_t i = 0; i < m_col_defs.size(); i++) {
|
|
ColValue &ct = row[i];
|
|
|
|
if (ct.isNull()) {
|
|
new_row.setColumnNull(i);
|
|
} else {
|
|
if (m_col_defs[i].type == ColumnType::integer_type) {
|
|
new_row.setIntColumnValue(i, row[i].getIntegerValue());
|
|
} else if (m_col_defs[i].type == ColumnType::float_type) {
|
|
new_row.setFloatColumnValue(i, row[i].getDoubleValue());
|
|
} else if (m_col_defs[i].type == ColumnType::varchar_type) {
|
|
new_row.setStringColumnValue(i, row[i].getStringValue());
|
|
} else if (m_col_defs[i].type == ColumnType::date_type) {
|
|
new_row.setDateColumnValue(i, row[i].getDateValue());
|
|
} else if (m_col_defs[i].type == ColumnType::bool_type) {
|
|
new_row.setBoolColumnValue(i, row[i].getBoolValue());
|
|
} else
|
|
throw Exception("unsupported column type");
|
|
}
|
|
}
|
|
|
|
validate_row(new_row);
|
|
index_row(row);
|
|
}
|
|
|
|
void Table::validate_column(const ColDefNode *col_def, ValueNode *col_val) {
|
|
if (!col_def->null && col_val->isNull()) {
|
|
throw Exception("Column " + col_def->name + " cannot be null");
|
|
}
|
|
if (col_def->type == ColumnType::varchar_type && !col_val->isNull() && col_val->getStringValue().size() > col_def->length) {
|
|
throw Exception("Column value of " + col_def->name + " is too long (" + col_val->getStringValue() + ")");
|
|
}
|
|
}
|
|
|
|
void Table::validate_column(const ColDefNode *col_def, ColValue &col_val) {
|
|
if (!col_def->null && col_val.isNull())
|
|
throw Exception("Column " + col_def->name + " cannot be null");
|
|
|
|
if (col_def->type == ColumnType::varchar_type && !col_val.isNull() && col_val.getStringValue().size() > col_def->length)
|
|
throw Exception("Column value of " + col_def->name + " is too long (" + col_val.getStringValue() + ")");
|
|
}
|
|
|
|
void Table::validate_row(Row &row) {
|
|
for(size_t i = 0; i < m_col_defs.size(); i++) {
|
|
ColDefNode col_def = m_col_defs[i];
|
|
ColValue &col_val = row[i];
|
|
|
|
validate_column(&col_def, col_val);
|
|
}
|
|
row.set_visible();
|
|
}
|
|
|
|
void Table::create_index(const Index& index) {
|
|
m_indexes.push_back(index);
|
|
}
|
|
|
|
bool Table::drop_index(const std::string &index_name) {
|
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
|
[&index_name](const Index &idx) {
|
|
return idx.get_index_name() == index_name;
|
|
});
|
|
|
|
if (it != m_indexes.end()) {
|
|
m_indexes.erase(it);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Table::index_row(Index &index, const ColDefNode &col_def, const Row &row, const size_t rowid) {
|
|
index.insert(reinterpret_cast<ColValue *>(&row[col_def.order]), rowid);
|
|
}
|
|
|
|
void Table::unindex_row(Index &index, const ColDefNode &col_def, const Row &row, const size_t rowid) {
|
|
index.remove(reinterpret_cast<ColValue *>(&row[col_def.order]), rowid);
|
|
}
|
|
|
|
void Table::reindex_row(Index &index, const ColDefNode &col_def, const Row &old_row, const Row &new_row, size_t rowid) {
|
|
unindex_row(index, col_def, old_row, rowid);
|
|
index_row(index, col_def, new_row, rowid);
|
|
}
|
|
|
|
void Table::index_row(const Row &row) {
|
|
if (!m_indexes.empty()) {
|
|
const size_t rowid = get_rowid(row);
|
|
|
|
std::unique_lock guard(m_insert_guard);
|
|
for (auto &idx : m_indexes) {
|
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
|
index_row(idx, cDef, row, rowid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Table::unindex_row(const Row &row) {
|
|
if (!m_indexes.empty()) {
|
|
const size_t rowid = get_rowid(row);
|
|
for (auto &idx : m_indexes) {
|
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
|
unindex_row(idx, cDef, row, rowid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Table::reindex_row(const Row &old_row, const Row &new_row) {
|
|
if (!m_indexes.empty()) {
|
|
const size_t rowid = get_rowid(new_row);
|
|
for (auto &idx : m_indexes) {
|
|
ColDefNode cDef = get_column_def(idx.get_column_name());
|
|
reindex_row(idx, cDef, old_row, new_row, rowid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Table::index_rows(const std::string &index_name) {
|
|
auto index = get_index(index_name);
|
|
|
|
ColDefNode cDef = get_column_def(index->get_column_name());
|
|
size_t rowid = 0;
|
|
for(const Row& r : m_rows) {
|
|
index_row(*index, cDef, r, rowid);
|
|
rowid++;
|
|
}
|
|
}
|
|
|
|
Index * Table::get_index(const std::string &index_name) {
|
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
|
[&index_name](const Index &idx) {
|
|
return idx.get_index_name() == index_name;
|
|
});
|
|
|
|
return (it != m_indexes.end()) ? &(*it) : nullptr;
|
|
}
|
|
|
|
Index * Table::get_index_for_column(const std::string &col_name) {
|
|
auto it = std::find_if(m_indexes.begin(), m_indexes.end(),
|
|
[&col_name](const Index &idx) {
|
|
return idx.get_column_name() == col_name;
|
|
});
|
|
|
|
return (it != m_indexes.end()) ? &(*it) : nullptr;
|
|
}
|
|
|
|
bool Table::empty() const {
|
|
if (m_rows.empty()) return true;
|
|
|
|
for (const auto & r : m_rows)
|
|
if (r.is_visible()) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
Row *Table::rows_scanner::next() {
|
|
if (m_use_rowids) {
|
|
while (m_rowids_idx < m_rowids.size()) {
|
|
auto row_ptr = &m_table->m_rows[m_rowids[m_rowids_idx]];
|
|
if (row_ptr->is_visible()) {
|
|
m_rowids_idx++;
|
|
return row_ptr;
|
|
}
|
|
m_rowids_idx++;
|
|
}
|
|
} else {
|
|
while (m_fscan_itr != m_table->m_rows.end()) {
|
|
if (m_fscan_itr->is_visible()) {
|
|
auto i = m_fscan_itr;
|
|
m_fscan_itr++;
|
|
return &(*i);
|
|
}
|
|
m_fscan_itr++;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace
|