initial matador ng commit

This commit is contained in:
Sascha Kühl 2025-02-02 20:37:12 +01:00
parent 19de5714f4
commit ded3daceb3
378 changed files with 14913 additions and 13431 deletions

View File

@ -1,50 +1,57 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.30)
project( project(
query matador
VERSION 1.0.0 VERSION 0.9.8
DESCRIPTION "SQL query fluent prototype for PostgreSQL, SQLite, MySQL and MSSQL" LANGUAGES C CXX
LANGUAGES CXX
) )
include(cmake/CPM.cmake)
include(CTest)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(GCC_CLANG_COMMON_FLAGS "-Wall -Wconversion -Wextra -pedantic -ftemplate-backtrace-limit=0") SET(GCC_CLANG_COMMON_FLAGS "-Wall -Wextra -pedantic -ftemplate-backtrace-limit=0")
SET(GCC_CLANG_COMMON_FLAGS_DEBUG "-O0 -g -DDEBUG")
SET(GCC_CLANG_COMMON_FLAGS_RELEASE "-O1 -DNDEBUG")
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (WIN32) message(STATUS "C++ Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
add_compile_options(/Zc:preprocessor)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(ODBC REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(PostgreSQL REQUIRED) find_package(PostgreSQL REQUIRED)
find_package(MySQL REQUIRED)
message(STATUS "Found ODBC config ${ODBC_CONFIG}") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "Adding ODBC include directory: ${ODBC_INCLUDE_DIRS}") message(STATUS "GCC detected - Adding compiler flags")
message(STATUS "Adding ODBC libs: ${ODBC_LIBRARIES}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_CLANG_COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${GCC_CLANG_COMMON_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
MESSAGE(STATUS "MSVC detected - Adding compiler flags")
SET(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
SET(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG")
SET(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG")
endif ()
#if(ENABLE_COVERAGE)
# # set compiler flags
# set(CMAKE_CXX_FLAGS "-O0 -coverage")
#
# # find required tools
# find_program(LCOV lcov REQUIRED)
# find_program(GENHTML genhtml REQUIRED)
#
# # add coverage target
# add_custom_target(coverage
# # gather data
# COMMAND ${LCOV} --directory . --exclude catch2 --exclude /usr/include --exclude test --capture --base-directory ${CMAKE_SOURCE_DIR} --output-file coverage.info
# # generate report
# COMMAND ${GENHTML} --demangle-cpp -o coverage coverage.info
# WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#endif()
message(STATUS "Found SQLite3 ${SQLite3_VERSION}")
message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}")
message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}")
message(STATUS "Adding MySQL include directory: ${MYSQL_INCLUDE_DIR}") add_subdirectory(source)
message(STATUS "Adding MySQL libs: ${MYSQL_LIBRARY}")
message(STATUS "Adding PostgreSQL include directory: ${PostgreSQL_INCLUDE_DIR}")
message(STATUS "Adding PostgreSQL libs: ${PostgreSQL_LIBRARY}")
message(STATUS "Common flags ${CMAKE_CXX_FLAGS}")
message(STATUS "Debug flags ${CMAKE_CXX_FLAGS_DEBUG}")
message(STATUS "Relase flags ${CMAKE_CXX_FLAGS_RELEASE}")
message(STATUS "Linker flags ${CMAKE_EXE_LINKER_FLAGS}")
enable_testing()
add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(backends) add_subdirectory(backends)
add_subdirectory(demo) add_subdirectory(test)

47
Todo.md
View File

@ -1,47 +0,0 @@
# Todo
- Add is_valid() method to connection & connection_impl
- Read in entity fields
- Add special handling for update in backends
- Add ODBC/SQL Server backend
Fetch eager strategies
======================
ONE TO ONE/MANY
*person* *address*
- has one address - belongs to person
=> join "address" on "person.id" == "address.person_id"
*address* *person*
- belongs to person - has one address
=> join "person" on "address.person_id" == "person.id"
*book* *author*
- belongs to author - has many books
- => join "author" on "book.author_id" == "author.id"
HAS MANY TO ONE (WITHOUT RELATION TABLE)
*author* *book*
- has many books - belongs to author
- => join "book" on "author.id" == "book.author_id"
if "has many" type has primary key & field "author_id"
if table name belongs to entity template type?
HAS MANY TO MANY (WITHOUT RELATION TABLE)
*student* *student_course* *course*
- has many courses - belongs to student
- belongs to course - has many students
=> join "student_course" on "student.id" == "student_course.student_id"
join "student_course" on "course.id" == "student_course.course_id"
if has many type hasn't primary key (is relation table)

View File

@ -1,3 +1,4 @@
add_subdirectory(sqlite) #add_subdirectory(sqlite)
add_subdirectory(postgres) add_subdirectory(postgres)
add_subdirectory(mysql) #add_subdirectory(mysql)
#add_subdirectory(odbc)

View File

@ -1,37 +0,0 @@
set(HEADER
include/mysql_connection.hpp
include/mysql_error.hpp
include/mysql_result_reader.hpp
include/mysql_dialect.hpp
include/mysql_statement.hpp
include/mysql_parameter_binder.hpp
include/mysql_prepared_result_reader.hpp
)
set(SOURCES
src/mysql_connection.cpp
src/mysql_error.cpp
src/mysql_result_reader.cpp
src/mysql_dialect.cpp
src/mysql_statement.cpp
src/mysql_parameter_binder.cpp
src/mysql_prepared_result_reader.cpp
)
set(LIBRARY_TARGET matador-mysql)
add_subdirectory(test)
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
set_target_properties(${LIBRARY_TARGET}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
)
target_include_directories(${LIBRARY_TARGET} PRIVATE
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/backends/mysql/include
${MYSQL_INCLUDE_DIR})
target_link_libraries(${LIBRARY_TARGET} matador ${MYSQL_LIBRARY})

View File

@ -1,61 +0,0 @@
#ifndef QUERY_POSTGRES_CONNECTION_HPP
#define QUERY_POSTGRES_CONNECTION_HPP
#ifdef _MSC_VER
#ifdef matador_mysql_EXPORTS
#define MATADOR_MYSQL_API __declspec(dllexport)
#else
#define MATADOR_MYSQL_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_MYSQL_API
#endif
#include "matador/sql/connection_impl.hpp"
#include <unordered_map>
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_connection : public matador::sql::connection_impl
{
public:
explicit mysql_connection(const sql::connection_info &info);
void open() override;
void close() override;
bool is_open() override;
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) override;
std::unique_ptr<sql::statement_impl> prepare(sql::query_context context) override;
size_t execute(const std::string &stmt) override;
std::vector<sql::column_definition> describe(const std::string& table) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private:
mutable std::unique_ptr<MYSQL> mysql_;
using string_to_int_map = std::unordered_map<std::string, unsigned long>;
static string_to_int_map statement_name_map_;
};
}
extern "C"
{
MATADOR_MYSQL_API matador::sql::connection_impl* create_database(const matador::sql::connection_info &info);
MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db);
}
#endif //QUERY_POSTGRES_CONNECTION_HPP

View File

@ -1,19 +0,0 @@
#ifndef QUERY_POSTGRES_DIALECT_HPP
#define QUERY_POSTGRES_DIALECT_HPP
#ifdef _MSC_VER
#ifdef matador_mysql_EXPORTS
#define MATADOR_MYSQL_API __declspec(dllexport)
#else
#define MATADOR_MYSQL_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_MYSQL_API
#endif
#include "matador/sql/dialect.hpp"
extern "C" [[maybe_unused]] MATADOR_MYSQL_API const matador::sql::dialect* get_dialect();
#endif //QUERY_POSTGRES_DIALECT_HPP

View File

@ -1,20 +0,0 @@
#ifndef QUERY_POSTGRES_ERROR_HPP
#define QUERY_POSTGRES_ERROR_HPP
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
#include <string>
namespace matador::backends::mysql {
void throw_mysql_error(const char *what, const std::string &source);
void throw_mysql_error(MYSQL *db, const std::string &source);
void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql);
}
#endif //QUERY_POSTGRES_ERROR_HPP

View File

@ -1,74 +0,0 @@
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
#define QUERY_POSTGRES_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
#include <vector>
namespace matador::backends::mysql {
struct mysql_result_info
{
unsigned long length = 0;
my_bool is_null = false;
my_bool error = false;
// std::unique_ptr<char[]> buffer;
char *buffer = nullptr;
unsigned long buffer_length = 0;
bool is_allocated = false;
~mysql_result_info()
{
if (is_allocated) {
delete [] buffer;
}
}
};
class mysql_parameter_binder final : public sql::parameter_binder
{
public:
explicit mysql_parameter_binder(size_t size);
void bind(size_t pos, char i) override;
void bind(size_t pos, short i) override;
void bind(size_t pos, int i) override;
void bind(size_t pos, long i) override;
void bind(size_t pos, long long int i) override;
void bind(size_t pos, unsigned char i) override;
void bind(size_t pos, unsigned short i) override;
void bind(size_t pos, unsigned int i) override;
void bind(size_t pos, unsigned long i) override;
void bind(size_t pos, unsigned long long int i) override;
void bind(size_t pos, bool b) override;
void bind(size_t pos, float d) override;
void bind(size_t pos, double d) override;
void bind(size_t pos, const char *string) override;
void bind(size_t pos, const char *string, size_t size) override;
void bind(size_t pos, const std::string &string) override;
void bind(size_t pos, const std::string &x, size_t size) override;
void bind(size_t pos, const utils::blob &blob) override;
[[nodiscard]] std::vector<MYSQL_BIND>& bind_params();
private:
struct is_null_t
{
my_bool is_null = false;
};
std::vector<MYSQL_BIND> bind_params_;
std::vector<is_null_t> is_null_vector;
std::vector<mysql_result_info> info_;
};
}
#endif //QUERY_POSTGRES_PARAMETER_BINDER_H

View File

@ -1,36 +0,0 @@
#ifndef QUERY_MYSQL_PREPARED_RESULT_READER_HPP
#define QUERY_MYSQL_PREPARED_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_prepared_result_reader : public sql::query_result_reader
{
public:
explicit mysql_prepared_result_reader(MYSQL_STMT *stmt);
~mysql_prepared_result_reader() override;
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
private:
MYSQL_STMT *stmt_{};
MYSQL_ROW current_row_{};
size_t row_count_{};
size_t column_count_{};
int row_index_{-1};
};
}
#endif //QUERY_MYSQL_PREPARED_RESULT_READER_HPP

View File

@ -1,36 +0,0 @@
#ifndef QUERY_POSTGRES_RESULT_READER_HPP
#define QUERY_POSTGRES_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_result_reader : public sql::query_result_reader
{
public:
explicit mysql_result_reader(MYSQL_RES *result, unsigned int column_count);
~mysql_result_reader() override;
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
private:
MYSQL_RES *result_{};
MYSQL_ROW current_row_{};
size_t row_count_{};
size_t column_count_{};
int row_index_{-1};
};
}
#endif //QUERY_POSTGRES_RESULT_READER_HPP

View File

@ -1,37 +0,0 @@
#ifndef QUERY_POSTGRES_STATEMENT_HPP
#define QUERY_POSTGRES_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp"
#include "mysql_parameter_binder.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_statement final : public sql::statement_impl
{
public:
mysql_statement(MYSQL_STMT *stmt, const sql::query_context &query);
size_t execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override;
void reset() override;
protected:
sql::parameter_binder& binder() override;
private:
MYSQL_STMT *stmt_{nullptr};
std::string name_;
mysql_parameter_binder binder_;
};
}
#endif //QUERY_POSTGRES_STATEMENT_HPP

View File

@ -1,280 +0,0 @@
#include "mysql_connection.hpp"
#include "mysql_error.hpp"
#include "mysql_result_reader.hpp"
#include "mysql_statement.hpp"
#include "matador/sql/record.hpp"
#include <memory>
#include <regex>
namespace matador::backends::mysql {
mysql_connection::string_to_int_map mysql_connection::statement_name_map_{};
mysql_connection::mysql_connection(const sql::connection_info &info)
: connection_impl(info) {}
void mysql_connection::open()
{
if (is_open()) {
return;
}
mysql_ = std::make_unique<MYSQL>();
if (!mysql_init(mysql_.get())) {
throw_mysql_error(mysql_.get(), "mysql_init");
}
if (!mysql_real_connect(mysql_.get(),
info().hostname.c_str(),
info().user.c_str(),
!info().password.empty() ? info().password.c_str() : nullptr,
info().database.c_str(),
info().port,
nullptr,
0)) {
// disconnect all handles
const std::string error_message = mysql_error(mysql_.get());
mysql_close(mysql_.get());
mysql_.reset();
// throw exception
throw_mysql_error(error_message.c_str(), "mysql_real_connect");
}
}
void mysql_connection::close()
{
if (mysql_) {
mysql_close(mysql_.get());
mysql_.reset();
}
}
bool mysql_connection::is_open()
{
return mysql_ != nullptr;
}
sql::data_type_t to_type(enum_field_types type, unsigned int flags)
{
switch (type) {
case MYSQL_TYPE_TINY:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_char : sql::data_type_t::type_char;
case MYSQL_TYPE_SHORT:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_short : sql::data_type_t::type_short;
case MYSQL_TYPE_LONG:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_int : sql::data_type_t::type_int;
case MYSQL_TYPE_LONGLONG:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_long_long : sql::data_type_t::type_long_long;
case MYSQL_TYPE_FLOAT:
return sql::data_type_t::type_float;
case MYSQL_TYPE_DOUBLE:
return sql::data_type_t::type_double;
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_VAR_STRING:
return sql::data_type_t::type_varchar;
case MYSQL_TYPE_BLOB:
return sql::data_type_t::type_blob;
case MYSQL_TYPE_STRING:
return sql::data_type_t::type_text;
case MYSQL_TYPE_DATE:
return sql::data_type_t::type_date;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
return sql::data_type_t::type_time;
default:
return sql::data_type_t::type_unknown;
}
}
utils::constraints to_constraints(unsigned int flags)
{
utils::constraints options{utils::constraints::NONE};
if (flags & PRI_KEY_FLAG) {
options |= utils::constraints::PRIMARY_KEY;
}
if (flags & UNIQUE_KEY_FLAG) {
options |= utils::constraints::UNIQUE;
}
return options;
}
sql::null_option to_null_option(unsigned int flags)
{
return flags & NOT_NULL_FLAG ? sql::null_option::NOT_NULL : sql::null_option::NULLABLE;
}
sql::data_type_t string2type(const std::string &type_string)
{
if (strncmp(type_string.c_str(), "tinyint", 7) == 0) {
return sql::data_type_t::type_char;
} else if (strncmp(type_string.c_str(), "smallint", 8) == 0) {
if (strstr(type_string.c_str(), "unsigned") != nullptr) {
return sql::data_type_t::type_unsigned_short;
} else {
return sql::data_type_t::type_short;
}
} else if (strncmp(type_string.c_str(), "int", 3) == 0) {
if (strstr(type_string.c_str(), "unsigned") != nullptr) {
return sql::data_type_t::type_unsigned_int;
} else {
return sql::data_type_t::type_int;
}
} else if (strncmp(type_string.c_str(), "bigint", 6) == 0) {
if (strstr(type_string.c_str(), "unsigned") != nullptr) {
return sql::data_type_t::type_unsigned_long_long;
} else {
return sql::data_type_t::type_long_long;
}
} else if (strcmp(type_string.c_str(), "date") == 0) {
return sql::data_type_t::type_date;
} else if (strncmp(type_string.c_str(), "datetime", 8) == 0) {
return sql::data_type_t::type_time;
} else if (strcmp(type_string.c_str(), "float") == 0) {
return sql::data_type_t::type_float;
} else if (strcmp(type_string.c_str(), "double") == 0) {
return sql::data_type_t::type_double;
} else if (strncmp(type_string.c_str(), "varchar", 7) == 0) {
return sql::data_type_t::type_varchar;
} else if (strncmp(type_string.c_str(), "text", 4) == 0) {
return sql::data_type_t::type_text;
} else {
return sql::data_type_t::type_unknown;
}
}
struct type_info
{
sql::data_type_t type{sql::data_type_t::type_unknown};
size_t size{};
};
type_info determine_type_info(const std::string &type_string)
{
static const std::regex TYPE_REGEX(R"(^(\w+)(\((\d+)(,(\d+))?\))?$)");
std::smatch matcher;
type_info result;
if (std::regex_match(type_string, matcher, TYPE_REGEX)) {
result.type = string2type(matcher[1].str());
if (matcher[3].matched) {
result.size = std::stoi(matcher[3].str());
}
}
return result;
}
std::unique_ptr<sql::query_result_impl> mysql_connection::fetch(const std::string &stmt)
{
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
auto field_count = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
std::vector<sql::column_definition> prototype;
for (unsigned i = 0; i < field_count; ++i) {
auto type = to_type(fields[i].type, fields[i].flags);
auto options = to_constraints(fields[i].flags);
auto null_opt = to_null_option(fields[i].flags);
prototype.emplace_back(fields[i].name, type, options, null_opt);
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_result_reader>(result, field_count), std::move(prototype)));
}
std::unique_ptr<sql::statement_impl> mysql_connection::prepare(sql::query_context context)
{
MYSQL_STMT *stmt = mysql_stmt_init(mysql_.get());
if (stmt == nullptr) {
throw_mysql_error(mysql_.get(), "mysql_stmt_init");
}
if (mysql_stmt_prepare(stmt, context.sql.c_str(), static_cast<unsigned long>(context.sql.size())) != 0) {
throw_mysql_error(stmt, "mysql_stmt_prepare", context.sql);
}
return std::make_unique<mysql_statement>(stmt, std::move(context));
}
size_t mysql_connection::execute(const std::string &stmt)
{
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
return mysql_affected_rows(mysql_.get());
}
std::vector<sql::column_definition> mysql_connection::describe(const std::string &table)
{
std::string stmt("SHOW COLUMNS FROM " + table);
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
mysql_result_reader reader(result, mysql_num_fields(result));
std::vector<sql::column_definition> prototype;
while (reader.fetch()) {
char *end = nullptr;
std::string name = reader.column(0);
auto typeinfo = determine_type_info(reader.column(1));
end = nullptr;
sql::null_option null_opt{sql::null_option::NULLABLE};
if (strtoul(reader.column(2), &end, 10) == 0) {
null_opt = sql::null_option::NOT_NULL;
}
prototype.push_back({name, typeinfo.type, {typeinfo.size}, null_opt, prototype.size()});
}
return prototype;
}
bool mysql_connection::exists(const std::string &/*schema_name*/, const std::string &table_name)
{
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + info().database + "' AND table_name = '" + table_name + "'");
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
return result->row_count == 1;
}
}
extern "C"
{
MATADOR_MYSQL_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info)
{
return new matador::backends::mysql::mysql_connection(info);
}
MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db)
{
delete db;
}
}

View File

@ -1,17 +0,0 @@
#include "mysql_dialect.hpp"
#include "matador/sql/dialect_builder.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect()
{
using namespace matador::sql;
const static dialect d = dialect_builder::builder()
.create()
.with_token_replace_map({
{dialect::token_t::START_QUOTE, "`"},
{dialect::token_t::END_QUOTE, "`"},
})
.with_default_schema_name("")
.build();
return &d;
}

View File

@ -1,30 +0,0 @@
#include "mysql_error.hpp"
#include <sstream>
namespace matador::backends::mysql {
void throw_mysql_error(const char *what, const std::string &source)
{
std::stringstream msg;
msg << "mysql error (" << source << "): " << what;
throw std::logic_error(msg.str());
}
void throw_mysql_error(MYSQL *db, const std::string &source)
{
if (mysql_errno(db) != 0) {
throw_mysql_error(mysql_error(db), source);
}
}
void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql)
{
if (mysql_stmt_errno(stmt) != 0) {
std::stringstream msg;
msg << "mysql error (" << source << ") " << mysql_stmt_error(stmt) << ": " << sql;
throw std::logic_error(msg.str());
}
}
}

View File

