Compare commits

...

9 Commits
v9.0.6 ... cql

Author SHA1 Message Date
Maarten L. Hekkelman
00b0473438 backup 2025-12-03 16:22:44 +01:00
Maarten L. Hekkelman
f15a76e29b exclude from all for fast_float 2025-11-27 09:05:51 +01:00
Maarten L. Hekkelman
915a147449 backup 2025-11-26 16:35:30 +01:00
Maarten L. Hekkelman
edf24ca9ff work 2025-11-26 13:46:55 +01:00
Maarten L. Hekkelman
46a9318aa5 revert version string generator 2025-11-26 13:11:54 +01:00
Maarten L. Hekkelman
4a7f48eed8 some initial work 2025-11-19 16:36:44 +01:00
Maarten L. Hekkelman
42e66afd92 Merge branch 'develop' into cql 2025-11-19 13:29:35 +01:00
Maarten L. Hekkelman
ea8dea8cbd Merge branch 'develop' into cql 2025-10-01 16:46:27 +02:00
Maarten L. Hekkelman
ff2a233156 stap 1, een test 2025-10-01 15:48:16 +02:00
6 changed files with 2002 additions and 29 deletions

View File

@@ -43,6 +43,7 @@ include(GenerateExportHeader)
include(CTest)
include(ExternalProject)
include(FetchContent)
include(VersionString)
# When building with ninja-multiconfig, build both debug and release by default
if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config")
@@ -176,7 +177,8 @@ if(NOT STD_CHARCONV_COMPILING)
message(NOTICE "libcifpp: Using fast_float for std::from_chars")
FetchContent_Declare(fastfloat
GIT_REPOSITORY "https://github.com/fastfloat/fast_float"
GIT_TAG v8.0.2)
GIT_TAG v8.0.2
EXCLUDE_FROM_ALL)
FetchContent_MakeAvailable(fastfloat)
endif()
@@ -239,6 +241,9 @@ if(CIFPP_RECREATE_SYMOP_DATA)
"$ENV{CLIBD}/symop.lib")
endif()
# Create a revision file, containing the current git version info
write_version_header("${CMAKE_CURRENT_SOURCE_DIR}/src/" LIB_NAME "LibCIFPP")
# Sources
set(project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/category.cpp
@@ -257,6 +262,7 @@ set(project_sources
${CMAKE_CURRENT_SOURCE_DIR}/src/point.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/symmetry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/model.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/cql/transaction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/cif2pdb.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb2cif.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/pdb/pdb_record.hpp
@@ -293,6 +299,7 @@ set(project_headers
include/cif++/row.hpp
include/cif++/symmetry.hpp
include/cif++/text.hpp
include/cif++/cql/transaction.hpp
include/cif++/utilities.hpp
include/cif++/validate.hpp
)
@@ -300,10 +307,6 @@ set(project_headers
add_library(cifpp)
add_library(cifpp::cifpp ALIAS cifpp)
# Create a revision file, containing the current git version info
include(VersionString)
add_version_header(cifpp "${CMAKE_CURRENT_SOURCE_DIR}/src/revision.hpp" LIB_NAME "LibCIFPP")
if(TARGET my-eigen3)
add_dependencies(cifpp my-eigen3)
endif()

View File

@@ -199,13 +199,24 @@ endif()
endfunction()
# Create a revision file, containing the current git version info, if any
function(add_version_header _target _header_file)
function(write_version_header dir)
set(flags )
set(options LIB_NAME)
set(options LIB_NAME FILE_NAME)
set(sources )
cmake_parse_arguments(VERSION_STRING_OPTION "${flags}" "${options}" "${sources}" ${ARGN})
# parameter check
if(NOT IS_DIRECTORY ${dir})
message(FATAL_ERROR "First parameter to write_version_header should be a directory where the final revision.hpp file will be placed")
endif()
if(VERSION_STRING_OPTION_FILE_NAME)
set(file_name "${VERSION_STRING_OPTION_FILE_NAME}")
else()
set(file_name "revision.hpp")
endif()
# Where to store intermediate files
set(VERSION_STRING_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/VersionString")
if(NOT EXISTS "${VERSION_STRING_DATA}")
@@ -259,27 +270,6 @@ function(add_version_header _target _header_file)
set(BOOL_IS_MAIN "true")
endif()
set(HEADER_IN_FILE "${_current_cmake_module_dir}/revision.hpp.in")
set(HEADER_OUT_FILE "${_header_file}")
file(WRITE "${VERSION_STRING_DATA}/generate-header.cmake.in" [[
set(REVISION_GIT_TAGREF "@REVISION_GIT_TAGREF@")
set(BUILD_NUMBER "@BUILD_NUMBER@")
set(REVISION_DATE_TIME "@REVISION_DATE_TIME@")
set(VAR_PREFIX "@VAR_PREFIX@")
set(IDENT_PREFIX "@IDENT_PREFIX@")
set(BOOL_IS_MAIN "@BOOL_IS_MAIN@")
configure_file("@HEADER_IN_FILE@" "@HEADER_OUT_FILE@" @ONLY)
]])
configure_file("${VERSION_STRING_DATA}/generate-header.cmake.in" "${VERSION_STRING_DATA}/generate-header.cmake" @ONLY)
add_custom_command(
OUTPUT "${_header_file}"
COMMAND "${CMAKE_COMMAND}" -P "${VERSION_STRING_DATA}/generate-header.cmake"
)
target_sources("${_target}" PRIVATE "${_header_file}")
set_target_properties("${_target}" PROPERTIES ADDITIONAL_CLEAN_FILES "${_header_file}")
configure_file("${_current_cmake_module_dir}/revision.hpp.in" "${dir}/${file_name}" @ONLY)
endfunction()

View File

@@ -0,0 +1,451 @@
/*-
* 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++/condition.hpp"
#include "cif++/datablock.hpp"
#include "cif++/item.hpp"
#include "cif++/row.hpp"
#include "cif++/validate.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
// --------------------------------------------------------------------
namespace cif::cql
{
class result;
class row;
class transaction;
class view;
// --------------------------------------------------------------------
struct column
{
std::string name;
size_t index;
};
using column_list = std::vector<column>;
// --------------------------------------------------------------------
class field_ref
{
public:
std::string_view name() const &
{
return m_col->name;
}
constexpr size_t num() const noexcept
{
return m_col->index;
}
std::string_view text() const &
{
return m_row[m_col->index].text();
}
/** Return the contents of this item as type @tparam T */
template <typename T = std::string>
auto as() const -> T
{
return m_row[m_col->index].as<T>();
}
/** Return the contents of this item as type @tparam T or, if not
* set, use @a dv as the default value.
*/
template <typename T>
auto value_or(const T &dv) const
{
return m_row[m_col->index].value_or(dv);
}
field_ref(row_handle rh, column_list::const_iterator col)
: m_row(rh)
, m_col(col)
{
}
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:
row_handle m_row;
column_list::const_iterator m_col;
};
// --------------------------------------------------------------------
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);
}
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, column_list::const_iterator col)
: m_row(row)
, m_col(col)
, m_current(m_row, m_col)
{
}
row_handle m_row;
column_list::const_iterator m_col;
field_ref m_current;
};
// --------------------------------------------------------------------
row_ref() = default;
row_ref(row_handle rh, const column_list &cols)
: m_row(rh)
, m_cols(&cols)
{
}
row_ref(row_ref r, const column_list &cols)
: m_row(r.m_row)
, m_cols(&cols)
{
}
row_ref(const row_ref &) = default;
row_ref &operator=(const row_ref &) = default;
// --------------------------------------------------------------------
const_field_iterator cbegin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
const_field_iterator begin() const noexcept { return const_field_iterator(m_row, m_cols->cbegin()); }
const_field_iterator cend() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
const_field_iterator end() const noexcept { return const_field_iterator(m_row, m_cols->cend()); }
field_ref front() const noexcept { return field_ref(m_row, m_cols->cbegin()); }
field_ref back() const noexcept { return field_ref(m_row, m_cols->cend()); }
size_t size() const noexcept { return m_cols->size(); }
bool empty() const noexcept { return m_cols->empty(); }
field_ref operator[](size_t index) const noexcept;
field_ref operator[](std::string_view name) const noexcept;
// --------------------------------------------------------------------
bool operator==(const row_ref &rhs) const { return m_row == rhs.m_row and m_cols == rhs.m_cols; }
bool operator!=(const row_ref &rhs) const { return m_row != rhs.m_row or m_cols != rhs.m_cols; }
private:
row_handle m_row;
const column_list *m_cols = nullptr;
};
// --------------------------------------------------------------------
class view : public std::enable_shared_from_this<view>
{
public:
virtual ~view() = default;
class const_row_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;
const_row_iterator(const const_row_iterator &) = default;
const_row_iterator(const_row_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;
}
const_row_iterator &operator++()
{
++m_index;
if (m_index < m_data.size())
m_current = m_data.at(m_index);
return *this;
}
const_row_iterator operator++(int)
{
const_row_iterator result(*this);
this->operator++();
return result;
}
bool operator==(const const_row_iterator &rhs) const
{
return &m_data == &rhs.m_data and m_index == rhs.m_index;
}
bool operator!=(const const_row_iterator &rhs) const
{
return &m_data != &rhs.m_data or m_index != rhs.m_index;
}
private:
const_row_iterator(const view &result, size_t index, row_ref current)
: m_data(result)
, m_index(index)
, m_current(current)
{
}
const view &m_data;
size_t m_index = 0;
row_ref m_current;
};
// --------------------------------------------------------------------
const_row_iterator begin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
const_row_iterator cbegin() const noexcept { return const_row_iterator(*this, 0, at(0)); }
const_row_iterator end() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
const_row_iterator cend() const noexcept { return const_row_iterator(*this, size(), row_ref{}); }
virtual row_ref front() const noexcept = 0;
virtual row_ref back() const noexcept = 0;
virtual size_t size() const noexcept = 0;
bool empty() const noexcept { return size() == 0; }
virtual row_ref at(size_t index) const = 0;
// --------------------------------------------------------------------
std::vector<std::string> columns() const
{
std::vector<std::string> result;
for (const auto &[name, ignore] : m_columns)
result.emplace_back(name);
return result;
}
protected:
friend class const_row_iterator;
view(const column_list &cols)
: m_columns(cols)
{
}
view(column_list &&cols)
: m_columns(std::forward<column_list>(cols))
{
}
column_list m_columns;
};
// --------------------------------------------------------------------
class simple_view : public view
{
public:
simple_view(const category &cat)
: view(get_column_list_for_category(cat))
, m_cat(cat)
{
}
simple_view(const simple_view &) = default;
simple_view(simple_view &&) = default;
virtual size_t size() const noexcept override { return m_cat.size(); }
virtual row_ref front() const noexcept override;
virtual row_ref back() const noexcept override;
virtual row_ref at(size_t index) const override;
protected:
static column_list get_column_list_for_category(const category &cat);
const category &m_cat;
};
// --------------------------------------------------------------------
class result
{
public:
// --------------------------------------------------------------------
using const_row_iterator = view::const_row_iterator;
// --------------------------------------------------------------------
result();
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(view &vw, const std::string &query = "");
row_ref one_row() const;
field_ref one_field() const;
// --------------------------------------------------------------------
const_row_iterator begin() const noexcept;
const_row_iterator cbegin() const noexcept;
const_row_iterator end() const noexcept;
const_row_iterator cend() const noexcept;
row_ref front() const noexcept;
row_ref back() const noexcept;
size_t size() const noexcept;
bool empty() const noexcept;
size_t column_count() const;
private:
friend class transaction;
friend class SelectStatement;
result expect_columns(size_t cols) const
{
if (auto actual = column_count(); cols != actual)
throw std::runtime_error("Unexpected number of columns");
return *this;
}
row_ref at(size_t index) const;
std::string m_query;
std::shared_ptr<view> m_view;
};
// --------------------------------------------------------------------
class transaction
{
public:
transaction(const datablock &db)
: m_db(const_cast<datablock &>(db))
{
}
result exec(std::string_view query);
private:
datablock &m_db;
};
} // namespace cif::cql