@ -1,192 +0,0 @@
#include "mysql_parameter_binder.hpp"
namespace matador::backends::mysql {
namespace detail {
template < class T >
void bind_value(enum_field_types type, T value, MYSQL_BIND &bind, my_bool &is_null)
{
if (bind.buffer == nullptr) {
// allocating memory
bind.buffer = new char[sizeof(T)];
bind.buffer_type = type;
bind.buffer_length = sizeof(T);
bind.is_null = &is_null;
bind.is_unsigned = std::is_unsigned<T>::value;
}
*static_cast<T*>(bind.buffer) = value;
is_null = false;
}
void bind_value(enum_field_types type, const char *value, size_t, MYSQL_BIND &bind, my_bool &is_null)
{
std::size_t len(strlen(value) + 1);
if (bind.buffer_length < len) {
// reallocate memory
delete [] static_cast<char*>(bind.buffer);
bind.buffer = nullptr;
bind.buffer_length = 0;
bind.buffer_type = type;
bind.is_null = &is_null;
}
if (bind.buffer == nullptr) {
// allocating memory
bind.buffer = new char[len];
memset(bind.buffer, 0, len);
}
bind.buffer_length = (unsigned long)(len - 1);
#ifdef _MSC_VER
strncpy_s(static_cast<char*>(bind.buffer), len, value, _TRUNCATE);
#else
strncpy(static_cast<char*>(bind.buffer), value, len);
#endif
is_null = false;
}
//void bind_value(enum_field_types type, const matador::date &x, MYSQL_BIND &bind, my_bool &is_null)
//{
// if (bind.buffer == nullptr) {
// size_t s = sizeof(MYSQL_TIME);
// bind.buffer = new char[s];
// bind.buffer_length = (unsigned long)s;
// bind.is_null = &is_null;
// bind.buffer_type = type;
// bind.length = nullptr;
// }
// memset(bind.buffer, 0, sizeof(MYSQL_TIME));
// is_null = false;
// auto *mt = static_cast<MYSQL_TIME*>(bind.buffer);
// mt->day = (unsigned int)x.day();
// mt->month = (unsigned int)x.month();
// mt->year = (unsigned int)x.year();
// mt->time_type = MYSQL_TIMESTAMP_DATE;
//}
//
//void bind_value(enum_field_types type, const matador::time &x, MYSQL_BIND &bind, my_bool &is_null)
//{
// if (bind.buffer == nullptr) {
// size_t s = sizeof(MYSQL_TIME);
// bind.buffer = new char[s];
// bind.buffer_length = (unsigned long)s;
// bind.buffer_type = type;
// bind.length = nullptr;
// bind.is_null = &is_null;
// }
// memset(bind.buffer, 0, sizeof(MYSQL_TIME));
// is_null = false;
// auto *mt = static_cast<MYSQL_TIME*>(bind.buffer);
// mt->day = (unsigned int)x.day();
// mt->month = (unsigned int)x.month();
// mt->year = (unsigned int)x.year();
// mt->hour = (unsigned int)x.hour();
// mt->minute = (unsigned int)x.minute();
// mt->second = (unsigned int)x.second();
// mt->second_part = (unsigned long)x.milli_second() * 1000;
// mt->time_type = MYSQL_TIMESTAMP_DATETIME;
//}
}
mysql_parameter_binder::mysql_parameter_binder(size_t size)
: bind_params_(size)
, is_null_vector(size)
, info_(size)
{}
void mysql_parameter_binder::bind(size_t pos, char i)
{
detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, short i)
{
detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, int i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, long i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, long long int i)
{
detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned char i)
{
detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned short i)
{
detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned int i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned long i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned long long int i)
{
detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, bool b)
{
detail::bind_value(MYSQL_TYPE_TINY, b, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, float d)
{
detail::bind_value(MYSQL_TYPE_FLOAT, d, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, double d)
{
detail::bind_value(MYSQL_TYPE_DOUBLE, d, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const char *str)
{
detail::bind_value(MYSQL_TYPE_STRING, str, strlen(str), bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const char *str, size_t size)
{
detail::bind_value(MYSQL_TYPE_VAR_STRING, str, size, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const std::string &str)
{
detail::bind_value(MYSQL_TYPE_STRING, str.data(), str.size(), bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const std::string &str, size_t size)
{
detail::bind_value(MYSQL_TYPE_VAR_STRING, str.data(), size, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const utils::blob &blob)
{
}
std::vector<MYSQL_BIND> &mysql_parameter_binder::bind_params()
{
return bind_params_;
}
}

View File

@ -1,28 +0,0 @@
#include "mysql_prepared_result_reader.hpp"
namespace matador::backends::mysql {
mysql_prepared_result_reader::mysql_prepared_result_reader(MYSQL_STMT *stmt)
: stmt_(stmt)
{}
mysql_prepared_result_reader::~mysql_prepared_result_reader()
{
}
size_t mysql_prepared_result_reader::column_count() const
{
return 0;
}
const char *mysql_prepared_result_reader::column(size_t index) const
{
return nullptr;
}
bool mysql_prepared_result_reader::fetch()
{
return false;
}
}

View File

@ -1,35 +0,0 @@
#include "mysql_result_reader.hpp"
namespace matador::backends::mysql {
mysql_result_reader::mysql_result_reader(MYSQL_RES *result, unsigned int column_count)
: result_(result)
, row_count_(mysql_num_rows(result_))
, column_count_(column_count)
{}
mysql_result_reader::~mysql_result_reader()
{
if (result_) {
mysql_free_result(result_);
}
}
size_t mysql_result_reader::column_count() const
{
return column_count_;
}
const char *mysql_result_reader::column(size_t index) const
{
return current_row_[index];
}
bool mysql_result_reader::fetch()
{
current_row_ = mysql_fetch_row(result_);
return current_row_ != nullptr;
}
}

View File

@ -1,53 +0,0 @@
#include "mysql_statement.hpp"
#include "mysql_error.hpp"
#include "mysql_prepared_result_reader.hpp"
namespace matador::backends::mysql {
mysql_statement::mysql_statement(MYSQL_STMT *stmt, const sql::query_context &query)
: statement_impl(query)
, stmt_(stmt)
, binder_(query_.bind_vars.size())
{}
size_t mysql_statement::execute()
{
if (!binder_.bind_params().empty()) {
if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
}
if (mysql_stmt_execute(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
return mysql_stmt_affected_rows(stmt_);
}
std::unique_ptr<sql::query_result_impl> mysql_statement::fetch()
{
if (!binder_.bind_params().empty()) {
if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
}
if (mysql_stmt_execute(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
if (mysql_stmt_store_result(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_prepared_result_reader>(stmt_), std::move(query_.prototype)));
}
void mysql_statement::reset() {}
sql::parameter_binder& mysql_statement::binder()
{
return binder_;
}
}

View File

@ -1,46 +0,0 @@
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(MYSQL_CONNECTION_STRING "mysql://test:test123!@127.0.0.1:3306/matador_test")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/mysql/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "mysql connection string: ${MYSQL_CONNECTION_STRING}")
set(TEST_SOURCES
../../tests/QueryTest.cpp
../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp
../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
set(LIBRARY_TEST_TARGET mysql_tests)
add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES})
target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE
Catch2::Catch2WithMain
matador
${CMAKE_DL_LIBS}
${MySQL_LIBRARY})
target_include_directories(${LIBRARY_TEST_TARGET}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/test
PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (MySQL)")

View File

@ -1,8 +0,0 @@
#ifndef MYSQL_CONNECTION_HPP
#define MYSQL_CONNECTION_HPP
namespace matador::test::connection {
const char* const dns = "@MYSQL_CONNECTION_STRING@";
}
#endif /*SQLITE_CONNECTION_HPP*/

View File

@ -18,7 +18,7 @@ set(SOURCES
set(LIBRARY_TARGET matador-postgres) set(LIBRARY_TARGET matador-postgres)
add_subdirectory(test) #add_subdirectory(test)
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER}) add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
@ -32,4 +32,10 @@ target_include_directories(${LIBRARY_TARGET} PRIVATE
${PROJECT_SOURCE_DIR}/backends/postgres/include ${PROJECT_SOURCE_DIR}/backends/postgres/include
${PostgreSQL_INCLUDE_DIRS}) ${PostgreSQL_INCLUDE_DIRS})
target_link_libraries(${LIBRARY_TARGET} matador ${PostgreSQL_LIBRARIES}) target_link_libraries(${LIBRARY_TARGET}
matador-core
matador-orm
${CMAKE_DL_LIBS}
${CMAKE_THREAD_LIBS_INIT}
${PostgreSQL_LIBRARIES}
)

View File

@ -12,7 +12,7 @@
#define MATADOR_POSTGRES_API #define MATADOR_POSTGRES_API
#endif #endif
#include "matador/sql/connection_impl.hpp" #include "matador/sql/interface/connection_impl.hpp"
#include <unordered_map> #include <unordered_map>
@ -20,22 +20,25 @@
namespace matador::backends::postgres { namespace matador::backends::postgres {
class postgres_connection : public matador::sql::connection_impl class postgres_connection : public sql::connection_impl
{ {
public: public:
explicit postgres_connection(const sql::connection_info &info); explicit postgres_connection(const sql::connection_info &info);
void open() override; utils::result<void, utils::error> open() override;
void close() override; utils::result<void, utils::error> close() override;
bool is_open() override; [[nodiscard]] utils::result<bool, utils::error> is_open() const override;
[[nodiscard]] utils::result<bool, utils::error> is_valid() const override;
[[nodiscard]] utils::result<utils::version, utils::error> client_version() const override;
[[nodiscard]] utils::result<utils::version, utils::error> server_version() const override;
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) override; utils::result<size_t, utils::error> execute(const std::string &stmt) override;
std::unique_ptr<sql::statement_impl> prepare(sql::query_context context) override; utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override;
size_t execute(const std::string &stmt) override; utils::result<std::vector<sql::column_definition>, utils::error> describe(const std::string& table) override;
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
std::vector<sql::column_definition> describe(const std::string& table) override; [[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private: private:
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ; [[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;

View File

@ -1,15 +1,27 @@
#ifndef QUERY_POSTGRES_ERROR_HPP #ifndef QUERY_POSTGRES_ERROR_HPP
#define QUERY_POSTGRES_ERROR_HPP #define QUERY_POSTGRES_ERROR_HPP
#include "matador/utils/error.hpp"
#include "matador/sql/error_code.hpp"
#include <libpq-fe.h> #include <libpq-fe.h>
#include <string> #include <string>
namespace matador::backends::postgres { namespace matador::backends::postgres {
void throw_postgres_error(const char *what, const std::string &source); utils::error make_error(sql::error_code ec,
void throw_postgres_error(PGconn *db, const std::string &source); const PGresult *res,
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql); const PGconn *db,
const std::string &msg,
const std::string &sql = {});
bool is_result_error(const PGresult *res);
// void throw_postgres_error(const char *what, const std::string &source);
// void throw_postgres_error(PGconn *db, const std::string &source);
// void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql);
} }

View File

@ -1,42 +1,52 @@
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H #ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
#define QUERY_POSTGRES_PARAMETER_BINDER_H #define QUERY_POSTGRES_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp" #include "matador/utils/attribute_writer.hpp"
#include <vector> #include <vector>
namespace matador::backends::postgres { namespace matador::backends::postgres {
class postgres_parameter_binder final : public sql::parameter_binder class postgres_parameter_binder final : public utils::attribute_writer
{ {
public: public:
struct bind_data {
explicit bind_data(size_t size);
std::vector<std::string> strings;
std::vector<std::vector<unsigned char>> bytes;
std::vector<const char*> values;
std::vector<int> lengths;
std::vector<int> formats;
};
explicit postgres_parameter_binder(size_t size); explicit postgres_parameter_binder(size_t size);
void bind(size_t pos, char i) override; void write_value(size_t pos, const uint8_t &x) override;
void bind(size_t pos, short i) override; void write_value(size_t pos, const uint16_t &x) override;
void bind(size_t pos, int i) override; void write_value(size_t pos, const uint32_t &x) override;
void bind(size_t pos, long i) override; void write_value(size_t pos, const uint64_t &x) override;
void bind(size_t pos, long long int i) override; void write_value(size_t pos, const int8_t &x) override;
void bind(size_t pos, unsigned char i) override; void write_value(size_t pos, const int16_t &x) override;
void bind(size_t pos, unsigned short i) override; void write_value(size_t pos, const int32_t &x) override;
void bind(size_t pos, unsigned int i) override; void write_value(size_t pos, const int64_t &x) override;
void bind(size_t pos, unsigned long i) override; void write_value(size_t pos, const bool &x) override;
void bind(size_t pos, unsigned long long int i) override; void write_value(size_t pos, const float &x) override;
void bind(size_t pos, bool b) override; void write_value(size_t pos, const double &x) override;
void bind(size_t pos, float d) override; void write_value(size_t pos, const time &x ) override;
void bind(size_t pos, double d) override; void write_value(size_t pos, const date &x ) override;
void bind(size_t pos, const char *string) override; void write_value(size_t pos, const char *x) override;
void bind(size_t pos, const char *string, size_t size) override; void write_value(size_t pos, const char *x, size_t size) override;
void bind(size_t pos, const std::string &string) override; void write_value(size_t pos, const std::string &x) override;
void bind(size_t pos, const std::string &x, size_t size) override; void write_value(size_t pos, const std::string &x, size_t size) override;
void write_value(size_t pos, const utils::blob &x) override;
void write_value(size_t pos, const utils::value &x, size_t size) override;
void bind(size_t pos, const utils::blob &blob) override; [[nodiscard]] const bind_data& params() const;
[[nodiscard]] const std::vector<const char*>& params() const;
private: private:
std::vector<std::string> strings_; bind_data bind_data_;
std::vector<const char*> params_; // std::vector<std::string> strings_;
// std::vector<const char*> params_;
}; };
} }

View File

@ -1,12 +1,40 @@
#ifndef QUERY_POSTGRES_RESULT_READER_HPP #ifndef QUERY_POSTGRES_RESULT_READER_HPP
#define QUERY_POSTGRES_RESULT_READER_HPP #define QUERY_POSTGRES_RESULT_READER_HPP
#include "matador/sql/interface/query_result_reader.hpp"
#include <libpq-fe.h> #include <libpq-fe.h>
#include "matador/sql/query_result_reader.hpp"
namespace matador::backends::postgres { namespace matador::backends::postgres {
class postgres_result_reader : public sql::query_result_reader namespace detail {
class empty_binder final : public utils::attribute_reader
{
public:
void read_value(const char *, size_t, int8_t &) override {}
void read_value(const char *, size_t, int16_t &) override {}
void read_value(const char *, size_t, int32_t &) override {}
void read_value(const char *, size_t, int64_t &) override {}
void read_value(const char *, size_t, uint8_t &) override {}
void read_value(const char *, size_t, uint16_t &) override {}
void read_value(const char *, size_t, uint32_t &) override {}
void read_value(const char *, size_t, uint64_t &) override {}
void read_value(const char *, size_t, bool &) override {}
void read_value(const char *, size_t, float &) override {}
void read_value(const char *, size_t, double &) override {}
void read_value(const char *, size_t, time &) override {}
void read_value(const char *, size_t, date &) override {}
void read_value(const char *, size_t, char *, size_t) override {}
void read_value(const char *, size_t, std::string &) override {}
void read_value(const char *, size_t, std::string &, size_t) override {}
void read_value(const char *, size_t, utils::blob &) override {}
void read_value(const char *, size_t, utils::value &, size_t) override {}
};
}
class postgres_result_reader final : public sql::query_result_reader
{ {
public: public:
explicit postgres_result_reader(PGresult *result); explicit postgres_result_reader(PGresult *result);
@ -14,7 +42,30 @@ public:
[[nodiscard]] size_t column_count() const override; [[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override; [[nodiscard]] const char *column(size_t index) const override;
bool fetch() override; utils::result<bool, utils::error> fetch() override;
[[nodiscard]] size_t start_column_index() const override;
void read_value(const char *id, size_t index, int8_t &value) override;
void read_value(const char *id, size_t index, int16_t &value) override;
void read_value(const char *id, size_t index, int32_t &value) override;
void read_value(const char *id, size_t index, int64_t &value) override;
void read_value(const char *id, size_t index, uint8_t &value) override;
void read_value(const char *id, size_t index, uint16_t &value) override;
void read_value(const char *id, size_t index, uint32_t &value) override;
void read_value(const char *id, size_t index, uint64_t &value) override;
void read_value(const char *id, size_t index, bool &value) override;
void read_value(const char *id, size_t index, float &value) override;
void read_value(const char *id, size_t index, double &value) override;
void read_value(const char *id, size_t index, matador::time &value) override;
void read_value(const char *id, size_t index, matador::date &value) override;
void read_value(const char *id, size_t index, char *value, size_t size) override;
void read_value(const char *id, size_t index, std::string &value) override;
void read_value(const char *id, size_t index, std::string &value, size_t size) override;
void read_value(const char *id, size_t index, utils::blob &value) override;
void read_value(const char *id, size_t index, utils::value &val, size_t size) override;
protected:
attribute_reader &result_binder() override;
private: private:
PGresult *result_{}; PGresult *result_{};
@ -22,6 +73,8 @@ private:
size_t row_count_{}; size_t row_count_{};
size_t column_count_{}; size_t column_count_{};
int row_index_{-1}; int row_index_{-1};
detail::empty_binder empty_binder_;
}; };
} }

View File

@ -1,7 +1,7 @@
#ifndef QUERY_POSTGRES_STATEMENT_HPP #ifndef QUERY_POSTGRES_STATEMENT_HPP
#define QUERY_POSTGRES_STATEMENT_HPP #define QUERY_POSTGRES_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp" #include "matador/sql/interface/statement_impl.hpp"
#include "postgres_parameter_binder.h" #include "postgres_parameter_binder.h"
@ -14,11 +14,11 @@ class postgres_statement final : public sql::statement_impl
public: public:
postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query); postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query);
size_t execute() override; utils::result<size_t, utils::error> execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override; utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch() override;
void reset() override; void reset() override;
protected: protected:
sql::parameter_binder& binder() override; utils::attribute_writer& binder() override;
private: private:
PGconn *db_{nullptr}; PGconn *db_{nullptr};

View File

@ -3,67 +3,101 @@
#include "postgres_result_reader.hpp" #include "postgres_result_reader.hpp"
#include "postgres_statement.hpp" #include "postgres_statement.hpp"
#include "matador/sql/error_code.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include <iostream> #include "matador/sql/internal/query_result_impl.hpp"
#include <sstream> #include <sstream>
namespace matador::backends::postgres { namespace matador::backends::postgres {
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{}; postgres_connection::string_to_int_map postgres_connection::statement_name_map_{};
postgres_connection::postgres_connection(const sql::connection_info &info) postgres_connection::postgres_connection(const sql::connection_info &info)
: connection_impl(info) {} : connection_impl(info) {
}
void postgres_connection::open() utils::result<void, utils::error> postgres_connection::open() {
{
if (is_open()) { if (is_open()) {
return; return utils::ok<void>();
} }
std::string connection("user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database + " port=" + std::to_string(info().port)); const std::string connection(
"user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database +
" port=" + std::to_string(info().port));
conn_ = PQconnectdb(connection.c_str()); conn_ = PQconnectdb(connection.c_str());
if (PQstatus(conn_) == CONNECTION_BAD) { if (PQstatus(conn_) == CONNECTION_BAD) {
const std::string msg = PQerrorMessage(conn_); const std::string msg = PQerrorMessage(conn_);
PQfinish(conn_); PQfinish(conn_);
conn_ = nullptr; conn_ = nullptr;
throw_postgres_error(msg.c_str(), "postgres"); return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect"));
} }
return utils::ok<void>();
} }
void postgres_connection::close() utils::result<void, utils::error> postgres_connection::close() {
{
if (conn_) { if (conn_) {
PQfinish(conn_); PQfinish(conn_);
conn_ = nullptr; conn_ = nullptr;
} }
return utils::ok<void>();
} }
bool postgres_connection::is_open() utils::result<bool, utils::error> postgres_connection::is_open() const {
{ return utils::ok(conn_ != nullptr);
return conn_ != nullptr;
} }
std::unique_ptr<sql::query_result_impl> postgres_connection::fetch(const std::string &stmt) utils::result<bool, utils::error> postgres_connection::is_valid() const {
{ return utils::ok(PQstatus(conn_) == CONNECTION_OK);
PGresult *res = PQexec(conn_, stmt.c_str()); }
throw_postgres_error(res, conn_, "postgres", stmt); utils::result<utils::version, utils::error> postgres_connection::client_version() const {
const auto client_version = PQlibVersion();
return utils::ok(utils::version{
static_cast<unsigned int>(client_version / 10000),
static_cast<unsigned int>((client_version % 10000) / 100),
static_cast<unsigned int>(client_version % 100)
});
}
std::vector<sql::column_definition> prototype; utils::result<utils::version, utils::error> postgres_connection::server_version() const {
auto num_col = PQnfields(res); const auto server_version = PQserverVersion(conn_);
for (int i = 0; i < num_col; ++i) {
const char *col_name = PQfname(res, i); if (server_version == 0) {
auto type = PQftype(res, i); return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version"));
auto size = PQfmod(res, i);
prototype.emplace_back(col_name);
} }
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype)));
return utils::ok(utils::version{
static_cast<unsigned int>(server_version / 10000),
static_cast<unsigned int>((server_version % 10000) / 100),
static_cast<unsigned int>(server_version % 100)
});
} }
std::string postgres_connection::generate_statement_name(const sql::query_context &query) utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &context) {
{ PGresult *res = PQexec(conn_, context.sql.c_str());
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql));
}
// std::vector<sql::column_definition> prototype;
// const auto num_col = PQnfields(res);
// for (int i = 0; i < num_col; ++i) {
// const char *col_name = PQfname(res, i);
// auto type = PQftype(res, i);
// auto size = PQfmod(res, i);
// prototype.emplace_back(col_name);
// }
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), context.prototype));
}
std::string postgres_connection::generate_statement_name(const sql::query_context &query) {
std::stringstream name; std::stringstream name;
name << query.table.name << "_" << query.command_name; name << query.table.name << "_" << query.command_name;
auto result = postgres_connection::statement_name_map_.find(name.str()); auto result = postgres_connection::statement_name_map_.find(name.str());
@ -77,71 +111,85 @@ std::string postgres_connection::generate_statement_name(const sql::query_contex
return name.str(); return name.str();
} }
std::unique_ptr<sql::statement_impl> postgres_connection::prepare(sql::query_context context) utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
{
auto statement_name = postgres_connection::generate_statement_name(context); auto statement_name = postgres_connection::generate_statement_name(context);
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr); PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(),
static_cast<int>(context.bind_vars.size()), nullptr);
throw_postgres_error(result, conn_, "postgres", context.sql); if (is_result_error(result)) {
return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql));
}
return std::make_unique<postgres_statement>(conn_, result, statement_name, std::move(context)); std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
return utils::ok(std::move(s));
} }
size_t postgres_connection::execute(const std::string &stmt) utils::result<size_t, utils::error> postgres_connection::execute(const std::string &stmt) {
{
PGresult *res = PQexec(conn_, stmt.c_str()); PGresult *res = PQexec(conn_, stmt.c_str());
throw_postgres_error(res, conn_, "postgres", stmt); if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK &&
status != PGRES_TUPLES_OK) {
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt));
}
const auto affected_rows = sql::to_long_long(PQcmdTuples(res)); const auto affected_rows = utils::to<size_t>(PQcmdTuples(res));
PQclear(res); PQclear(res);
return affected_rows; return utils::ok(static_cast<size_t>(affected_rows));
} }
sql::data_type_t string2type(const char *type) utils::basic_type string2type(const char *type) {
{
if (strcmp(type, "int2") == 0) { if (strcmp(type, "int2") == 0) {
return sql::data_type_t::type_short; return utils::basic_type::type_int16;
} else if (strcmp(type, "int4") == 0) { } else if (strcmp(type, "int4") == 0) {
return sql::data_type_t::type_int; return utils::basic_type::type_int32;
} else if (strcmp(type, "int8") == 0) { } else if (strcmp(type, "int8") == 0) {
return sql::data_type_t::type_long_long; return utils::basic_type::type_int64;
} else if (strncmp(type, "int8", 6) == 0) { } else if (strcmp(type, "bool") == 0) {
return sql::data_type_t::type_long_long; return utils::basic_type::type_bool;
} else if (strcmp(type, "date") == 0) { } else if (strcmp(type, "date") == 0) {
return sql::data_type_t::type_date; return utils::basic_type::type_date;
} else if (strncmp(type, "timestamp", 8) == 0) { } else if (strcmp(type, "timestamp") == 0) {
return sql::data_type_t::type_time; return utils::basic_type::type_time;
} else if (strcmp(type, "float4") == 0) { } else if (strcmp(type, "float4") == 0) {
return sql::data_type_t::type_float; return utils::basic_type::type_float;
} else if (strcmp(type, "float8") == 0) { } else if (strcmp(type, "float8") == 0) {
return sql::data_type_t::type_double; return utils::basic_type::type_double;
} else if (strncmp(type, "varchar", 7) == 0) { } else if (strncmp(type, "varchar", 7) == 0) {
return sql::data_type_t::type_varchar; return utils::basic_type::type_varchar;
} else if (strncmp(type, "character varying", 7) == 0) { } else if (strcmp(type, "character varying") == 0) {
return sql::data_type_t::type_varchar; return utils::basic_type::type_varchar;
} else if (strncmp(type, "text", 0) == 0) { } else if (strcmp(type, "text") == 0) {
return sql::data_type_t::type_text; return utils::basic_type::type_text;
} else if (strcmp(type, "bytea") == 0) {
return utils::basic_type::type_blob;
} else { } else {
return sql::data_type_t::type_unknown; return utils::basic_type::type_null;
} }
} }
std::vector<sql::column_definition> postgres_connection::describe(const std::string &table) utils::result<std::vector<sql::column_definition>, utils::error> postgres_connection::describe(const std::string &table) {
{ const std::string stmt(
std::string stmt( "SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='"
"SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='" + table + "'"); + table + "'");
PGresult *res = PQexec(conn_, stmt.c_str()); PGresult *res = PQexec(conn_, stmt.c_str());
throw_postgres_error(res, conn_, "postgres", stmt); if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt));
}
postgres_result_reader reader(res); postgres_result_reader reader(res);
std::vector<sql::column_definition> prototype; std::vector<sql::column_definition> prototype;
while (reader.fetch()) { while (auto fetched = reader.fetch()) {
if (!fetched.is_ok()) {
return utils::failure(fetched.release_error());
}
if (!*fetched) {
break;
}
char *end = nullptr; char *end = nullptr;
// Todo: Handle error // Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10) - 1; auto index = strtoul(reader.column(0), &end, 10) - 1;
@ -158,31 +206,39 @@ std::vector<sql::column_definition> postgres_connection::describe(const std::str
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index); prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
} }
return std::move(prototype); return utils::ok(prototype);
} }
bool postgres_connection::exists(const std::string &schema_name, const std::string &table_name) utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name, const std::string &table_name) {
{ const std::string stmt(
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name + "'"); "SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name
+ "'");
PGresult *res = PQexec(conn_, stmt.c_str()); PGresult *res = PQexec(conn_, stmt.c_str());
throw_postgres_error(res, conn_, "postgres", stmt); if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt));
}
return sql::to_long_long(PQcmdTuples(res)) == 1; return utils::ok(utils::to<size_t>(PQcmdTuples(res)) == 1);
}
std::string postgres_connection::to_escaped_string(const utils::blob& value) const
{
size_t escapedDataLength;
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
return {reinterpret_cast<char*>(escapedData), escapedDataLength-1};
} }
} }
extern "C" extern "C" {
{ MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) {
MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info)
{
return new matador::backends::postgres::postgres_connection(info); return new matador::backends::postgres::postgres_connection(info);
} }
MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db) MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db) {
{
delete db; delete db;
} }
} }

View File

@ -2,20 +2,28 @@
#include "matador/sql/dialect_builder.hpp" #include "matador/sql/dialect_builder.hpp"
#include "matador/utils/basic_types.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect() [[maybe_unused]] const matador::sql::dialect *get_dialect()
{ {
using namespace matador::sql; using namespace matador::sql;
const static dialect d = dialect_builder::builder() const static dialect d = dialect_builder::builder()
.create() .create()
.with_placeholder_func([](size_t index) { .with_placeholder_func([](const size_t index) {
return "$" + std::to_string(index); return "$" + std::to_string(index);
}) })
.with_token_replace_map({ .with_token_replace_map({
{dialect::token_t::BEGIN_BINARY_DATA, "E'\\"} {dialect_token::BEGIN_BINARY_DATA, "'"}
}) })
.with_data_type_replace_map({ .with_data_type_replace_map({
{data_type_t::type_blob, "BYTEA"} {matador::utils::basic_type::type_int8, "SMALLINT"},
}) {matador::utils::basic_type::type_uint8, "SMALLINT"},
{matador::utils::basic_type::type_float, "REAL"},
{matador::utils::basic_type::type_double, "DOUBLE PRECISION"},
{matador::utils::basic_type::type_time, "TIMESTAMP"},
{matador::utils::basic_type::type_blob, "BYTEA"}
})
.with_bool_strings("TRUE", "FALSE")
.with_default_schema_name("public") .with_default_schema_name("public")
.build(); .build();
return &d; return &d;

View File

@ -6,6 +6,30 @@
namespace matador::backends::postgres { namespace matador::backends::postgres {
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg,
const std::string &sql) {
utils::error err(ec, msg);
err.add_error_info("dbms", "postgres");
if (!sql.empty()) {
err.add_error_info("sql", sql);
}
if (res == nullptr) {
err.add_error_info("message", PQerrorMessage(db));
} else if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
err.add_error_info("message", PQresultErrorField(res, PG_DIAG_SQLSTATE));
}
return err;
}
bool is_result_error(const PGresult *res) {
if (res == nullptr) {
return true;
}
const auto status = PQresultStatus(res);
return status != PGRES_TUPLES_OK && status == PGRES_COMMAND_OK;
}
void throw_postgres_error(const char *what, const std::string &source) void throw_postgres_error(const char *what, const std::string &source)
{ {
std::stringstream msg; std::stringstream msg;
@ -26,8 +50,9 @@ void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source,
std::stringstream msg; std::stringstream msg;
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql; msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
throw std::logic_error(msg.str()); throw std::logic_error(msg.str());
} else if ((PQresultStatus(res) != PGRES_COMMAND_OK && }
PQresultStatus(res) != PGRES_TUPLES_OK)) { if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK &&
status != PGRES_TUPLES_OK) {
std::stringstream msg; std::stringstream msg;
msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql; msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql;
throw std::logic_error(msg.str()); throw std::logic_error(msg.str());

View File

@ -1,29 +1,32 @@
#include "postgres_parameter_binder.h" #include "postgres_parameter_binder.h"
#include "matador/utils/string.hpp"
#include <cstring>
namespace matador::backends::postgres { namespace matador::backends::postgres {
namespace detail { namespace detail {
template<class T>
template < class T > void bind_value(postgres_parameter_binder::bind_data &data, const size_t index, const T &x) {
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, T &x) data.strings[index] = std::to_string(x);
{ data.values[index] = data.strings[index].c_str();
strings[index] = std::to_string(x); data.lengths[index] = static_cast<int>(data.strings[index].size());
params[index] = strings[index].c_str(); data.formats[index] = 0;
} }
template <> // template<>
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, char &x) // void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const char &x) {
{ // data.strings[index] = std::to_string(x);
strings[index] = std::to_string(x); // data.values[index] = data.strings[index].data();
params[index] = strings[index].data(); // data.formats[index] = 0;
} // }
template <> // template<>
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, unsigned char &x) // void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const unsigned char &x) {
{ // data.strings[index] = std::to_string(x);
strings[index] = std::to_string(x); // data.values[index] = data.strings[index].data();
params[index] = strings[index].data(); // data.formats[index] = 0;
} // }
//template <> //template <>
//void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t &index, const matador::date &x) //void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t &index, const matador::date &x)
@ -40,108 +43,111 @@ void bind_value(std::vector<std::string> &strings, std::vector<const char*> &par
// params[index] = strings[index].c_str(); // params[index] = strings[index].c_str();
// ++index; // ++index;
//} //}
} }
postgres_parameter_binder::postgres_parameter_binder(size_t size) postgres_parameter_binder::bind_data::bind_data(const size_t size)
: strings_(size) : strings(size)
, params_(size) , bytes(size)
, values(size)
, lengths(size)
, formats(size)
{} {}
void postgres_parameter_binder::bind(size_t pos, char i) postgres_parameter_binder::postgres_parameter_binder(size_t size)
{ : bind_data_(size)
detail::bind_value(strings_, params_, pos, i); {}
void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) {
detail::bind_value(bind_data_, pos, x);
} }
void postgres_parameter_binder::bind(size_t pos, short i) void postgres_parameter_binder::write_value(const size_t pos, const int16_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, int i) void postgres_parameter_binder::write_value(const size_t pos, const int32_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, long i) void postgres_parameter_binder::write_value(const size_t pos, const int64_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, long long int i) void postgres_parameter_binder::write_value(const size_t pos, const uint8_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, unsigned char i) void postgres_parameter_binder::write_value(const size_t pos, const uint16_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, unsigned short i) void postgres_parameter_binder::write_value(const size_t pos, const uint32_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, unsigned int i) void postgres_parameter_binder::write_value(const size_t pos, const uint64_t &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, unsigned long i) void postgres_parameter_binder::write_value(const size_t pos, const bool &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, unsigned long long int i) void postgres_parameter_binder::write_value(const size_t pos, const float &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, i);
} }
void postgres_parameter_binder::bind(size_t pos, bool b) void postgres_parameter_binder::write_value(const size_t pos, const double &x) {
{ detail::bind_value(bind_data_, pos, x);
detail::bind_value(strings_, params_, pos, b);
} }
void postgres_parameter_binder::bind(size_t pos, float d) void postgres_parameter_binder::write_value(const size_t pos, const char *x) {
{ bind_data_.values[pos] = x;
detail::bind_value(strings_, params_, pos, d); bind_data_.lengths[pos] = static_cast<int>(std::strlen(x));
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::bind(size_t pos, double d) void postgres_parameter_binder::write_value(const size_t pos, const char *x, size_t /*size*/) {
{ bind_data_.values[pos] = x;
detail::bind_value(strings_, params_, pos, d); bind_data_.lengths[pos] = static_cast<int>(std::strlen(x));
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::bind(size_t pos, const char *str) void postgres_parameter_binder::write_value(const size_t pos, const std::string &x) {
{ bind_data_.values[pos] = x.data();
params_[pos] = str; bind_data_.lengths[pos] = static_cast<int>(x.size());
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::bind(size_t pos, const char *str, size_t size) void postgres_parameter_binder::write_value(const size_t pos, const std::string &x, size_t /*size*/) {
{ write_value(pos, x);
params_[pos] = str;
} }
void postgres_parameter_binder::bind(size_t pos, const std::string &str) void postgres_parameter_binder::write_value(const size_t pos, const time &/*x*/) {
{ // bind_data_.strings[pos] = utils::to_string(x, "%Y-%m-%d %T.%f");
strings_[pos] = str; bind_data_.values[pos] = bind_data_.strings[pos].data();
params_[pos] = strings_[pos].c_str(); bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::bind(size_t pos, const std::string &str, size_t size) void postgres_parameter_binder::write_value(const size_t pos, const date &/*x*/) {
{ // bind_data_.strings[pos] = utils::to_string(x, utils::date_format::ISO8601);
bind(pos, str); bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::bind(size_t pos, const utils::blob &blob) void postgres_parameter_binder::write_value(const size_t pos, const utils::blob &x) {
{ bind_data_.bytes[pos] = x;
params_[pos] = ""; bind_data_.values[pos] = reinterpret_cast<char*>(bind_data_.bytes[pos].data());
bind_data_.lengths[pos] = static_cast<int>(bind_data_.bytes[pos].size());
bind_data_.formats[pos] = 1;
} }
const std::vector<const char *> &postgres_parameter_binder::params() const void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {}
{
return params_; const postgres_parameter_binder::bind_data &postgres_parameter_binder::params() const {
return bind_data_;
} }
} }

View File

@ -1,6 +1,7 @@
#include "postgres_result_reader.hpp" #include "postgres_result_reader.hpp"
#include "matador/sql/to_value.hpp" #include "matador/utils/convert.hpp"
#include "matador/utils/value.hpp"
namespace matador::backends::postgres { namespace matador::backends::postgres {
@ -22,14 +23,209 @@ size_t postgres_result_reader::column_count() const
return column_count_; return column_count_;
} }
const char *postgres_result_reader::column(size_t index) const const char *postgres_result_reader::column( const size_t index) const
{ {
return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index)); return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index));
} }
bool postgres_result_reader::fetch() utils::result<bool, utils::error> postgres_result_reader::fetch() { return utils::ok(++row_index_ < row_count_); }
{
return ++row_index_ < row_count_; size_t postgres_result_reader::start_column_index() const {
return 0;
} }
} void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int8_t &value) {
if (auto res = utils::to<int8_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int16_t &value) {
if (auto res = utils::to<int16_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int32_t &value) {
if (auto res = utils::to<int32_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int64_t &value) {
if (auto res = utils::to<int64_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint8_t &value) {
if (auto res = utils::to<uint8_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint16_t &value) {
if (auto res = utils::to<uint16_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint32_t &value) {
if (auto res = utils::to<uint32_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint64_t &value) {
if (auto res = utils::to<uint64_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, bool &value) {
if (auto res = utils::to<bool>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, float &value) {
if (auto res = utils::to<float>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, double &value) {
if (auto res = utils::to<double>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, time &value) {
// if (const auto val = column(index); strlen(val) > 0) {
// value = time::parse(val, "%Y-%m-%d %T.%f");
// }
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, date &value) {
// if (const auto val = column(index); strlen(val) > 0) {
// value.set(val, matador::utils::date_format::ISO8601);
// }
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) {
auto val = column(index);
if (const size_t len = strlen(val); len > size) {
#ifdef _MSC_VER
strncpy_s(value, size, val, len);
#else
strncpy(value, val, size);
#endif
value[size-1] = '\n';
} else {
#ifdef _MSC_VER
strcpy_s(value, size, val);
#else
strcpy(value, val);
#endif
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value) {
value.assign(column(index));
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value, size_t /*s*/) {
value.assign(column(index));
}
void postgres_result_reader::read_value( const char* id, const size_t index, utils::blob& value )
{
const auto *data = reinterpret_cast<const unsigned char*>(column(index));
// auto length = PQgetlength(result_, row_index_, static_cast<int>(index));
size_t length;
unsigned char* unescaped = PQunescapeBytea(data, &length);
value.assign(unescaped, unescaped+length);
}
template <typename Type>
void set_value(const char* str, utils::value& value) {
if (const auto res = utils::to<Type>(str); res.is_ok()) {
value = res.value();
}
}
template <>
void set_value<utils::blob>(const char* str, utils::value& value) {
size_t length;
unsigned char* unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char*>(str), &length);
value = utils::blob(unescaped, unescaped+length);
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) {
switch (val.type()) {
case utils::basic_type::type_int8:
set_value<int8_t>(column(index), val);
break;
case utils::basic_type::type_int16:
set_value<int16_t>(column(index), val);
break;
case utils::basic_type::type_int32:
set_value<int32_t>(column(index), val);
break;
case utils::basic_type::type_int64:
set_value<int64_t>(column(index), val);
break;
case utils::basic_type::type_uint8:
set_value<uint8_t>(column(index), val);
break;
case utils::basic_type::type_uint16:
set_value<uint16_t>(column(index), val);
break;
case utils::basic_type::type_uint32:
set_value<uint32_t>(column(index), val);
break;
case utils::basic_type::type_uint64:
set_value<uint64_t>(column(index), val);
break;
case utils::basic_type::type_float:
set_value<float>(column(index), val);
break;
case utils::basic_type::type_double:
set_value<double>(column(index), val);
break;
case utils::basic_type::type_bool:
set_value<bool>(column(index), val);
break;
case utils::basic_type::type_text:
case utils::basic_type::type_varchar: {
if (const auto *column_value = column(index); column_value == nullptr) {
val = std::string{};
} else {
val = std::string{column_value};
}
break;
}
case utils::basic_type::type_time:
case utils::basic_type::type_date: {
val = std::string{column(index)};
break;
}
case utils::basic_type::type_null: {
val = nullptr_t{};
break;
}
case utils::basic_type::type_blob: {
set_value<utils::blob>(column(index), val);
break;
}
}
}
utils::attribute_reader &postgres_result_reader::result_binder() {
return empty_binder_;
}
} // namespace matador::backends::postgres

View File

@ -12,27 +12,48 @@ postgres_statement::postgres_statement(PGconn *db, PGresult *result, std::string
, binder_(query_.bind_vars.size()) , binder_(query_.bind_vars.size())
{} {}
size_t postgres_statement::execute() utils::result<size_t, utils::error> postgres_statement::execute()
{ {
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0); PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(binder_.params().values.size()),
binder_.params().values.data(),
binder_.params().lengths.data(),
binder_.params().formats.data(),
0);
throw_postgres_error(res, db_, "postgres", query_.sql); if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql));
}
return std::stoul(PQcmdTuples(res)); const auto *tuples = PQcmdTuples(res);
if (strlen(tuples) == 0) {
return utils::ok(static_cast<size_t>(0));
}
return utils::ok(static_cast<size_t>(std::stoul(tuples)));
} }
std::unique_ptr<sql::query_result_impl> postgres_statement::fetch() utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch()
{ {
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0); PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(binder_.params().values.size()),
binder_.params().values.data(),
binder_.params().lengths.data(),
binder_.params().formats.data(),
0);
throw_postgres_error(res, db_, "postgres", query_.sql); if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(query_.prototype))); return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), query_.prototype));
} }
void postgres_statement::reset() {} void postgres_statement::reset() {}
sql::parameter_binder& postgres_statement::binder() utils::attribute_writer& postgres_statement::binder()
{ {
return binder_; return binder_;
} }

View File

@ -1,45 +1,50 @@
Include(FetchContent) CPMAddPackage("gh:catchorg/Catch2@3.7.1")
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test") set(POSTGRES_CONNECTION_STRING "postgres://test:test123@localhost:15432/testdb")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}") message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
set(TEST_SOURCES set(TEST_SOURCES
../../tests/QueryTest.cpp ../../../test/models/coordinate.hpp
../../tests/ConnectionTest.cpp ../../../test/models/location.hpp
../../tests/QueryRecordTest.cpp ../../../test/models/types.hpp
../../tests/StatementTest.cpp ../../../test/backends/ColorEnumTraits.cpp
../../tests/TypeTraitsTest.cpp ../../../test/backends/ColorEnumTraits.hpp
../../tests/StatementCacheTest.cpp ../../../test/backends/ConnectionTest.cpp
../../tests/SessionTest.cpp) ../../../test/backends/QueryBasicTest.cpp
../../../test/backends/QueryFixture.cpp
../../../test/backends/QueryFixture.hpp
../../../test/backends/QueryRecordTest.cpp
../../../test/backends/QueryStatementTests.cpp
../../../test/backends/QueryTest.cpp
../../../test/backends/SessionFixture.cpp
../../../test/backends/SessionFixture.hpp
../../../test/backends/SessionTest.cpp
../../../test/backends/StatementCacheTest.cpp
../../../test/backends/StatementTest.cpp
../../../test/backends/TypeTraitsTest.cpp
)
set(LIBRARY_TEST_TARGET postgres_tests) set(LIBRARY_TEST_TARGET PostgresTests)
add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES}) add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES})
target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE
Catch2::Catch2WithMain Catch2::Catch2WithMain
matador matador-utils
matador-query
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
${PostgreSQL_LIBRARY}) ${PostgreSQL_LIBRARY})
add_dependencies(${LIBRARY_TEST_TARGET} matador-postgres)
target_include_directories(${LIBRARY_TEST_TARGET} target_include_directories(${LIBRARY_TEST_TARGET}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/test PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/test
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)") #catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)")

View File

@ -1,37 +0,0 @@
set(HEADER
include/sqlite_connection.hpp
include/sqlite_error.hpp
include/sqlite_dialect.hpp
include/sqlite_result_reader.hpp
include/sqlite_statement.hpp
include/sqlite_parameter_binder.h
include/sqlite_prepared_result_reader.hpp
)
set(SOURCES
src/sqlite_connection.cpp
src/sqlite_error.cpp
src/sqlite_dialect.cpp
src/sqlite_result_reader.cpp
src/sqlite_statement.cpp
src/sqlite_parameter_binder.cpp
src/sqlite_prepared_result_reader.cpp
)
set(LIBRARY_TARGET matador-sqlite)
add_subdirectory(test)
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
set_target_properties(${LIBRARY_TARGET}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
)
target_include_directories(${LIBRARY_TARGET} PRIVATE
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/backends/sqlite/include
${SQLite3_INCLUDE_DIRS})
target_link_libraries(${LIBRARY_TARGET} matador ${SQLite3_LIBRARIES})

View File