1371
src/cql/transaction.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,7 @@ list(
spinner
reconstruction
validate-pdbx
cql
matrix
)

157
test/cql-test.cpp Normal file
View File

@@ -0,0 +1,157 @@
/*-
* 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.
*/
#include "test-main.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cif++.hpp>
#include <cif++/cql/transaction.hpp>
#include <stdexcept>
// --------------------------------------------------------------------
cif::file operator""_cf(const char *text, std::size_t length)
{
struct membuf : public std::streambuf
{
membuf(char *text, std::size_t length)
{
this->setg(text, text, text + length);
}
} buffer(const_cast<char *>(text), length);
std::istream is(&buffer);
return cif::file(is);
}
// --------------------------------------------------------------------
TEST_CASE("cql-1")
{
cif::file f(gTestDir / ".." / "examples" / "1cbs.cif.gz");
auto &db = f.front();
cif::cql::transaction tx(db);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry").one_field().as<int>() == 1);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = '1CBS'").one_field().as<int>() == 1);
// CHECK(tx.exec("SELECT COUNT(*) FROM entry WHERE id = 'XXXX'").one_field().as<int>() == 0);
// CHECK(tx.exec("SELECT COUNT(*) FROM citation").one_field().as<int>() == 4);
// CHECK(tx.exec("SELECT COUNT(page_last) FROM citation").one_field().as<int>() == 1);
const char *kPrimaryAuthors[] = {
"Kleywegt, G.J.",
"Bergfors, T.",
"Senn, H.",
"Le Motte, P.",
"Gsell, B.",
"Shudo, K.",
"Jones, T.A."
};
auto r = tx.exec("SELECT name, ordinal FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row[1].as<size_t>() == ix);
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row["ordinal"].as<int>() == ix);
}
r = tx.exec("SELECT ordinal, name FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
CHECK(row[1].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row[0].as<size_t>() == ix);
// CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row["ordinal"].as<int>() == ix);
}
r = tx.exec("SELECT * FROM citation_author WHERE citation_id = 'primary';");
CHECK(r.size() == 7);
for (size_t ix = 0; auto row : r)
{
REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char *)));
for (auto fld : row)
{
switch (fld.num())
{
case 0:
CHECK(fld.name() == "citation_id");
CHECK(fld.as<std::string>() == "primary");
break;
case 1:
CHECK(fld.name() == "name");
CHECK(fld.as<std::string>() == kPrimaryAuthors[ix]);
break;
case 2:
CHECK(fld.name() == "ordinal");
CHECK(fld.as<int>() == ix);
break;
default:
REQUIRE(false);
break;
}
}
++ix;
// CHECK(row[0].as<std::string>() == kPrimaryAuthors[ix++]);
// CHECK(row[1].as<int>() == ix);
CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
CHECK(row["ordinal"].as<size_t>() == ix);
}
// CHECK(tx.query_value<int>("SELECT COUNT(*) FROM citation_author WHERE citation_id = 'primary';") == 7);
// for (size_t ix = 0; auto row : r)
// {
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
// // CHECK(row["name"].as<std::string>() == kPrimaryAuthors[ix++]);
// // CHECK(row["ordinal"].as<int>() == ix);
// }
// for (size_t ix = 0; const auto &[name, ordinal] : tx.stream<std::string, int>("SELECT name FROM citation_author WHERE citation_id = 'primary'"))
// {
// REQUIRE(ix < (sizeof(kPrimaryAuthors) / sizeof(char*)));
// CHECK(name == kPrimaryAuthors[ix++]);
// CHECK(ordinal == ix);
// }
}