@ -1,65 +0,0 @@
#ifndef QUERY_SQLITE_CONNECTION_HPP
#define QUERY_SQLITE_CONNECTION_HPP
#ifdef _MSC_VER
#ifdef matador_sqlite_EXPORTS
#define MATADOR_SQLITE_API __declspec(dllexport)
#else
#define MATADOR_SQLITE_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_SQLITE_API
#endif
#include "matador/sql/connection_impl.hpp"
#include "sqlite_result_reader.hpp"
#include <sqlite3.h>
namespace matador::backends::sqlite {
class sqlite_connection : public matador::sql::connection_impl
{
public:
explicit sqlite_connection(const sql::connection_info &info);
void open() override;
void close() override;
bool is_open() override;
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) override;
std::unique_ptr<sql::statement_impl> prepare(sql::query_context query) override;
size_t execute(const std::string &stmt) override;
std::vector<sql::column_definition> describe(const std::string& table) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private:
struct fetch_context
{
std::vector<sql::column_definition> prototype;
sqlite_result_reader::rows rows;
};
private:
static int parse_result(void* param, int column_count, char** values, char** columns);
fetch_context fetch_internal(const std::string &stmt);
private:
sqlite3 *db_{};
};
}
extern "C"
{
MATADOR_SQLITE_API matador::sql::connection_impl* create_database(const matador::sql::connection_info &info);
MATADOR_SQLITE_API void destroy_database(matador::sql::connection_impl *db);
}
#endif //QUERY_SQLITE_CONNECTION_HPP

View File

@ -1,19 +0,0 @@
#ifndef QUERY_SQLITE_DIALECT_HPP
#define QUERY_SQLITE_DIALECT_HPP
#ifdef _MSC_VER
#ifdef matador_sqlite_EXPORTS
#define MATADOR_SQLITE_API __declspec(dllexport)
#else
#define MATADOR_SQLITE_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_SQLITE_API
#endif
#include "matador/sql/dialect.hpp"
extern "C" [[maybe_unused]] MATADOR_SQLITE_API const matador::sql::dialect* get_dialect();
#endif //QUERY_SQLITE_DIALECT_HPP

View File

@ -1,15 +0,0 @@
#ifndef QUERY_SQLITE_ERROR_HPP
#define QUERY_SQLITE_ERROR_HPP
#include <string>
struct sqlite3;
namespace matador::backends::sqlite {
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source);
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source, const std::string &sql);
}
#endif //QUERY_SQLITE_ERROR_HPP

View File

@ -1,44 +0,0 @@
#ifndef QUERY_SQLITE_PARAMETER_BINDER_H
#define QUERY_SQLITE_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp"
#include <sqlite3.h>
#include <memory>
#include <vector>
namespace matador::backends::sqlite {
class sqlite_parameter_binder final : public sql::parameter_binder
{
public:
explicit sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt);
void bind(size_t pos, char i) override;
void bind(size_t pos, short i) override;
void bind(size_t pos, int i) override;
void bind(size_t pos, long i) override;
void bind(size_t pos, long long int i) override;
void bind(size_t pos, unsigned char i) override;
void bind(size_t pos, unsigned short i) override;
void bind(size_t pos, unsigned int i) override;
void bind(size_t pos, unsigned long i) override;
void bind(size_t pos, unsigned long long int i) override;
void bind(size_t pos, bool b) override;
void bind(size_t pos, float d) override;
void bind(size_t pos, double d) override;
void bind(size_t pos, const char *string) override;
void bind(size_t pos, const char *str, size_t size) override;
void bind(size_t pos, const std::string &str) override;
void bind(size_t pos, const std::string &str, size_t size) override;
void bind(size_t pos, const utils::blob &blob) override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
std::vector<std::shared_ptr<std::string> > host_strings_;
};
}
#endif //QUERY_SQLITE_PARAMETER_BINDER_H

View File

@ -1,42 +0,0 @@
#ifndef QUERY_SQLITE_PREPARED_RESULT_READER_HPP
#define QUERY_SQLITE_PREPARED_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#include <sqlite3.h>
namespace matador::backends::sqlite {
class sqlite_prepared_result_reader final : public sql::query_result_reader
{
public:
sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt);
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
void read_value(const char *id, size_t index, char &value) override;
void read_value(const char *id, size_t index, short &value) override;
void read_value(const char *id, size_t index, int &value) override;
void read_value(const char *id, size_t index, long &value) override;
void read_value(const char *id, size_t index, long long int &value) override;
void read_value(const char *id, size_t index, unsigned char &value) override;
void read_value(const char *id, size_t index, unsigned short &value) override;
void read_value(const char *id, size_t index, unsigned int &value) override;
void read_value(const char *id, size_t index, unsigned long &value) override;
void read_value(const char *id, size_t index, unsigned long long int &value) override;
void read_value(const char *id, size_t index, bool &value) override;
void read_value(const char *id, size_t index, float &value) override;
void read_value(const char *id, size_t index, double &value) override;
void read_value(const char *id, size_t index, char *value, size_t s) override;
void read_value(const char *id, size_t index, std::string &value) override;
void read_value(const char *id, size_t index, std::string &value, size_t s) override;
void read_value(const char *id, size_t index, sql::value &val, size_t size) override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
};
}
#endif //QUERY_SQLITE_PREPARED_RESULT_READER_HPP

View File

@ -1,33 +0,0 @@
#ifndef QUERY_SQLITE_RESULT_READER_HPP
#define QUERY_SQLITE_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#include <vector>
namespace matador::backends::sqlite {
class sqlite_result_reader final : public sql::query_result_reader
{
public:
using columns = std::vector<char*>;
using rows = std::vector<columns>;
public:
sqlite_result_reader(rows result, size_t column_count);
~sqlite_result_reader() override;
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char* column(size_t index) const override;
[[nodiscard]] bool fetch() override;
private:
rows result_;
long long row_index_ = -1;
size_t column_count_{};
};
}
#endif //QUERY_SQLITE_RESULT_READER_HPP

View File

@ -1,31 +0,0 @@
#ifndef QUERY_SQLITE_STATEMENT_HPP
#define QUERY_SQLITE_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp"
#include "sqlite_parameter_binder.h"
namespace matador::backends::sqlite {
class sqlite_statement final : public sql::statement_impl
{
public:
sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query);
~sqlite_statement();
size_t execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override;
void reset() override;
protected:
sql::parameter_binder& binder() override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
sqlite_parameter_binder binder_;
};
}
#endif //QUERY_SQLITE_STATEMENT_HPP

View File

@ -1,202 +0,0 @@
#include "sqlite_connection.hpp"
#include "sqlite_error.hpp"
#include "sqlite_result_reader.hpp"
#include "sqlite_statement.hpp"
#include "matador/sql/record.hpp"
#include <cstring>
#include <memory>
#include <utility>
namespace matador::backends::sqlite {
sqlite_connection::sqlite_connection(const sql::connection_info &info)
: connection_impl(info) {
}
void sqlite_connection::open()
{
if (is_open()) {
return;
}
const auto ret = sqlite3_open(info().database.c_str(), &db_);
if (ret != SQLITE_OK) {
throw_sqlite_error(ret, db_, "open");
}
}
void sqlite_connection::close()
{
int ret = sqlite3_close(db_);
throw_sqlite_error(ret, db_, "close");
db_ = nullptr;
}
bool sqlite_connection::is_open()
{
return db_ != nullptr;
}
int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns)
{
auto *context = static_cast<fetch_context*>(param);
sqlite_result_reader::columns column;
for(int i = 0; i < column_count; ++i) {
// copy and store column data;
if (values[i] == nullptr) {
auto val = new char[1];
val[0] = '\0';
column.push_back(val);
} else {
size_t size = strlen(values[i]);
auto val = new char[size + 1];
std::memcpy(val, values[i], size);
val[size] = '\0';
column.push_back(val);
}
}
context->rows.emplace_back(column);
if (context->prototype.empty()) {
for(int i = 0; i < column_count; ++i) {
context->prototype.emplace_back(columns[i]);
}
}
return 0;
}
sqlite_connection::fetch_context sqlite_connection::fetch_internal(const std::string &stmt)
{
fetch_context context;
char *errmsg = nullptr;
const int ret = sqlite3_exec(db_, stmt.c_str(), parse_result, &context, &errmsg);
throw_sqlite_error(ret, db_, "sqlite", stmt);
return context;
}
size_t sqlite_connection::execute(const std::string &stmt)
{
char *errmsg = nullptr;
int ret = sqlite3_exec(db_, stmt.c_str(), nullptr, nullptr, &errmsg);
throw_sqlite_error(ret, db_, "sqlite", stmt);
return sqlite3_changes(db_);
}
std::unique_ptr<sql::query_result_impl> sqlite_connection::fetch(const std::string &stmt)
{
auto context = fetch_internal(stmt);
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<sqlite_result_reader>(std::move(context.rows), context.prototype.size()), std::move(context.prototype)));
}
std::unique_ptr<sql::statement_impl> sqlite_connection::prepare(sql::query_context query)
{
sqlite3_stmt *stmt{};
int ret = sqlite3_prepare_v2(db_, query.sql.c_str(), static_cast<int>(query.sql.size()), &stmt, nullptr);
throw_sqlite_error(ret, db_, "sqlite3_prepare_v2", query.sql);
return std::make_unique<sqlite_statement>(db_, stmt, query);
}
sql::data_type_t string2type(const char *type)
{
if (strncmp(type, "INTEGER", 7) == 0) {
return sql::data_type_t::type_int;
} else if (strncmp(type, "TINYINT", 7) == 0) {
return sql::data_type_t::type_char;
} else if (strncmp(type, "SMALLINT", 8) == 0) {
return sql::data_type_t::type_short;
} else if (strncmp(type, "BIGINT", 6) == 0) {
return sql::data_type_t::type_long_long;
} else if (strcmp(type, "BOOLEAN") == 0) {
return sql::data_type_t::type_bool;
} else if (strcmp(type, "REAL") == 0) {
return sql::data_type_t::type_double;
} else if (strcmp(type, "FLOAT") == 0) {
return sql::data_type_t::type_float;
} else if (strcmp(type, "DOUBLE") == 0) {
return sql::data_type_t::type_double;
} else if (strcmp(type, "BLOB") == 0) {
return sql::data_type_t::type_blob;
} else if (strcmp(type, "NULL") == 0) {
return sql::data_type_t::type_null;
} else if (strncmp(type, "VARCHAR", 7) == 0) {
return sql::data_type_t::type_varchar;
} else if (strcmp(type, "DATE") == 0) {
return sql::data_type_t::type_date;
} else if (strcmp(type, "DATETIME") == 0) {
return sql::data_type_t::type_time;
} else if (strcmp(type, "TEXT") == 0) {
return sql::data_type_t::type_text;
} else {
return sql::data_type_t::type_unknown;
}
}
std::vector<sql::column_definition> sqlite_connection::describe(const std::string& table)
{
const auto result = fetch_internal("PRAGMA table_info(" + table + ")");
sqlite_result_reader reader(result.rows, result.prototype.size());
std::vector<sql::column_definition> prototype;
while (reader.fetch()) {
char *end = nullptr;
// Todo: add index to column
auto index = strtoul(reader.column(0), &end, 10);
std::string name = reader.column(1);
auto type = (string2type(reader.column(2)));
end = nullptr;
sql::null_option null_opt{sql::null_option::NULLABLE};
if (strtoul(reader.column(3), &end, 10) == 0) {
null_opt = sql::null_option::NOT_NULL;
}
// f.default_value(res->column(4));
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
}
return std::move(prototype);
}
bool sqlite_connection::exists(const std::string &schema_name, const std::string &table_name)
{
const auto result = fetch_internal("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1");
sqlite_result_reader reader(result.rows, result.prototype.size());
if (!reader.fetch()) {
// Todo: throw an exception?
return false;
}
int v{};
reader.read_value(nullptr, 0, v);
return v == 1;
}
}
extern "C"
{
MATADOR_SQLITE_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info)
{
return new matador::backends::sqlite::sqlite_connection(info);
}
MATADOR_SQLITE_API void destroy_database(matador::sql::connection_impl *db)
{
delete db;
}
}

View File

@ -1,19 +0,0 @@
#include "sqlite_dialect.hpp"
#include "matador/sql/dialect_builder.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect()
{
using namespace matador::sql;
const static dialect d = dialect_builder::builder()
.create()
.with_token_replace_map({
{dialect::token_t::BEGIN, "BEGIN TRANSACTION"},
{dialect::token_t::COMMIT, "COMMIT TRANSACTION"},
{dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"}
})
.with_default_schema_name("main")
.build();
return &d;
}

View File

@ -1,28 +0,0 @@
#include "sqlite_error.hpp"
#include <stdexcept>
#include <sstream>
#include <sqlite3.h>
namespace matador::backends::sqlite {
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source)
{
if (ec != SQLITE_OK && ec != SQLITE_DONE) {
std::stringstream msg;
msg << "sqlite error (" << source << "): " << sqlite3_errmsg(db);
throw std::logic_error(msg.str());
}
}
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source, const std::string &sql)
{
if (ec != SQLITE_OK&& ec != SQLITE_DONE) {
std::stringstream msg;
msg << "sqlite error (" << source << ", sql: " << sql << "): " << sqlite3_errmsg(db);
throw std::logic_error(msg.str());
}
}
}

View File

@ -1,126 +0,0 @@
#include "sqlite_parameter_binder.h"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_parameter_binder::sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt)
: db_(db)
, stmt_(stmt)
{}
void sqlite_parameter_binder::bind(size_t pos, char i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, short i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, int i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, long i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, long long int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned char i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned short i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned long i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned long long int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, bool b)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), b);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, float d)
{
int ret = sqlite3_bind_double(stmt_, static_cast<int>(++pos), d);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, double d)
{
int ret = sqlite3_bind_double(stmt_, static_cast<int>(++pos), d);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, const char *str)
{
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), str, static_cast<int>(strlen(str)), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const char *x, size_t size)
{
auto len = strlen(x);
size = (len > size) ? size : len;
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), x, static_cast<int>(size), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const std::string &str)
{
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), str.c_str(), static_cast<int>(str.size()), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const std::string &x, size_t size)
{
auto len = x.size();
if (size == 0) {
size = len;
} else {
size = (len > size) ? size : len;
}
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), x.data(), static_cast<int>(size), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const utils::blob &blob)
{
}
}

View File

@ -1,114 +0,0 @@
#include "sqlite_prepared_result_reader.hpp"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_prepared_result_reader::sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt)
: db_(db)
, stmt_(stmt)
{}
size_t sqlite_prepared_result_reader::column_count() const
{
return sqlite3_column_count(stmt_);
}
const char *sqlite_prepared_result_reader::column(size_t index) const
{
return reinterpret_cast<const char*>(sqlite3_column_text(stmt_, static_cast<int>(index)));
}
bool sqlite_prepared_result_reader::fetch()
{
int ret = sqlite3_step(stmt_);
if (ret != SQLITE_ROW) {
throw_sqlite_error(ret, db_, "sqlite3_step");
}
return ret != SQLITE_DONE;
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char &value)
{
value = static_cast<char>(sqlite3_column_int(stmt_, static_cast<int>(index)));
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, short &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long long int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned char &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned short &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long long int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, bool &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, float &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, double &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char *value, size_t s)
{
query_result_reader::read_value(id, index, value, s);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value, size_t s)
{
query_result_reader::read_value(id, index, value, s);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::value &val, size_t size)
{
query_result_reader::read_value(id, index, val, size);
}
}

View File

@ -1,35 +0,0 @@
#include "sqlite_result_reader.hpp"
#include <algorithm>
namespace matador::backends::sqlite {
sqlite_result_reader::sqlite_result_reader(sqlite_result_reader::rows result, size_t column_count)
: result_(std::move(result))
, column_count_(column_count) {}
sqlite_result_reader::~sqlite_result_reader()
{
std::for_each(result_.begin(), result_.end(), [](rows ::value_type& row) {
std::for_each(row.begin(), row.end(), [](const char *val) {
delete [] val;
});
});
}
size_t sqlite_result_reader::column_count() const
{
return column_count_;
}
const char* sqlite_result_reader::column(size_t index) const
{
return result_[row_index_][index];
}
bool sqlite_result_reader::fetch()
{
return ++row_index_ < result_.size();
}
}

View File

@ -1,52 +0,0 @@
#include "sqlite_statement.hpp"
#include "sqlite_prepared_result_reader.hpp"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_statement::sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query)
: statement_impl(query)
, db_(db)
, stmt_(stmt)
, binder_(db, stmt)
{}
sqlite_statement::~sqlite_statement()
{
sqlite3_finalize(stmt_);
}
size_t sqlite_statement::execute()
{
// get next row
int ret = sqlite3_reset(stmt_);
throw_sqlite_error(ret, db_, "sqlite3_reset");
if (ret = sqlite3_step(stmt_); ret != SQLITE_DONE) {
throw_sqlite_error(ret, db_, "sqlite3_step");
}
return sqlite3_changes(db_);
}
std::unique_ptr<sql::query_result_impl> sqlite_statement::fetch()
{
int ret = sqlite3_reset(stmt_);
throw_sqlite_error(ret, db_, "sqlite3_reset");
auto reader = std::make_unique<sqlite_prepared_result_reader>(db_, stmt_);
return std::move(std::make_unique<sql::query_result_impl>(std::move(reader), query_.prototype));
}
void sqlite_statement::reset()
{
if (stmt_) {
sqlite3_reset(stmt_);
sqlite3_clear_bindings(stmt_);
}
}
sql::parameter_binder& sqlite_statement::binder()
{
return binder_;
}
}

View File

@ -1,46 +0,0 @@
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(SQLITE_CONNECTION_STRING "sqlite://test.db")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/sqlite/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "sqlite connection string: ${SQLITE_CONNECTION_STRING}")
set(TEST_SOURCES
../../tests/QueryTest.cpp
../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp
../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
set(LIBRARY_TEST_TARGET sqlite_tests)
add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES})
target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE
Catch2::Catch2WithMain
matador
${CMAKE_DL_LIBS}
${SQLite3_LIBRARY})
target_include_directories(${LIBRARY_TEST_TARGET}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/test
PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (SQLite3)")

View File

@ -1,8 +0,0 @@
#ifndef SQLITE_CONNECTION_HPP
#define SQLITE_CONNECTION_HPP
namespace matador::test::connection {
const char* const dns = "@SQLITE_CONNECTION_STRING@";
}
#endif /*SQLITE_CONNECTION_HPP*/

View File

@ -1,402 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/query_builder.hpp"
#include "connection.hpp"
#include <list>
class QueryRecordFixture
{
public:
QueryRecordFixture()
: db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{
db.open();
}
~QueryRecordFixture() {
drop_table_if_exists("flight");
drop_table_if_exists("airplane");
drop_table_if_exists("person");
drop_table_if_exists("quotes");
}
protected:
matador::sql::connection db;
matador::sql::schema schema;
private:
void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) {
db.query(schema).drop().table(table_name).execute();
}
}
};
using namespace matador::sql;
TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement", "[session][record]")
{
REQUIRE(!db.exists("person"));
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
REQUIRE(db.exists("person"));
db.query(schema).drop()
.table("person")
.execute();
REQUIRE(!db.exists("person"));
}
TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement with foreign key", "[session][record]")
{
db.query(schema).create()
.table("airplane", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("brand", 255),
make_column<std::string>("model", 255),
})
.execute();
REQUIRE(db.exists("airplane"));
db.query(schema).create()
.table("flight", {
make_pk_column<unsigned long>("id"),
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
make_column<std::string>("pilot_name", 255),
})
.execute();
REQUIRE(db.exists("flight"));
db.query(schema).drop()
.table("flight")
.execute();
REQUIRE(!db.exists("flight"));
db.query(schema).drop()
.table("airplane")
.execute();
REQUIRE(!db.exists("airplane"));
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
auto res = db.query(schema).insert()
.into("person", {"id", "name", "age"})
.values({7, "george", 45})
.execute();
REQUIRE(res == 1);
auto result = db.query(schema).select({"id", "name", "age"})
.from("person")
.fetch_all();
for (const auto &i: result) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
// REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).template as<long long>() == 7);
REQUIRE(i.at(1).name() == "name");
// REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).template as<std::string>() == "george");
REQUIRE(i.at(2).name() == "age");
// REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).template as<int>() == 45);
}
db.query(schema).drop()
.table("person")
.execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement with foreign key", "[session][record]")
{
db.query(schema).create()
.table("airplane", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("brand", 255),
make_column<std::string>("model", 255),
})
.execute();
db.query(schema).create()
.table("flight", {
make_pk_column<unsigned long>("id"),
make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
make_column<std::string>("pilot_name", 255),
})
.execute();
auto res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute();
REQUIRE(res == 1);
auto count = db.query(schema).select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3);
res = db.query(schema).insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute();
REQUIRE(res == 1);
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
REQUIRE(!db.exists("flight"));
REQUIRE(!db.exists("airplane"));
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute update record statement", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
auto res = db.query(schema).insert()
.into("person", {"id", "name", "age"})
.values({7, "george", 45})
.execute();
REQUIRE(res == 1);
res = db.query(schema).update("person")
.set({{"id", 7},
{"name", "jane"},
{"age", 35}})
.where("id"_col == 7)
.execute();
REQUIRE(res == 1);
auto result = db.query(schema).select({"id", "name", "age"})
.from("person")
.fetch_all();
for (const auto &i: result) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).is_integer());
REQUIRE(i.at(0).as<long long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).is_varchar());
REQUIRE(i.at(1).as<std::string>() == "jane");
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).is_integer());
REQUIRE(i.at(2).as<int>() == 35);
}
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1);
auto result = db.query(schema).select({"id", "name", "age"})
.from("person")
.fetch_all();
std::list<std::string> expected_names{"george", "jane", "michael", "bob"};
for (const auto &p: result) {
REQUIRE(p.at(1).str() == expected_names.front());
expected_names.pop_front();
}
REQUIRE(expected_names.empty());
auto rec = db.query(schema).select({"id", "name", "age"})
.from("person")
.fetch_one();
REQUIRE(rec.has_value());
REQUIRE(rec->at(1).str() == "george");
auto name = db.query(schema).select({"name"})
.from("person")
.fetch_value<std::string>();
REQUIRE(name == "george");
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with order by", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1);
auto result = db.query(schema).select({"id", "name", "age"})
.from("person")
.order_by("name").asc()
.fetch_all();
std::list<std::string> expected_names{"bob", "george", "jane", "michael"};
for (const auto &p: result) {
REQUIRE(p.at(1).str() == expected_names.front());
expected_names.pop_front();
}
REQUIRE(expected_names.empty());
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with group by and order by", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
})
.execute();
auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute();
REQUIRE(res == 1);
auto result = db.query(schema).select({count("age").as("age_count"), "age"})
.from("person")
.group_by("age")
.order_by("age_count").desc()
.fetch_all();
std::list<std::pair<int, int>> expected_values{{2, 45},
{2, 13},
{1, 67}};
for (const auto &r: result) {
REQUIRE(r.at(0).as<int>() == expected_values.front().first);
REQUIRE(r.at(1).as<int>() == expected_values.front().second);
expected_values.pop_front();
}
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Execute delete statement", "[session][record]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
}).execute();
auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1);
res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1);
auto count = db.query(schema).select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 2);
res = db.query(schema).remove()
.from("person")
.where("id"_col == 1)
.execute();
REQUIRE(res == 1);
count = db.query(schema).select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 1);
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryRecordFixture, "Test quoted identifier", "[session][record]") {
db.query(schema).create()
.table("quotes", {
make_column<std::string>("from", 255),
make_column<std::string>("to", 255)
}).execute();
// check table description
std::vector<std::string> columns = { "from", "to"};
std::vector<data_type_t> types = {data_type_t::type_varchar, data_type_t::type_varchar};
auto fields = db.describe("quotes");
for (const auto &field : fields) {
REQUIRE(field.name() == columns[field.index()]);
REQUIRE(field.type() == types[field.index()]);
}
db.query(schema).insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute();
auto res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE(res.has_value());
REQUIRE("Berlin" == res->at("from").str());
REQUIRE("London" == res->at("to").str());
db.query(schema).update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute();
res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Hamburg" == res->at("from").str());
REQUIRE("New York" == res->at("to").str());
db.query(schema).drop().table("quotes").execute();
}

View File

@ -1,535 +0,0 @@
#include "catch2/catch_test_macros.hpp"
#include "matador/sql/column_definition.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/query_builder.hpp"
#include "matador/sql/session.hpp"
#include "connection.hpp"
#include "models/airplane.hpp"
#include "models/flight.hpp"
#include "models/person.hpp"
#include "models/recipe.hpp"
#include <iostream>
using namespace matador::sql;
using namespace matador::test;
class QueryFixture
{
public:
QueryFixture()
: db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{
db.open();
}
~QueryFixture()
{
drop_table_if_exists("flight");
drop_table_if_exists("airplane");
drop_table_if_exists("person");
drop_table_if_exists("recipe_ingredients");
drop_table_if_exists("recipes");
drop_table_if_exists("ingredients");
}
protected:
matador::sql::connection db;
matador::sql::schema schema;
private:
void drop_table_if_exists(const std::string &table_name)
{
if (db.exists(table_name)) {
db.query(schema).drop().table(table_name).execute();
}
}
};
TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[session]")
{
schema.attach<airplane>("airplane");
schema.attach<flight>("flight");
db.query(schema).create()
.table<airplane>("airplane")
.execute();
REQUIRE(db.exists("airplane"));
db.query(schema).create()
.table<flight>("flight")
.execute();
REQUIRE(db.exists("flight"));
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
REQUIRE(!db.exists("flight"));
REQUIRE(!db.exists("airplane"));
}
TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[session]")
{
schema.attach<person>("person");
db.query(schema).create()
.table<person>("person")
.execute();
person george{7, "george", 45};
george.image.push_back(37);
auto res = db.query(schema)
.insert()
.into("person", column_generator::generate<person>(schema, true))
.values(george)
.execute();
REQUIRE(res == 1);
// fetch person as record
auto result_record = db.query(schema)
.select(column_generator::generate<person>(schema, true))
.from("person")
.where("id"_col == 7)
.fetch_all();
for (const auto &i: result_record) {
REQUIRE(i.size() == 4);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).is_integer());
REQUIRE(i.at(0).as<long long>() == george.id);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).is_varchar());
REQUIRE(i.at(1).as<std::string>() == george.name);
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).is_integer());
REQUIRE(i.at(2).as<long long>() == george.age);
}
// fetch person as person
auto result_person = db.query(schema)
.select(column_generator::generate<person>(schema, true))
.from("person")
.where("id"_col == 7)
.fetch_all<person>();
for (const auto &i: result_person) {
REQUIRE(i.id == 7);
REQUIRE(i.name == "george");
REQUIRE(i.age == 45);
}
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[session]")
{
db.query(schema).create()
.table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
auto res = db.query(schema).insert()
.into("person", {{"", "id", ""}, {"", "name", ""}, {"", "color", ""}})
.values({7, "george", "green"})
.execute();
REQUIRE(res == 1);
// fetch person as record
auto result_record = db.query(schema).select({"id", "name", "color"})
.from("person")
.where("id"_col == 7)
.fetch_all();
for (const auto &i: result_record) {
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).is_integer());
REQUIRE(i.at(0).as<unsigned long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).is_varchar());
REQUIRE(i.at(1).as<std::string>() == "george");
REQUIRE(i.at(2).name() == "color");
REQUIRE(i.at(2).is_varchar());
REQUIRE(i.at(2).as<std::string>() == "green");
}
db.query(schema).drop().table("person").execute();
}
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[session]")
{
schema.attach<airplane>("airplane");
schema.attach<flight>("flight");
db.query(schema).create()
.table<airplane>("airplane")
.execute();
db.query(schema).create()
.table<flight>("flight")
.execute();
std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747")
};
for (const auto &plane: planes) {
auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1);
}
auto count = db.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>().value();
REQUIRE(count == 3);
flight f4711{4, planes.at(1), "hans"};
auto res = db.query(schema)
.insert()
.into("flight", column_generator::generate<flight>(schema, true))
.values(f4711)
.execute();
REQUIRE(res == 1);
auto f = *db.query(schema)
.select(column_generator::generate<flight>(schema, true))
.from("flight")
.fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4);
REQUIRE(f.at(1).as<unsigned long>() == 2);
REQUIRE(f.at(2).as<std::string>() == "hans");
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
}
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[session][join_left]")
{
schema.attach<airplane>("airplane");
schema.attach<flight>("flight");
db.query(schema).create()
.table<airplane>("airplane")
.execute();
db.query(schema).create()
.table<flight>("flight")
.execute();
std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747")
};
for (const auto &plane: planes) {
auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1);
}
auto count = db.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>().value();
REQUIRE(count == 3);
std::vector<entity<flight>> flights{
make_entity<flight>(4, planes.at(0), "hans"),
make_entity<flight>(5, planes.at(0), "otto"),
make_entity<flight>(6, planes.at(1), "george"),
make_entity<flight>(7, planes.at(2), "paul")
};
for (const auto &f: flights) {
auto res = db.query(schema)
.insert()
.into("flight", {"id", "airplane_id", "pilot_name"})
.values(*f)
.execute();
REQUIRE(res == 1);
}
auto f = *db.query(schema)
.select(column_generator::generate<flight>(schema, true))
.from("flight")
.fetch_all().begin();
REQUIRE(f.at(0).as<unsigned long>() == 4);
REQUIRE(f.at(1).as<unsigned long>() == 1);
REQUIRE(f.at(2).as<std::string>() == "hans");
auto result = db.query(schema).select({"f.id", "ap.brand", "ap.model", "f.pilot_name"})
.from({"flight", "f"})
.join_left({"airplane", "ap"})
.on("f.airplane_id"_col == "ap.id"_col)
.order_by("f.id").asc()
.fetch_all();
std::vector<std::pair<unsigned long, std::string>> expected_result {
{4, "hans"},
{5, "otto"},
{6, "george"},
{7, "paul"}
};
size_t index{0};
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>() == expected_result[index].first);
REQUIRE(r.at(3).as<std::string>() == expected_result[index++].second);
}
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
}
TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[session][join_left][find]") {
schema.attach<airplane>("airplane");
schema.attach<flight>("flight");
db.query(schema).create()
.table<airplane>("airplane")
.execute();
db.query(schema).create()
.table<flight>("flight")
.execute();
std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747")
};
for (const auto &plane: planes) {
auto res = db
.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1);
}
auto count = db
.query(schema)
.select({count_all()})
.from("airplane")
.fetch_value<int>().value();
REQUIRE(count == 3);
std::vector<entity<flight>> flights{
make_entity<flight>(4, planes.at(0), "hans"),
make_entity<flight>(5, planes.at(0), "otto"),
make_entity<flight>(6, planes.at(1), "george"),
make_entity<flight>(7, planes.at(2), "paul")
};
for (const auto &f: flights) {
auto res = db.query(schema)
.insert()
.into("flight", column_generator::generate<flight>(schema, true))
.values(*f)
.execute();
REQUIRE(res == 1);
}
auto f = db.query(schema)
.select(column_generator::generate<flight>(schema, true))
.from("flight")
.fetch_one();
REQUIRE(f.has_value());
REQUIRE(f->at(0).as<unsigned long>() == 4);
REQUIRE(f->at(1).as<unsigned long>() == 1);
REQUIRE(f->at(2).as<std::string>() == "hans");
auto result = db
.query(schema)
.select({"f.id", "f.airplane_id", "ap.brand", "ap.model", "f.pilot_name"})
.from({"flight", "f"})
.join_left({"airplane", "ap"})
.on("f.airplane_id"_col == "ap.id"_col)
.where("f.id"_col == 4)
.fetch_one<flight>();
auto expected_flight = flights[0];
REQUIRE(result);
REQUIRE(result->id == expected_flight->id);
REQUIRE(result->pilot_name == expected_flight->pilot_name);
REQUIRE(result->airplane.get());
REQUIRE(result->airplane->id == 1);
REQUIRE(result->airplane->model == "A380");
REQUIRE(result->airplane->brand == "Airbus");
db.query(schema).drop().table("flight").execute();
db.query(schema).drop().table("airplane").execute();
}
TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[session][join][many_to_many]") {
schema.attach<recipe>("recipes");
schema.attach<ingredient>("ingredients");
schema.attach<recipe_ingredient>("recipe_ingredients");
db.query(schema).create()
.table<recipe>("recipes")
.execute();
db.query(schema).create()
.table<ingredient>("ingredients")
.execute();
db.query(schema).create()
.table<recipe_ingredient>("recipe_ingredients")
.execute();
std::vector<ingredient> ingredients {
{1, "Apple"},
{2, "Strawberry"},
{3, "Pineapple"},
{4, "Sugar"},
{5, "Flour"},
{6, "Butter"},
{7, "Beans"}
};
for (const auto &i: ingredients) {
auto res = db
.query(schema)
.insert()
.into("ingredients", column_generator::generate<ingredient>(schema, true))
.values(i)
.execute();
REQUIRE(res == 1);
}
std::vector<recipe> recipes{
{7, "Apple Crumble"},
{8, "Beans Chili"},
{9, "Fruit Salad"}
};
for (const auto &r: recipes) {
auto res = db
.query(schema)
.insert()
.into("recipes", column_generator::generate<recipe>(schema, true))
.values(r)
.execute();
REQUIRE(res == 1);
}
std::vector<std::pair<int, int>> recipe_ingredients {
{ 7, 1 },
{ 7, 4 },
{ 7, 5 },
{ 8, 6 },
{ 8, 7 },
{ 9, 1 },
{ 9, 2 },
{ 9, 3 }
};
for (const auto &ri: recipe_ingredients) {
auto res = db
.query(schema)
.insert()
.into("recipe_ingredients", column_generator::generate<recipe_ingredient>(schema, true))
.values({ri.first, ri.second})
.execute();
REQUIRE(res == 1);
}
auto result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"})
.on("r.id"_col == "ri.recipe_id"_col)
.fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long>> expected_result_one_join {
{7, "Apple Crumble", 1},
{7, "Apple Crumble", 4},
{7, "Apple Crumble", 5},
{8, "Beans Chili", 6},
{8, "Beans Chili", 7},
{9, "Fruit Salad", 1},
{9, "Fruit Salad", 2},
{9, "Fruit Salad", 3}
};
size_t index{0};
for (const auto &r: result) {
REQUIRE(r.size() == 3);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_one_join[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_one_join[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_one_join[index]));
++index;
}
result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.fetch_all();
std::vector<std::tuple<unsigned long, std::string, unsigned long, std::string>> expected_result_two_joins {
{7, "Apple Crumble", 1, "Apple"},
{7, "Apple Crumble", 4, "Sugar"},
{7, "Apple Crumble", 5, "Flour"},
{8, "Beans Chili", 6, "Butter"},
{8, "Beans Chili", 7, "Beans"},
{9, "Fruit Salad", 1, "Apple"},
{9, "Fruit Salad", 2, "Strawberry"},
{9, "Fruit Salad", 3, "Pineapple"}
};
index = 0;
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
}
result = db
.query(schema)
.select({"r.id", "r.name", "ri.ingredient_id", "i.name"})
.from({"recipes", "r"})
.join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col)
.join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col)
.where("r.id"_col == 8)
.fetch_all();
index = 3;
for (const auto &r: result) {
REQUIRE(r.size() == 4);
REQUIRE(r.at(0).as<unsigned long>().value() == std::get<0>(expected_result_two_joins[index]));
REQUIRE(r.at(1).as<std::string>().value() == std::get<1>(expected_result_two_joins[index]));
REQUIRE(r.at(2).as<unsigned long>().value() == std::get<2>(expected_result_two_joins[index]));
REQUIRE(r.at(3).as<std::string>().value() == std::get<3>(expected_result_two_joins[index]));
++index;
}
db.query(schema).drop().table("recipe_ingredients").execute();
db.query(schema).drop().table("recipes").execute();
db.query(schema).drop().table("ingredients").execute();
}

View File

@ -1,99 +0,0 @@
#include "catch2/catch_test_macros.hpp"
#include "connection.hpp"
#include "matador/sql/session.hpp"
#include "models/airplane.hpp"
#include "models/flight.hpp"
class SessionFixture
{
public:
SessionFixture()
: pool(matador::test::connection::dns, 4)
, ses(pool)
{}
~SessionFixture()
{
drop_table_if_exists("flights");
drop_table_if_exists("airplanes");
}
protected:
matador::sql::connection_pool<matador::sql::connection> pool;
matador::sql::session ses;
private:
void drop_table_if_exists(const std::string &table_name)
{
if (ses.table_exists(table_name)) {
ses.drop_table(table_name);
}
}
};
using namespace matador;
TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") {
using namespace matador;
ses.attach<test::airplane>("airplanes");
ses.attach<test::flight>("flights");
ses.create_schema();
auto plane = ses.insert<test::airplane>(1, "Boeing", "A380");
auto f = ses.insert<test::flight>(2, plane, "sully");
auto result = ses.find<test::flight>(2);
REQUIRE(result.is_ok());
}
TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") {
using namespace matador::test;
ses.attach<airplane>("airplanes");
ses.create_schema();
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
auto result = ses.find<airplane>(2);
REQUIRE(!result.is_ok());
REQUIRE((result.err() == sql::session_error::FailedToFindObject));
result = ses.find<airplane>(1);
REQUIRE(result);
auto read_a380 = result.value();
REQUIRE(a380->id == read_a380->id);
}
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
using namespace matador::test;
ses.attach<airplane>("airplanes");
ses.create_schema();
std::vector<std::unique_ptr<airplane>> planes;
planes.emplace_back(new airplane(1, "Airbus", "A380"));
planes.emplace_back(new airplane(2, "Boeing", "707"));
planes.emplace_back(new airplane(3, "Boeing", "747"));
for (auto &&plane: planes) {
ses.insert(plane.release());
}
auto result = ses.find<airplane>();
std::vector<std::tuple<int, std::string, std::string>> expected_result {
{1, "Airbus", "A380"},
{2, "Boeing", "707"},
{3, "Boeing", "747"}
};
REQUIRE(result);
auto all_planes = result.release();
size_t index {0};
for (const auto &i: all_planes) {
REQUIRE(i.id == std::get<0>(expected_result[index]));
REQUIRE(i.brand == std::get<1>(expected_result[index]));
REQUIRE(i.model == std::get<2>(expected_result[index]));
++index;
}
}

View File

@ -1,117 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/column_definition.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/connection.hpp"
#include "connection.hpp"
#include "models/airplane.hpp"
using namespace matador::sql;
using namespace matador::test;
class StatementTestFixture
{
public:
StatementTestFixture()
: db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{
db.open();
db.query(schema).create().table<airplane>("airplane").execute();
}
~StatementTestFixture()
{
drop_table_if_exists("airplane");
}
protected:
matador::sql::connection db;
matador::sql::schema schema;
std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"),
make_entity<airplane>(2, "Boeing", "707"),
make_entity<airplane>(3, "Boeing", "747")
};
private:
void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) {
db.query(schema).drop().table(table_name).execute();
}
}
};
TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]")
{
schema.attach<airplane>("airplane");
table ap{"airplane"};
SECTION("Insert with prepared statement and placeholder") {
auto stmt = db.query(schema).insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values<airplane>()
.prepare();
for (const auto &plane: planes) {
auto res = stmt.bind(*plane).execute();
REQUIRE(res == 1);
stmt.reset();
}
auto result = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.fetch_all<airplane>();
size_t index{0};
for (const auto &i: result) {
REQUIRE(i.id == planes[index]->id);
REQUIRE(i.brand == planes[index]->brand);
REQUIRE(i.model == planes[index++]->model);
}
}
SECTION("Select with prepared statement") {
for (const auto &plane: planes) {
auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1);
}
auto stmt = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.where("brand"_col == _)
.prepare();
stmt.bind(0, "Airbus");
auto result = stmt.fetch<airplane>();
for (const auto &i: result) {
REQUIRE(i.id == planes[0]->id);
REQUIRE(i.brand == planes[0]->brand);
REQUIRE(i.model == planes[0]->model);
}
stmt.reset();
stmt.bind(0, "Boeing");
result = stmt.fetch<airplane>();
size_t index{1};
for (const auto &i: result) {
REQUIRE(i.id == planes[index]->id);
REQUIRE(i.brand == planes[index]->brand);
REQUIRE(i.model == planes[index++]->model);
}
}
}

View File

@ -1,131 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/sql/connection.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "connection.hpp"
#include "models/location.hpp"
using namespace matador::sql;
using namespace matador::test;
class TypeTraitsTestFixture
{
public:
TypeTraitsTestFixture()
: db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{
db.open();
db.query(schema).create()
.table<location>("location")
.execute();
}
~TypeTraitsTestFixture()
{
db.query(schema).drop().table("location").execute();
}
protected:
matador::sql::connection db;
matador::sql::schema schema;
private:
void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) {
db.query(schema).drop().table(table_name).execute();
}
}
};
static const matador::utils::enum_mapper<Color> color_enum({
{Color::Green, "green"},
{Color::Red, "red"},
{Color::Blue, "blue"},
{Color::Yellow, "yellow"},
{Color::Black, "black"},
{Color::White, "white"},
{Color::Brown, "brown"}
});
template<>
struct matador::sql::data_type_traits<Color, void>
{
inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(size); }
static void read_value(query_result_reader &reader, const char *id, size_t index, Color &value)
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = color_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(const Color &value)
{
return color_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, Color &value)
{
binder.bind(index, color_enum.to_string(value));
}
};
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]")
{
schema.attach<location>("location");
SECTION("Insert and select with direct execution") {
location loc{1, "center", {1, 2, 3}, Color::Black};
auto res = db
.query(schema)
.insert()
.into("location", column_generator::generate<location>(schema, true))
.values(loc)
.execute();
REQUIRE(res == 1);
auto result = db
.query(schema)
.select(column_generator::generate<location>(schema, true))
.from("location")
.fetch_all<location>();
for (const auto &l: result) {
REQUIRE(l.name == "center");
}
}
SECTION("Insert and select with prepared statement") {
location loc{1, "center", {1, 2, 3}, Color::Black};
auto stmt = db
.query(schema)
.insert()
.into("location", column_generator::generate<location>(schema, true))
.values<location>()
.prepare();
auto res = stmt
.bind(loc)
.execute();
REQUIRE(res == 1);
auto result = db
.query(schema)
.select(column_generator::generate<location>(schema, true))
.from("location")
.fetch_all<location>();
for (const auto &l: result) {
REQUIRE(l.name == "center");
}
}
}

1269
cmake/CPM.cmake Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,129 +0,0 @@
# - Find mysqlclient
# Find the native MySQL includes and library
#
# MYSQL_INCLUDE_DIR - where to find mysql.h, etc.
# MYSQL_LIBRARY - List of libraries when using MySQL.
# MYSQL_FOUND - True if MySQL found.
IF (MYSQL_INCLUDE_DIR)
# Already in cache, be silent
SET(MYSQL_FIND_QUIETLY TRUE)
ENDIF (MYSQL_INCLUDE_DIR)
if(WIN32)
find_path(MYSQL_INCLUDE_DIR mysql.h
PATHS
$ENV{MYSQL_INCLUDE_DIR}
$ENV{MYSQL_DIR}/include
$ENV{ProgramFiles}/MySQL/*/include
$ENV{SystemDrive}/MySQL/*/include
$ENV{ProgramW6432}/MySQL/*/include
$ENV{ProgramFiles}/MariaDB/*/include
$ENV{SystemDrive}/MariaDB/*/include
$ENV{ProgramW6432}/MariaDB/*/include
)
else(WIN32)
find_path(MYSQL_INCLUDE_DIR mysql.h
PATHS
$ENV{MYSQL_DIR}/include
$ENV{MYSQL_INCLUDE_DIR}
/usr/local/mysql/include
/opt/mysql/mysql/include
PATH_SUFFIXES
mysql
)
endif(WIN32)
if(WIN32)
if (${CMAKE_BUILD_TYPE})
STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER)
endif()
# path suffix for debug/release mode
# binary_dist: mysql binary distribution
# build_dist: custom build
if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug")
SET(binary_dist debug)
SET(build_dist Debug)
else(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug")
ADD_DEFINITIONS(-DDBUG_OFF)
SET(binary_dist opt)
SET(build_dist Release)
endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug")
FIND_LIBRARY(MYSQL_LIBRARY NAMES libmysql libmariadb
PATHS
$ENV{MYSQL_DIR}/lib
# $ENV{MYSQL_DIR}/lib/${binary_dist}
# $ENV{MYSQL_DIR}/libmysql/${build_dist}
# $ENV{MYSQL_DIR}/client/${build_dist}
# $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist}
$ENV{ProgramFiles}/MySQL/*/lib
$ENV{SystemDrive}/MySQL/*/lib
$ENV{ProgramW6432}/MySQL/*/lib
$ENV{ProgramFiles}/MariaDB/*/lib
$ENV{SystemDrive}/MariaDB/*/lib
$ENV{ProgramW6432}/MariaDB/*/lib
# $ENV{SystemDrive}/MySQL/*/lib/${binary_dist}
# $ENV{ProgramFiles}/MariaDB/*/lib/${binary_dist}
# $ENV{SystemDrive}/MariaDB/*/lib/${binary_dist}
# $ENV{MYSQL_DIR}/lib/opt
# $ENV{MYSQL_DIR}/client/release
# $ENV{ProgramFiles}/MySQL/*/lib/opt
# $ENV{SystemDrive}/MySQL/*/lib/opt
# $ENV{ProgramW6432}/MySQL/*/lib
# $ENV{ProgramFiles}/MariaDB/*/lib/opt
# $ENV{SystemDrive}/MariaDB/*/lib/opt
# $ENV{ProgramW6432}/MariaDB/*/lib
)
else(WIN32)
find_library(MYSQL_LIBRARY NAMES libmysql
PATHS
$ENV{MYSQL_DIR}/libmysql_r/.libs
$ENV{MYSQL_DIR}/lib
$ENV{MYSQL_DIR}/lib/mysql
/usr/local/mysql/lib
/opt/mysql/mysql/lib
PATH_SUFFIXES
mysql
)
endif(WIN32)
if(WIN32)
else(WIN32)
set(MYSQL_LIB_PATHS
$ENV{MYSQL_DIR}/libmysql_r/.libs
$ENV{MYSQL_DIR}/lib
$ENV{MYSQL_DIR}/lib/mysql
/usr/local/mysql/lib
/opt/mysql/mysql/lib
PATH_SUFFIXES
mysql
)
find_library(MYSQL_LIBRARY NAMES mysqlclient
PATHS
${MYSQL_LIB_PATHS}
)
endif(WIN32)
IF (MYSQL_INCLUDE_DIR)
MESSAGE(STATUS "MariaDB include ${MYSQL_INCLUDE_DIR}")
ELSE (MYSQL_INCLUDE_DIR)
MESSAGE(STATUS "MariaDB include dir not found")
ENDIF (MYSQL_INCLUDE_DIR)
IF (MYSQL_LIBRARY)
MESSAGE(STATUS "MariaDB libs ${MYSQL_LIBRARY}")
ELSE (MYSQL_LIBRARY)
MESSAGE(STATUS "MariaDB libs dir not found")
ENDIF (MYSQL_LIBRARY)
IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
SET(MYSQL_FOUND TRUE)
ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
SET(MYSQL_FOUND FALSE)
SET(MYSQL_LIBRARY)
ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY)
MARK_AS_ADVANCED(MYSQL_LIBRARY MYSQL_INCLUDE_DIR)

View File

@ -0,0 +1,40 @@
#ifndef BASIC_PROTOTYPE_INFO_HPP
#define BASIC_PROTOTYPE_INFO_HPP
#include <typeindex>
namespace matador::object {
class schema_node;
class basic_object_info {
public:
virtual ~basic_object_info() = default;
[[nodiscard]] std::type_index type_index() const;
protected:
basic_object_info(schema_node &node, std::type_index type_index);
protected:
schema_node &node_; /**< prototype node of the represented object type */
std::type_index type_index_; /**< type index of the represented object type */
};
template<typename Type>
class object_info final : public basic_object_info {
public:
explicit object_info(schema_node &node)
: basic_object_info(node, typeid(Type)) {}
};
namespace detail {
struct null_type {};
}
using null_info = object_info<detail::null_type>;
}
#endif //BASIC_PROTOTYPE_INFO_HPP

View File

@ -0,0 +1,32 @@
#ifndef OBJECT_ERROR_CODE_HPP
#define OBJECT_ERROR_CODE_HPP
#include <cstdint>
#include <system_error>
namespace matador::object {
enum class error_code : uint8_t {
OK = 0,
NodeNotFound = 1,
NodeAlreadyExists = 2,
Failure,
};
class object_category_impl final : public std::error_category
{
public:
[[nodiscard]] const char* name() const noexcept override;
[[nodiscard]] std::string message(int ev) const override;
};
const std::error_category& object_category();
std::error_code make_error_code(error_code e);
std::error_condition make_error_condition(error_code e);
}
template <>
struct std::is_error_code_enum<matador::object::error_code> : true_type {};
#endif //OBJECT_ERROR_CODE_HPP

View File

@ -0,0 +1,63 @@
#ifndef SCHEMA_HPP
#define SCHEMA_HPP
#include "matador/object/error_code.hpp"
#include "matador/object/schema_node.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/error.hpp"
#include <memory>
#include <string>
#include <unordered_map>
namespace matador::object {
class schema {
public:
schema();
template <typename Type>
utils::result<void, utils::error> attach(const std::string name, const std::string &parent = "") {
auto node = schema_node::make_node<Type>(*this, name);
attach_node(node, parent);
return utils::ok<void>();
}
template <typename Type, typename ParentType>
utils::result<void, utils::error> attach(const std::string name) {
// auto node = std::make_unique<schema_node>(*this);
return utils::ok<void>();
}
[[nodiscard]] bool empty() const;
[[nodiscard]] size_t size() const;
private:
using t_node_map = std::unordered_map<std::string, std::shared_ptr<schema_node>>;
// type_index -> [name -> prototype]
using t_type_index_node_map = std::unordered_map<std::type_index, t_node_map>;
using node_ptr = std::shared_ptr<schema_node>;
utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
const std::string &parent);
utils::result<std::shared_ptr<schema_node>, utils::error> find_parent(const std::string &name) const;
utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::string &name) const;
utils::result<std::shared_ptr<schema_node>, utils::error> push_back_child(const node_ptr &parent, const node_ptr &child);
bool has_node(const std::type_index& index, const std::string &name) const;
private:
std::shared_ptr<schema_node> root_;
t_node_map node_map_;
t_type_index_node_map type_index_node_map_;
};
}
#endif //SCHEMA_HPP

View File

@ -0,0 +1,75 @@
#ifndef SCHEMA_NODE_HPP
#define SCHEMA_NODE_HPP
#include "matador/object/basic_object_info.hpp"
#include <memory>
#include <string>
namespace matador::object {
class schema;
class schema_node final {
public:
template < typename Type >
static std::shared_ptr<schema_node> make_node(schema& tree, const std::string& name) {
return std::make_shared<schema_node>(tree, name, static_cast<Type*>(nullptr));
}
schema_node(const schema_node& other) = delete;
schema_node(schema_node&& other) = default;
schema_node& operator=(const schema_node& other) = delete;
schema_node& operator=(schema_node&& other) = delete;
~schema_node() = default;
[[nodiscard]] std::string name() const;
[[nodiscard]] std::type_index type_index() const;
/**
* Appends the given prototype node as a sibling
* on the same level.
*
* @param sibling The new sibling node.
*/
void append(const std::shared_ptr<schema_node> &sibling);
/**
* Inserts the given node to the list of children.
*
* @param child The child node to add.
*/
void insert(const std::shared_ptr<schema_node> &child);
private:
explicit schema_node(schema& tree);
template < typename Type >
schema_node(schema& tree, std::string name, Type *obj)
: schema_(tree)
, info_(std::make_unique<object_info<Type>>(*this))
, first_child_(std::make_shared<schema_node>(tree))
, last_child_(std::make_shared<schema_node>(tree))
, name_(std::move(name)) {
first_child_->next_sibling_ = last_child_;
last_child_->previous_sibling_ = first_child_;
}
private:
friend schema;
schema &schema_;
std::unique_ptr<basic_object_info> info_;
std::shared_ptr<schema_node> parent_;
std::shared_ptr<schema_node> previous_sibling_;
std::shared_ptr<schema_node> next_sibling_;
std::shared_ptr<schema_node> first_child_;
std::shared_ptr<schema_node> last_child_;
std::string name_;
size_t depth_{0};
};
}
#endif //SCHEMA_NODE_HPP

View File

@ -0,0 +1,63 @@
#ifndef ATTRIBUTE_STRING_WRITER_HPP
#define ATTRIBUTE_STRING_WRITER_HPP
#include "matador/utils/attribute_writer.hpp"
#include <optional>
namespace matador::sql {
class dialect;
class connection_impl;
}
namespace matador::query {
class attribute_string_writer final : public utils::attribute_writer
{
public:
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
template<typename Type>
[[nodiscard]] std::string to_string(const Type &value)
{
result_.clear();
write_value(0, value);
return result_;
}
[[nodiscard]] const sql::dialect& dialect() const;
void write_value(size_t pos, const int8_t& x) override;
void write_value(size_t pos, const int16_t& x) override;
void write_value(size_t pos, const int32_t& x) override;
void write_value(size_t pos, const int64_t& x) override;
void write_value(size_t pos, const uint8_t& x) override;
void write_value(size_t pos, const uint16_t& x) override;
void write_value(size_t pos, const uint32_t& x) override;
void write_value(size_t pos, const uint64_t& x) override;
void write_value(size_t pos, const bool& x) override;
void write_value(size_t pos, const float& x) override;
void write_value(size_t pos, const double& x) override;
void write_value(size_t pos, const time& x) override;
void write_value(size_t pos, const date& x) override;
void write_value(size_t pos, const char* x) override;
void write_value(size_t pos, const char* x, size_t size) override;
void write_value(size_t pos, const std::string& x) override;
void write_value(size_t pos, const std::string& x, size_t size) override;
void write_value(size_t pos, const utils::blob& x) override;
void write_value(size_t pos, const utils::value& x, size_t size) override;
private:
std::string result_;
const sql::dialect &dialect_;
std::optional<std::reference_wrapper<const sql::connection_impl>> conn_;
};
// "This is a binary Data string" as binary data:
// MySQL: X'5468697320697320612062616E617279204461746120737472696E67'
// Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67'
// MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67
// Sqlite: X'5468697320697320612062616E617279204461746120737472696E67'
}
#endif //ATTRIBUTE_STRING_WRITER_HPP

View File

@ -8,9 +8,11 @@
#include <string> #include <string>
namespace matador::sql { namespace matador::sql {
class dialect; class dialect;
class query_context; struct query_context;
}
namespace matador::query {
class basic_condition class basic_condition
{ {
@ -18,7 +20,7 @@ public:
basic_condition() = default; basic_condition() = default;
virtual ~basic_condition() = default; virtual ~basic_condition() = default;
enum class operand_t : uint8_t enum class operand_type : uint8_t
{ {
EQUAL = 0, EQUAL = 0,
NOT_EQUAL, NOT_EQUAL,
@ -33,26 +35,26 @@ public:
LIKE LIKE
}; };
virtual std::string evaluate(const dialect &dialect, query_context &query) const = 0; virtual std::string evaluate(const sql::dialect &dialect, sql::query_context &query) const = 0;
static std::unordered_map<operand_t, std::string> operands; static std::unordered_map<operand_type, std::string> operands;
}; };
class basic_column_condition : public basic_condition class basic_column_condition : public basic_condition
{ {
public: public:
column field_; sql::column field_;
std::string operand; std::string operand;
basic_column_condition(column fld, basic_condition::operand_t op); basic_column_condition(sql::column fld, operand_type op);
}; };
class basic_in_condition : public basic_condition class basic_in_condition : public basic_condition
{ {
public: public:
column field_; sql::column field_;
explicit basic_in_condition(column fld); explicit basic_in_condition(sql::column fld);
[[nodiscard]] virtual size_t size() const = 0; [[nodiscard]] virtual size_t size() const = 0;
}; };

View File

@ -1,17 +1,17 @@
#ifndef QUERY_CONDITION_HPP #ifndef QUERY_CONDITION_HPP
#define QUERY_CONDITION_HPP #define QUERY_CONDITION_HPP
#include "matador/sql/any_type_to_string_visitor.hpp" #include "matador/query/basic_condition.hpp"
#include "matador/sql/query_result.hpp"
#include "matador/sql/basic_condition.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/placeholder.hpp"
#include "matador/sql/query_context.hpp" #include "matador/sql/query_context.hpp"
#include "matador/utils/placeholder.hpp"
#include <memory> #include <memory>
#include <utility> #include <utility>
namespace matador::sql { namespace matador::query {
/** /**
* @class condition * @class condition
@ -28,58 +28,55 @@ namespace matador::sql {
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
class query_select;
template<class L, class R, class Enabled = void> template<class L, class R, class Enabled = void>
class condition; class condition;
template<> template<>
class condition<column, placeholder, typename std::enable_if<true>::type> : public basic_column_condition class condition<sql::column, utils::placeholder, std::enable_if_t<true>> final : public basic_column_condition
{ {
public: public:
condition(const column &fld, basic_condition::operand_t op, const placeholder &val); condition(const sql::column &fld, operand_type op, const utils::placeholder &val);
placeholder value; utils::placeholder value;
std::string evaluate(const dialect &d, query_context &query) const override; std::string evaluate(const sql::dialect &d, sql::query_context &query) const override;
}; };
template<class T> template<class T>
class condition<column, T, typename std::enable_if< class condition<sql::column, T, std::enable_if_t<
std::is_scalar<T>::value && std::is_scalar_v<T> &&
!std::is_enum<T>::value && !std::is_same_v<std::string, T> &&
!std::is_same<std::string, T>::value && !std::is_same_v<const char*, T>>> final : public basic_column_condition
!std::is_same<const char*, T>::value>::type> : public basic_column_condition
{ {
public: public:
condition(const column &fld, basic_condition::operand_t op, T val) condition(const sql::column &fld, const operand_type op, T val)
: basic_column_condition(fld, op) : basic_column_condition(fld, op)
, value(val) , value(val)
{ } { }
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value); return d.prepare_condition(field_) + " " + operand + " " + std::to_string(value);
} }
}; };
template<class T> template<class T>
class condition<column, T, typename std::enable_if< class condition<sql::column, T, std::enable_if_t<
std::is_same<std::string, T>::value || std::is_same_v<std::string, T> ||
std::is_same<const char*, T>::value>::type> : public basic_column_condition std::is_same_v<const char*, T>>>final : public basic_column_condition
{ {
public: public:
condition(const column &fld, basic_condition::operand_t op, T val) condition(const sql::column &fld, const operand_type op, T val)
: basic_column_condition(fld, op) : basic_column_condition(fld, op)
,value(val) ,value(val)
{ } { }
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " " + operand + " '" + value + "'"; return d.prepare_identifier(field_) + " " + operand + " '" + value + "'";
@ -87,61 +84,39 @@ public:
}; };
template<class T> template<class T>
class condition<column, T, std::enable_if_t<std::is_enum_v<T>>> final : public basic_column_condition class condition<T, sql::column, std::enable_if_t<
{
public:
condition(const column &fld, basic_condition::operand_t op, T val)
: basic_column_condition(fld, op)
, value(val)
{ }
T value;
std::string evaluate(const dialect &d, query_context &query) const override
{
auto at = data_type_traits<T>::create_value(value);
any_type_to_string_visitor value_to_string(d, query);
std::visit(value_to_string, at);
return value_to_string.result + " " + operand + " " + d.prepare_identifier(field_);
}
};
template<class T>
class condition<T, column, std::enable_if_t<
std::is_scalar_v<T> && std::is_scalar_v<T> &&
!std::is_same_v<std::string, T> && !std::is_same_v<std::string, T> &&
!std::is_same_v<const char*, T>>> final : public basic_column_condition !std::is_same_v<const char*, T>>> final : public basic_column_condition
{ {
public: public:
condition(T val, basic_condition::operand_t op, const column &fld) condition(T val, const operand_type op, const sql::column &fld)
: basic_column_condition(fld, op) : basic_column_condition(fld, op)
, value(val) , value(val)
{ } { }
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_); return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_);
} }
}; };
template<class T> template<class T>
class condition<T, column, typename std::enable_if< class condition<T, sql::column, std::enable_if_t<
std::is_same<std::string, T>::value || std::is_same_v<std::string, T> ||
std::is_same<const char*, T>::value>::type> : public basic_column_condition std::is_same_v<const char*, T>>> final : public basic_column_condition
{ {
public: public:
condition(T val, basic_condition::operand_t op, const column &fld) condition(T val, const operand_type op, const sql::column &fld)
: basic_column_condition(fld, op) : basic_column_condition(fld, op)
, value(val) , value(val)
{ } { }
T value; T value;
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_); return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_);
} }
@ -160,18 +135,18 @@ public:
* @endcode * @endcode
*/ */
template < class V > template < class V >
class condition<column, std::initializer_list<V>> : public basic_in_condition { class condition<sql::column, std::initializer_list<V>> final : public basic_in_condition {
public: public:
/** /**
* @brief Creates an IN condition * @brief Creates an IN condition
* *
* Creates an IN condition for the given column and * Creates an IN condition for the given sql::column and
* the given list of arguments. * the given list of arguments.
* *
* @param col Column for the IN condition * @param col Column for the IN condition
* @param args List of arguments * @param args List of arguments
*/ */
condition(const column &col, const std::initializer_list<V> &args) condition(const sql::column &col, const std::initializer_list<V> &args)
: basic_in_condition(col), args_(args) {} : basic_in_condition(col), args_(args) {}
/** /**
@ -181,9 +156,10 @@ public:
* query string based on the given compile type * query string based on the given compile type
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query Query to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override { std::string evaluate(const sql::dialect &d, sql::query_context &query) const override {
auto count = size(); auto count = size();
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
@ -220,15 +196,15 @@ private:
/** /**
* @brief Condition class representing an IN condition * @brief Condition class representing an IN condition
* *
* This class represents an query IN condition and evaluates to * This class represents a query IN condition and evaluates to
* this condition based on the current database d * this condition based on the current database d
* *
* @code * @code
* WHERE age IN (select age_value from <table>) * WHERE age IN (select age_value from table)
* @endcode * @endcode
*/ */
template <> template <>
class condition<column, query_context> : public basic_column_condition class condition<sql::column, sql::query_context> final : public basic_column_condition
{ {
public: public:
/** /**
@ -242,7 +218,7 @@ public:
* @param op Operand of the condition * @param op Operand of the condition
* @param q The query to be evaluated to the IN arguments * @param q The query to be evaluated to the IN arguments
*/ */
condition(column col, basic_condition::operand_t op, const query_context &q); condition(sql::column col, operand_type op, sql::query_context &q);
/** /**
* @brief Evaluates the condition * @brief Evaluates the condition
@ -251,12 +227,13 @@ public:
* query string based on the given compile type * query string based on the given compile type
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query Query to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override; std::string evaluate(const sql::dialect &d, sql::query_context &query) const override;
private: private:
query_context query_; sql::query_context &query_;
}; };
/** /**
@ -268,7 +245,7 @@ private:
* @tparam T The type of the boundary values * @tparam T The type of the boundary values
*/ */
template < class T > template < class T >
class condition<column, std::pair<T, T>> : public basic_condition { class condition<sql::column, std::pair<T, T>> final : public basic_condition {
public: public:
/** /**
* @brief Create a new between condition * @brief Create a new between condition
@ -276,7 +253,7 @@ public:
* @param col The column for the range check * @param col The column for the range check
* @param range The boundary values defining the range * @param range The boundary values defining the range
*/ */
condition(column col, const std::pair<T, T> &range) condition(sql::column col, const std::pair<T, T> &range)
: field_(std::move(col)), range_(range) {} : field_(std::move(col)), range_(range) {}
/** /**
@ -286,16 +263,17 @@ public:
* based on the given compile type * based on the given compile type
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query Query to evaluate
* @return A condition BETWEEN part of the query * @return A condition BETWEEN part of the query
*/ */
std::string evaluate(const dialect &d, query_context &query) const override { std::string evaluate(const sql::dialect &d, sql::query_context &query) const override {
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second); return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second);
} }
private: private:
column field_; sql::column field_;
std::pair<T, T> range_; std::pair<T, T> range_;
}; };
@ -312,7 +290,7 @@ private:
* @tparam R2 The right hand type of the right operator * @tparam R2 The right hand type of the right operator
*/ */
template<class L1, class R1, class L2, class R2> template<class L1, class R1, class L2, class R2>
class condition<condition<L1, R1>, condition<L2, R2>> : public basic_condition class condition<condition<L1, R1>, condition<L2, R2>> final : public basic_condition
{ {
public: public:
/** /**
@ -321,31 +299,32 @@ public:
* @param r right hand operator of the condition * @param r right hand operator of the condition
* @param op The operand (AND or OR) * @param op The operand (AND or OR)
*/ */
condition(condition<L1, R1> &&l, condition<L2, R2> &&r, basic_condition::operand_t op) condition(condition<L1, R1> &&l, condition<L2, R2> &&r, const operand_type op)
: left(std::move(l)), right(std::move(r)), operand(op) { } : left(std::move(l)), right(std::move(r)), operand(op) { }
/** /**
* @brief Evaluates the condition * @brief Evaluates the condition
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query Query to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
// ensure the numbering order for host vars // ensure the numbering order for host vars
auto cl = left.evaluate(d, query); auto cl = left.evaluate(d, query);
auto cr = right.evaluate(d, query); auto cr = right.evaluate(d, query);
if (operand == basic_condition::operand_t::AND) { if (operand == operand_type::AND) {
return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")"; return "(" + cl + " " + operands[operand] + " " + cr + ")";
} else { } else {
return cl + " " + basic_condition::operands[operand] + " " + cr; return cl + " " + operands[operand] + " " + cr;
} }
} }
private: private:
condition<L1, R1> left; condition<L1, R1> left;
condition<L2, R2> right; condition<L2, R2> right;
basic_condition::operand_t operand; basic_condition::operand_type operand;
}; };
/** /**
@ -357,7 +336,7 @@ private:
* @tparam R Right hand type of the condition to be negated * @tparam R Right hand type of the condition to be negated
*/ */
template<class L, class R> template<class L, class R>
class condition<condition<L, R>, void> : public basic_condition class condition<condition<L, R>, void> final : public basic_condition
{ {
public: public:
/** /**
@ -365,17 +344,18 @@ public:
* @param c The condition to be negated * @param c The condition to be negated
*/ */
condition(const condition<L, R> &c) // NOLINT(*-explicit-constructor) condition(const condition<L, R> &c) // NOLINT(*-explicit-constructor)
: cond(c), operand(basic_condition::operands[basic_condition::operand_t::NOT]) { } : cond(c), operand(operands[operand_type::NOT]) { }
/** /**
* @brief Evaluates the condition * @brief Evaluates the condition
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query The context of the query
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{ {
return operand + " (" + cond.evaluate(d) + ")"; return operand + " (" + cond.evaluate(d, query) + ")";
} }
private: private:
@ -384,25 +364,23 @@ private:
}; };
template<> template<>
class condition<column, column> : public basic_column_condition class condition<sql::column, sql::column> final : public basic_column_condition
{ {
public: public:
condition(const column &a, basic_condition::operand_t op, column b) condition(const sql::column &a, const operand_type op, sql::column b)
: basic_column_condition(a, op) : basic_column_condition(a, op)
, other_column_(std::move(b)) {} , other_column_(std::move(b)) {}
/** /**
* @brief Evaluates the condition * @brief Evaluates the condition
* *
* @param d The d used to evaluate * @param d The d used to evaluate
* @param query The context of the query
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(const dialect &d, query_context &query) const override std::string evaluate(const sql::dialect &d, sql::query_context &query) const override;
{
return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_);
}
private: private:
column other_column_; sql::column other_column_;
}; };
/** /**
@ -430,9 +408,9 @@ private:
* @return The condition object * @return The condition object
*/ */
template < class V > template < class V >
condition<column, std::initializer_list<V>> in(const column &col, std::initializer_list<V> args) condition<sql::column, std::initializer_list<V>> in(const sql::column &col, std::initializer_list<V> args)
{ {
return condition<column, std::initializer_list<V>>(col, args); return condition<sql::column, std::initializer_list<V>>(col, args);
} }
/** /**
@ -442,8 +420,7 @@ condition<column, std::initializer_list<V>> in(const column &col, std::initializ
* @param q The query to be executes as sub select * @param q The query to be executes as sub select
* @return The condition object * @return The condition object
*/ */
condition<column, query_context> in(const column &col, const query_context &q); condition<sql::column, sql::query_context> in(const sql::column &col, sql::query_context &&q);
condition<column, query_context> in(const column &col, const query_select &q);
/** /**
* @brief Creates a between condition. * @brief Creates a between condition.
@ -458,9 +435,9 @@ condition<column, query_context> in(const column &col, const query_select &q);
* @return The condition object * @return The condition object
*/ */
template<class T> template<class T>
condition<column, std::pair<T, T>> between(const column &col, T low, T high) condition<sql::column, std::pair<T, T>> between(const sql::column &col, T low, T high)
{ {
return condition<column, std::pair<T, T>>(col, std::make_pair(low, high)); return condition<sql::column, std::pair<T, T>>(col, std::make_pair(low, high));
} }
/** /**
@ -473,7 +450,7 @@ condition<column, std::pair<T, T>> between(const column &col, T low, T high)
* @param val The value to the like operator * @param val The value to the like operator
* @return The like condition object * @return The like condition object
*/ */
condition<column, std::string> like(const column &col, const std::string &val); condition<sql::column, std::string> like(const sql::column &col, const std::string &val);
/** /**
* @brief Condition equality operator for a column and a value * @brief Condition equality operator for a column and a value
@ -487,12 +464,12 @@ condition<column, std::string> like(const column &col, const std::string &val);
* @return The condition object representing the equality operation * @return The condition object representing the equality operation
*/ */
template<class T> template<class T>
condition<column, T> operator==(const column &col, T val) condition<sql::column, T> operator==(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::EQUAL, val); return condition<sql::column, T>(col, basic_condition::operand_type::EQUAL, val);
} }
condition<column, column> operator==(const column &a, const column &b); condition<sql::column, sql::column> operator==(const sql::column &a, const sql::column &b);
/** /**
* @brief Condition equality method for a column and a query * @brief Condition equality method for a column and a query
@ -504,12 +481,12 @@ condition<column, column> operator==(const column &a, const column &b);
* @param q The query to compare with * @param q The query to compare with
* @return The condition object representing the equality operation * @return The condition object representing the equality operation
*/ */
condition<column, query_context> equals(const column &col, query_context &q); condition<sql::column, sql::query_context> equals(const sql::column &col, sql::query_context &q);
/** /**
* @brief Condition inequality operator for a column and a value * @brief Condition inequality operator for a column and a value
* *
* Creates a condition condition object of a column and a value * Creates a condition object of a column and a value
* checked on inequality. * checked on inequality.
* *
* @tparam T The type of the value * @tparam T The type of the value
@ -518,9 +495,9 @@ condition<column, query_context> equals(const column &col, query_context &q);
* @return The condition object representing the inequality operation * @return The condition object representing the inequality operation
*/ */
template<class T> template<class T>
condition<column, T> operator!=(const column &col, T val) condition<sql::column, T> operator!=(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::NOT_EQUAL, val); return condition<sql::column, T>(col, basic_condition::operand_type::NOT_EQUAL, val);
} }
/** /**
@ -535,9 +512,9 @@ condition<column, T> operator!=(const column &col, T val)
* @return The condition object representing the less operation * @return The condition object representing the less operation
*/ */
template<class T> template<class T>
condition<column, T> operator<(const column &col, T val) condition<sql::column, T> operator<(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::LESS, val); return condition<sql::column, T>(col, basic_condition::operand_type::LESS, val);
} }
/** /**
@ -552,9 +529,9 @@ condition<column, T> operator<(const column &col, T val)
* @return The condition object representing the less or equal operation * @return The condition object representing the less or equal operation
*/ */
template<class T> template<class T>
condition<column, T> operator<=(const column &col, T val) condition<sql::column, T> operator<=(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::LESS_EQUAL, val); return condition<sql::column, T>(col, basic_condition::operand_type::LESS_EQUAL, val);
} }
/** /**
@ -569,9 +546,9 @@ condition<column, T> operator<=(const column &col, T val)
* @return The condition object representing the greater operation * @return The condition object representing the greater operation
*/ */
template<class T> template<class T>
condition<column, T> operator>(const column &col, T val) condition<sql::column, T> operator>(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::GREATER, val); return condition<sql::column, T>(col, basic_condition::operand_type::GREATER, val);
} }
/** /**
@ -586,9 +563,9 @@ condition<column, T> operator>(const column &col, T val)
* @return The condition object representing the greater or equal operation * @return The condition object representing the greater or equal operation
*/ */
template<class T> template<class T>
condition<column, T> operator>=(const column &col, T val) condition<sql::column, T> operator>=(const sql::column &col, T val)
{ {
return condition<column, T>(col, basic_condition::operand_t::GREATER_EQUAL, val); return condition<sql::column, T>(col, basic_condition::operand_type::GREATER_EQUAL, val);
} }
/** /**
@ -605,7 +582,7 @@ condition<column, T> operator>=(const column &col, T val)
template<class L1, class R1, class L2, class R2> template<class L1, class R1, class L2, class R2>
condition<condition<L1, R1>, condition<L2, R2>> operator&&(condition<L1, R1> l, condition<L2, R2> r) condition<condition<L1, R1>, condition<L2, R2>> operator&&(condition<L1, R1> l, condition<L2, R2> r)
{ {
return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_t::AND); return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_type::AND);
} }
/** /**
@ -622,7 +599,7 @@ condition<condition<L1, R1>, condition<L2, R2>> operator&&(condition<L1, R1> l,
template<class L1, class R1, class L2, class R2> template<class L1, class R1, class L2, class R2>
condition<condition<L1, R1>, condition<L2, R2>> operator||(condition<L1, R1> l, condition<L2, R2> r) condition<condition<L1, R1>, condition<L2, R2>> operator||(condition<L1, R1> l, condition<L2, R2> r)
{ {
return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_t::OR); return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_type::OR);
} }
/** /**

View File

@ -1,12 +1,12 @@
#ifndef QUERY_FK_VALUE_EXTRACTOR_HPP #ifndef QUERY_FK_VALUE_EXTRACTOR_HPP
#define QUERY_FK_VALUE_EXTRACTOR_HPP #define QUERY_FK_VALUE_EXTRACTOR_HPP
#include "matador/sql/any_type.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp" #include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/utils/types.hpp"
namespace matador::sql::detail { namespace matador::query::detail {
class fk_value_extractor class fk_value_extractor
{ {
@ -14,14 +14,14 @@ public:
fk_value_extractor() = default; fk_value_extractor() = default;
template<class Type> template<class Type>
any_type extract(Type &x) utils::database_type extract(Type &x)
{ {
matador::utils::access::process(*this, x); access::process(*this, x);
return value_; return value_;
} }
template<typename ValueType> template<typename ValueType>
void on_primary_key(const char *, ValueType &pk, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0) void on_primary_key(const char *, ValueType &pk, std::enable_if_t<std::is_integral_v<ValueType> && !std::is_same_v<bool, ValueType>>* = nullptr)
{ {
value_ = pk; value_ = pk;
} }
@ -35,13 +35,17 @@ public:
template<class Pointer> template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
private: private:
any_type value_{}; utils::database_type value_{};
}; };
} }

View File

@ -0,0 +1,27 @@
#ifndef EXECUTABLE_QUERY_HPP
#define EXECUTABLE_QUERY_HPP
#include "query_intermediate.hpp"
#include "../../utils/error.hpp"
#include "../../utils/result.hpp"
namespace matador::sql {
class executor;
class statement;
}
namespace matador::query {
class executable_query : public query_intermediate {
public:
using query_intermediate::query_intermediate;
[[nodiscard]] utils::result<size_t, utils::error> execute(const sql::executor &exec) const;
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(const sql::executor &exec) const;
[[nodiscard]] std::string str(const sql::executor &exec) const;
};
}
#endif //EXECUTABLE_QUERY_HPP

View File

@ -0,0 +1,82 @@
#ifndef FETCHABLE_QUERY_HPP
#define FETCHABLE_QUERY_HPP
#include "query_intermediate.hpp"
#include "../query_compiler.hpp"
#include "../../sql/query_result.hpp"
#include "../../sql/record.hpp"
#include "../../utils/error.hpp"
#include "../../utils/result.hpp"
namespace matador::sql {
class executor;
class statement;
}
namespace matador::query {
class fetchable_query : public query_intermediate
{
protected:
using query_intermediate::query_intermediate;
public:
template < class Type >
utils::result<sql::query_result<Type>, utils::error> fetch_all(sql::executor &exec)
{
auto result = fetch(exec);
if (!result.is_ok()) {
return utils::error(result.err());
}
return utils::ok(sql::query_result<Type>(result.release()));
}
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::executor &exec) const;
template < class Type >
utils::result<std::unique_ptr<Type>, utils::error> fetch_one(const sql::executor &exec)
{
auto result = fetch(exec);
if (!result.is_ok()) {
return utils::error(result.err());
}
auto objects = sql::query_result<Type>(result.release());
auto first = objects.begin();
if (first == objects.end()) {
return utils::ok(std::unique_ptr<Type>{nullptr});
}
return utils::ok(std::unique_ptr<Type>{first.release()});
}
[[nodiscard]] utils::result<std::optional<sql::record>, utils::error> fetch_one(const sql::executor &exec) const;
template<typename Type>
utils::result<std::optional<Type>, utils::error> fetch_value(const sql::executor &exec)
{
const auto result = fetch_one(exec);
if (!result.is_ok()) {
return utils::failure(result.err());
}
if (result->has_value()) {
return utils::ok(std::optional<Type>{result->value().at(0).as<Type>().value()});
}
return utils::ok(std::optional<Type>{std::nullopt});
}
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(const sql::executor &exec) const;
[[nodiscard]] std::string str(const sql::executor &exec) const;
private:
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::executor &exec) const;
};
}
#endif //FETCHABLE_QUERY_HPP

View File

@ -0,0 +1,27 @@
#ifndef QUERY_CREATE_INTERMEDIATE_HPP
#define QUERY_CREATE_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_create_intermediate : public query_intermediate
{
public:
query_create_intermediate();
executable_query table(const sql::table &table, std::initializer_list<sql::column_definition> columns);
executable_query table(const sql::table &table, const std::vector<sql::column_definition> &columns);
// template<class Type>
// executable_query table(const sql::table &table, const sql::schema &schema)
// {
// return this->table(table, column_definition_generator::generate<Type>(schema));
// }
};
}
#endif //QUERY_CREATE_INTERMEDIATE_HPP

View File

@ -0,0 +1,29 @@
#ifndef QUERY_DELETE_FROM_INTERMEDIATE_HPP
#define QUERY_DELETE_FROM_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
#include "matador/query/basic_condition.hpp"
#include "matador/query/intermediates/query_execute_where_intermediate.hpp"
namespace matador::query {
class query_delete_from_intermediate : public executable_query
{
public:
using executable_query::executable_query;
template<class Condition>
query_execute_where_intermediate where(const Condition &cond)
{
return where_clause(std::make_unique<Condition>(std::move(cond)));
}
private:
query_execute_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
}
#endif //QUERY_DELETE_FROM_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_DELETE_INTERMEDIATE_HPP
#define QUERY_DELETE_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
namespace matador::query {
class query_delete_from_intermediate;
class query_delete_intermediate : public query_intermediate
{
public:
query_delete_intermediate();
query_delete_from_intermediate from(const sql::table &table);
};
}
#endif //QUERY_DELETE_INTERMEDIATE_HPP

View File

@ -0,0 +1,18 @@
#ifndef QUERY_DROP_INTERMEDIATE_HPP
#define QUERY_DROP_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_drop_intermediate : query_intermediate
{
public:
query_drop_intermediate();
executable_query table(const sql::table &table);
};
}
#endif //QUERY_DROP_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP
#define QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_execute_offset_intermediate;
class query_execute_limit_intermediate : public executable_query
{
public:
using executable_query::executable_query;
query_execute_offset_intermediate offset(size_t offset);
};
}
#endif //QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP
#define QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_execute_limit_intermediate;
class query_execute_offset_intermediate : public executable_query
{
public:
using executable_query::executable_query;
query_execute_limit_intermediate limit(size_t limit);
};
}
#endif //QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP

View File

@ -0,0 +1,21 @@
#ifndef QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP
#define QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
namespace matador::query {
class query_execute_order_direction_intermediate;
class query_execute_order_by_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_execute_order_direction_intermediate asc();
query_execute_order_direction_intermediate desc();
};
}
#endif //QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP
#define QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_execute_limit_intermediate;
class query_execute_order_direction_intermediate : public executable_query
{
public:
using executable_query::executable_query;
query_execute_limit_intermediate limit(size_t limit);
};
}
#endif //QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP

View File

@ -0,0 +1,22 @@
#ifndef QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP
#define QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
namespace matador::query {
class query_execute_limit_intermediate;
class query_execute_order_by_intermediate;
class query_execute_where_intermediate : public executable_query
{
public:
using executable_query::executable_query;
query_execute_limit_intermediate limit(size_t limit);
query_execute_order_by_intermediate order_by(const sql::column &col);
};
}
#endif //QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP

View File

@ -0,0 +1,40 @@
#ifndef QUERY_FROM_INTERMEDIATE_HPP
#define QUERY_FROM_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
#include "matador/query/join_data.hpp"
#include "matador/query/intermediates/query_where_intermediate.hpp"
namespace matador::query {
class query_join_intermediate;
class query_from_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_join_intermediate join_left(const sql::table &t);
query_from_intermediate join_left(join_data &data);
query_from_intermediate join_left(std::vector<join_data> &data_vector);
template<class Condition>
query_where_intermediate where(const Condition &cond)
{
return where_clause(std::make_unique<Condition>(std::move(cond)));
}
query_where_intermediate where(std::unique_ptr<basic_condition> &&cond)
{
return where_clause(std::move(cond));
}
query_group_by_intermediate group_by(const sql::column &col);
query_order_by_intermediate order_by(const sql::column &col);
private:
query_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
}
#endif //QUERY_FROM_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_GROUP_BY_INTERMEDIATE_HPP
#define QUERY_GROUP_BY_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
namespace matador::query {
class query_order_by_intermediate;
class query_group_by_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_order_by_intermediate order_by(const sql::column &col);
};
}
#endif //QUERY_GROUP_BY_INTERMEDIATE_HPP

View File

@ -0,0 +1,27 @@
#ifndef QUERY_INSERT_INTERMEDIATE_HPP
#define QUERY_INSERT_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
namespace matador::query {
class query_into_intermediate;
class query_insert_intermediate : public query_intermediate
{
public:
query_insert_intermediate();
// template<class Type>
// query_into_intermediate into(const sql::table &table, const sql::schema &schema) {
// return into(table, column_generator::generate<Type>(schema));
// }
query_into_intermediate into(const sql::table &table, std::initializer_list<sql::column> columns);
query_into_intermediate into(const sql::table &table, std::vector<sql::column> &&columns);
query_into_intermediate into(const sql::table &table, const std::vector<std::string> &column_names);
query_into_intermediate into(const sql::table &table);
};
}
#endif //QUERY_INSERT_INTERMEDIATE_HPP

View File

@ -0,0 +1,21 @@
#ifndef QUERY_INTERMEDIATE_HPP
#define QUERY_INTERMEDIATE_HPP
#include "matador/query/query_data.hpp"
#include <memory>
namespace matador::query {
class query_intermediate {
public:
query_intermediate();
query_intermediate(const std::shared_ptr<query_data> &context); // NOLINT(*-explicit-constructor)
protected:
std::shared_ptr<query_data> context_;
};
}
#endif //QUERY_INTERMEDIATE_HPP

View File

@ -0,0 +1,43 @@
#ifndef QUERY_INTO_INTERMEDIATE_HPP
#define QUERY_INTO_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
#include "matador/query/value_extractor.hpp"
#include "matador/utils/placeholder.hpp"
namespace matador::query {
// template < class Type >
// std::vector<utils::any_type> as_placeholder(const Type &obj)
// {
// placeholder_generator generator;
// access::process(generator, obj);
// return generator.placeholder_values;
// }
class query_into_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
executable_query values(std::initializer_list<std::variant<utils::placeholder, utils::database_type>> values);
executable_query values(std::vector<std::variant<utils::placeholder, utils::database_type>> &&values);
// template<class Type>
// executable_query values()
// {
// Type obj;
// return values(std::move(as_placeholder(obj)));
// }
template<class Type>
executable_query values(const Type &obj)
{
return values(std::move(value_extractor::extract(obj)));
}
};
}
#endif //QUERY_INTO_INTERMEDIATE_HPP

View File

@ -0,0 +1,32 @@
#ifndef QUERY_JOIN_INTERMEDIATE_HPP
#define QUERY_JOIN_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
#include "matador/query/intermediates/query_from_intermediate.hpp"
namespace matador::query {
using query_on_intermediate = query_from_intermediate;
class query_join_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
template<class Condition>
query_on_intermediate on(const Condition &cond)
{
return on_clause(std::make_unique<Condition>(std::move(cond)));
}
query_on_intermediate on(std::unique_ptr<basic_condition> &&cond)
{
return on_clause(std::move(cond));
}
private:
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
};
}
#endif //QUERY_JOIN_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_LIMIT_INTERMEDIATE_HPP
#define QUERY_LIMIT_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
namespace matador::query {
class query_offset_intermediate;
class query_limit_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_offset_intermediate offset(size_t offset);
};
}
#endif //QUERY_LIMIT_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_OFFSET_INTERMEDIATE_HPP
#define QUERY_OFFSET_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
namespace matador::query {
class query_limit_intermediate;
class query_offset_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_limit_intermediate limit(size_t limit);
};
}
#endif //QUERY_OFFSET_INTERMEDIATE_HPP

View File

@ -0,0 +1,21 @@
#ifndef QUERY_ORDER_BY_INTERMEDIATE_HPP
#define QUERY_ORDER_BY_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
namespace matador::query {
class query_order_direction_intermediate;
class query_order_by_intermediate : public query_intermediate
{
public:
using query_intermediate::query_intermediate;
query_order_direction_intermediate asc();
query_order_direction_intermediate desc();
};
}
#endif //QUERY_ORDER_BY_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP
#define QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
namespace matador::query {
class query_limit_intermediate;
class query_order_direction_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_limit_intermediate limit(size_t limit);
};
}
#endif //QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_SELECT_INTERMEDIATE_HPP
#define QUERY_SELECT_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
namespace matador::query {
class query_from_intermediate;
class query_select_intermediate : public query_intermediate
{
public:
explicit query_select_intermediate(const std::vector<sql::column>& columns);
query_from_intermediate from(const sql::table& t);
};
}
#endif //QUERY_SELECT_INTERMEDIATE_HPP

View File

@ -0,0 +1,29 @@
#ifndef QUERY_SET_INTERMEDIATE_HPP
#define QUERY_SET_INTERMEDIATE_HPP
#include "matador/query/intermediates/executable_query.hpp"
#include "matador/query/basic_condition.hpp"
#include "matador/query/intermediates/query_execute_where_intermediate.hpp"
namespace matador::query {
class query_set_intermediate : public executable_query
{
public:
using executable_query::executable_query;
template<class Condition>
query_execute_where_intermediate where(const Condition &cond)
{
return where_clause(std::make_unique<Condition>(std::move(cond)));
}
private:
query_execute_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
}
#endif //QUERY_SET_INTERMEDIATE_HPP

View File

@ -0,0 +1,45 @@
#ifndef QUERY_UPDATE_INTERMEDIATE_HPP
#define QUERY_UPDATE_INTERMEDIATE_HPP
#include "matador/query/intermediates/query_intermediate.hpp"
#include "matador/query/intermediates/query_set_intermediate.hpp"
#include "matador/query/key_value_generator.hpp"
#include "matador/query/internal/key_value_pair.hpp"
namespace matador::query {
// template < class Type >
// std::vector<internal::key_value_pair> as_key_value_placeholder(const Type &obj)
// {
// placeholder_key_value_generator generator;
// access::process(generator, obj);
//
// return generator.placeholder_values;
// }
class query_update_intermediate : public query_intermediate
{
public:
explicit query_update_intermediate(const sql::table& table);
query_set_intermediate set(std::initializer_list<internal::key_value_pair> columns);
query_set_intermediate set(std::vector<internal::key_value_pair> &&columns);
// template<class Type>
// query_set_intermediate set()
// {
// Type obj;
// return set(std::move(as_key_value_placeholder(obj)));
// }
template<class Type>
query_set_intermediate set(const Type &obj)
{
return set(key_value_generator::generate(obj));
}
};
}
#endif //QUERY_UPDATE_INTERMEDIATE_HPP

View File

@ -0,0 +1,22 @@
#ifndef QUERY_WHERE_INTERMEDIATE_HPP
#define QUERY_WHERE_INTERMEDIATE_HPP
#include "matador/query/intermediates/fetchable_query.hpp"
namespace matador::query {
class query_group_by_intermediate;
class query_order_by_intermediate;
class query_where_intermediate : public fetchable_query
{
public:
using fetchable_query::fetchable_query;
query_group_by_intermediate group_by(const sql::column &col);
query_order_by_intermediate order_by(const sql::column &col);
};
}
#endif //QUERY_WHERE_INTERMEDIATE_HPP

View File

@ -0,0 +1,49 @@
#ifndef QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP
#define QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP
#include "matador/utils/types.hpp"
#include "matador/query/attribute_string_writer.hpp"
#include <string>
namespace matador {
class date;
class time;
}
namespace matador::sql {
class dialect;
struct query_context;
}
namespace matador::query::internal {
struct basic_type_to_string_visitor
{
explicit basic_type_to_string_visitor(attribute_string_writer &writer, sql::query_context &query);
void operator()(const int8_t &x) { result = writer->to_string(x); }
void operator()(const int16_t &x) { result = writer->to_string(x); }
void operator()(const int32_t &x) { result = writer->to_string(x); }
void operator()(const int64_t &x) { result = writer->to_string(x); }
void operator()(const uint8_t &x) { result = writer->to_string(x); }
void operator()(const uint16_t &x) { result = writer->to_string(x); }
void operator()(const uint32_t &x) { result = writer->to_string(x); }
void operator()(const uint64_t &x) { result = writer->to_string(x); }
void operator()(const bool &x) { result = writer->to_string(x); }
void operator()(const float &x) { result = writer->to_string(x); }
void operator()(const double &x) { result = writer->to_string(x); }
void operator()(const char *x) { result = writer->to_string(x); }
void operator()(const std::string &x) { result = writer->to_string(x); }
void operator()(const matador::date &x) { result = writer->to_string(x); }
void operator()(const matador::time &x) { result = writer->to_string(x); }
void operator()(const utils::blob &x) { result = writer->to_string(x); }
attribute_string_writer *writer{};
sql::query_context &query;
std::string result;
};
}
#endif //QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP

View File

@ -0,0 +1,25 @@
#ifndef QUERY_KEY_VALUE_PAIR_HPP
#define QUERY_KEY_VALUE_PAIR_HPP
#include "matador/sql/column.hpp"
#include "matador/utils/types.hpp"
namespace matador::query::internal {
class key_value_pair {
public:
key_value_pair(const sql::column &col, utils::database_type value);
key_value_pair(std::string name, utils::database_type value);
key_value_pair(const char *name, utils::database_type value);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] const utils::database_type& value() const;
private:
std::string name_;
utils::database_type value_;
};
}
#endif //QUERY_KEY_VALUE_PAIR_HPP

View File

@ -1,39 +1,41 @@
#ifndef QUERY_QUERY_PARTS_HPP #ifndef QUERY_QUERY_PARTS_HPP
#define QUERY_QUERY_PARTS_HPP #define QUERY_QUERY_PARTS_HPP
#include "matador/sql/basic_condition.hpp" #include "matador/query/query_part_visitor.hpp"
#include "matador/sql/query_part_visitor.hpp" #include "matador/query/basic_condition.hpp"
#include "matador/query/internal/key_value_pair.hpp"
#include "matador/query/query_part.hpp"
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/column_definition.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/key_value_pair.hpp"
#include "matador/sql/query_part.hpp"
#include "matador/sql/table.hpp" #include "matador/sql/table.hpp"
#include "matador/utils/placeholder.hpp"
#include <memory> #include <memory>
namespace matador::sql { namespace matador::query::internal {
class basic_condition;
/** /**
* Represents the SQL SELECT part * Represents the SQL SELECT part
*/ */
class query_select_part : public query_part class query_select_part final : public query_part
{ {
public: public:
explicit query_select_part(std::vector<column> columns); explicit query_select_part(std::vector<sql::column> columns);
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
[[nodiscard]] const std::vector<column>& columns() const; [[nodiscard]] const std::vector<sql::column>& columns() const;
private: private:
std::vector<column> columns_; std::vector<sql::column> columns_;
}; };
/** /**
* Represents the SQL FROM part * Represents the SQL FROM part
*/ */
class query_from_part : public query_part class query_from_part final : public query_part
{ {
public: public:
explicit query_from_part(sql::table t); explicit query_from_part(sql::table t);
@ -47,7 +49,7 @@ private:
sql::table table_; sql::table table_;
}; };
class query_join_part : public query_part class query_join_part final : public query_part
{ {
public: public:
explicit query_join_part(sql::table t); explicit query_join_part(sql::table t);
@ -61,12 +63,12 @@ private:
sql::table table_; sql::table table_;
}; };
class query_on_part : public query_part class query_on_part final : public query_part
{ {
public: public:
template < class Condition > template < class Condition >
explicit query_on_part(const Condition &cond) explicit query_on_part(const Condition &cond)
: query_part(dialect::token_t::ON) : query_part(sql::dialect_token::ON)
, condition_(new Condition(cond)) {} , condition_(new Condition(cond)) {}
explicit query_on_part(std::unique_ptr<basic_condition> &&cond); explicit query_on_part(std::unique_ptr<basic_condition> &&cond);
@ -79,12 +81,12 @@ private:
std::unique_ptr<basic_condition> condition_; std::unique_ptr<basic_condition> condition_;
}; };
class query_where_part : public query_part class query_where_part final : public query_part
{ {
public: public:
template < class Condition > template < class Condition >
explicit query_where_part(const Condition &cond) explicit query_where_part(const Condition &cond)
: query_part(dialect::token_t::WHERE) : query_part(sql::dialect_token::WHERE)
, condition_(new Condition(cond)) {} , condition_(new Condition(cond)) {}
explicit query_where_part(std::unique_ptr<basic_condition> &&cond); explicit query_where_part(std::unique_ptr<basic_condition> &&cond);
@ -100,13 +102,13 @@ private:
class query_table_name_part : public query_part class query_table_name_part : public query_part
{ {
protected: protected:
explicit query_table_name_part(sql::dialect::token_t token, std::string table_name); explicit query_table_name_part(sql::dialect_token token, std::string table_name);
protected: protected:
std::string table_name_; std::string table_name_;
}; };
class query_group_by_part : public query_part class query_group_by_part final : public query_part
{ {
public: public:
explicit query_group_by_part(sql::column col); explicit query_group_by_part(sql::column col);
@ -120,7 +122,7 @@ private:
sql::column column_; sql::column column_;
}; };
class query_order_by_part : public query_part class query_order_by_part final : public query_part
{ {
public: public:
explicit query_order_by_part(sql::column col); explicit query_order_by_part(sql::column col);
@ -134,7 +136,7 @@ private:
sql::column column_; sql::column column_;
}; };
class query_order_by_asc_part : public query_part class query_order_by_asc_part final : public query_part
{ {
public: public:
query_order_by_asc_part(); query_order_by_asc_part();
@ -143,7 +145,7 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_order_by_desc_part : public query_part class query_order_by_desc_part final : public query_part
{ {
public: public:
query_order_by_desc_part(); query_order_by_desc_part();
@ -152,7 +154,7 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_offset_part : public query_part class query_offset_part final : public query_part
{ {
public: public:
explicit query_offset_part(size_t offset); explicit query_offset_part(size_t offset);
@ -166,7 +168,7 @@ private:
size_t offset_; size_t offset_;
}; };
class query_limit_part : public query_part class query_limit_part final : public query_part
{ {
public: public:
explicit query_limit_part(size_t limit); explicit query_limit_part(size_t limit);
@ -180,7 +182,7 @@ private:
size_t limit_; size_t limit_;
}; };
class query_insert_part : public query_part class query_insert_part final : public query_part
{ {
public: public:
query_insert_part(); query_insert_part();
@ -189,39 +191,39 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_into_part : public query_part class query_into_part final : public query_part
{ {
public: public:
query_into_part(sql::table t, std::vector<sql::column> columns); query_into_part(sql::table t, std::vector<sql::column> columns);
[[nodiscard]] const sql::table& table() const; [[nodiscard]] const sql::table& table() const;
[[nodiscard]] const std::vector<column>& columns() const; [[nodiscard]] const std::vector<sql::column>& columns() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
private: private:
sql::table table_; sql::table table_;
std::vector<column> columns_; std::vector<sql::column> columns_;
}; };
/** /**
* Represents the SQL VALUES part * Represents the SQL VALUES part
*/ */
class query_values_part : public query_part class query_values_part final : public query_part
{ {
public: public:
explicit query_values_part(std::vector<any_type> &&values); explicit query_values_part(std::vector<std::variant<utils::placeholder, utils::database_type>> &&values);
[[nodiscard]] const std::vector<any_type>& values() const; [[nodiscard]] const std::vector<std::variant<utils::placeholder, utils::database_type>>& values() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
private: private:
std::vector<any_type> values_; std::vector<std::variant<utils::placeholder, utils::database_type>> values_;
}; };
class query_update_part : public query_part class query_update_part final : public query_part
{ {
public: public:
explicit query_update_part(sql::table table); explicit query_update_part(sql::table table);
@ -235,21 +237,21 @@ private:
sql::table table_; sql::table table_;
}; };
class query_set_part : public query_part class query_set_part final : public query_part
{ {
public: public:
explicit query_set_part(const std::vector<sql::key_value_pair>& key_value_pairs); explicit query_set_part(const std::vector<internal::key_value_pair>& key_value_pairs);
[[nodiscard]] const std::vector<sql::key_value_pair>& key_values() const; [[nodiscard]] const std::vector<internal::key_value_pair>& key_values() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
private: private:
std::vector<sql::key_value_pair> key_value_pairs_; std::vector<internal::key_value_pair> key_value_pairs_;
}; };
class query_delete_part : public query_part class query_delete_part final : public query_part
{ {
public: public:
query_delete_part(); query_delete_part();
@ -258,10 +260,10 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_delete_from_part : public query_part class query_delete_from_part final : public query_part
{ {
public: public:
query_delete_from_part(sql::table table); explicit query_delete_from_part(sql::table table);
[[nodiscard]] const sql::table& table() const; [[nodiscard]] const sql::table& table() const;
@ -272,7 +274,7 @@ private:
sql::table table_; sql::table table_;
}; };
class query_create_part : public query_part class query_create_part final : public query_part
{ {
public: public:
query_create_part(); query_create_part();
@ -281,7 +283,7 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_create_table_part : public query_part class query_create_table_part final : public query_part
{ {
public: public:
query_create_table_part(sql::table table, std::vector<sql::column_definition> columns); query_create_table_part(sql::table table, std::vector<sql::column_definition> columns);
@ -297,7 +299,7 @@ private:
std::vector<sql::column_definition> columns_; std::vector<sql::column_definition> columns_;
}; };
class query_drop_part : public query_part class query_drop_part final : public query_part
{ {
public: public:
query_drop_part(); query_drop_part();
@ -306,7 +308,7 @@ private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
}; };
class query_drop_table_part : public query_part class query_drop_table_part final : public query_part
{ {
public: public:
explicit query_drop_table_part(sql::table table); explicit query_drop_table_part(sql::table table);

View File

@ -0,0 +1,19 @@
#ifndef JOIN_DATA_HPP
#define JOIN_DATA_HPP
#include "matador/query/basic_condition.hpp"
#include "matador/sql/table.hpp"
#include <memory>
namespace matador::query {
struct join_data
{
std::shared_ptr<sql::table> join_table;
std::unique_ptr<basic_condition> condition;
};
}
#endif //JOIN_DATA_HPP

View File

@ -1,28 +1,30 @@
#ifndef QUERY_KEY_VALUE_GENERATOR_HPP #ifndef QUERY_KEY_VALUE_GENERATOR_HPP
#define QUERY_KEY_VALUE_GENERATOR_HPP #define QUERY_KEY_VALUE_GENERATOR_HPP
#include "matador/sql/fk_value_extractor.hpp" #include "matador/query/fk_value_extractor.hpp"
#include "matador/sql/key_value_pair.hpp" #include "matador/query/internal/key_value_pair.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include <vector> #include <vector>
namespace matador::sql { namespace matador::query {
class key_value_generator class key_value_generator
{ {
private: private:
public: public:
explicit key_value_generator(std::vector<key_value_pair> &result) : result_(result) {} explicit key_value_generator(std::vector<internal::key_value_pair> &result) : result_(result) {}
public: public:
template < class Type > template < class Type >
static std::vector<key_value_pair> generate(const Type &obj) static std::vector<internal::key_value_pair> generate(const Type &obj)
{ {
std::vector<key_value_pair> result; std::vector<internal::key_value_pair> result;
key_value_generator generator(result); key_value_generator generator(result);
matador::utils::access::process(generator, obj); access::process(generator, obj);
return std::move(result); return result;
} }
template < class V > template < class V >
@ -56,7 +58,7 @@ public:
private: private:
detail::fk_value_extractor fk_value_extractor_; detail::fk_value_extractor fk_value_extractor_;
std::vector<key_value_pair> &result_; std::vector<internal::key_value_pair> &result_;
}; };
} }

View File

@ -0,0 +1,35 @@
#ifndef QUERY_QUERY_HPP
#define QUERY_QUERY_HPP
#include "matador/query/query_intermediates.hpp"
namespace matador::sql {
class connection;
}
namespace matador::query {
sql::column alias(const std::string &column, const std::string &as);
sql::column alias(sql::column &&col, const std::string &as);
sql::column count(const std::string &column);
sql::column count_all();
class query
{
public:
[[nodiscard]] static query_create_intermediate create();
[[nodiscard]] static query_drop_intermediate drop();
[[nodiscard]] static query_select_intermediate select(std::initializer_list<sql::column> columns);
[[nodiscard]] static query_select_intermediate select(const std::vector<sql::column>& columns);
[[nodiscard]] static query_select_intermediate select(const std::vector<std::string> &column_names);
[[nodiscard]] static query_select_intermediate select(std::vector<sql::column> columns, std::initializer_list<sql::column> additional_columns);
// template<class Type>
// [[nodiscard]] static query_select_intermediate select(const sql::schema &schema) {
// return select(sql::column_generator::generate<Type>(schema));
// }
[[nodiscard]] static query_insert_intermediate insert();
[[nodiscard]] static query_update_intermediate update(const sql::table &table);
[[nodiscard]] static query_delete_intermediate remove();
};
}
#endif //QUERY_QUERY_HPP

View File

@ -0,0 +1,65 @@
#ifndef QUERY_QUERY_COMPILER_HPP
#define QUERY_QUERY_COMPILER_HPP
#include "matador/query/query_part_visitor.hpp"
#include "matador/query/query_data.hpp"
#include "matador/sql/query_context.hpp"
namespace matador::sql {
class connection_impl;
class dialect;
}
namespace matador::query {
struct query_data;
class query_compiler final : public query_part_visitor
{
public:
sql::query_context compile(const query_data &data,
const sql::dialect &d,
std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
protected:
void visit(internal::query_select_part &select_part) override;
void visit(internal::query_from_part &from_part) override;
void visit(internal::query_join_part &join_part) override;
void visit(internal::query_on_part &on_part) override;
void visit(internal::query_where_part &where_part) override;
void visit(internal::query_group_by_part &group_by_part) override;
void visit(internal::query_order_by_part &order_by_part) override;
void visit(internal::query_order_by_asc_part &order_by_asc_part) override;
void visit(internal::query_order_by_desc_part &order_by_desc_part) override;
void visit(internal::query_offset_part &offset_part) override;
void visit(internal::query_limit_part &limit_part) override;
void visit(internal::query_insert_part &insert_part) override;
void visit(internal::query_into_part &into_part) override;
void visit(internal::query_values_part &values_part) override;
void visit(internal::query_update_part &update_part) override;
void visit(internal::query_set_part &set_part) override;
void visit(internal::query_delete_part &delete_part) override;
void visit(internal::query_delete_from_part &delete_from_part) override;
void visit(internal::query_create_part &create_part) override;
void visit(internal::query_create_table_part &create_table_part) override;
void visit(internal::query_drop_part &drop_part) override;
void visit(internal::query_drop_table_part &drop_table_part) override;
static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const sql::table& t);
protected:
const query_data *data_{};
sql::query_context query_;
size_t table_index{0};
const sql::dialect *dialect_{nullptr};
std::optional<std::reference_wrapper<const sql::connection_impl>> connection_{};
};
}
#endif //QUERY_QUERY_COMPILER_HPP

View File

@ -0,0 +1,21 @@
#ifndef QUERY_DATA_HPP
#define QUERY_DATA_HPP
#include <memory>
#include <vector>
#include "matador/sql/column_definition.hpp"
#include "matador/sql/table.hpp"
#include "matador/query/query_part.hpp"
namespace matador::query {
struct query_data
{
std::vector<std::unique_ptr<query_part>> parts{};
std::vector<sql::column_definition> columns{};
std::unordered_map<std::string, sql::table> tables{};
};
}
#endif //QUERY_DATA_HPP

View File

@ -0,0 +1,30 @@
#ifndef QUERY_QUERY_INTERMEDIATES_HPP
#define QUERY_QUERY_INTERMEDIATES_HPP
#include "matador/query/intermediates/executable_query.hpp"
#include "matador/query/intermediates/fetchable_query.hpp"
#include "matador/query/intermediates/query_create_intermediate.hpp"
#include "matador/query/intermediates/query_delete_from_intermediate.hpp"
#include "matador/query/intermediates/query_delete_intermediate.hpp"
#include "matador/query/intermediates/query_drop_intermediate.hpp"
#include "matador/query/intermediates/query_execute_limit_intermediate.hpp"
#include "matador/query/intermediates/query_execute_offset_intermediate.hpp"
#include "matador/query/intermediates/query_execute_order_by_intermediate.hpp"
#include "matador/query/intermediates/query_execute_order_direction_intermediate.hpp"
#include "matador/query/intermediates/query_execute_where_intermediate.hpp"
#include "matador/query/intermediates/query_from_intermediate.hpp"
#include "matador/query/intermediates/query_group_by_intermediate.hpp"
#include "matador/query/intermediates/query_insert_intermediate.hpp"
#include "matador/query/intermediates/query_intermediate.hpp"
#include "matador/query/intermediates/query_into_intermediate.hpp"
#include "matador/query/intermediates/query_join_intermediate.hpp"
#include "matador/query/intermediates/query_limit_intermediate.hpp"
#include "matador/query/intermediates/query_offset_intermediate.hpp"
#include "matador/query/intermediates/query_order_by_intermediate.hpp"
#include "matador/query/intermediates/query_order_direction_intermediate.hpp"
#include "matador/query/intermediates/query_select_intermediate.hpp"
#include "matador/query/intermediates/query_set_intermediate.hpp"
#include "matador/query/intermediates/query_update_intermediate.hpp"
#include "matador/query/intermediates/query_where_intermediate.hpp"
#endif //QUERY_QUERY_INTERMEDIATES_HPP

View File

@ -1,25 +1,25 @@
#ifndef QUERY_QUERY_PART_HPP #ifndef QUERY_QUERY_PART_HPP
#define QUERY_QUERY_PART_HPP #define QUERY_QUERY_PART_HPP
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect_token.hpp"
namespace matador::sql { namespace matador::query {
class query_part_visitor; class query_part_visitor;
class query_part class query_part
{ {
protected: protected:
explicit query_part(sql::dialect::token_t token); explicit query_part(sql::dialect_token token);
public: public:
virtual ~query_part() = default; virtual ~query_part() = default;
virtual void accept(query_part_visitor &visitor) = 0; virtual void accept(query_part_visitor &visitor) = 0;
[[nodiscard]] dialect::token_t token() const; [[nodiscard]] sql::dialect_token token() const;
protected: protected:
sql::dialect::token_t token_; sql::dialect_token token_;
}; };
} }

Some files were not shown because too many files have changed in this diff Show More