Compare commits

..
59 Commits
Author SHA1 Message Date
Sascha Kühl 19de5714f4 added data_type_traits for enums 2024-08-22 16:08:05 +02:00
Sascha Kühl 484c2d8b05 removed quotes for enum value in condition 2024-08-22 16:07:45 +02:00
Sascha Kühl 43d2f81b95 added all value constructor to class table 2024-08-22 16:07:10 +02:00
Sascha Kühl 27f6c81da2 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	demo/main.cpp
2024-08-21 15:36:34 +02:00
Sascha Kühl 12e5b27a8c progress on in conditions with queries and enums 2024-08-21 15:35:29 +02:00
sascha ff3a2e4217 fixed record class 2024-07-15 08:14:11 +02:00
Sascha Kühl ac8b2ed4e4 added more join_left methods to query_from_intermediate to write shorter fluent sql queries 2024-04-16 17:11:08 +02:00
Sascha Kühl 38da76bfb3 improved result test with specific error test 2024-04-16 17:09:52 +02:00
Sascha Kühl 4199c5029c added session test with has one relation 2024-04-15 15:54:37 +02:00
sascha fb57545cce added join_column_collector and implemented inverse has_many_to_many 2024-04-14 22:12:48 +02:00
sascha 7bb7afa227 integrated auto_reset_event into test utils 2024-04-14 11:41:36 +02:00
sascha 2022f6f75b updated catch2 to 3.5.4 2024-04-14 11:41:09 +02:00
Sascha Kühl b4765cac88 added multi threaded pool test 2024-04-12 16:34:30 +02:00
Sascha Kühl 25bcc362f2 use result in entity_query_builder 2024-04-10 16:13:37 +02:00
sascha b7c12d8217 added result class 2024-04-10 07:58:41 +02:00
sascha 1fe5c9bac4 added missing include 2024-04-09 20:19:30 +02:00
Sascha Kühl 8f754f3542 added value class 2024-04-09 16:03:42 +02:00
Sascha Kühl f977b2afc9 added session tests 2024-04-08 16:38:49 +02:00
sascha 5a3cdc8645 removed query directory 2024-04-07 15:18:41 +02:00
sascha fa7ff832c9 handle many-to-many relations separately when process entities 2024-04-07 14:45:57 +02:00
sascha 30e2e0c221 made dialect parameter const in condition::evaluate 2024-04-07 14:44:42 +02:00
sascha 85f87367c0 added default values for column alias name in constructor 2024-04-07 14:44:13 +02:00
sascha 31e9c7e9ac added missing checks to QueryTest 2024-03-29 11:41:37 +01:00
sascha 6f3e589e10 added to many fields to entities author, book, recipe and ingredient 2024-03-29 11:41:14 +01:00
sascha 05c0c0393d added type check methods to column_definition 2024-03-29 11:40:02 +01:00
sascha e14d9a77eb fixed name ambiguity 2024-03-29 11:39:41 +01:00
Sascha Kühl 4cda0f2664 many to many query progress (multiple joins) 2024-03-28 15:51:45 +01:00
Sascha Kühl 50a0eb580a renamed column_generator to column_definition_generator and column_name_generator to column_generator 2024-03-27 15:15:45 +01:00
sascha be9f66c427 start to implement session::find() 2024-03-27 07:39:06 +01:00
sascha 5d06a13775 added a bool operator to entity class 2024-03-27 07:38:42 +01:00
Sascha Kühl 4a04a678f9 query select changes 2024-03-26 16:05:04 +01:00
sascha b15b8da31f fixed field class and all tests 2024-03-19 19:49:17 +01:00
sascha 12919ba372 record refactoring 2024-03-17 16:32:08 +01:00
sascha 9a02abacea added index attribute to field class 2024-03-10 17:22:38 +01:00
sascha 6bbc870362 added conversion from/to blob 2024-03-10 16:55:55 +01:00
sascha dd94ed1020 added field class 2024-03-10 11:51:07 +01:00
sascha 2b552c4383 entity query builder progress 2024-03-07 19:57:42 +01:00
sascha 496e94ddcd entity query builder progress 2024-03-05 20:14:34 +01:00
sascha be610ffcad entity query and record progress 2024-03-04 20:09:39 +01:00
sascha 830185c3c5 some code formatting 2024-03-02 08:19:05 +01:00
sascha 34740bf126 changed default mysql connection string 2024-03-02 08:18:52 +01:00
sascha c9a84e0568 schema progress 2024-02-29 20:00:39 +01:00
sascha 7e1713ddd3 query progress 2024-02-28 18:02:12 +01:00
sascha b9709d14c2 moved query parts from intermediates declaration to definition file. 2024-02-27 20:16:59 +01:00
sascha 2d9a4f3866 added noop_connection for testing 2024-02-26 20:06:46 +01:00
sascha b1f2d94c7a fixed query limit offset order 2024-02-25 19:04:04 +01:00
sascha 6f5326941e query parts progress 2024-02-24 18:16:23 +01:00
sascha 0822332669 query parts progress 2024-02-22 22:25:29 +01:00
sascha 16d7fd7e76 query parts progress 2024-02-22 07:29:17 +01:00
sascha b255ae22b4 added query helper macro and join query parts 2024-02-19 22:57:43 +01:00
sascha b11993d60b small query compiler progress 2024-02-18 19:58:56 +01:00
sascha a3f467a5a8 query compiler progress 2024-02-18 18:12:00 +01:00
sascha b966a7a1a7 add column info (progress, not compiling) 2024-02-08 20:09:24 +01:00
sascha 78c5f64b34 added schema creation to session class 2024-02-06 20:10:34 +01:00
sascha 0f13884f06 added session test (progress) 2024-02-04 22:24:50 +01:00
sascha fa3ea28920 added foreign_attributes class containing cascade and fetch information for foreign relations 2024-02-02 20:08:56 +01:00
sascha aa221aa571 sql select join progress 2024-02-02 19:30:16 +01:00
sascha e8b0f0e802 sql select join progress 2024-02-02 07:54:19 +01:00
sascha 27353e011a renamed session test files 2024-02-01 17:16:53 +01:00
136 changed files with 7109 additions and 1789 deletions
+2
View File
@@ -1,2 +1,4 @@
.idea .idea
cmake-build-debug cmake-build-debug
Testing
debug
+14 -1
View File
@@ -1,11 +1,20 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
project(query) project(
query
VERSION 1.0.0
DESCRIPTION "SQL query fluent prototype for PostgreSQL, SQLite, MySQL and MSSQL"
LANGUAGES CXX
)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(GCC_CLANG_COMMON_FLAGS "-Wall -Wconversion -Wextra -pedantic -ftemplate-backtrace-limit=0") set(GCC_CLANG_COMMON_FLAGS "-Wall -Wconversion -Wextra -pedantic -ftemplate-backtrace-limit=0")
if (WIN32)
add_compile_options(/Zc:preprocessor)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
find_package(ODBC REQUIRED) find_package(ODBC REQUIRED)
@@ -13,6 +22,10 @@ find_package(SQLite3 REQUIRED)
find_package(PostgreSQL REQUIRED) find_package(PostgreSQL REQUIRED)
find_package(MySQL REQUIRED) find_package(MySQL REQUIRED)
message(STATUS "Found ODBC config ${ODBC_CONFIG}")
message(STATUS "Adding ODBC include directory: ${ODBC_INCLUDE_DIRS}")
message(STATUS "Adding ODBC libs: ${ODBC_LIBRARIES}")
message(STATUS "Found SQLite3 ${SQLite3_VERSION}") message(STATUS "Found SQLite3 ${SQLite3_VERSION}")
message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}") message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}")
message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}") message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}")
+2
View File
@@ -2,6 +2,8 @@
A fluent sql query_context builder A fluent sql query_context builder
```MATADOR_BACKENDS_PATH=/home/sascha/Develop/query/cmake-build-debug/backends```
Object definition Object definition
```cpp ```cpp
enum class Color { enum class Color {
+42 -1
View File
@@ -3,4 +3,45 @@
- Add is_valid() method to connection & connection_impl - Add is_valid() method to connection & connection_impl
- Read in entity fields - Read in entity fields
- Add special handling for update in backends - Add special handling for update in backends
- Add ODBC/SQL Server backend - 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)
+1 -1
View File
@@ -37,7 +37,7 @@ public:
size_t execute(const std::string &stmt) override; size_t execute(const std::string &stmt) override;
sql::record describe(const std::string& table) 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; bool exists(const std::string &schema_name, const std::string &table_name) override;
+5 -5
View File
@@ -181,13 +181,13 @@ std::unique_ptr<sql::query_result_impl> mysql_connection::fetch(const std::strin
auto field_count = mysql_num_fields(result); auto field_count = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result); auto fields = mysql_fetch_fields(result);
sql::record prototype; std::vector<sql::column_definition> prototype;
for (unsigned i = 0; i < field_count; ++i) { for (unsigned i = 0; i < field_count; ++i) {
auto type = to_type(fields[i].type, fields[i].flags); auto type = to_type(fields[i].type, fields[i].flags);
auto options = to_constraints(fields[i].flags); auto options = to_constraints(fields[i].flags);
auto null_opt = to_null_option(fields[i].flags); auto null_opt = to_null_option(fields[i].flags);
prototype.append({fields[i].name, type, options, null_opt}); 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))); return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_result_reader>(result, field_count), std::move(prototype)));
@@ -216,7 +216,7 @@ size_t mysql_connection::execute(const std::string &stmt)
return mysql_affected_rows(mysql_.get()); return mysql_affected_rows(mysql_.get());
} }
sql::record mysql_connection::describe(const std::string &table) std::vector<sql::column_definition> mysql_connection::describe(const std::string &table)
{ {
std::string stmt("SHOW COLUMNS FROM " + table); std::string stmt("SHOW COLUMNS FROM " + table);
@@ -230,7 +230,7 @@ sql::record mysql_connection::describe(const std::string &table)
} }
mysql_result_reader reader(result, mysql_num_fields(result)); mysql_result_reader reader(result, mysql_num_fields(result));
sql::record prototype; std::vector<sql::column_definition> prototype;
while (reader.fetch()) { while (reader.fetch()) {
char *end = nullptr; char *end = nullptr;
@@ -242,7 +242,7 @@ sql::record mysql_connection::describe(const std::string &table)
if (strtoul(reader.column(2), &end, 10) == 0) { if (strtoul(reader.column(2), &end, 10) == 0) {
null_opt = sql::null_option::NOT_NULL; null_opt = sql::null_option::NOT_NULL;
} }
prototype.append({name, typeinfo.type, {typeinfo.size}, null_opt, prototype.size()}); prototype.push_back({name, typeinfo.type, {typeinfo.size}, null_opt, prototype.size()});
} }
return prototype; return prototype;
+6 -5
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release GIT_TAG v3.5.4 # or a later release
) )
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
@@ -12,7 +12,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest) include(CTest)
include(Catch) include(Catch)
set(MYSQL_CONNECTION_STRING "mysql://test:test123!@127.0.0.1:3306/testdb") 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) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/mysql/test/connection.hpp @ONLY IMMEDIATE)
@@ -20,12 +20,13 @@ message(STATUS "mysql connection string: ${MYSQL_CONNECTION_STRING}")
set(TEST_SOURCES set(TEST_SOURCES
../../tests/QueryTest.cpp ../../tests/QueryTest.cpp
../../tests/SessionTest.cpp ../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp) ../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
set(LIBRARY_TEST_TARGET mysql_tests) set(LIBRARY_TEST_TARGET mysql_tests)
@@ -33,7 +33,7 @@ public:
size_t execute(const std::string &stmt) override; size_t execute(const std::string &stmt) override;
sql::record describe(const std::string& table) 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; bool exists(const std::string &schema_name, const std::string &table_name) override;
@@ -51,14 +51,13 @@ std::unique_ptr<sql::query_result_impl> postgres_connection::fetch(const std::st
throw_postgres_error(res, conn_, "postgres", stmt); throw_postgres_error(res, conn_, "postgres", stmt);
sql::record prototype; std::vector<sql::column_definition> prototype;
auto num_col = PQnfields(res); auto num_col = PQnfields(res);
for (int i = 0; i < num_col; ++i) { for (int i = 0; i < num_col; ++i) {
const char *col_name = PQfname(res, i); const char *col_name = PQfname(res, i);
auto type = PQftype(res, i); auto type = PQftype(res, i);
auto size = PQfmod(res, i); auto size = PQfmod(res, i);
// std::cout << "column " << col_name << ", type " << type << " (size: " << size << ")\n"; prototype.emplace_back(col_name);
prototype.append({col_name});
} }
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype))); return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype)));
} }
@@ -66,7 +65,7 @@ std::unique_ptr<sql::query_result_impl> postgres_connection::fetch(const std::st
std::string postgres_connection::generate_statement_name(const sql::query_context &query) 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());
if (result == postgres_connection::statement_name_map_.end()) { if (result == postgres_connection::statement_name_map_.end()) {
@@ -131,7 +130,7 @@ sql::data_type_t string2type(const char *type)
} }
} }
sql::record postgres_connection::describe(const std::string &table) std::vector<sql::column_definition> postgres_connection::describe(const std::string &table)
{ {
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='" + table + "'"); "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 + "'");
@@ -141,7 +140,7 @@ sql::record postgres_connection::describe(const std::string &table)
throw_postgres_error(res, conn_, "postgres", stmt); throw_postgres_error(res, conn_, "postgres", stmt);
postgres_result_reader reader(res); postgres_result_reader reader(res);
sql::record prototype; std::vector<sql::column_definition> prototype;
while (reader.fetch()) { while (reader.fetch()) {
char *end = nullptr; char *end = nullptr;
// Todo: Handle error // Todo: Handle error
@@ -156,7 +155,7 @@ sql::record postgres_connection::describe(const std::string &table)
null_opt = sql::null_option::NOT_NULL; null_opt = sql::null_option::NOT_NULL;
} }
// f.default_value(res->column(4)); // f.default_value(res->column(4));
prototype.append({name, type, utils::null_attributes, null_opt, index}); prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
} }
return std::move(prototype); return std::move(prototype);
+4 -4
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release GIT_TAG v3.5.4 # or a later release
) )
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
@@ -20,12 +20,12 @@ message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
set(TEST_SOURCES set(TEST_SOURCES
../../tests/QueryTest.cpp ../../tests/QueryTest.cpp
../../tests/SessionTest.cpp
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp) ../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
set(LIBRARY_TEST_TARGET postgres_tests) set(LIBRARY_TEST_TARGET postgres_tests)
@@ -33,14 +33,14 @@ public:
size_t execute(const std::string &stmt) override; size_t execute(const std::string &stmt) override;
sql::record describe(const std::string& table) 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; bool exists(const std::string &schema_name, const std::string &table_name) override;
private: private:
struct fetch_context struct fetch_context
{ {
sql::record prototype; std::vector<sql::column_definition> prototype;
sqlite_result_reader::rows rows; sqlite_result_reader::rows rows;
}; };
@@ -32,7 +32,7 @@ public:
void read_value(const char *id, size_t index, char *value, size_t s) 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) 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, std::string &value, size_t s) override;
void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override; void read_value(const char *id, size_t index, sql::value &val, size_t size) override;
private: private:
sqlite3 *db_{nullptr}; sqlite3 *db_{nullptr};
+4 -4
View File
@@ -65,7 +65,7 @@ int sqlite_connection::parse_result(void* param, int column_count, char** values
if (context->prototype.empty()) { if (context->prototype.empty()) {
for(int i = 0; i < column_count; ++i) { for(int i = 0; i < column_count; ++i) {
context->prototype.append(sql::column{columns[i]}); context->prototype.emplace_back(columns[i]);
} }
} }
@@ -144,12 +144,12 @@ sql::data_type_t string2type(const char *type)
} }
} }
sql::record sqlite_connection::describe(const std::string& table) std::vector<sql::column_definition> sqlite_connection::describe(const std::string& table)
{ {
const auto result = fetch_internal("PRAGMA table_info(" + table + ")"); const auto result = fetch_internal("PRAGMA table_info(" + table + ")");
sqlite_result_reader reader(result.rows, result.prototype.size()); sqlite_result_reader reader(result.rows, result.prototype.size());
sql::record prototype; std::vector<sql::column_definition> prototype;
while (reader.fetch()) { while (reader.fetch()) {
char *end = nullptr; char *end = nullptr;
// Todo: add index to column // Todo: add index to column
@@ -163,7 +163,7 @@ sql::record sqlite_connection::describe(const std::string& table)
null_opt = sql::null_option::NOT_NULL; null_opt = sql::null_option::NOT_NULL;
} }
// f.default_value(res->column(4)); // f.default_value(res->column(4));
prototype.append({name, type, utils::null_attributes, null_opt, index}); prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
} }
return std::move(prototype); return std::move(prototype);
@@ -107,8 +107,8 @@ void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std
query_result_reader::read_value(id, index, value, s); query_result_reader::read_value(id, index, value, s);
} }
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) 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, value, type, size); query_result_reader::read_value(id, index, val, size);
} }
} }
+5 -4
View File
@@ -3,7 +3,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0 # or a later release GIT_TAG v3.5.4 # or a later release
) )
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
@@ -20,12 +20,13 @@ message(STATUS "sqlite connection string: ${SQLITE_CONNECTION_STRING}")
set(TEST_SOURCES set(TEST_SOURCES
../../tests/QueryTest.cpp ../../tests/QueryTest.cpp
../../tests/SessionTest.cpp ../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp) ../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
set(LIBRARY_TEST_TARGET sqlite_tests) set(LIBRARY_TEST_TARGET sqlite_tests)
@@ -3,20 +3,22 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/query_builder.hpp"
#include "connection.hpp" #include "connection.hpp"
#include <list> #include <list>
class SessionRecordFixture class QueryRecordFixture
{ {
public: public:
SessionRecordFixture() QueryRecordFixture()
: db(matador::test::connection::dns) : db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{ {
db.open(); db.open();
} }
~SessionRecordFixture() { ~QueryRecordFixture() {
drop_table_if_exists("flight"); drop_table_if_exists("flight");
drop_table_if_exists("airplane"); drop_table_if_exists("airplane");
drop_table_if_exists("person"); drop_table_if_exists("person");
@@ -25,21 +27,22 @@ public:
protected: protected:
matador::sql::connection db; matador::sql::connection db;
matador::sql::schema schema;
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) { if (db.exists(table_name)) {
db.drop().table(table_name).execute(); db.query(schema).drop().table(table_name).execute();
} }
} }
}; };
using namespace matador::sql; using namespace matador::sql;
TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement", "[session][record]")
{ {
REQUIRE(!db.exists("person")); REQUIRE(!db.exists("person"));
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -49,16 +52,16 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement", "[ses
REQUIRE(db.exists("person")); REQUIRE(db.exists("person"));
db.drop() db.query(schema).drop()
.table("person") .table("person")
.execute(); .execute();
REQUIRE(!db.exists("person")); REQUIRE(!db.exists("person"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with foreign key", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement with foreign key", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("airplane", { .table("airplane", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("brand", 255), make_column<std::string>("brand", 255),
@@ -68,7 +71,7 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with fo
REQUIRE(db.exists("airplane")); REQUIRE(db.exists("airplane"));
db.create() db.query(schema).create()
.table("flight", { .table("flight", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_fk_column<unsigned long>("airplane_id", "airplane", "id"), make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
@@ -78,22 +81,22 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with fo
REQUIRE(db.exists("flight")); REQUIRE(db.exists("flight"));
db.drop() db.query(schema).drop()
.table("flight") .table("flight")
.execute(); .execute();
REQUIRE(!db.exists("flight")); REQUIRE(!db.exists("flight"));
db.drop() db.query(schema).drop()
.table("airplane") .table("airplane")
.execute(); .execute();
REQUIRE(!db.exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -101,14 +104,14 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[ses
}) })
.execute(); .execute();
auto res = db.insert() auto res = db.query(schema).insert()
.into("person", {"id", "name", "age"}) .into("person", {"id", "name", "age"})
.values({7, "george", 45}) .values({7, "george", 45})
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select({"id", "name", "age"}) auto result = db.query(schema).select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
@@ -125,14 +128,14 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[ses
REQUIRE(i.at(2).template as<int>() == 45); REQUIRE(i.at(2).template as<int>() == 45);
} }
db.drop() db.query(schema).drop()
.table("person") .table("person")
.execute(); .execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with foreign key", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement with foreign key", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("airplane", { .table("airplane", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("brand", 255), make_column<std::string>("brand", 255),
@@ -140,7 +143,7 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with fo
}) })
.execute(); .execute();
db.create() db.query(schema).create()
.table("flight", { .table("flight", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_fk_column<unsigned long>("airplane_id", "airplane", "id"), make_fk_column<unsigned long>("airplane_id", "airplane", "id"),
@@ -148,31 +151,31 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with fo
}) })
.execute(); .execute();
auto res = db.insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute(); auto res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute(); res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute(); res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto count = db.select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.query(schema).select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3); REQUIRE(count == 3);
res = db.insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute(); res = db.query(schema).insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
db.drop().table("flight").execute(); db.query(schema).drop().table("flight").execute();
db.drop().table("airplane").execute(); db.query(schema).drop().table("airplane").execute();
REQUIRE(!db.exists("flight")); REQUIRE(!db.exists("flight"));
REQUIRE(!db.exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute update record statement", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -180,14 +183,14 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[ses
}) })
.execute(); .execute();
auto res = db.insert() auto res = db.query(schema).insert()
.into("person", {"id", "name", "age"}) .into("person", {"id", "name", "age"})
.values({7, "george", 45}) .values({7, "george", 45})
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.update("person") res = db.query(schema).update("person")
.set({{"id", 7}, .set({{"id", 7},
{"name", "jane"}, {"name", "jane"},
{"age", 35}}) {"age", 35}})
@@ -196,29 +199,29 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[ses
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select({"id", "name", "age"}) auto result = db.query(schema).select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
for (const auto &i: result) { for (const auto &i: result) {
REQUIRE(i.size() == 3); REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long); REQUIRE(i.at(0).is_integer());
REQUIRE(i.at(0).as<long long>() == 7); REQUIRE(i.at(0).as<long long>() == 7);
REQUIRE(i.at(1).name() == "name"); REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar); REQUIRE(i.at(1).is_varchar());
REQUIRE(i.at(1).as<std::string>() == "jane"); REQUIRE(i.at(1).as<std::string>() == "jane");
REQUIRE(i.at(2).name() == "age"); REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int); REQUIRE(i.at(2).is_integer());
REQUIRE(i.at(2).as<int>() == 35); REQUIRE(i.at(2).as<int>() == 35);
} }
db.drop().table("person").execute(); db.query(schema).drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -226,16 +229,16 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][r
}) })
.execute(); .execute();
auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select({"id", "name", "age"}) auto result = db.query(schema).select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
@@ -246,22 +249,23 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][r
} }
REQUIRE(expected_names.empty()); REQUIRE(expected_names.empty());
auto rec = db.select({"id", "name", "age"}) auto rec = db.query(schema).select({"id", "name", "age"})
.from("person") .from("person")
.fetch_one(); .fetch_one();
REQUIRE(rec.at(1).str() == "george"); REQUIRE(rec.has_value());
REQUIRE(rec->at(1).str() == "george");
auto name = db.select({"name"}) auto name = db.query(schema).select({"name"})
.from("person") .from("person")
.fetch_value<std::string>(); .fetch_value<std::string>();
REQUIRE(name == "george"); REQUIRE(name == "george");
db.drop().table("person").execute(); db.query(schema).drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with order by", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -269,16 +273,16 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by"
}) })
.execute(); .execute();
auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select({"id", "name", "age"}) auto result = db.query(schema).select({"id", "name", "age"})
.from("person") .from("person")
.order_by("name").asc() .order_by("name").asc()
.fetch_all(); .fetch_all();
@@ -290,12 +294,12 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by"
} }
REQUIRE(expected_names.empty()); REQUIRE(expected_names.empty());
db.drop().table("person").execute(); db.query(schema).drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by and order by", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with group by and order by", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
@@ -303,18 +307,18 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by
}) })
.execute(); .execute();
auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select({alias(count("age"), "age_count"), "age"}) auto result = db.query(schema).select({count("age").as("age_count"), "age"})
.from("person") .from("person")
.group_by("age") .group_by("age")
.order_by("age_count").desc() .order_by("age_count").desc()
@@ -329,41 +333,41 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by
expected_values.pop_front(); expected_values.pop_front();
} }
db.drop().table("person").execute(); db.query(schema).drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute delete statement", "[session][record]") TEST_CASE_METHOD(QueryRecordFixture, "Execute delete statement", "[session][record]")
{ {
db.create() db.query(schema).create()
.table("person", { .table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255), make_column<std::string>("name", 255),
make_column<unsigned short>("age") make_column<unsigned short>("age")
}).execute(); }).execute();
auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto count = db.select({count_all()}).from("person").fetch_value<int>(); auto count = db.query(schema).select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 2); REQUIRE(count == 2);
res = db.remove() res = db.query(schema).remove()
.from("person") .from("person")
.where("id"_col == 1) .where("id"_col == 1)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
count = db.select({count_all()}).from("person").fetch_value<int>(); count = db.query(schema).select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 1); REQUIRE(count == 1);
db.drop().table("person").execute(); db.query(schema).drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Test quoted identifier", "[session][record]") { TEST_CASE_METHOD(QueryRecordFixture, "Test quoted identifier", "[session][record]") {
db.create() db.query(schema).create()
.table("quotes", { .table("quotes", {
make_column<std::string>("from", 255), make_column<std::string>("from", 255),
make_column<std::string>("to", 255) make_column<std::string>("to", 255)
@@ -379,19 +383,20 @@ TEST_CASE_METHOD(SessionRecordFixture, " Test quoted identifier", "[session][rec
REQUIRE(field.type() == types[field.index()]); REQUIRE(field.type() == types[field.index()]);
} }
db.insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute(); db.query(schema).insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute();
auto res = db.select({"from", "to"}).from("quotes").fetch_one(); auto res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Berlin" == res.at("from").str()); REQUIRE(res.has_value());
REQUIRE("London" == res.at("to").str()); REQUIRE("Berlin" == res->at("from").str());
REQUIRE("London" == res->at("to").str());
db.update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute(); db.query(schema).update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute();
res = db.select({"from", "to"}).from("quotes").fetch_one(); res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Hamburg" == res.at("from").str()); REQUIRE("Hamburg" == res->at("from").str());
REQUIRE("New York" == res.at("to").str()); REQUIRE("New York" == res->at("to").str());
db.drop().table("quotes").execute(); db.query(schema).drop().table("quotes").execute();
} }
+528 -8
View File
@@ -1,15 +1,535 @@
#include "catch2/catch_test_macros.hpp" #include "catch2/catch_test_macros.hpp"
#include <connection.hpp> #include "matador/sql/column_definition.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/query_builder.hpp"
#include "matador/sql/session.hpp"
TEST_CASE("Query test", "[query]") { #include "connection.hpp"
SECTION("Create") {
REQUIRE(true); #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();
} }
SECTION("Insert") {
REQUIRE(true); ~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");
} }
SECTION("Select") {
REQUIRE(true); 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();
}
+62 -145
View File
@@ -1,182 +1,99 @@
#include "catch2/catch_test_macros.hpp" #include "catch2/catch_test_macros.hpp"
#include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/session.hpp"
#include "connection.hpp" #include "connection.hpp"
#include "matador/sql/session.hpp"
#include "models/airplane.hpp" #include "models/airplane.hpp"
#include "models/flight.hpp" #include "models/flight.hpp"
#include "models/person.hpp"
using namespace matador::sql; class SessionFixture
using namespace matador::test;
class QueryFixture
{ {
public: public:
QueryFixture() SessionFixture()
: db(matador::test::connection::dns) : pool(matador::test::connection::dns, 4)
, ses(pool)
{}
~SessionFixture()
{ {
db.open(); drop_table_if_exists("flights");
} drop_table_if_exists("airplanes");
~QueryFixture() {
drop_table_if_exists("flight");
drop_table_if_exists("airplane");
drop_table_if_exists("person");
} }
protected: protected:
matador::sql::connection db; matador::sql::connection_pool<matador::sql::connection> pool;
matador::sql::session ses;
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name)
if (db.exists(table_name)) { {
db.drop().table(table_name).execute(); if (ses.table_exists(table_name)) {
ses.drop_table(table_name);
} }
} }
}; };
TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[session]") { using namespace matador;
db.create()
.table<airplane>("airplane")
.execute();
REQUIRE(db.exists("airplane")); 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");
db.create() auto result = ses.find<test::flight>(2);
.table<flight>("flight") REQUIRE(result.is_ok());
.execute();
REQUIRE(db.exists("flight"));
db.drop().table("flight").execute();
db.drop().table("airplane").execute();
REQUIRE(!db.exists("flight"));
REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[session]") { TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") {
db.create() using namespace matador::test;
.table<person>("person") ses.attach<airplane>("airplanes");
.execute(); ses.create_schema();
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
person george{7, "george", 45}; auto result = ses.find<airplane>(2);
george.image.push_back(37); REQUIRE(!result.is_ok());
REQUIRE((result.err() == sql::session_error::FailedToFindObject));
auto res = db.insert() result = ses.find<airplane>(1);
.into("person", george)
.execute();
REQUIRE(res == 1);
// fetch person as record REQUIRE(result);
auto result_record = db.select<person>() auto read_a380 = result.value();
.from("person") REQUIRE(a380->id == read_a380->id);
.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).type() == data_type_t::type_unsigned_long);
REQUIRE(i.at(0).as<long long>() == george.id);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).as<std::string>() == george.name);
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_unsigned_int);
REQUIRE(i.at(2).as<long long>() == george.age);
}
// fetch person as person
auto result_person = db.select<person>()
.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.drop().table("person").execute();
} }
TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") { TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
db.create() using namespace matador::test;
.table("person", { ses.attach<airplane>("airplanes");
make_pk_column<unsigned long>("id"), ses.create_schema();
make_column<std::string>("name", 255),
make_column<std::string>("color", 63)
})
.execute();
auto res = db.insert() std::vector<std::unique_ptr<airplane>> planes;
.into("person", {"id", "name", "color"}) planes.emplace_back(new airplane(1, "Airbus", "A380"));
.values({7, "george", "green"}) planes.emplace_back(new airplane(2, "Boeing", "707"));
.execute(); planes.emplace_back(new airplane(3, "Boeing", "747"));
REQUIRE(res == 1); for (auto &&plane: planes) {
ses.insert(plane.release());
// fetch person as record
auto result_record = db.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).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).as<unsigned long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).as<std::string>() == "george");
REQUIRE(i.at(2).name() == "color");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_varchar);
REQUIRE(i.at(2).as<std::string>() == "green");
} }
db.drop().table("person").execute(); auto result = ses.find<airplane>();
}
TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]") { std::vector<std::tuple<int, std::string, std::string>> expected_result {
db.create() {1, "Airbus", "A380"},
.table<airplane>("airplane") {2, "Boeing", "707"},
.execute(); {3, "Boeing", "747"}
db.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")
}; };
REQUIRE(result);
for (const auto &plane : planes) { auto all_planes = result.release();
auto res = db.insert().into<airplane>("airplane").values(*plane).execute(); size_t index {0};
REQUIRE(res == 1); 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;
} }
auto count = db.select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3);
flight f4711{4, planes.at(1), "hans"};
auto res = db.insert().into<flight>("flight").values(f4711).execute();
REQUIRE(res == 1);
auto f = *db.select<flight>().from("flight").fetch_all<flight>().begin();
REQUIRE(f.id == 4);
REQUIRE(f.pilot_name == "hans");
REQUIRE(f.airplane.get() != nullptr);
REQUIRE(f.airplane->id == 2);
db.drop().table("flight").execute();
db.drop().table("airplane").execute();
} }
+1 -1
View File
@@ -22,7 +22,7 @@ protected:
matador::sql::session ses; matador::sql::session ses;
}; };
TEST_CASE_METHOD(StatementCacheFixture, " Acquire prepared statement", "[statement cache]") { TEST_CASE_METHOD(StatementCacheFixture, "Acquire prepared statement", "[statement cache]") {
sql::statement_cache cache; sql::statement_cache cache;
auto conn = pool.acquire(); auto conn = pool.acquire();
+26 -10
View File
@@ -1,6 +1,6 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/sql/column.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
@@ -16,9 +16,10 @@ class StatementTestFixture
public: public:
StatementTestFixture() StatementTestFixture()
: db(matador::test::connection::dns) : db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{ {
db.open(); db.open();
db.create().table<airplane>("airplane").execute(); db.query(schema).create().table<airplane>("airplane").execute();
} }
~StatementTestFixture() ~StatementTestFixture()
@@ -28,6 +29,7 @@ public:
protected: protected:
matador::sql::connection db; matador::sql::connection db;
matador::sql::schema schema;
std::vector<entity<airplane>> planes{ std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"), make_entity<airplane>(1, "Airbus", "A380"),
@@ -38,18 +40,21 @@ protected:
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) { if (db.exists(table_name)) {
db.drop().table(table_name).execute(); db.query(schema).drop().table(table_name).execute();
} }
} }
}; };
TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement]") TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]")
{ {
schema.attach<airplane>("airplane");
table ap{"airplane"};
SECTION("Insert with prepared statement and placeholder") { SECTION("Insert with prepared statement and placeholder") {
auto stmt = db.insert() auto stmt = db.query(schema).insert()
.into<airplane>("airplane") .into("airplane", column_generator::generate<airplane>(schema, true))
.values<airplane>().prepare(); .values<airplane>()
.prepare();
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = stmt.bind(*plane).execute(); auto res = stmt.bind(*plane).execute();
@@ -57,7 +62,10 @@ TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement
stmt.reset(); stmt.reset();
} }
auto result = db.select<airplane>().from("airplane").fetch_all<airplane>(); auto result = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.fetch_all<airplane>();
size_t index{0}; size_t index{0};
for (const auto &i: result) { for (const auto &i: result) {
@@ -69,11 +77,19 @@ TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement
SECTION("Select with prepared statement") { SECTION("Select with prepared statement") {
for (const auto &plane: planes) { for (const auto &plane: planes) {
auto res = db.insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.query(schema)
.insert()
.into("airplane", column_generator::generate<airplane>(schema, true))
.values(*plane)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto stmt = db.select<airplane>().from("airplane").where("brand"_col == _).prepare(); auto stmt = db.query(schema)
.select(column_generator::generate<airplane>(schema, true))
.from(ap)
.where("brand"_col == _)
.prepare();
stmt.bind(0, "Airbus"); stmt.bind(0, "Airbus");
+52 -33
View File
@@ -1,6 +1,7 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/utils/enum_mapper.hpp" #include "matador/utils/enum_mapper.hpp"
@@ -8,6 +9,7 @@
#include "models/location.hpp" #include "models/location.hpp"
using namespace matador::sql;
using namespace matador::test; using namespace matador::test;
class TypeTraitsTestFixture class TypeTraitsTestFixture
@@ -15,25 +17,27 @@ class TypeTraitsTestFixture
public: public:
TypeTraitsTestFixture() TypeTraitsTestFixture()
: db(matador::test::connection::dns) : db(matador::test::connection::dns)
, schema(db.dialect().default_schema_name())
{ {
db.open(); db.open();
db.create() db.query(schema).create()
.table<location>("location") .table<location>("location")
.execute(); .execute();
} }
~TypeTraitsTestFixture() ~TypeTraitsTestFixture()
{ {
db.drop().table("location").execute(); db.query(schema).drop().table("location").execute();
} }
protected: protected:
matador::sql::connection db; matador::sql::connection db;
matador::sql::schema schema;
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name) {
if (db.exists(table_name)) { if (db.exists(table_name)) {
db.drop().table(table_name).execute(); db.query(schema).drop().table(table_name).execute();
} }
} }
}; };
@@ -48,47 +52,51 @@ static const matador::utils::enum_mapper<Color> color_enum({
{Color::Brown, "brown"} {Color::Brown, "brown"}
}); });
namespace matador::sql {
template<> template<>
struct data_type_traits<Color, void> struct matador::sql::data_type_traits<Color, void>
{ {
inline static data_type_t builtin_type(std::size_t size) inline static data_type_t builtin_type(std::size_t size)
{ return data_type_traits<std::string>::builtin_type(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) static void read_value(query_result_reader &reader, const char *id, size_t index, Color &value)
{ {
std::string enum_string; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
auto enum_opt = color_enum.to_enum(enum_string); if (const auto enum_opt = color_enum.to_enum(enum_string)) {
if (enum_opt) { value = enum_opt.value();
value = enum_opt.value(); }
} }
}
static any_type create_value(Color &value) static any_type create_value(const Color &value)
{ {
return color_enum.to_string(value); return color_enum.to_string(value);
} }
static void bind_value(parameter_binder &binder, size_t index, Color &value) static void bind_value(parameter_binder &binder, size_t index, Color &value)
{ {
binder.bind(index, color_enum.to_string(value)); binder.bind(index, color_enum.to_string(value));
} }
}; };
}
TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]")
{ {
schema.attach<location>("location");
SECTION("Insert and select with direct execution") { SECTION("Insert and select with direct execution") {
location loc{1, "center", {1, 2, 3}, Color::Black}; location loc{1, "center", {1, 2, 3}, Color::Black};
auto res = db.insert().into<location>("location").values(loc).execute(); auto res = db
.query(schema)
.insert()
.into("location", column_generator::generate<location>(schema, true))
.values(loc)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select<location>().from("location").fetch_all<location>(); auto result = db
.query(schema)
.select(column_generator::generate<location>(schema, true))
.from("location")
.fetch_all<location>();
for (const auto &l: result) { for (const auto &l: result) {
REQUIRE(l.name == "center"); REQUIRE(l.name == "center");
@@ -98,12 +106,23 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ
SECTION("Insert and select with prepared statement") { SECTION("Insert and select with prepared statement") {
location loc{1, "center", {1, 2, 3}, Color::Black}; location loc{1, "center", {1, 2, 3}, Color::Black};
auto stmt = db.insert().into<location>("location").values<location>().prepare(); auto stmt = db
auto res = stmt.bind(loc).execute(); .query(schema)
.insert()
.into("location", column_generator::generate<location>(schema, true))
.values<location>()
.prepare();
auto res = stmt
.bind(loc)
.execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = db.select<location>().from("location"). auto result = db
template fetch_all<location>(); .query(schema)
.select(column_generator::generate<location>(schema, true))
.from("location")
.fetch_all<location>();
for (const auto &l: result) { for (const auto &l: result) {
REQUIRE(l.name == "center"); REQUIRE(l.name == "center");
+27
View File
@@ -0,0 +1,27 @@
{
"name": "book",
"fields": {
"id": {
"type": "unsigned long",
"constraint": "primary_key"
},
"first_name": {
"type": "string",
"size": 63
},
"last_name": {
"type": "string",
"size": 63
},
"date_of_birth": {
"type": "string",
"size": 31
},
"year_of_birth": {
"type": "unsigned short"
},
"distinguished": {
"type": "boolean"
}
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "book",
"fields": {
"id": {
"type": "unsigned long",
"constraint": "primary_key"
},
"title": {
"type": "string",
"size": 511
},
"book_author": {
"type": "author",
"constraint": {
"type": "foreign_key",
"references": "id"
}
},
"published_in": {
"type": "unsigned_short"
}
}
}
+291 -13
View File
@@ -1,17 +1,295 @@
#include <cstdlib> #include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/schema.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/entity.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/enum_mapper.hpp"
#include "matador/sql/query_helper.hpp"
#include <iostream> #include <iostream>
#include <string> #include <string>
int main() { struct author {
const std::string env_var {"MATADOR_BACKENDS_PATH"}; unsigned long id{};
std::string first_name;
std::string last_name;
std::string date_of_birth;
unsigned short year_of_birth{};
bool distinguished{false};
// char var[1024]; template<typename Operator>
// size_t len{}; void process( Operator& op ) {
// const auto error = getenv_s(&len, var, 1024, env_var.c_str()); namespace field = matador::utils::access;
// if (error > 0) { field::primary_key( op, "id", id );
// std::cout << "error: unknown env var " << env_var << "\n"; field::attribute( op, "first_name", first_name, 63 );
// } else { field::attribute( op, "last_name", last_name, 63 );
// std::cout << "env var: " << var << "\n"; field::attribute( op, "date_of_birth", date_of_birth, 31 );
// } field::attribute( op, "year_of_birth", year_of_birth );
return 0; field::attribute( op, "distinguished", distinguished );
} }
};
struct book {
unsigned long id{};
matador::sql::entity<author> book_author;
std::string title;
unsigned short published_in{};
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key( op, "id", id );
field::attribute( op, "title", title, 511 );
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes );
field::attribute( op, "published_in", published_in );
}
};
struct payload {
unsigned long id{};
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key( op, "id", id );
}
};
struct job {
enum class job_state {
Pending,
Running,
Succeeded,
Failed,
Canceled
};
enum class job_mode {
Foreground,
Background
};
unsigned long id{};
matador::sql::entity<payload> payload;
std::string type;
std::string description;
job_state state;
job_mode mode;
template<typename Operator>
void process( Operator& op ) {
namespace field = matador::utils::access;
field::primary_key( op, "id", id );
field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes );
field::attribute( op, "type", type, 511 );
field::attribute( op, "description", description, 511 );
field::attribute( op, "state", state );
field::attribute( op, "mode", mode );
}
};
static const matador::utils::enum_mapper<job::job_state> job_state_enum({
{job::job_state::Pending, "Pending"},
{job::job_state::Running, "Running"},
{job::job_state::Succeeded, "Succeeded"},
{job::job_state::Failed, "Failed"},
{job::job_state::Canceled, "Canceled"}
});
static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
{job::job_mode::Foreground, "Foreground"},
{job::job_mode::Background, "Background"}
});
template<>
struct matador::sql::data_type_traits<job::job_state, 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, job::job_state &value)
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_state_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(const job::job_state &value)
{
return job_state_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_state &value)
{
binder.bind(index, job_state_enum.to_string(value));
}
};
template<>
struct matador::sql::data_type_traits<job::job_mode, 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, job::job_mode &value)
{
std::string enum_string;
reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) {
value = enum_opt.value();
}
}
static any_type create_value(const job::job_mode &value)
{
return job_mode_enum.to_string(value);
}
static void bind_value(parameter_binder &binder, size_t index, job::job_mode &value)
{
binder.bind(index, job_mode_enum.to_string(value));
}
};
QUERY_HELPER( authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished )
QUERY_HELPER( books, id, author_id, title, published_in )
QUERY_HELPER( job, id, payload, type, description, state, mode )
QUERY_HELPER( payload, id )
QUERY_HELPER( temporary_table, id );
int main() {
using namespace matador::sql;
using namespace matador;
const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"};
schema s( "main" );
s.attach<author>( "authors" );
s.attach<book>( "books" );
connection c( dns );
c.open();
s.create( c );
auto create_authors_sql = c.query( s )
.create()
.table<author>( qh::authors )
.execute();
c.query( s )
.create()
.table<book>( qh::books )
.execute();
std::cout << "SQL: " << create_authors_sql << "\n";
author mc;
mc.id = 1;
mc.first_name = "Michael";
mc.last_name = "Crichton";
mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954;
mc.distinguished = true;
auto insert_authors_sql = c.query( s )
.insert()
.into( qh::authors )
.values( mc )
.execute();
std::cout << "SQL: " << insert_authors_sql << "\n";
auto result = c.query( s )
.select( qh::authors.columns )
.from( qh::authors )
.fetch_all();
for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
auto update_authors_sql = c.query( s )
.update( qh::authors )
.set( {{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}} )
.where( qh::authors.last_name == "Crichton" )
.execute();
std::cout << "SQL: " << update_authors_sql << "\n";
auto authors = c.query( s )
.select( qh::authors.columns )
.from( qh::authors )
.fetch_all<author>();
for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
c.query( s )
.insert()
.into( qh::books )
.values( {2, "It", mc.id, 1980} )
.execute();
c.query( s )
.insert()
.into( qh::books )
.values( {3, "Misery", mc.id, 1984} )
.execute();
auto select_books_sql = c.query( s )
.select( qh::books.columns, {qh::authors.last_name} )
.from( qh::books )
.join_left( qh::authors )
.on( qh::books.author_id == qh::authors.id )
.where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
.group_by( qh::books.published_in )
.order_by( qh::books.title ).asc()
.limit( 5 )
.offset( 2 )
.fetch_all();
for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
// SELECT book.title, book.id, book.author_id, book.published_in, author.name
// FROM book
// INNER JOIN author ON book.author_id = author.id
// WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5
c.query( s ).drop().table( qh::books ).execute();
auto drop_authors_sql = c.query( s )
.drop()
.table( qh::authors )
.execute();
std::cout << "SQL: " << drop_authors_sql << "\n";
auto res = c.query( s )
.select( {qh::payload.id} )
.from( qh::payload )
.join_left( qh::job )
.on( qh::job.payload == qh::payload.id )
.where(
in( qh::payload.id, c.query( s )
.select( {qh::job.state} )
.from( qh::job )
.where( qh::job.state == job::job_state::Running )
) &&
in( qh::payload.id, c.query( s )
.select( {qh::temporary_table.id} )
.from( qh::temporary_table ) )
)
.build();
// .fetch_value<unsigned long>();
std::cout << "SQL: " << res.sql << "\n";
return 0;
}
+11
View File
@@ -10,6 +10,17 @@
namespace matador::sql { namespace matador::sql {
using any_db_type = std::variant<
long long,
unsigned long long,
double,
bool,
const char*,
std::string,
utils::blob,
placeholder,
nullptr_t>;
using any_type = std::variant< using any_type = std::variant<
char, short, int, long, long long, char, short, int, long, long long,
unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long,
@@ -0,0 +1,53 @@
#ifndef QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP
#define QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP
#include "matador/utils/types.hpp"
#include "matador/sql/placeholder.hpp"
#include <string>
namespace matador::sql {
class dialect;
class query_context;
struct any_type_to_string_visitor
{
explicit any_type_to_string_visitor(const dialect &d, query_context &query);
void operator()(char &x) { to_string(x); }
void operator()(short &x) { to_string(x); }
void operator()(int &x) { to_string(x); }
void operator()(long &x) { to_string(x); }
void operator()(long long &x) { to_string(x); }
void operator()(unsigned char &x) { to_string(x); }
void operator()(unsigned short &x) { to_string(x); }
void operator()(unsigned int &x) { to_string(x); }
void operator()(unsigned long &x) { to_string(x); }
void operator()(unsigned long long &x) { to_string(x); }
void operator()(bool &x) { to_string(x); }
void operator()(float &x) { to_string(x); }
void operator()(double &x) { to_string(x); }
void operator()(const char *x) { to_string(x); }
void operator()(std::string &x) { to_string(x); }
void operator()(utils::blob &x) { to_string(x); }
void operator()(placeholder &x) { to_string(x); }
template<typename Type>
void to_string(Type &val)
{
result = std::to_string(val);
}
void to_string(const char *val);
void to_string(std::string &val);
void to_string(utils::blob &val);
void to_string(placeholder &val);
const dialect &d;
query_context &query;
std::string result;
};
}
#endif //QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP
+30 -7
View File
@@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
namespace matador::sql { namespace matador::sql {
@@ -26,28 +27,50 @@ public:
const dialect& connection_dialect(const std::string &connection_type); const dialect& connection_dialect(const std::string &connection_type);
private: private:
struct backend_context { struct basic_backend_context {
backend_context(const std::string &connection_type, virtual ~basic_backend_context() = default;
const std::string &backends_path); [[nodiscard]] virtual connection_impl* create(const connection_info&) = 0;
virtual void destroy(connection_impl*) = 0;
[[nodiscard]] virtual const sql::dialect* dialect() const = 0;
};
class backend_context final : public basic_backend_context {
public:
explicit backend_context(const std::string &connection_type);
backend_context(const backend_context&) = delete; backend_context(const backend_context&) = delete;
backend_context& operator=(const backend_context&) = delete; backend_context& operator=(const backend_context&) = delete;
backend_context(backend_context&&) noexcept = default; backend_context(backend_context&&) noexcept = default;
backend_context& operator=(backend_context&&) noexcept = default; backend_context& operator=(backend_context&&) noexcept = default;
~backend_context(); ~backend_context() override;
[[nodiscard]] connection_impl* create(const connection_info&) override;
void destroy(connection_impl *conn) override;
[[nodiscard]] const class dialect* dialect() const override;
private:
typedef connection_impl*(*create_func)(const connection_info&); typedef connection_impl*(*create_func)(const connection_info&);
typedef void (*destroy_func)(connection_impl*); typedef void (*destroy_func)(connection_impl*);
typedef const dialect*(*dialect_func)(); typedef const class dialect*(*dialect_func)();
create_func create_connection{}; create_func create_connection{};
destroy_func destroy_connection{}; destroy_func destroy_connection{};
dialect_func get_dialect{}; dialect_func get_dialect{};
utils::library lib; utils::library lib;
}; };
class noop_backend_context final : public basic_backend_context {
public:
connection_impl *create(const connection_info &info) override;
void destroy(connection_impl *impl) override;
[[nodiscard]] const sql::dialect *dialect() const override;
private:
std::unordered_set<std::unique_ptr<connection_impl>> noop_connections_;
};
private: private:
using backends_t = std::unordered_map<std::string, std::unique_ptr<backend_context>>; using backends_t = std::unordered_map<std::string, std::unique_ptr<basic_backend_context>>;
backends_t backends_; backends_t backends_;
std::string backends_path_;
}; };
} }
#endif //QUERY_BACKEND_PROVIDER_HPP #endif //QUERY_BACKEND_PROVIDER_HPP
+1 -1
View File
@@ -33,7 +33,7 @@ public:
LIKE LIKE
}; };
virtual std::string evaluate(dialect &dialect, query_context &query) const = 0; virtual std::string evaluate(const dialect &dialect, query_context &query) const = 0;
static std::unordered_map<operand_t, std::string> operands; static std::unordered_map<operand_t, std::string> operands;
}; };
+23 -154
View File
@@ -1,174 +1,43 @@
#ifndef QUERY_COLUMN_HPP #ifndef QUERY_COLUMN_HPP
#define QUERY_COLUMN_HPP #define QUERY_COLUMN_HPP
#include "matador/sql/any_type.hpp" #include <string>
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/field_attributes.hpp"
#include <optional>
#include <vector>
namespace matador::sql { namespace matador::sql {
enum class null_option : uint8_t { struct table;
NULLABLE, NOT_NULL
enum class sql_function_t {
NONE,
COUNT,
AVG,
SUM,
MIN,
MAX
}; };
class column { struct column
public: {
column(sql_function_t func, std::string name); column(const char *name); // NOLINT(*-explicit-constructor)
column(const char *name, std::string alias = ""); // NOLINT(*-explicit-constructor) column(std::string name); // NOLINT(*-explicit-constructor)
column(std::string name, std::string alias = ""); // NOLINT(*-explicit-constructor) column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor)
column(std::string table_name, std::string name, std::string as = "");
column(std::string table_name, const char* name, std::string as = "");
column(struct table &t, const char* name, std::string as = "");
column(const column&) = default; [[nodiscard]] bool equals(const column &x) const;
column& operator=(const column&) = default;
column(column&&) noexcept = default;
column& operator=(column&&) noexcept = default;
template<typename Type> column& as(std::string a);
explicit column(std::string name, utils::field_attributes attr)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{}
template<typename Type>
column(std::string name, const Type &, utils::field_attributes attr, null_option null_opt)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr, null_opt)
{}
column(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index = 0);
template<typename Type>
column(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr, null_opt)
{}
column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] size_t index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] data_type_t type() const;
[[nodiscard]] const std::string& alias() const;
[[nodiscard]] const std::string& ref_table() const;
[[nodiscard]] const std::string& ref_column() const;
void type(data_type_t type);
void alias(const std::string &as);
template< typename Type >
[[nodiscard]] bool is_type_of() const {
return std::holds_alternative<Type>(value_);
}
[[nodiscard]] std::string str() const;
template<typename Type>
void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes)
{
type_ = data_type_traits<Type>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const std::string &value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const char *value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
template<class Type>
Type as() const
{
const Type* ptr= std::get_if<Type>(&value_);
if (ptr) {
return *ptr;
}
any_type_to_visitor<Type> visitor;
std::visit(visitor, const_cast<any_type&>(value_));
return visitor.result;
}
[[nodiscard]] bool is_function() const; [[nodiscard]] bool is_function() const;
[[nodiscard]] sql_function_t function() const;
private: std::string table;
template<class Operator> std::string name;
void process(Operator &op) std::string alias;
{
op.on_attribute(name_.c_str(), value_, type_, attributes_);
}
using data_type_index = std::vector<data_type_t>;
private:
friend class record;
static const data_type_index data_type_index_;
std::string name_;
size_t index_{};
utils::field_attributes attributes_;
null_option null_option_{null_option::NOT_NULL};
data_type_t type_{data_type_t::type_unknown};
any_type value_;
sql_function_t function_{sql_function_t::NONE}; sql_function_t function_{sql_function_t::NONE};
std::string alias_;
std::string ref_table_;
std::string ref_column_;
}; };
/**
* User defined literal to have a shortcut creating a column object
* @param name Name of the column
* @param len Length of the column name
* @return A column object with given name
*/
column operator "" _col(const char *name, size_t len); column operator "" _col(const char *name, size_t len);
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL);
template < typename Type >
column make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL)
{
return make_column(name, data_type_traits<Type>::builtin_type(0), attr, null_opt);
}
template <>
column make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt);
template < typename Type >
column make_pk_column(const std::string &name, size_t size = 0)
{
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY });
}
template <>
column make_pk_column<std::string>(const std::string &name, size_t size);
template < typename Type >
column make_fk_column(const std::string &name, size_t size, const std::string &ref_table, const std::string &ref_column)
{
return {name, data_type_traits<Type>::builtin_type(size), ref_table, ref_column, { size, utils::constraints::FOREIGN_KEY }};
}
template < typename Type >
[[maybe_unused]] column make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column)
{
return {name, data_type_traits<Type>::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template <>
column make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table, const std::string &ref_column);
} }
#endif //QUERY_COLUMN_HPP #endif //QUERY_COLUMN_HPP
+173
View File
@@ -0,0 +1,173 @@
#ifndef QUERY_COLUMN_DEFINITION_HPP
#define QUERY_COLUMN_DEFINITION_HPP
#include "matador/sql/any_type.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/field_attributes.hpp"
#include <optional>
#include <vector>
namespace matador::sql {
enum class null_option : uint8_t {
NULLABLE, NOT_NULL
};
class column_definition {
public:
explicit column_definition(const char *name); // NOLINT(*-explicit-constructor)
explicit column_definition(std::string name); // NOLINT(*-explicit-constructor)
column_definition(const column_definition&) = default;
column_definition& operator=(const column_definition&) = default;
column_definition(column_definition&&) noexcept = default;
column_definition& operator=(column_definition&&) noexcept = default;
template<typename Type>
explicit column_definition(std::string name, utils::field_attributes attr)
: column_definition(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{}
template<typename Type>
column_definition(std::string name, const Type &, utils::field_attributes attr, null_option null_opt)
: column_definition(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr, null_opt)
{}
column_definition(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index = 0);
template<typename Type>
column_definition(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt)
: column_definition(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr, null_opt)
{}
column_definition(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] int index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] data_type_t type() const;
[[nodiscard]] const std::string& ref_table() const;
[[nodiscard]] const std::string& ref_column() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
void type(data_type_t type);
template< typename Type >
[[nodiscard]] bool is_type_of() const {
return std::holds_alternative<Type>(value_);
}
[[nodiscard]] std::string str() const;
template<typename Type>
void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes)
{
type_ = data_type_traits<Type>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const std::string &value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
void set(const char *value, const utils::field_attributes &attr)
{
type_ = data_type_traits<std::string>::builtin_type(attr.size());
attributes_ = attr;
value_ = value;
}
template<class Type>
Type as() const
{
const Type* ptr= std::get_if<Type>(&value_);
if (ptr) {
return *ptr;
}
any_type_to_visitor<Type> visitor;
std::visit(visitor, const_cast<any_type&>(value_));
return visitor.result;
}
friend std::ostream& operator<<(std::ostream &out, const column_definition &col);
private:
template<class Operator>
void process(Operator &op)
{
op.on_attribute(name_.c_str(), value_, type_, attributes_);
}
using data_type_index = std::vector<data_type_t>;
private:
static const data_type_index data_type_index_;
std::string name_;
int index_{-1};
utils::field_attributes attributes_;
null_option null_option_{null_option::NOT_NULL};
data_type_t type_{data_type_t::type_unknown};
any_type value_;
std::string ref_table_;
std::string ref_column_;
};
/**
* User defined literal to have a shortcut creating a column object
* @param name Name of the column
* @param len Length of the column name
* @return A column object with given name
*/
column_definition make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL);
template < typename Type >
column_definition make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL)
{
return make_column(name, data_type_traits<Type>::builtin_type(0), attr, null_opt);
}
template <>
column_definition make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt);
template < typename Type >
column_definition make_pk_column(const std::string &name, size_t size = 0)
{
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY });
}
template <>
column_definition make_pk_column<std::string>(const std::string &name, size_t size);
template < typename Type >
column_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table, const std::string &ref_column)
{
return {name, data_type_traits<Type>::builtin_type(size), ref_table, ref_column, { size, utils::constraints::FOREIGN_KEY }};
}
template < typename Type >
[[maybe_unused]] column_definition make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column)
{
return {name, data_type_traits<Type>::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template <>
column_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table, const std::string &ref_column);
}
#endif //QUERY_COLUMN_DEFINITION_HPP
@@ -0,0 +1,133 @@
#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#include "matador/sql/column_definition.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include <typeindex>
#include <vector>
namespace matador::sql {
class schema;
class fk_column_generator
{
public:
fk_column_generator() = default;
template<class Type>
column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
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)
{
type_ = data_type_traits<ValueType>::builtin_type(0);
}
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, 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 *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private:
data_type_t type_{};
};
class column_definition_generator
{
private:
column_definition_generator(std::vector<column_definition> &columns, const schema &repo);
public:
~column_definition_generator() = default;
template < class Type >
static std::vector<column_definition> generate(const schema &repo)
{
std::vector<column_definition> columns;
column_definition_generator gen(columns, repo);
Type obj;
matador::utils::access::process(gen, obj);
return std::move(columns);
}
template < class V >
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0);
void on_primary_key(const char *id, std::string &pk, size_t size);
void on_revision(const char *id, unsigned long long &rev);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes);
template<typename Type>
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
{
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class Pointer>
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
{
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
}
template<class ContainerType>
void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, 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 *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
private:
size_t index_ = 0;
std::vector<column_definition> &columns_;
const schema &repo_;
fk_column_generator fk_column_generator_;
};
template<typename V>
void column_definition_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
}
template<typename Type>
void column_definition_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
}
template<typename Type>
void column_definition_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr, null_option::NULLABLE);
}
}
#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP
+82 -82
View File
@@ -1,128 +1,128 @@
#ifndef QUERY_COLUMN_GENERATOR_HPP #ifndef QUERY_COLUMN_GENERATOR_HPP
#define QUERY_COLUMN_GENERATOR_HPP #define QUERY_COLUMN_GENERATOR_HPP
#include "matador/sql/column.hpp"
#include "matador/sql/data_type_traits.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 <typeindex> #include "matador/sql/column.hpp"
#include "matador/sql/schema.hpp"
#include <string>
#include <vector> #include <vector>
#include <stack>
namespace matador::sql { namespace matador::sql {
class table_repository;
class fk_column_generator
{
public:
fk_column_generator() = default;
template<class Type>
column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column{id, type_, 0, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
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)
{
type_ = data_type_traits<ValueType>::builtin_type(0);
}
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {}
private:
data_type_t type_{};
};
class column_generator class column_generator
{ {
private: private:
column_generator(std::vector<column> &columns, const table_repository &repo); column_generator(std::vector<column> &column_infos,
const sql::schema &ts,
const std::string &table_name,
bool force_lazy);
public: public:
~column_generator() = default; ~column_generator() = default;
template < class Type > template < class Type >
static std::vector<column> generate(const table_repository &repo) static std::vector<column> generate(const sql::schema &ts, bool force_lazy = false)
{ {
const auto info = ts.info<Type>();
if (!info) {
return {};
}
std::vector<column> columns; std::vector<column> columns;
column_generator gen(columns, repo); column_generator gen(columns, ts, info.value().name, force_lazy);
Type obj; Type obj;
matador::utils::access::process(gen, obj); matador::utils::access::process(gen, obj);
return std::move(columns); return std::move(columns);
} }
template < class V > template < class V >
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0); void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
void on_primary_key(const char *id, std::string &pk, size_t size); {
void on_revision(const char *id, unsigned long long &rev); push(id);
}
void on_primary_key(const char *id, std::string &, size_t);
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type> template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
template<typename Type> push(id);
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes); }
template<class Pointer> template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, utils::cascade_type) void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); push(id);
} else {
const auto info = table_schema_.info<typename Pointer::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename Pointer::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
} }
template<class Pointer> template<class Pointer>
void on_has_one(const char *id, Pointer &x, utils::cascade_type) void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
{ {
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); push(id);
} else {
const auto info = table_schema_.info<typename Pointer::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename Pointer::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) {
return;
}
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
return;
}
table_name_stack_.push(info.value().name);
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this, obj);
table_name_stack_.pop();
}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many_to_many(const char *id, ContainerType &c, 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 *id, ContainerType &c, const utils::foreign_attributes &attr)
{
}
private: private:
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti); void push(const std::string &column_name);
private: private:
size_t index_ = 0; std::stack<std::string> table_name_stack_;
std::vector<column> &columns_; std::vector<column> &column_infos_;
const table_repository &repo_; const sql::schema &table_schema_;
int column_index{0};
fk_column_generator fk_column_generator_; bool force_lazy_{false};
}; };
template<typename V>
void column_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
} }
template<typename Type>
void column_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
}
template<typename Type>
void column_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr, null_option::NULLABLE);
}
}
#endif //QUERY_COLUMN_GENERATOR_HPP #endif //QUERY_COLUMN_GENERATOR_HPP
@@ -1,65 +0,0 @@
#ifndef QUERY_COLUMN_NAME_GENERATOR_HPP
#define QUERY_COLUMN_NAME_GENERATOR_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
#include <string>
#include <vector>
namespace matador::sql {
class column_name_generator
{
private:
explicit column_name_generator(std::vector<std::string> &column_names);
public:
~column_name_generator() = default;
template < class Type >
static std::vector<std::string> generate()
{
std::vector<std::string> columns;
column_name_generator gen(columns);
Type obj;
matador::utils::access::process(gen, obj);
return std::move(columns);
}
template < class V >
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
{
column_names_.emplace_back(id);
}
void on_primary_key(const char *id, std::string &, size_t);
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
column_names_.emplace_back(id);
}
template<class Pointer>
void on_belongs_to(const char *id, Pointer &, utils::cascade_type)
{
column_names_.emplace_back(id);
}
template<class Pointer>
void on_has_one(const char *id, Pointer &, utils::cascade_type)
{
column_names_.emplace_back(id);
}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {}
template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {}
private:
std::vector<std::string> &column_names_;
};
}
#endif //QUERY_COLUMN_NAME_GENERATOR_HPP
+81 -28
View File
@@ -1,12 +1,15 @@
#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/sql/query_result.hpp"
#include "matador/sql/basic_condition.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/placeholder.hpp"
#include "matador/sql/query_context.hpp" #include "matador/sql/query_context.hpp"
#include <memory> #include <memory>
#include <utility>
namespace matador::sql { namespace matador::sql {
@@ -25,6 +28,8 @@ 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;
@@ -36,12 +41,13 @@ public:
placeholder value; placeholder value;
std::string evaluate(dialect &d, query_context &query) const override; std::string evaluate(const dialect &d, query_context &query) const override;
}; };
template<class T> template<class T>
class condition<column, T, typename std::enable_if< class condition<column, T, typename std::enable_if<
std::is_scalar<T>::value && std::is_scalar<T>::value &&
!std::is_enum<T>::value &&
!std::is_same<std::string, T>::value && !std::is_same<std::string, T>::value &&
!std::is_same<const char*, T>::value>::type> : public basic_column_condition !std::is_same<const char*, T>::value>::type> : public basic_column_condition
{ {
@@ -53,10 +59,10 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name()); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_.name()) + " " + operand + " " + std::to_string(value); return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value);
} }
}; };
@@ -73,18 +79,40 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
query.bind_vars.emplace_back(field_.name()); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_.name()) + " " + operand + " '" + value + "'"; return d.prepare_identifier(field_) + " " + operand + " '" + value + "'";
} }
}; };
template<class T> template<class T>
class condition<T, column, typename std::enable_if< class condition<column, T, std::enable_if_t<std::is_enum_v<T>>> final : public basic_column_condition
std::is_scalar<T>::value && {
!std::is_same<std::string, T>::value && public:
!std::is_same<const char*, T>::value>::type> : public basic_column_condition 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_same_v<std::string, T> &&
!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, basic_condition::operand_t op, const column &fld)
@@ -94,9 +122,9 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_.name()); return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_);
} }
}; };
@@ -113,9 +141,9 @@ public:
T value; T value;
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_.name()); return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_);
} }
}; };
@@ -155,13 +183,13 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override { std::string evaluate(const dialect &d, 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);
} }
std::string result = d.prepare_identifier(field_.name()) + " IN ("; std::string result = d.prepare_identifier(field_) + " IN (";
if (args_.size() < 2) { if (args_.size() < 2) {
for (const auto &val : args_) { for (const auto &val : args_) {
result.append(std::to_string(val)); result.append(std::to_string(val));
@@ -214,7 +242,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, query_context &q); condition(column col, basic_condition::operand_t op, const query_context &q);
/** /**
* @brief Evaluates the condition * @brief Evaluates the condition
@@ -225,10 +253,10 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition IN part of the query * @return A condition IN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override; std::string evaluate(const dialect &d, query_context &query) const override;
private: private:
query_context &query_; query_context query_;
}; };
/** /**
@@ -260,10 +288,10 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return A condition BETWEEN part of the query * @return A condition BETWEEN part of the query
*/ */
std::string evaluate(dialect &d, query_context &query) const override { std::string evaluate(const dialect &d, 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_.name()) + " 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:
@@ -302,7 +330,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, 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);
@@ -345,7 +373,7 @@ public:
* @param d The d used to evaluate * @param d The d used to evaluate
* @return The evaluated string based on the compile type * @return The evaluated string based on the compile type
*/ */
std::string evaluate(dialect &d, query_context &query) const override std::string evaluate(const dialect &d, query_context &query) const override
{ {
return operand + " (" + cond.evaluate(d) + ")"; return operand + " (" + cond.evaluate(d) + ")";
} }
@@ -355,6 +383,28 @@ private:
std::string operand; std::string operand;
}; };
template<>
class condition<column, column> : public basic_column_condition
{
public:
condition(const column &a, basic_condition::operand_t op, column b)
: basic_column_condition(a, op)
, other_column_(std::move(b)) {}
/**
* @brief Evaluates the condition
*
* @param d The d used to evaluate
* @return The evaluated string based on the compile type
*/
std::string evaluate(const dialect &d, query_context &query) const override
{
return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_);
}
private:
column other_column_;
};
/** /**
* @file condition.hpp * @file condition.hpp
* @brief Contains functions to create query conditions * @brief Contains functions to create query conditions
@@ -392,7 +442,8 @@ 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, query_context &&q); condition<column, query_context> in(const column &col, const 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.
@@ -441,6 +492,8 @@ condition<column, T> operator==(const column &col, T val)
return condition<column, T>(col, basic_condition::operand_t::EQUAL, val); return condition<column, T>(col, basic_condition::operand_t::EQUAL, val);
} }
condition<column, column> operator==(const column &a, const column &b);
/** /**
* @brief Condition equality method for a column and a query * @brief Condition equality method for a column and a query
* *
+8 -21
View File
@@ -4,7 +4,7 @@
#include "matador/sql/connection_info.hpp" #include "matador/sql/connection_info.hpp"
#include "matador/sql/connection_impl.hpp" #include "matador/sql/connection_impl.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/query_intermediates.hpp" #include "matador/sql/query.hpp"
#include "matador/sql/query_context.hpp" #include "matador/sql/query_context.hpp"
#include "matador/sql/query_result.hpp" #include "matador/sql/query_result.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
@@ -16,11 +16,13 @@
namespace matador::sql { namespace matador::sql {
class schema;
class connection class connection
{ {
public: public:
explicit connection(connection_info info, const std::shared_ptr<table_repository> &repo = std::make_shared<table_repository>()); explicit connection(connection_info info);
explicit connection(const std::string& dns, const std::shared_ptr<table_repository> &repo = std::make_shared<table_repository>()); explicit connection(const std::string& dns);
connection(const connection &x); connection(const connection &x);
connection& operator=(const connection &x); connection& operator=(const connection &x);
connection(connection &&x) noexcept = default; connection(connection &&x) noexcept = default;
@@ -31,19 +33,12 @@ public:
[[nodiscard]] bool is_open() const; [[nodiscard]] bool is_open() const;
[[nodiscard]] const connection_info& info() const; [[nodiscard]] const connection_info& info() const;
query_create_intermediate create(); [[nodiscard]] std::vector<sql::column_definition> describe(const std::string &table_name) const;
query_drop_intermediate drop();
template < class Type >
query_select_intermediate select();
query_select_intermediate select(std::initializer_list<column> columns);
query_insert_intermediate insert();
query_update_intermediate update(const std::string &table);
query_delete_intermediate remove();
[[nodiscard]] record describe(const std::string &table_name) const;
[[nodiscard]] bool exists(const std::string &schema_name, const std::string &table_name) const; [[nodiscard]] bool exists(const std::string &schema_name, const std::string &table_name) const;
[[nodiscard]] bool exists(const std::string &table_name) const; [[nodiscard]] bool exists(const std::string &table_name) const;
sql::query query(const sql::schema &schema) const;
query_result<record> fetch(const query_context &q) const; query_result<record> fetch(const query_context &q) const;
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const; [[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
[[nodiscard]] size_t execute(const std::string &sql) const; [[nodiscard]] size_t execute(const std::string &sql) const;
@@ -51,21 +46,13 @@ public:
statement prepare(query_context &&query) const; statement prepare(query_context &&query) const;
const class dialect& dialect() const; const class dialect& dialect() const;
std::shared_ptr<table_repository> tables() const;
private: private:
connection_info connection_info_; connection_info connection_info_;
std::unique_ptr<connection_impl> connection_; std::unique_ptr<connection_impl> connection_;
utils::logger logger_; utils::logger logger_;
const class dialect &dialect_; const class dialect &dialect_;
std::shared_ptr<table_repository> table_repository_;
}; };
template<class Type>
query_select_intermediate connection::select()
{
return query_select_intermediate{*this, column_generator::generate<Type>(*table_repository_)};
}
} }
#endif //QUERY_CONNECTION_HPP #endif //QUERY_CONNECTION_HPP
+1 -1
View File
@@ -26,7 +26,7 @@ public:
virtual std::unique_ptr<query_result_impl> fetch(const std::string &stmt) = 0; virtual std::unique_ptr<query_result_impl> fetch(const std::string &stmt) = 0;
virtual std::unique_ptr<statement_impl> prepare(query_context context) = 0; virtual std::unique_ptr<statement_impl> prepare(query_context context) = 0;
virtual record describe(const std::string &table) = 0; virtual std::vector<sql::column_definition> describe(const std::string &table) = 0;
virtual bool exists(const std::string &schema_name, const std::string &table_name) = 0; virtual bool exists(const std::string &schema_name, const std::string &table_name) = 0;
protected: protected:
+24 -8
View File
@@ -7,6 +7,7 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <optional> #include <optional>
#include <condition_variable>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
@@ -86,19 +87,21 @@ public:
} }
connection_pointer acquire() { connection_pointer acquire() {
std::unique_lock<std::mutex> lock(mutex_);
while (idle_connections_.empty()) {
cv.wait(lock);
}
return get_next_connection();
}
connection_pointer try_acquire() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
if (idle_connections_.empty()) { if (idle_connections_.empty()) {
return {nullptr, this}; return {nullptr, this};
} }
pointer next_connection{nullptr}; return get_next_connection();
for (auto &item : idle_connections_) {
next_connection = item.second;
auto node = idle_connections_.extract(item.first);
inuse_connections_.insert(std::move(node));
break;
}
return {next_connection, this};
} }
connection_pointer acquire(size_t id) { connection_pointer acquire(size_t id) {
@@ -151,8 +154,21 @@ public:
const connection_info &info() const { const connection_info &info() const {
return info_; return info_;
} }
private:
connection_pointer get_next_connection() {
pointer next_connection{nullptr};
for (auto &item : idle_connections_) {
next_connection = item.second;
auto node = idle_connections_.extract(item.first);
inuse_connections_.insert(std::move(node));
break;
}
return {next_connection, this};
}
private: private:
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::condition_variable cv;
std::vector<IdConnection<Connection>> connection_repo_; std::vector<IdConnection<Connection>> connection_repo_;
using pointer = IdConnection<Connection>*; using pointer = IdConnection<Connection>*;
using connection_map = std::unordered_map<size_t, pointer>; using connection_map = std::unordered_map<size_t, pointer>;
+9
View File
@@ -55,6 +55,12 @@ void convert(std::string &dest, SourceType source, typename std::enable_if<std::
} }
} }
template < typename SourceType >
void convert(utils::blob &dest, SourceType source, typename std::enable_if<!std::is_same<utils::blob, SourceType>::value>::type* = nullptr)
{
throw std::logic_error("couldn't convert value to matador::utils::blob");
}
void convert(std::string &dest, const char* source); void convert(std::string &dest, const char* source);
unsigned long long to_unsigned_long_long(const char *source); unsigned long long to_unsigned_long_long(const char *source);
@@ -108,8 +114,11 @@ void convert(DestType &dest, bool source, typename std::enable_if<std::is_floati
template < typename DestType > template < typename DestType >
void convert(DestType &dest, const utils::blob &data) void convert(DestType &dest, const utils::blob &data)
{ {
throw std::logic_error("couldn't convert matador::utils::blob into destination type");
} }
void convert(utils::blob &dest, const utils::blob &data);
} }
#endif //QUERY_CONVERT_HPP #endif //QUERY_CONVERT_HPP
+31 -30
View File
@@ -41,16 +41,6 @@ enum class data_type_t : uint8_t {
type_unknown /*!< Data type unknown */ type_unknown /*!< Data type unknown */
}; };
enum class sql_function_t {
NONE,
COUNT,
AVG,
SUM,
MIN,
MAX
};
/** /**
* @tparam T The type of the traits * @tparam T The type of the traits
* @brief Type traits for database types * @brief Type traits for database types
@@ -63,13 +53,22 @@ template < class Type, class Enable = void >
struct data_type_traits; struct data_type_traits;
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
template <> struct data_type_traits<nullptr_t, void>
{
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_null; }
static void read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/);
static void bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/);
static void bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/);
inline static any_type create_value(const char &value) { return value; }
};
template <> struct data_type_traits<char, void> template <> struct data_type_traits<char, void>
{ {
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; } inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; }
static void read_value(query_result_reader &reader, const char *id, size_t index, char &value); static void read_value(query_result_reader &reader, const char *id, size_t index, char &value);
static void bind_value(parameter_binder &binder, size_t index, char &value); static void bind_value(parameter_binder &binder, size_t index, char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, char &value); static void bind_result_value(result_parameter_binder &binder, size_t index, char &value);
inline static any_type create_value(char &value) { return value; } inline static any_type create_value(const char &value) { return value; }
}; };
template <> struct data_type_traits<short, void> template <> struct data_type_traits<short, void>
@@ -78,7 +77,7 @@ template <> struct data_type_traits<short, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, short &value); static void read_value(query_result_reader &reader, const char *id, size_t index, short &value);
static void bind_value(parameter_binder &binder, size_t index, short &value); static void bind_value(parameter_binder &binder, size_t index, short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, short &value); static void bind_result_value(result_parameter_binder &binder, size_t index, short &value);
inline static any_type create_value(short &value) { return value; } inline static any_type create_value(const short &value) { return value; }
}; };
template <> struct data_type_traits<int, void> template <> struct data_type_traits<int, void>
@@ -87,7 +86,7 @@ template <> struct data_type_traits<int, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, int &value); static void read_value(query_result_reader &reader, const char *id, size_t index, int &value);
static void bind_value(parameter_binder &binder, size_t index, int &value); static void bind_value(parameter_binder &binder, size_t index, int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, int &value); static void bind_result_value(result_parameter_binder &binder, size_t index, int &value);
inline static any_type create_value(int &value) { return value; } inline static any_type create_value(const int &value) { return value; }
}; };
template <> struct data_type_traits<long, void> template <> struct data_type_traits<long, void>
@@ -96,7 +95,7 @@ template <> struct data_type_traits<long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, long &value); static void read_value(query_result_reader &reader, const char *id, size_t index, long &value);
static void bind_value(parameter_binder &binder, size_t index, long &value); static void bind_value(parameter_binder &binder, size_t index, long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long &value); static void bind_result_value(result_parameter_binder &binder, size_t index, long &value);
inline static any_type create_value(long &value) { return value; } inline static any_type create_value(const long &value) { return value; }
}; };
template <> struct data_type_traits<long long, void> template <> struct data_type_traits<long long, void>
@@ -105,7 +104,7 @@ template <> struct data_type_traits<long long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, long long &value); static void read_value(query_result_reader &reader, const char *id, size_t index, long long &value);
static void bind_value(parameter_binder &binder, size_t index, long long &value); static void bind_value(parameter_binder &binder, size_t index, long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long long &value); static void bind_result_value(result_parameter_binder &binder, size_t index, long long &value);
inline static any_type create_value(long long &value) { return value; } inline static any_type create_value(const long long &value) { return value; }
}; };
template <> struct data_type_traits<unsigned char, void> template <> struct data_type_traits<unsigned char, void>
@@ -114,7 +113,7 @@ template <> struct data_type_traits<unsigned char, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value); static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned char &value); static void bind_value(parameter_binder &binder, size_t index, unsigned char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value); static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value);
inline static any_type create_value(unsigned char &value) { return value; } inline static any_type create_value(const unsigned char &value) { return value; }
}; };
template <> struct data_type_traits<unsigned short, void> template <> struct data_type_traits<unsigned short, void>
@@ -123,7 +122,7 @@ template <> struct data_type_traits<unsigned short, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value); static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned short &value); static void bind_value(parameter_binder &binder, size_t index, unsigned short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value); static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value);
inline static any_type create_value(unsigned short &value) { return value; } inline static any_type create_value(const unsigned short &value) { return value; }
}; };
template <> struct data_type_traits<unsigned int, void> template <> struct data_type_traits<unsigned int, void>
@@ -132,7 +131,7 @@ template <> struct data_type_traits<unsigned int, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value); static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned int &value); static void bind_value(parameter_binder &binder, size_t index, unsigned int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value); static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value);
inline static any_type create_value(unsigned int &value) { return value; } inline static any_type create_value(const unsigned int &value) { return value; }
}; };
template <> struct data_type_traits<unsigned long, void> template <> struct data_type_traits<unsigned long, void>
@@ -141,7 +140,7 @@ template <> struct data_type_traits<unsigned long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value); static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long &value); static void bind_value(parameter_binder &binder, size_t index, unsigned long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value); static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value);
inline static any_type create_value(unsigned long &value) { return value; } inline static any_type create_value(const unsigned long &value) { return value; }
}; };
template <> struct data_type_traits<unsigned long long, void> template <> struct data_type_traits<unsigned long long, void>
@@ -150,7 +149,7 @@ template <> struct data_type_traits<unsigned long long, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value); static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long long &value); static void bind_value(parameter_binder &binder, size_t index, unsigned long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value); static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value);
inline static any_type create_value(unsigned long long &value) { return value; } inline static any_type create_value(const unsigned long long &value) { return value; }
}; };
template <> struct data_type_traits<bool, void> template <> struct data_type_traits<bool, void>
@@ -159,7 +158,7 @@ template <> struct data_type_traits<bool, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, bool &value); static void read_value(query_result_reader &reader, const char *id, size_t index, bool &value);
static void bind_value(parameter_binder &binder, size_t index, bool &value); static void bind_value(parameter_binder &binder, size_t index, bool &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, bool &value); static void bind_result_value(result_parameter_binder &binder, size_t index, bool &value);
inline static any_type create_value(bool &value) { return value; } inline static any_type create_value(const bool &value) { return value; }
}; };
template <> struct data_type_traits<float, void> template <> struct data_type_traits<float, void>
@@ -168,7 +167,7 @@ template <> struct data_type_traits<float, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, float &value); static void read_value(query_result_reader &reader, const char *id, size_t index, float &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, float &value); static void bind_result_value(result_parameter_binder &binder, size_t index, float &value);
static void bind_value(parameter_binder &binder, size_t index, float &value); static void bind_value(parameter_binder &binder, size_t index, float &value);
inline static any_type create_value(float &value) { return value; } inline static any_type create_value(const float &value) { return value; }
}; };
template <> struct data_type_traits<double, void> template <> struct data_type_traits<double, void>
@@ -177,7 +176,7 @@ template <> struct data_type_traits<double, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, double &value); static void read_value(query_result_reader &reader, const char *id, size_t index, double &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, double &value); static void bind_result_value(result_parameter_binder &binder, size_t index, double &value);
static void bind_value(parameter_binder &binder, size_t index, double &value); static void bind_value(parameter_binder &binder, size_t index, double &value);
inline static any_type create_value(double &value) { return value; } inline static any_type create_value(const double &value) { return value; }
}; };
template <> struct data_type_traits<const char*, void> template <> struct data_type_traits<const char*, void>
@@ -204,16 +203,16 @@ template <> struct data_type_traits<std::string, void>
static void read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size); static void read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size);
static void bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size = 0); static void bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
static void bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size = 0); static void bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
inline static any_type create_value(std::string &value) { return value; } inline static any_type create_value(const std::string &value) { return value; }
}; };
template <> struct data_type_traits<utils::blob, void> template <> struct data_type_traits<utils::blob, void>
{ {
inline static data_type_t builtin_type(std::size_t size) { return data_type_t::type_blob; } inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_blob; }
static void read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value); static void read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value);
static void bind_value(parameter_binder &binder, size_t index, utils::blob &value); static void bind_value(parameter_binder &binder, size_t index, utils::blob &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, utils::blob &value); static void bind_result_value(result_parameter_binder &binder, size_t index, utils::blob &value);
inline static any_type create_value(utils::blob &value) { return value; } inline static any_type create_value(const utils::blob &value) { return value; }
}; };
//template <> struct data_type_traits<matador::date> //template <> struct data_type_traits<matador::date>
@@ -233,7 +232,7 @@ template <> struct data_type_traits<utils::blob, void>
//}; //};
template < typename EnumType > template < typename EnumType >
struct data_type_traits<EnumType, typename std::enable_if<std::is_enum<EnumType>::value>::type> struct data_type_traits<EnumType, std::enable_if_t<std::is_enum_v<EnumType>>>
{ {
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; } inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; }
static void read_value(query_result_reader &reader, const char *id, size_t index, EnumType &value) static void read_value(query_result_reader &reader, const char *id, size_t index, EnumType &value)
@@ -242,13 +241,15 @@ struct data_type_traits<EnumType, typename std::enable_if<std::is_enum<EnumType>
} }
static void bind_value(parameter_binder &binder, size_t index, EnumType &value) static void bind_value(parameter_binder &binder, size_t index, EnumType &value)
{ {
data_type_traits<int>::bind_value(binder, index, (int&)value); data_type_traits<int>::bind_value(binder, index, static_cast<int&>(value));
} }
static void bind_result_value(result_parameter_binder &binder, size_t index, EnumType &value) static void bind_result_value(result_parameter_binder &binder, size_t index, EnumType &value)
{ {
data_type_traits<int>::bind_result_value(binder, index, (int&)value); data_type_traits<int>::bind_result_value(binder, index, static_cast<int&>(value));
}
static any_type create_value(const EnumType &value) {
return static_cast<int>(value);
} }
inline static any_type create_value(EnumType &value) { return (int)value; }
}; };
/// @endcond /// @endcond
+2 -2
View File
@@ -1,6 +1,7 @@
#ifndef QUERY_DIALECT_HPP #ifndef QUERY_DIALECT_HPP
#define QUERY_DIALECT_HPP #define QUERY_DIALECT_HPP
#include "matador/sql/column.hpp"
#include "matador/sql/data_type_traits.hpp" #include "matador/sql/data_type_traits.hpp"
#include <cstdint> #include <cstdint>
@@ -11,8 +12,6 @@
namespace matador::sql { namespace matador::sql {
class column;
class dialect final class dialect final
{ {
public: public:
@@ -88,6 +87,7 @@ public:
* @return The prepared string * @return The prepared string
*/ */
[[nodiscard]] std::string prepare_identifier(const column &col) const; [[nodiscard]] std::string prepare_identifier(const column &col) const;
[[nodiscard]] std::string prepare_identifier_string(const std::string &col) const;
/** /**
* Prepare string literal * Prepare string literal
+2
View File
@@ -33,6 +33,8 @@ public:
pointer get() { return obj_.get(); } pointer get() { return obj_.get(); }
const value_type* get() const { return obj_.get(); } const value_type* get() const { return obj_.get(); }
operator bool() const { return obj_.get() != nullptr; } // NOLINT(*-explicit-constructor)
private: private:
std::shared_ptr<value_type> obj_; std::shared_ptr<value_type> obj_;
}; };
@@ -0,0 +1,283 @@
#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP
#define QUERY_ENTITY_QUERY_BUILDER_HPP
#include "matador/sql/connection.hpp"
#include "matador/sql/condition.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/sql/query.hpp"
#include "matador/sql/query_intermediates.hpp"
#include "matador/sql/value.hpp"
#include "matador/utils/result.hpp"
#include <iostream>
namespace matador::sql {
struct join_columns
{
std::string join_column;
std::string inverse_join_column;
};
class join_column_collector
{
public:
template<class Type>
join_columns collect()
{
join_columns_ = {};
Type obj;
matador::utils::access::process(*this, obj);
return join_columns_;
}
template < class V >
void on_primary_key(const char * /*id*/, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0) {}
void on_primary_key(const char * /*id*/, std::string &, size_t) {}
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template<typename Type>
void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {}
template<class ContainerType>
void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {}
template<class ContainerType>
void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/)
{
join_columns_.join_column = join_column;
join_columns_.inverse_join_column = inverse_join_column;
}
template<class ContainerType>
void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const utils::foreign_attributes &/*attr*/) {}
private:
join_columns join_columns_;
};
struct entity_query_data {
std::string root_table_name;
std::string pk_column_;
std::vector<column> columns;
std::vector<join_data> joins;
std::unique_ptr<basic_condition> where_clause;
};
enum class query_build_error : std::uint8_t {
Ok = 0,
UnknownType,
MissingPrimaryKey,
UnexpectedError
};
class query_builder_exception : public std::exception
{
public:
explicit query_builder_exception(query_build_error error) : error_(error) {}
[[nodiscard]] query_build_error error() const { return error_; }
private:
const query_build_error error_;
};
class entity_query_builder
{
public:
explicit entity_query_builder(const schema &scm)
: schema_(scm) {}
template<class EntityType, typename PrimaryKeyType>
utils::result<entity_query_data, query_build_error> build(const PrimaryKeyType &pk) {
const auto info = schema_.info<EntityType>();
if (!info) {
return utils::error(query_build_error::UnknownType);
}
pk_ = pk;
table_info_stack_.push(info.value());
entity_query_data_ = { info->name };
EntityType obj;
try {
matador::utils::access::process(*this, obj);
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::error(ex.error())};
} catch (...) {
return {utils::error(query_build_error::UnexpectedError)};
}
}
template<class EntityType>
utils::result<entity_query_data, query_build_error> build() {
const auto info = schema_.info<EntityType>();
if (!info) {
return utils::error(query_build_error::UnknownType);
}
pk_ = nullptr;
table_info_stack_.push(info.value());
entity_query_data_ = { info->name };
EntityType obj;
try {
matador::utils::access::process(*this, obj);
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::error(ex.error())};
} catch (...) {
return {utils::error(query_build_error::UnexpectedError)};
}
}
template < class V >
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
{
push(id);
if (!is_root_entity()) {
return;
}
if (pk_.is_null()) {
entity_query_data_.pk_column_ = id;
} else if (pk_.is_integer()) {
entity_query_data_.where_clause = make_condition(column{table_info_stack_.top().name, id, ""} == *pk_.as<V>());
entity_query_data_.pk_column_ = id;
}
}
void on_primary_key(const char *id, std::string &, size_t);
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
push(id);
}
template<class Pointer>
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{
on_foreign_object(id, obj, attr);
}
template<class Pointer>
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{
on_foreign_object(id, obj, attr);
}
template<class ContainerType>
void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->prototype.primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column});
}
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr)
{
if (attr.fetch() != utils::fetch_type::EAGER) {
return;
}
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->prototype.primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column});
append_join({id, inverse_join_column}, {info->name, pk->name()});
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
{
if (attr.fetch() != utils::fetch_type::EAGER) {
return;
}
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename ContainerType::value_type::value_type obj;
matador::utils::access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->prototype.primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
const auto join_columns = join_column_collector_.collect<typename ContainerType::value_type::value_type>();
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_columns.inverse_join_column});
append_join({id, join_columns.join_column}, {info->name, pk->name()});
}
private:
template<class Pointer>
void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr);
void push(const std::string &column_name);
[[nodiscard]] bool is_root_entity() const;
void append_join(const column &left, const column &right);
private:
value pk_;
std::stack<table_info> table_info_stack_;
const schema &schema_;
entity_query_data entity_query_data_;
int column_index{0};
join_column_collector join_column_collector_;
};
template<class Pointer>
void entity_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr)
{
if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename Pointer::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
table_info_stack_.push(info.value());
typename Pointer::value_type obj;
matador::utils::access::process(*this, obj);
table_info_stack_.pop();
auto pk = info->prototype.primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
} else {
push(id);
}
}
}
#endif //QUERY_ENTITY_QUERY_BUILDER_HPP
+73
View File
@@ -0,0 +1,73 @@
#ifndef QUERY_FIELD_HPP
#define QUERY_FIELD_HPP
#include "matador/sql/value.hpp"
#include <optional>
#include <string>
namespace matador::sql {
class field
{
public:
explicit field(std::string name);
template<typename Type>
field(std::string name, Type value, size_t size = 0, int index = -1)
: name_(std::move(name))
, index_(index)
, value_(value, size) {}
field(std::string name, data_type_t data_type, size_t size = 0, int index = -1);
field(const field &x) = default;
field& operator=(const field &x) = default;
field(field &&x) noexcept;
field& operator=(field &&x) noexcept;
template<typename Type>
field& operator=(Type value) {
value_ = std::move(value);
return *this;
}
[[nodiscard]] const std::string& name() const;
[[nodiscard]] size_t size() const;
[[nodiscard]] int index() const;
template<class Type>
std::optional<Type> as() const
{
return value_.as<Type>();
}
[[nodiscard]] std::string str() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
friend std::ostream& operator<<(std::ostream &out, const field &col);
private:
template<class Operator>
void process(Operator &op)
{
op.on_attribute(name_.c_str(), value_, value_.size());
}
private:
friend class record;
std::string name_;
int index_{-1};
value value_;
};
}
#endif //QUERY_FIELD_HPP
+4 -4
View File
@@ -31,13 +31,13 @@ public:
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer> template<class Pointer>
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} 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 *, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
private: private:
@@ -0,0 +1,38 @@
#ifndef QUERY_HAS_MANY_TO_MANY_RELATION_HPP
#define QUERY_HAS_MANY_TO_MANY_RELATION_HPP
#include "matador/sql/entity.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace matador::sql {
template < class LocalType, class ForeignType >
class has_many_to_many_relation
{
public:
has_many_to_many_relation() = default;
has_many_to_many_relation(std::string local_name, std::string remote_name)
: local_name_(std::move(local_name))
, remote_name_(std::move(remote_name)) {}
template<class Operator>
void process(Operator &op) {
namespace field = matador::utils::access;
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes);
field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes);
}
entity<LocalType> local() const { return local_; }
entity<LocalType> remote() const { return remote_; }
private:
std::string local_name_;
std::string remote_name_;
sql::entity<LocalType> local_;
sql::entity<ForeignType> remote_;
};
}
#endif //QUERY_HAS_MANY_TO_MANY_RELATION_HPP
+4 -4
View File
@@ -40,19 +40,19 @@ public:
} }
template<class Type, template < class ... > class Pointer> template<class Type, template < class ... > class Pointer>
void on_belongs_to(const char *id, Pointer<Type> &x, utils::cascade_type) void on_belongs_to(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/)
{ {
result_.emplace_back(id, fk_value_extractor_.extract(*x)); result_.emplace_back(id, fk_value_extractor_.extract(*x));
} }
template<class Type, template < class ... > class Pointer> template<class Type, template < class ... > class Pointer>
void on_has_one(const char *id, Pointer<Type> &x, utils::cascade_type) void on_has_one(const char *id, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/)
{ {
result_.emplace_back(id, fk_value_extractor_.extract(*x)); result_.emplace_back(id, fk_value_extractor_.extract(*x));
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
private: private:
detail::fk_value_extractor fk_value_extractor_; detail::fk_value_extractor fk_value_extractor_;
+3 -1
View File
@@ -2,13 +2,15 @@
#define QUERY_KEY_VALUE_PAIR_HPP #define QUERY_KEY_VALUE_PAIR_HPP
#include "matador/sql/any_type.hpp" #include "matador/sql/any_type.hpp"
#include "matador/sql/column.hpp"
namespace matador::sql { namespace matador::sql {
class key_value_pair class key_value_pair
{ {
public: public:
key_value_pair(const std::string &name, any_type value); key_value_pair(const sql::column &col, any_type value);
key_value_pair(std::string name, any_type value);
key_value_pair(const char *name, any_type value); key_value_pair(const char *name, any_type value);
[[nodiscard]] const std::string& name() const; [[nodiscard]] const std::string& name() const;
+26
View File
@@ -0,0 +1,26 @@
#ifndef QUERY_NOOP_CONNECTION_HPP
#define QUERY_NOOP_CONNECTION_HPP
#include "matador/sql/connection_impl.hpp"
namespace matador::sql {
class noop_connection final : public connection_impl
{
public:
explicit noop_connection(const connection_info &info);
void open() override;
void close() override;
bool is_open() override;
size_t execute(const std::string &stmt) override;
std::unique_ptr<query_result_impl> fetch(const std::string &stmt) override;
std::unique_ptr<statement_impl> prepare(query_context context) 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:
bool is_open_{false};
};
}
#endif //QUERY_NOOP_CONNECTION_HPP
+32
View File
@@ -0,0 +1,32 @@
#ifndef QUERY_QUERY_HPP
#define QUERY_QUERY_HPP
#include "matador/sql/query_intermediates.hpp"
namespace matador::sql {
class connection;
class query
{
public:
explicit query(connection &db, const sql::schema &schema);
query(const query &) = delete;
query& operator=(const query &) = delete;
query_create_intermediate create();
query_drop_intermediate drop();
query_select_intermediate select(std::initializer_list<column> columns);
query_select_intermediate select(const std::vector<column>& columns);
query_select_intermediate select(std::vector<column> columns, std::initializer_list<column> additional_columns);
query_insert_intermediate insert();
query_update_intermediate update(const sql::table &table);
query_delete_intermediate remove();
private:
connection &connection_;
const sql::schema &schema_;
};
}
#endif //QUERY_QUERY_HPP
+5 -4
View File
@@ -2,6 +2,7 @@
#define QUERY_QUERY_BUILDER_HPP #define QUERY_QUERY_BUILDER_HPP
#include "matador/sql/basic_condition.hpp" #include "matador/sql/basic_condition.hpp"
#include "matador/sql/column_definition.hpp"
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/key_value_pair.hpp" #include "matador/sql/key_value_pair.hpp"
@@ -123,11 +124,11 @@ public:
query_builder& update(const std::string &table); query_builder& update(const std::string &table);
query_builder& remove(); query_builder& remove();
query_builder& table(const std::string &table, std::initializer_list<column> columns); query_builder& table(const std::string &table, std::initializer_list<column_definition> columns);
query_builder& table(const std::string &table, const std::vector<column> &columns); query_builder& table(const std::string &table, const std::vector<column_definition> &columns);
query_builder& table(const std::string &table); query_builder& table(const std::string &table);
query_builder& into(const std::string &table, std::initializer_list<std::string> column_names); query_builder& into(const std::string &table, std::initializer_list<column> column_names);
query_builder& into(const std::string &table, const std::vector<std::string> &column_names); query_builder& into(const std::string &table, const std::vector<column> &column_names);
query_builder& values(std::initializer_list<any_type> values); query_builder& values(std::initializer_list<any_type> values);
query_builder& values(const std::vector<any_type> &values); query_builder& values(const std::vector<any_type> &values);
query_builder& from(const std::string &table, const std::string &as = ""); query_builder& from(const std::string &table, const std::string &as = "");
+59
View File
@@ -0,0 +1,59 @@
#ifndef QUERY_QUERY_COMPILER_HPP
#define QUERY_QUERY_COMPILER_HPP
#include "matador/sql/query_part_visitor.hpp"
#include "matador/sql/query_parts.hpp"
#include "matador/sql/query_context.hpp"
#include <typeindex>
#include <string>
namespace matador::sql {
class dialect;
struct query_data;
class query_compiler : public query_part_visitor
{
public:
explicit query_compiler(const sql::dialect& d);
query_context compile(const query_data *data);
private:
void visit(query_select_part &select_part) override;
void visit(query_from_part &from_part) override;
void visit(query_join_part &join_part) override;
void visit(query_on_part &on_part) override;
void visit(query_where_part &where_part) override;
void visit(query_group_by_part &group_by_part) override;
void visit(query_order_by_part &order_by_part) override;
void visit(query_order_by_asc_part &order_by_asc_part) override;
void visit(query_order_by_desc_part &order_by_desc_part) override;
void visit(query_offset_part &offset_part) override;
void visit(query_limit_part &limit_part) override;
void visit(query_insert_part &insert_part) override;
void visit(query_into_part &into_part) override;
void visit(query_values_part &values_part) override;
void visit(query_update_part &update_part) override;
void visit(query_set_part &set_part) override;
void visit(query_delete_part &delete_part) override;
void visit(query_delete_from_part &delete_from_part) override;
void visit(query_create_part &create_part) override;
void visit(query_create_table_part &create_table_part) override;
void visit(query_drop_part &drop_part) override;
void visit(query_drop_table_part &drop_table_part) override;
private:
const sql::dialect &dialect_;
query_context query_;
};
}
#endif //QUERY_QUERY_COMPILER_HPP
+4 -3
View File
@@ -1,7 +1,8 @@
#ifndef QUERY_QUERY_CONTEXT_HPP #ifndef QUERY_QUERY_CONTEXT_HPP
#define QUERY_QUERY_CONTEXT_HPP #define QUERY_QUERY_CONTEXT_HPP
#include "matador/sql/record.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/table.hpp"
namespace matador::sql { namespace matador::sql {
@@ -9,8 +10,8 @@ struct query_context
{ {
std::string sql; std::string sql;
std::string command_name; std::string command_name;
std::string table_name; sql::table table{""};
record prototype; std::vector<column_definition> prototype;
std::vector<std::string> result_vars; std::vector<std::string> result_vars;
std::vector<std::string> bind_vars; std::vector<std::string> bind_vars;
}; };
+23
View File
@@ -0,0 +1,23 @@
#ifndef QUERY_QUERY_DATA_HPP
#define QUERY_QUERY_DATA_HPP
#include "matador/sql/query_part.hpp"
#include "matador/sql/table.hpp"
#include <memory>
#include <vector>
namespace matador::sql {
class query_part;
struct query_data
{
// SqlCommands command;
std::vector<std::unique_ptr<query_part>> parts;
std::vector<column_definition> columns;
};
}
#endif //QUERY_QUERY_DATA_HPP
+24
View File
@@ -0,0 +1,24 @@
#ifndef QUERY_QUERY_HELPER_HPP
#define QUERY_QUERY_HELPER_HPP
#include "matador/utils/macro_map.hpp"
#include "matador/sql/table.hpp"
#include "matador/sql/column.hpp"
#include <string>
#include <ostream>
#define FIELD(x) const sql::column x{*this, #x, ""};
#define QUERY_HELPER(C, ...) \
namespace matador::qh { \
namespace internal { \
struct C##_query : sql::table { \
C##_query() : table(#C) {} \
MAP(FIELD, __VA_ARGS__) \
}; } \
static const internal:: C##_query C; \
}
#endif //QUERY_QUERY_HELPER_HPP
+160 -84
View File
@@ -1,17 +1,17 @@
#ifndef QUERY_QUERY_INTERMEDIATES_HPP #ifndef QUERY_QUERY_INTERMEDIATES_HPP
#define QUERY_QUERY_INTERMEDIATES_HPP #define QUERY_QUERY_INTERMEDIATES_HPP
#include "matador/sql/column.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/column_name_generator.hpp"
#include "matador/sql/key_value_generator.hpp" #include "matador/sql/key_value_generator.hpp"
#include "matador/sql/key_value_pair.hpp" #include "matador/sql/key_value_pair.hpp"
#include "matador/sql/placeholder_generator.hpp" #include "matador/sql/placeholder_generator.hpp"
#include "matador/sql/query_builder.hpp"
#include "matador/sql/query_result.hpp" #include "matador/sql/query_result.hpp"
#include "matador/sql/query_data.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include "matador/sql/statement.hpp" #include "matador/sql/statement.hpp"
#include "matador/sql/table_repository.hpp" #include "matador/sql/schema.hpp"
#include "matador/sql/value_extractor.hpp" #include "matador/sql/value_extractor.hpp"
#include <string> #include <string>
@@ -21,26 +21,36 @@ namespace matador::sql {
class basic_condition; class basic_condition;
class connection; class connection;
class query_intermediate class basic_query_intermediate
{ {
public: public:
query_intermediate(connection &db, query_builder &query); explicit basic_query_intermediate(connection &db, const sql::schema &schema);
protected: protected:
connection &connection_; connection &connection_;
query_builder &builder_; const sql::schema &schema_;
}; };
class query_execute_finish : public query_intermediate class query_intermediate : public basic_query_intermediate
{
public:
query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr<query_data> &data);
protected:
std::shared_ptr<query_data> data_;
};
class query_execute : public query_intermediate
{ {
public: public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
size_t execute(); size_t execute();
statement prepare(); statement prepare();
[[nodiscard]] query_context build() const;
}; };
class query_select_finish : public query_intermediate class query_select : public query_intermediate
{ {
protected: protected:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
@@ -51,52 +61,73 @@ public:
{ {
return query_result<Type>(fetch()); return query_result<Type>(fetch());
} }
query_result<record> fetch_all(); query_result<record> fetch_all();
record fetch_one();
template<typename Type> template < class Type >
Type fetch_value() std::unique_ptr<Type> fetch_one()
{ {
return fetch_one().at(0).as<Type>(); auto result = query_result<Type>(fetch());
auto first = result.begin();
if (first == result.end()) {
return nullptr;
}
return std::unique_ptr<Type>{first.release()};
}
std::optional<record> fetch_one();
template<typename Type>
std::optional<Type> fetch_value()
{
const auto result = fetch_one();
if (result.has_value()) {
return result.value().at(0).as<Type>().value();
}
return std::nullopt;
} }
statement prepare(); statement prepare();
[[nodiscard]] query_context build() const;
private: private:
std::unique_ptr<query_result_impl> fetch(); std::unique_ptr<query_result_impl> fetch();
}; };
class query_limit_intermediate : public query_select_finish class query_offset_intermediate;
class query_limit_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_offset_intermediate offset(size_t offset);
}; };
class query_offset_intermediate : public query_intermediate class query_offset_intermediate : public query_select
{ {
public: public:
using query_intermediate::query_intermediate; using query_select::query_select;
query_limit_intermediate limit(size_t limit); query_limit_intermediate limit(size_t limit);
}; };
class query_order_direction_intermediate : public query_select_finish class query_order_direction_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_offset_intermediate offset(size_t offset);
query_limit_intermediate limit(size_t limit); query_limit_intermediate limit(size_t limit);
}; };
class query_order_by_intermediate; class query_order_by_intermediate;
class query_group_by_intermediate : public query_select_finish class query_group_by_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_order_by_intermediate order_by(const std::string &name); query_order_by_intermediate order_by(const column &col);
}; };
class query_order_by_intermediate : public query_intermediate class query_order_by_intermediate : public query_intermediate
@@ -108,41 +139,84 @@ public:
query_order_direction_intermediate desc(); query_order_direction_intermediate desc();
}; };
class query_where_intermediate : public query_select_finish class query_where_intermediate : public query_select
{ {
public: public:
using query_select_finish::query_select_finish; using query_select::query_select;
query_group_by_intermediate group_by(const std::string &name); query_group_by_intermediate group_by(const column &col);
query_order_by_intermediate order_by(const std::string &name); query_order_by_intermediate order_by(const column &col);
}; };
class query_from_intermediate : public query_select_finish class query_join_intermediate;
{
public:
using query_select_finish::query_select_finish;
query_where_intermediate where(const basic_condition &cond); struct join_data
query_group_by_intermediate group_by(const std::string &name); {
query_order_by_intermediate order_by(const std::string &name); table join_table;
std::unique_ptr<basic_condition> condition;
}; };
class query_start_intermediate class query_from_intermediate : public query_select
{ {
public: public:
explicit query_start_intermediate(connection &s); using query_select::query_select;
query_join_intermediate join_left(const 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 column &col);
query_order_by_intermediate order_by(const column &col);
private:
query_where_intermediate where_clause(std::unique_ptr<basic_condition> &&cond);
};
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);
};
class query_start_intermediate : public basic_query_intermediate
{
public:
explicit query_start_intermediate(connection &db, const sql::schema &schema);
protected: protected:
connection &connection_; std::shared_ptr<query_data> data_ { std::make_shared<query_data>() };
query_builder builder_;
}; };
class query_select_intermediate : public query_start_intermediate class query_select_intermediate : public query_start_intermediate
{ {
public: public:
query_select_intermediate(connection &s, const std::vector<column>& columns); query_select_intermediate(connection &db, const sql::schema &schema, const std::vector<column>& columns);
query_from_intermediate from(const std::string &table, const std::string &as = ""); query_from_intermediate from(const table& t);
}; };
template < class Type > template < class Type >
@@ -159,109 +233,111 @@ class query_into_intermediate : public query_intermediate
public: public:
using query_intermediate::query_intermediate; using query_intermediate::query_intermediate;
query_execute_finish values(std::initializer_list<any_type> values); query_execute values(std::initializer_list<any_type> values);
query_execute values(std::vector<any_type> &&values);
template<class Type> template<class Type>
query_execute_finish values() query_execute values()
{ {
Type obj; Type obj;
return {connection_, builder_.values(as_placeholder(obj))}; return values(std::move(as_placeholder(obj)));
} }
template<class Type> template<class Type>
query_execute_finish values(const Type &obj) query_execute values(const Type &obj)
{ {
return {connection_, builder_.values(value_extractor::extract(obj))}; return values(std::move(value_extractor::extract(obj)));
} }
}; };
class query_create_intermediate : public query_start_intermediate class query_create_intermediate : public query_start_intermediate
{ {
public: public:
explicit query_create_intermediate(connection &db); explicit query_create_intermediate(connection &db, const sql::schema &schema);
query_execute_finish table(const std::string &table, std::initializer_list<column> columns); query_execute table(const sql::table &table, std::initializer_list<column_definition> columns);
query_execute table(const sql::table &table, const std::vector<column_definition> &columns);
template<class Type> template<class Type>
query_execute_finish table(const std::string &table_name) query_execute table(const sql::table &table)
{ {
if (!tables()->exists<Type>()) { return this->table(table, column_definition_generator::generate<Type>(schema_));
tables()->attach<Type>(table_name);
}
return {connection_, builder_.table(table_name, column_generator::generate<Type>(*tables()))};
} }
private:
std::shared_ptr<table_repository> tables() const;
}; };
class query_drop_intermediate : query_start_intermediate class query_drop_intermediate : query_start_intermediate
{ {
public: public:
explicit query_drop_intermediate(connection &s); explicit query_drop_intermediate(connection &db, const sql::schema &schema);
query_execute_finish table(const std::string &table); query_execute table(const sql::table &table);
}; };
class query_insert_intermediate : public query_start_intermediate class query_insert_intermediate : public query_start_intermediate
{ {
public: public:
explicit query_insert_intermediate(connection &s); explicit query_insert_intermediate(connection &db, const sql::schema &schema);
query_into_intermediate into(const std::string &table, std::initializer_list<std::string> column_names); query_into_intermediate into(const sql::table &table, std::initializer_list<column> column_names);
template<class Type> query_into_intermediate into(const sql::table &table, std::vector<column> &&column_names);
query_into_intermediate into(const std::string &table) query_into_intermediate into(const sql::table &table);
{
return {connection_, builder_.into(table, column_name_generator::generate<Type>())};
}
template<class Type>
query_execute_finish into(const std::string &table, const Type &obj)
{
return {connection_, builder_.into(table, column_name_generator::generate<Type>())
.values(value_extractor::extract(obj))};
}
}; };
class query_execute_where_intermediate : public query_execute_finish class query_execute_where_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
query_execute_finish limit(int limit); query_order_by_intermediate order_by(const column &col);
}; };
class query_set_intermediate : public query_execute_finish class query_set_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
query_execute_where_intermediate where(const basic_condition &cond); 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);
}; };
class query_update_intermediate : public query_start_intermediate class query_update_intermediate : public query_start_intermediate
{ {
public: public:
query_update_intermediate(connection &s, const std::string& table_name); query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table);
query_set_intermediate set(std::initializer_list<key_value_pair> columns); query_set_intermediate set(std::initializer_list<key_value_pair> columns);
query_set_intermediate set(std::vector<key_value_pair> &&columns);
template<class Type> template<class Type>
query_set_intermediate set(const Type &obj) query_set_intermediate set(const Type &obj)
{ {
return {connection_, builder_.set(key_value_generator::generate(obj))}; return set(key_value_generator::generate(obj));
} }
}; };
class query_delete_from_intermediate : public query_execute_finish class query_delete_from_intermediate : public query_execute
{ {
public: public:
using query_execute_finish::query_execute_finish; using query_execute::query_execute;
query_execute_where_intermediate where(const basic_condition &cond); 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);
}; };
class query_delete_intermediate : public query_start_intermediate class query_delete_intermediate : public query_start_intermediate
{ {
public: public:
explicit query_delete_intermediate(connection &s); explicit query_delete_intermediate(connection &db, const sql::schema &schema);
query_delete_from_intermediate from(const std::string &table); query_delete_from_intermediate from(const sql::table &table);
}; };
} }
+26
View File
@@ -0,0 +1,26 @@
#ifndef QUERY_QUERY_PART_HPP
#define QUERY_QUERY_PART_HPP
#include "matador/sql/dialect.hpp"
namespace matador::sql {
class query_part_visitor;
class query_part
{
protected:
explicit query_part(sql::dialect::token_t token);
public:
virtual ~query_part() = default;
virtual void accept(query_part_visitor &visitor) = 0;
[[nodiscard]] dialect::token_t token() const;
protected:
sql::dialect::token_t token_;
};
}
#endif //QUERY_QUERY_PART_HPP
@@ -0,0 +1,65 @@
#ifndef QUERY_QUERY_PART_VISITOR_HPP
#define QUERY_QUERY_PART_VISITOR_HPP
namespace matador::sql {
class query_select_part;
class query_from_part;
class query_join_part;
class query_on_part;
class query_where_part;
class query_group_by_part;
class query_order_by_part;
class query_order_by_asc_part;
class query_order_by_desc_part;
class query_offset_part;
class query_limit_part;
class query_insert_part;
class query_into_part;
class query_values_part;
class query_update_part;
class query_set_part;
class query_delete_part;
class query_delete_from_part;
class query_create_part;
class query_create_table_part;
class query_drop_part;
class query_drop_table_part;
class query_part_visitor
{
public:
virtual ~query_part_visitor() = default;
virtual void visit(query_select_part &select_part) = 0;
virtual void visit(query_from_part &from_part) = 0;
virtual void visit(query_join_part &join_part) = 0;
virtual void visit(query_on_part &on_part) = 0;
virtual void visit(query_where_part &where_part) = 0;
virtual void visit(query_group_by_part &group_by_part) = 0;
virtual void visit(query_order_by_part &order_by_part) = 0;
virtual void visit(query_order_by_asc_part &order_by_asc_part) = 0;
virtual void visit(query_order_by_desc_part &order_by_desc_part) = 0;
virtual void visit(query_offset_part &offset_part) = 0;
virtual void visit(query_limit_part &limit_part) = 0;
virtual void visit(query_insert_part &insert_part) = 0;
virtual void visit(query_into_part &into_part) = 0;
virtual void visit(query_values_part &values_part) = 0;
virtual void visit(query_update_part &update_part) = 0;
virtual void visit(query_set_part &set_part) = 0;
virtual void visit(query_delete_part &delete_part) = 0;
virtual void visit(query_delete_from_part &delete_from_part) = 0;
virtual void visit(query_create_part &create_part) = 0;
virtual void visit(query_create_table_part &create_table_part) = 0;
virtual void visit(query_drop_part &drop_part) = 0;
virtual void visit(query_drop_table_part &drop_table_part) = 0;
};
}
#endif //QUERY_QUERY_PART_VISITOR_HPP
+324
View File
@@ -0,0 +1,324 @@
#ifndef QUERY_QUERY_PARTS_HPP
#define QUERY_QUERY_PARTS_HPP
#include "matador/sql/basic_condition.hpp"
#include "matador/sql/query_part_visitor.hpp"
#include "matador/sql/column.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 <memory>
namespace matador::sql {
class basic_condition;
/**
* Represents the SQL SELECT part
*/
class query_select_part : public query_part
{
public:
explicit query_select_part(std::vector<column> columns);
void accept(query_part_visitor &visitor) override;
[[nodiscard]] const std::vector<column>& columns() const;
private:
std::vector<column> columns_;
};
/**
* Represents the SQL FROM part
*/
class query_from_part : public query_part
{
public:
explicit query_from_part(sql::table t);
[[nodiscard]] const sql::table& table() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
};
class query_join_part : public query_part
{
public:
explicit query_join_part(sql::table t);
[[nodiscard]] const sql::table& table() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
};
class query_on_part : public query_part
{
public:
template < class Condition >
explicit query_on_part(const Condition &cond)
: query_part(dialect::token_t::ON)
, condition_(new Condition(cond)) {}
explicit query_on_part(std::unique_ptr<basic_condition> &&cond);
[[nodiscard]] const basic_condition& condition() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::unique_ptr<basic_condition> condition_;
};
class query_where_part : public query_part
{
public:
template < class Condition >
explicit query_where_part(const Condition &cond)
: query_part(dialect::token_t::WHERE)
, condition_(new Condition(cond)) {}
explicit query_where_part(std::unique_ptr<basic_condition> &&cond);
[[nodiscard]] const basic_condition& condition() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::unique_ptr<basic_condition> condition_;
};
class query_table_name_part : public query_part
{
protected:
explicit query_table_name_part(sql::dialect::token_t token, std::string table_name);
protected:
std::string table_name_;
};
class query_group_by_part : public query_part
{
public:
explicit query_group_by_part(sql::column col);
[[nodiscard]] const sql::column& column() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::column column_;
};
class query_order_by_part : public query_part
{
public:
explicit query_order_by_part(sql::column col);
[[nodiscard]] const sql::column& column() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::column column_;
};
class query_order_by_asc_part : public query_part
{
public:
query_order_by_asc_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_order_by_desc_part : public query_part
{
public:
query_order_by_desc_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_offset_part : public query_part
{
public:
explicit query_offset_part(size_t offset);
[[nodiscard]] size_t offset() const;
private:
void accept(query_part_visitor &visitor) override;
private:
size_t offset_;
};
class query_limit_part : public query_part
{
public:
explicit query_limit_part(size_t limit);
[[nodiscard]] size_t limit() const;
private:
void accept(query_part_visitor &visitor) override;
private:
size_t limit_;
};
class query_insert_part : public query_part
{
public:
query_insert_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_into_part : public query_part
{
public:
query_into_part(sql::table t, std::vector<sql::column> columns);
[[nodiscard]] const sql::table& table() const;
[[nodiscard]] const std::vector<column>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
std::vector<column> columns_;
};
/**
* Represents the SQL VALUES part
*/
class query_values_part : public query_part
{
public:
explicit query_values_part(std::vector<any_type> &&values);
[[nodiscard]] const std::vector<any_type>& values() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::vector<any_type> values_;
};
class query_update_part : public query_part
{
public:
explicit query_update_part(sql::table table);
[[nodiscard]] const sql::table& table() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
};
class query_set_part : public query_part
{
public:
explicit query_set_part(const std::vector<sql::key_value_pair>& key_value_pairs);
[[nodiscard]] const std::vector<sql::key_value_pair>& key_values() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::vector<sql::key_value_pair> key_value_pairs_;
};
class query_delete_part : public query_part
{
public:
query_delete_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_delete_from_part : public query_part
{
public:
query_delete_from_part(sql::table table);
[[nodiscard]] const sql::table& table() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
};
class query_create_part : public query_part
{
public:
query_create_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_create_table_part : public query_part
{
public:
query_create_table_part(sql::table table, std::vector<sql::column_definition> columns);
[[nodiscard]] const sql::table& table() const;
[[nodiscard]] const std::vector<sql::column_definition>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
std::vector<sql::column_definition> columns_;
};
class query_drop_part : public query_part
{
public:
query_drop_part();
private:
void accept(query_part_visitor &visitor) override;
};
class query_drop_table_part : public query_part
{
public:
explicit query_drop_table_part(sql::table table);
[[nodiscard]] const sql::table& table() const;
private:
void accept(query_part_visitor &visitor) override;
private:
sql::table table_;
};
}
#endif //QUERY_QUERY_PARTS_HPP
+7 -6
View File
@@ -1,14 +1,15 @@
#ifndef QUERY_QUERY_RESULT_HPP #ifndef QUERY_QUERY_RESULT_HPP
#define QUERY_QUERY_RESULT_HPP #define QUERY_QUERY_RESULT_HPP
#include <functional>
#include "matador/sql/query_result_impl.hpp" #include "matador/sql/query_result_impl.hpp"
#include <functional>
#include <memory> #include <memory>
namespace matador::sql { namespace matador::sql {
class record;
template < typename Type > template < typename Type >
class query_result; class query_result;
@@ -108,13 +109,13 @@ private:
namespace detail { namespace detail {
template < typename Type > template < typename Type >
Type* create_prototype(const record &/*prototype*/) Type* create_prototype(const std::vector<column_definition> &/*prototype*/)
{ {
return new Type{}; return new Type{};
} }
template <> template <>
record* create_prototype<record>(const record &prototype); record* create_prototype<record>(const std::vector<column_definition> &prototype);
} }
@@ -129,7 +130,7 @@ public:
explicit query_result(std::unique_ptr<query_result_impl> impl) explicit query_result(std::unique_ptr<query_result_impl> impl)
: impl_(std::move(impl)) {} : impl_(std::move(impl)) {}
query_result(std::unique_ptr<query_result_impl> impl, record record_prototype) query_result(std::unique_ptr<query_result_impl> impl, std::vector<column_definition> record_prototype)
: record_prototype_(std::move(record_prototype)) : record_prototype_(std::move(record_prototype))
, impl_(std::move(impl)) {} , impl_(std::move(impl)) {}
@@ -152,7 +153,7 @@ private:
} }
private: private:
record record_prototype_; std::vector<column_definition> record_prototype_;
std::unique_ptr<query_result_impl> impl_; std::unique_ptr<query_result_impl> impl_;
}; };
+28 -15
View File
@@ -3,10 +3,11 @@
#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/sql/any_type.hpp" #include "matador/sql/any_type.hpp"
#include "matador/sql/query_result_reader.hpp" #include "matador/sql/query_result_reader.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/data_type_traits.hpp" #include "matador/sql/data_type_traits.hpp"
#include <memory> #include <memory>
@@ -14,6 +15,8 @@
namespace matador::sql { namespace matador::sql {
class value;
namespace detail { namespace detail {
class pk_reader class pk_reader
{ {
@@ -35,14 +38,16 @@ public:
template < class Type > template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template < class Pointer > template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template < class Pointer > template < class Pointer >
void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} 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 *, utils::cascade_type) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many_to_many(const char *id, ContainerType &c, 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 *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private: private:
size_t column_index_{}; size_t column_index_{};
@@ -54,7 +59,7 @@ private:
class query_result_impl class query_result_impl
{ {
public: public:
query_result_impl(std::unique_ptr<query_result_reader> &&reader, record prototype); query_result_impl(std::unique_ptr<query_result_reader> &&reader, std::vector<column_definition> prototype);
template<typename ValueType> template<typename ValueType>
void on_primary_key(const char *id, ValueType &value, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0) void on_primary_key(const char *id, ValueType &value, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
@@ -71,29 +76,37 @@ public:
} }
void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, value &val, const utils::field_attributes &attr = utils::null_attributes);
template < class Pointer > template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &x, utils::cascade_type) void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr)
{ {
if (!x.get()) { if (!x.get()) {
x.reset(new typename Pointer::value_type); x.reset(new typename Pointer::value_type);
} }
pk_reader_.read(*x, column_index_++); if (attr.fetch() == utils::fetch_type::LAZY) {
pk_reader_.read(*x, column_index_++);
} else {
utils::access::process(*this, *x);
}
} }
template < class Pointer > template < class Pointer >
void on_has_one(const char * /*id*/, Pointer &x, utils::cascade_type) void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr)
{ {
if (!x.get()) { if (!x.get()) {
x.reset(new typename Pointer::value_type); x.reset(new typename Pointer::value_type);
} }
pk_reader_.read(*x, column_index_++); if (attr.fetch() == utils::fetch_type::LAZY) {
pk_reader_.read(*x, column_index_++);
} else {
utils::access::process(*this, *x);
}
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
template<class Type> template<class Type>
void bind(const Type &) {} void bind(const Type &) {}
@@ -109,11 +122,11 @@ public:
return true; return true;
} }
[[nodiscard]] const record& prototype() const; [[nodiscard]] const std::vector<column_definition>& prototype() const;
protected: protected:
size_t column_index_ = 0; size_t column_index_ = 0;
record prototype_; std::vector<column_definition> prototype_;
std::unique_ptr<query_result_reader> reader_; std::unique_ptr<query_result_reader> reader_;
detail::pk_reader pk_reader_; detail::pk_reader pk_reader_;
}; };
+3 -1
View File
@@ -6,6 +6,8 @@
namespace matador::sql { namespace matador::sql {
class value;
class query_result_reader class query_result_reader
{ {
public: public:
@@ -34,7 +36,7 @@ public:
virtual void read_value(const char *id, size_t index, std::string &value); virtual void read_value(const char *id, size_t index, std::string &value);
virtual void read_value(const char *id, size_t index, std::string &value, size_t s); virtual void read_value(const char *id, size_t index, std::string &value, size_t s);
virtual void read_value(const char *id, size_t index, utils::blob &value); virtual void read_value(const char *id, size_t index, utils::blob &value);
virtual void read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size); virtual void read_value(const char *id, size_t index, value &val, size_t size);
}; };
} }
+22 -27
View File
@@ -3,27 +3,30 @@
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/sql/column.hpp" #include "matador/sql/field.hpp"
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
namespace matador::sql { namespace matador::sql {
struct column;
class record class record
{ {
private: private:
using column_by_index = std::vector<column>; using field_ref = std::reference_wrapper<field>;
using column_index_pair = std::pair<std::reference_wrapper<column>, size_t>; using field_by_index = std::vector<field_ref>;
using column_by_name_map = std::unordered_map<std::string, column_index_pair>; using field_index_pair = std::pair<field, field_by_index::difference_type>;
using field_by_name_map = std::unordered_map<std::string, field_index_pair>;
public: public:
using iterator = column_by_index::iterator; using iterator = field_by_index::iterator;
using const_iterator = column_by_index::const_iterator; using const_iterator = field_by_index::const_iterator;
record() = default; record() = default;
record(std::initializer_list<column> columns); record(std::initializer_list<field> columns);
explicit record(const std::vector<column> &columns); explicit record(const std::vector<field> &columns);
record(const record &x); record(const record &x);
record& operator=(const record &x); record& operator=(const record &x);
record(record&&) noexcept = default; record(record&&) noexcept = default;
@@ -33,25 +36,17 @@ public:
template<class Operator> template<class Operator>
void process(Operator &op) void process(Operator &op)
{ {
for(auto &col : columns_) { for(auto &f : fields_) {
col.process(op); f.get().process(op);
} }
} }
template < typename Type >
void append(const std::string &name, long size = -1)
{
append(make_column<Type>(name, size));
}
void append(column col); void append(const field &col);
[[nodiscard]] bool has_primary_key() const; [[nodiscard]] const std::vector<field_ref>& columns() const;
[[nodiscard]] const column& primary_key() const;
[[nodiscard]] const std::vector<column>& columns() const; [[nodiscard]] const field& at(const column &col) const;
[[nodiscard]] const field& at(size_t index) const;
[[nodiscard]] const column& at(const std::string &name) const;
[[nodiscard]] const column& at(size_t index) const;
iterator find(const std::string &column_name); iterator find(const std::string &column_name);
[[nodiscard]] const_iterator find(const std::string &column_name) const; [[nodiscard]] const_iterator find(const std::string &column_name) const;
@@ -68,17 +63,17 @@ public:
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty() const;
void clear(); void clear();
[[nodiscard]] bool unknown() const; // [[nodiscard]] bool unknown() const;
private: private:
void init(); void init();
void add_to_map(column &col, size_t index); void add_to_map(field &col, size_t index);
private: private:
column_by_index columns_; field_by_index fields_;
column_by_name_map columns_by_name_; field_by_name_map fields_by_name_;
int pk_index_{-1}; // int pk_index_{-1};
}; };
} }
+89
View File
@@ -0,0 +1,89 @@
#ifndef QUERY_SCHEMA_HPP
#define QUERY_SCHEMA_HPP
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/table_definition.hpp"
#include <optional>
#include <string>
#include <typeindex>
#include <unordered_map>
namespace matador::sql {
class connection;
struct table_info
{
std::string name;
table_definition prototype;
};
class schema
{
public:
using repository = std::unordered_map<std::type_index, table_info>;
using repository_by_name = std::unordered_map<std::string, std::reference_wrapper<table_info>>;
using iterator = repository::iterator;
using const_iterator = repository::const_iterator;
schema() = delete;
explicit schema(std::string name);
schema(const schema&) = delete;
schema& operator=(const schema&) = delete;
schema(schema&&) noexcept = default;
schema& operator=(schema&&) noexcept = default;
[[nodiscard]] std::string name() const;
void create(connection &c);
template<typename Type>
const table_info& attach(const std::string &table_name)
{
return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_definition_generator::generate<Type>(*this)}});
}
const table_info& attach(std::type_index ti, const table_info& table);
template<typename Type>
[[nodiscard]] std::optional<table_info> info() const
{
return info(std::type_index(typeid(Type)));
}
[[nodiscard]] std::optional<table_info> info(std::type_index ti) const;
[[nodiscard]] std::optional<table_info> info(const std::string &name) const;
template<typename Type>
[[nodiscard]] std::pair<std::string, std::string> reference() const
{
return reference(std::type_index(typeid(Type)));
}
[[nodiscard]] std::pair<std::string, std::string> reference(const std::type_index &ti) const;
template<typename Type>
[[nodiscard]] bool exists() const
{
return exists(std::type_index(typeid(Type)));
}
[[nodiscard]] bool exists(const std::type_index &ti) const;
iterator begin();
[[nodiscard]] const_iterator begin() const;
iterator end();
[[nodiscard]] const_iterator end() const;
[[nodiscard]] bool empty() const;
private:
std::string name_;
repository repository_;
repository_by_name repository_by_name_;
};
}
#endif //QUERY_SCHEMA_HPP
+131 -16
View File
@@ -1,13 +1,12 @@
#ifndef QUERY_SESSION_HPP #ifndef QUERY_SESSION_HPP
#define QUERY_SESSION_HPP #define QUERY_SESSION_HPP
#include "matador/sql/column_generator.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp" #include "matador/sql/connection_pool.hpp"
#include "matador/sql/query_builder.hpp" #include "matador/sql/entity.hpp"
#include "matador/sql/query_intermediates.hpp" #include "matador/sql/entity_query_builder.hpp"
#include "matador/sql/statement.hpp" #include "matador/sql/statement.hpp"
#include "matador/sql/table_repository.hpp" #include "matador/sql/schema.hpp"
#include <unordered_map> #include <unordered_map>
@@ -15,41 +14,157 @@ namespace matador::sql {
class dialect; class dialect;
enum class session_error {
Ok = 0,
NoConnectionAvailable,
UnknownType,
FailedToBuildQuery,
FailedToFindObject
};
class session class session
{ {
public: public:
explicit session(connection_pool<connection> &pool); explicit session(connection_pool<connection> &pool);
template<typename Type>
void attach(const std::string &table_name);
void create_schema();
template<typename Type>
entity<Type> insert(Type *obj);
template< class Type, typename... Args >
entity<Type> insert(Args&&... args) {
return insert(new Type(std::forward<Args>(args)...));
}
template<typename Type, typename PrimaryKeyType>
utils::result<entity<Type>, session_error> find(const PrimaryKeyType &pk) {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::error(session_error::NoConnectionAvailable);
}
auto info = schema_->info<Type>();
if (!info) {
return utils::error(session_error::UnknownType);
}
entity_query_builder eqb(*schema_);
auto data = eqb.build<Type>(pk);
if (!data.is_ok()) {
return utils::error(session_error::FailedToBuildQuery);
}
auto obj = build_select_query(c, data.release()).template fetch_one<Type>();
if (!obj) {
return utils::error(session_error::FailedToFindObject);
}
return utils::ok(entity<Type>{ obj.release() });
}
template<typename Type>
utils::result<query_result<Type>, session_error> find() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::error(session_error::NoConnectionAvailable);
}
auto info = schema_->info<Type>();
if (!info) {
return utils::error(session_error::UnknownType);
}
entity_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::error(session_error::FailedToBuildQuery);
}
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
}
template<typename Type>
utils::result<query_from_intermediate, session_error> select() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::error(session_error::NoConnectionAvailable);
}
auto info = schema_->info<Type>();
if (!info) {
return utils::error(session_error::UnknownType);
}
entity_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::error(session_error::FailedToBuildQuery);
}
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
}
template<typename Type>
void drop_table();
void drop_table(const std::string &table_name);
[[nodiscard]] query_result<record> fetch(const query_context &q) const; [[nodiscard]] query_result<record> fetch(const query_context &q) const;
// [[nodiscard]] query_result<record> fetch(const std::string &sql) const; // [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
[[nodiscard]] size_t execute(const std::string &sql) const; [[nodiscard]] size_t execute(const std::string &sql) const;
statement prepare(query_context q) const; statement prepare(query_context q) const;
record describe_table(const std::string &table_name) const; std::vector<sql::column_definition> describe_table(const std::string &table_name) const;
bool table_exists(const std::string &table_name) const; bool table_exists(const std::string &table_name) const;
template<typename Type>
void attach(const std::string &table_name)
{
table_repository_.attach<Type>(table_name);
}
[[nodiscard]] const table_repository& tables() const;
const class dialect& dialect() const; const class dialect& dialect() const;
private: private:
friend class query_select_finish; friend class query_select;
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const; [[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
query_select build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const;
private: private:
connection_pool<connection> &pool_; connection_pool<connection> &pool_;
const class dialect &dialect_; const class dialect &dialect_;
table_repository table_repository_; std::unique_ptr<schema> schema_;
mutable std::unordered_map<std::string, record> prototypes_; mutable std::unordered_map<std::string, table_definition> prototypes_;
}; };
template<typename Type>
void session::attach(const std::string &table_name)
{
schema_->attach<Type>(table_name);
}
template<typename Type>
entity<Type> session::insert(Type *obj)
{
auto c = pool_.acquire();
auto info = schema_->info<Type>();
if (!info) {
return {};
}
c->query(*schema_)
.insert()
.into(info->name, column_generator::generate<Type>(*schema_, true))
.values(*obj)
.execute();
return entity{obj};
}
template<typename Type>
void session::drop_table()
{
auto info = schema_->info<Type>();
if (info) {
return drop_table(info.name);
}
}
} }
#endif //QUERY_SESSION_HPP #endif //QUERY_SESSION_HPP
+41
View File
@@ -0,0 +1,41 @@
#ifndef QUERY_TABLE_HPP
#define QUERY_TABLE_HPP
#include "matador/sql/column.hpp"
#include <typeindex>
#include <string>
#include <vector>
namespace matador::sql {
struct table
{
table(const char *name, std::string as = "") // NOLINT(*-explicit-constructor)
: name(name), alias(std::move(as)) {}
table(std::string name, std::string as = "") // NOLINT(*-explicit-constructor)
: name(std::move(name))
, alias(std::move(as)) {}
table(std::string name, std::string as, const std::vector<column> &columns)
: name(std::move(name))
, alias(std::move(as))
, columns(columns) {}
table& as(const std::string &a) {
alias = a;
return *this;
}
[[nodiscard]] table as(const std::string &a) const {
return { name, a, columns };
}
std::string name;
std::string alias;
std::vector<column> columns;
};
}
#endif //QUERY_TABLE_HPP
+73
View File
@@ -0,0 +1,73 @@
#ifndef QUERY_TABLE_DEFINITION_HPP
#define QUERY_TABLE_DEFINITION_HPP
#include "matador/sql/column.hpp"
#include "matador/sql/column_definition.hpp"
#include <unordered_map>
namespace matador::sql {
class table_definition final
{
private:
using column_by_index = std::vector<column_definition>;
using column_index_pair = std::pair<std::reference_wrapper<column_definition>, column_by_index::difference_type>;
using column_by_name_map = std::unordered_map<std::string, column_index_pair>;
public:
using iterator = column_by_index::iterator;
using const_iterator = column_by_index::const_iterator;
table_definition() = default;
table_definition(std::initializer_list<column_definition> columns);
explicit table_definition(const std::vector<column_definition> &columns);
table_definition(const table_definition &x);
table_definition& operator=(const table_definition &x);
table_definition(table_definition&&) noexcept = default;
table_definition& operator=(table_definition&&) noexcept = default;
~table_definition() = default;
[[nodiscard]] bool has_primary_key() const;
[[nodiscard]] std::optional<column_definition> primary_key() const;
template < typename Type >
void append(const std::string &name, long size = -1)
{
append(make_column<Type>(name, size));
}
void append(column_definition col);
[[nodiscard]] const std::vector<column_definition>& columns() const;
[[nodiscard]] const column_definition& at(const column &col) const;
[[nodiscard]] const column_definition& at(size_t index) const;
iterator find(const std::string &column_name);
[[nodiscard]] const_iterator find(const std::string &column_name) const;
iterator begin();
[[nodiscard]] const_iterator begin() const;
[[nodiscard]] const_iterator cbegin() const;
iterator end();
[[nodiscard]] const_iterator end() const;
[[nodiscard]] const_iterator cend() const;
[[nodiscard]] size_t size() const;
[[nodiscard]] bool empty() const;
void clear();
private:
void init();
void add_to_map(column_definition &col, size_t index);
private:
column_by_index columns_;
column_by_name_map columns_by_name_;
int pk_index_{-1};
};
}
#endif //QUERY_TABLE_DEFINITION_HPP
-62
View File
@@ -1,62 +0,0 @@
#ifndef QUERY_TABLE_REPOSITORY_HPP
#define QUERY_TABLE_REPOSITORY_HPP
#include "matador/sql/column_generator.hpp"
#include "matador/sql/record.hpp"
#include <optional>
#include <string>
#include <typeindex>
#include <unordered_map>
namespace matador::sql {
struct table_info
{
std::string name;
record prototype;
};
class table_repository
{
public:
template<typename Type>
const table_info& attach(const std::string &table_name)
{
return attach(std::type_index(typeid(Type)), table_info{table_name, record{column_generator::generate<Type>(*this)}});
}
const table_info& attach(std::type_index ti, const table_info& table);
template<typename Type>
std::optional<table_info> info()
{
return info(std::type_index(typeid(Type)));
}
std::optional<table_info> info(std::type_index ti);
template<typename Type>
[[nodiscard]] std::pair<std::string, std::string> reference() const
{
return reference(std::type_index(typeid(Type)));
}
[[nodiscard]] std::pair<std::string, std::string> reference(const std::type_index &ti) const;
template<typename Type>
[[nodiscard]] bool exists() const
{
return exists(std::type_index(typeid(Type)));
}
[[nodiscard]] bool exists(const std::type_index &ti) const;
private:
using repository = std::unordered_map<std::type_index, table_info>;
repository repository_;
};
}
#endif //QUERY_TABLE_REPOSITORY_HPP
+81
View File
@@ -0,0 +1,81 @@
#ifndef QUERY_VALUE_HPP
#define QUERY_VALUE_HPP
#include "matador/sql/any_type.hpp"
#include "matador/sql/any_type_to_visitor.hpp"
#include "matador/sql/data_type_traits.hpp"
#include "matador/utils/types.hpp"
#include <optional>
namespace matador::sql {
namespace detail {
template<typename Type>
size_t determine_size(const Type &/*val*/)
{
return 0;
}
size_t determine_size(const std::string &val);
size_t determine_size(const char *val);
size_t determine_size(const utils::blob &val);
}
class value
{
public:
value() = default;
template<typename Type>
explicit value(Type value, size_t size = 0)
: size_(size)
, value_(value)
, type_(data_type_traits<Type>::builtin_type(size)) {}
explicit value(data_type_t data_type, size_t size = 0);
value(const value &x) = default;
value& operator=(const value &x) = default;
template<typename Type>
value& operator=(Type val)
{
value_ = val;
size_ = detail::determine_size(val);
type_ = data_type_traits<Type>::builtin_type(size_);
return *this;
}
value(value &&x) noexcept;
value& operator=(value &&x) noexcept;
template<class Type>
std::optional<Type> as() const
{
if (std::holds_alternative<Type>(value_)) {
return std::get<Type>(value_);
} else {
any_type_to_visitor<Type> visitor;
std::visit(visitor, const_cast<any_type &>(value_));
return visitor.result;
}
}
[[nodiscard]] std::string str() const;
[[nodiscard]] size_t size() const;
[[nodiscard]] data_type_t type() const;
[[nodiscard]] bool is_integer() const;
[[nodiscard]] bool is_floating_point() const;
[[nodiscard]] bool is_bool() const;
[[nodiscard]] bool is_string() const;
[[nodiscard]] bool is_varchar() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
[[nodiscard]] bool is_unknown() const;
private:
any_type value_;
size_t size_{};
data_type_t type_{data_type_t::type_unknown};
};
}
#endif //QUERY_VALUE_HPP
+8 -4
View File
@@ -4,6 +4,8 @@
#include "matador/sql/fk_value_extractor.hpp" #include "matador/sql/fk_value_extractor.hpp"
#include "matador/sql/data_type_traits.hpp" #include "matador/sql/data_type_traits.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include <vector> #include <vector>
namespace matador::sql { namespace matador::sql {
@@ -41,19 +43,21 @@ public:
void on_attribute(const char *id, std::string &x, const utils::field_attributes &/*attr*/ = utils::null_attributes); void on_attribute(const char *id, std::string &x, const utils::field_attributes &/*attr*/ = utils::null_attributes);
template<class Type, template < class ... > class Pointer> template<class Type, template < class ... > class Pointer>
void on_belongs_to(const char * /*id*/, Pointer<Type> &x, utils::cascade_type) void on_belongs_to(const char * /*id*/, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/)
{ {
values_.emplace_back(fk_value_extractor_.extract(*x)); values_.emplace_back(fk_value_extractor_.extract(*x));
} }
template<class Type, template < class ... > class Pointer> template<class Type, template < class ... > class Pointer>
void on_has_one(const char * /*id*/, Pointer<Type> &x, utils::cascade_type) void on_has_one(const char * /*id*/, Pointer<Type> &x, const utils::foreign_attributes &/*attr*/)
{ {
values_.emplace_back(fk_value_extractor_.extract(*x)); values_.emplace_back(fk_value_extractor_.extract(*x));
} }
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
void on_has_many(const char *, ContainerType &, utils::cascade_type) {} void on_has_many_to_many(const char *id, ContainerType &c, 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 *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
private: private:
template<typename Type> template<typename Type>
+14 -10
View File
@@ -1,8 +1,6 @@
#ifndef QUERY_ACCESS_HPP #ifndef QUERY_ACCESS_HPP
#define QUERY_ACCESS_HPP #define QUERY_ACCESS_HPP
#include "matador/utils/cascade_type.hpp"
#include <string> #include <string>
#include <optional> #include <optional>
@@ -12,6 +10,7 @@ template < class Type, template < class ... > class ContainerType >
class container; class container;
class field_attributes; class field_attributes;
class foreign_attributes;
namespace access { namespace access {
@@ -56,23 +55,28 @@ void attribute(Operator &op, const char *id, Type &value) {
} }
template<class Operator, class Type> template<class Operator, class Type>
void has_one(Operator &op, const char *id, Type &value, cascade_type cascade) { void has_one(Operator &op, const char *id, Type &value, const foreign_attributes &attr) {
op.on_has_one(id, value, cascade); op.on_has_one(id, value, attr);
} }
template<class Operator, class Type> template<class Operator, class Type>
void belongs_to(Operator &op, const char *id, Type &value, cascade_type cascade) { void belongs_to(Operator &op, const char *id, Type &value, const foreign_attributes &attr) {
op.on_belongs_to(id, value, cascade); op.on_belongs_to(id, value, attr);
} }
template<class Operator, class Type, template<class ...> class ContainerType> template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, cascade_type cascade) { void has_many(Operator &op, ContainerType<Type> &container, const char *join_column, const foreign_attributes &attr) {
op.on_has_many(id, container, cascade); op.on_has_many(container, join_column, attr);
} }
template<class Operator, class Type, template<class ...> class ContainerType> template<class Operator, class Type, template<class ...> class ContainerType>
void has_many(Operator &op, const char *id, container<Type, ContainerType> &container, const char *left_column, const char *right_column, cascade_type cascade) { void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const char *join_column, const char *inverse_join_column, const foreign_attributes &attr) {
op.on_has_many(id, container, left_column, right_column, cascade); op.on_has_many_to_many(id, container, join_column, inverse_join_column, attr);
}
template<class Operator, class Type, template<class ...> class ContainerType>
void has_many_to_many(Operator &op, const char *id, ContainerType<Type> &container, const foreign_attributes &attr) {
op.on_has_many_to_many(id, container, attr);
} }
} }
+21
View File
@@ -0,0 +1,21 @@
#ifndef QUERY_FETCH_TYPE_HPP
#define QUERY_FETCH_TYPE_HPP
#include <cstdint>
namespace matador::utils {
/**
* @brief Defines fetch types
*
* Defines fetch types for foreign relations
*/
enum class fetch_type : uint8_t
{
LAZY, /**< Indicates lazy fetch */
EAGER /**< Indicates eager fetch */
};
}
#endif //QUERY_FETCH_TYPE_HPP
@@ -0,0 +1,34 @@
#ifndef QUERY_FOREIGN_ATTRIBUTES_HPP
#define QUERY_FOREIGN_ATTRIBUTES_HPP
#include "matador/utils/fetch_type.hpp"
#include "matador/utils/cascade_type.hpp"
namespace matador::utils {
class foreign_attributes
{
public:
foreign_attributes() = default;
foreign_attributes(cascade_type cascade); // NOLINT(*-explicit-constructor)
foreign_attributes(fetch_type fetch); // NOLINT(*-explicit-constructor)
foreign_attributes(cascade_type cascade, fetch_type fetch);
foreign_attributes(const foreign_attributes &x) = default;
foreign_attributes& operator=(const foreign_attributes &x) = default;
foreign_attributes(foreign_attributes &&x) = default;
foreign_attributes& operator=(foreign_attributes &&x) = default;
~foreign_attributes() = default;
[[nodiscard]] cascade_type cascade() const;
[[nodiscard]] fetch_type fetch() const;
private:
cascade_type cascade_{cascade_type::NONE};
fetch_type fetch_{fetch_type::LAZY};
};
const foreign_attributes default_foreign_attributes {};
}
#endif //QUERY_FOREIGN_ATTRIBUTES_HPP
+42
View File
@@ -0,0 +1,42 @@
#ifndef QUERY_MACRO_MAP_HPP
#define QUERY_MACRO_MAP_HPP
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,
#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
/**
* Applies the function macro `f` to each of the remaining parameters.
*/
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
/**
* Applies the function macro `f` to each of the remaining parameters and
* inserts commas between the results.
*/
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#endif //QUERY_MACRO_MAP_HPP
+113
View File
@@ -0,0 +1,113 @@
#ifndef QUERY_RESULT_HPP
#define QUERY_RESULT_HPP
#include <variant>
namespace matador::utils {
template < typename ValueType, typename ErrorType >
class [[nodiscard]] result;
template <typename ValueType>
struct is_result : std::false_type {};
template < typename ValueType, typename ErrorType >
struct is_result<result<ValueType, ErrorType>> : std::true_type {};
template < typename ValueType >
class ok
{
public:
using value_type = ValueType;
explicit constexpr ok(const ValueType &value) : value_(value) {}
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
constexpr ValueType&& release() { return std::move(value_); }
const ValueType& value() const { return value_; }
ValueType& value() { return value_; }
private:
ValueType value_;
};
template < typename ErrorType >
class error
{
public:
using value_type = ErrorType;
explicit constexpr error(const ErrorType &error) : error_(error) {}
explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {}
constexpr ErrorType&& release() { return std::move(error_); }
const ErrorType& value() const { return error_; }
ErrorType value() { return error_; }
private:
ErrorType error_;
};
template < typename ValueType, typename ErrorType >
class result
{
public:
using value_type = ok<ValueType>;
using error_type = error<ErrorType>;
result() : result_(ValueType{}) {}
result(value_type value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor)
result(error_type error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor)
result(const result<ValueType, ErrorType> &x) = default;
result& operator=(const result<ValueType, ErrorType> &x) = default;
result(result<ValueType, ErrorType> &&x) = default;
result& operator=(result<ValueType, ErrorType> &&x) = default;
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
[[nodiscard]] bool is_ok() const { return std::holds_alternative<value_type>(result_); }
[[nodiscard]] bool is_error() const { return std::holds_alternative<error_type>(result_); }
ValueType&& release() { return std::get<value_type>(result_).release(); }
ErrorType&& release_error() { return std::get<error_type>(result_).release(); }
const ValueType& value() const { return std::get<value_type>(result_).value(); }
const ErrorType& err() const { return std::get<error_type>(result_).value(); }
ErrorType err() { return std::get<error_type>(result_).value(); }
constexpr const ValueType* operator->() const { return &value(); }
constexpr ValueType* operator->() { return &std::get<value_type>(result_).value(); }
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >>
result<SecondValueType, ErrorType> transform(Func &&f) {
if (is_ok()) {
return result<SecondValueType, ErrorType>(ok(f(release())));
}
return result<SecondValueType, ErrorType>(error(release_error()));
}
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >::value_type::value_type>
result<SecondValueType, ErrorType> and_then(Func &&f) {
if (is_ok()) {
return f(release());
}
return result<SecondValueType, ErrorType>(error(release_error()));
}
template<typename Func, typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::error_type::value_type>
result<ValueType, SecondErrorType> or_else(Func &&f) {
if (is_error()) {
return f(err());
}
return result<ValueType, SecondErrorType>(ok(release()));
}
private:
std::variant<value_type, error_type> result_;
};
}
#endif //QUERY_RESULT_HPP
+3 -4
View File
@@ -15,10 +15,9 @@ namespace matador::utils {
* *
* @param str The string to split. * @param str The string to split.
* @param delim The delimiter character. * @param delim The delimiter character.
* @param values The result vector. * @return The the vector with split strings.
* @return The size of the vector.
*/ */
size_t split(const std::string &str, char delim, std::vector<std::string> &values); std::vector<std::string> split(const std::string &str, char delim);
/** /**
* Replaces all occurrences of string from in given string * Replaces all occurrences of string from in given string
@@ -39,7 +38,7 @@ std::string to_string(const blob &data);
* given stream * given stream
* *
* @tparam R Type og the range (e.g. map, list, vector, etc) * @tparam R Type og the range (e.g. map, list, vector, etc)
* @param range The range with the elements to join * @param range The range with the elements to join_left
* @param delim The delimiter for the elements * @param delim The delimiter for the elements
* @return The ostream reference * @return The ostream reference
*/ */
+45 -10
View File
@@ -1,7 +1,7 @@
set(SQL_SOURCES set(SQL_SOURCES
sql/dialect.cpp sql/dialect.cpp
sql/query_builder.cpp sql/query_builder.cpp
sql/column.cpp sql/column_definition.cpp
sql/key_value_pair.cpp sql/key_value_pair.cpp
sql/basic_condition.cpp sql/basic_condition.cpp
sql/connection.cpp sql/connection.cpp
@@ -12,11 +12,11 @@ set(SQL_SOURCES
sql/session.cpp sql/session.cpp
sql/backend_provider.cpp sql/backend_provider.cpp
sql/query_result_impl.cpp sql/query_result_impl.cpp
sql/column_definition_generator.cpp
sql/column_generator.cpp sql/column_generator.cpp
sql/column_name_generator.cpp
sql/key_value_generator.cpp sql/key_value_generator.cpp
sql/fk_value_extractor.cpp sql/fk_value_extractor.cpp
sql/table_repository.cpp sql/schema.cpp
sql/query_result.cpp sql/query_result.cpp
sql/query_result_reader.cpp sql/query_result_reader.cpp
sql/statement_cache.cpp sql/statement_cache.cpp
@@ -28,12 +28,23 @@ set(SQL_SOURCES
sql/result_parameter_binder.cpp sql/result_parameter_binder.cpp
sql/statement.cpp sql/statement.cpp
sql/convert.cpp sql/convert.cpp
sql/column.cpp
sql/query.cpp
sql/query_parts.cpp
sql/query_compiler.cpp
sql/noop_connection.cpp
sql/query_part.cpp
sql/any_type_to_string_visitor.cpp
sql/field.cpp
sql/table_definition.cpp
sql/value.cpp
sql/entity_query_builder.cpp
) )
set(SQL_HEADER set(SQL_HEADER
../include/matador/sql/dialect.hpp ../include/matador/sql/dialect.hpp
../include/matador/sql/query_builder.hpp ../include/matador/sql/query_builder.hpp
../include/matador/sql/column.hpp ../include/matador/sql/column_definition.hpp
../include/matador/sql/data_type_traits.hpp ../include/matador/sql/data_type_traits.hpp
../include/matador/sql/key_value_pair.hpp ../include/matador/sql/key_value_pair.hpp
../include/matador/sql/basic_condition.hpp ../include/matador/sql/basic_condition.hpp
@@ -48,14 +59,14 @@ set(SQL_HEADER
../include/matador/sql/session.hpp ../include/matador/sql/session.hpp
../include/matador/sql/backend_provider.hpp ../include/matador/sql/backend_provider.hpp
../include/matador/sql/query_result_impl.hpp ../include/matador/sql/query_result_impl.hpp
../include/matador/sql/column_definition_generator.hpp
../include/matador/sql/column_generator.hpp ../include/matador/sql/column_generator.hpp
../include/matador/sql/column_name_generator.hpp
../include/matador/sql/value_extractor.hpp ../include/matador/sql/value_extractor.hpp
../include/matador/sql/any_type.hpp ../include/matador/sql/any_type.hpp
../include/matador/sql/key_value_generator.hpp ../include/matador/sql/key_value_generator.hpp
../include/matador/sql/entity.hpp ../include/matador/sql/entity.hpp
../include/matador/sql/fk_value_extractor.hpp ../include/matador/sql/fk_value_extractor.hpp
../include/matador/sql/table_repository.hpp ../include/matador/sql/schema.hpp
../include/matador/sql/any_type_to_visitor.hpp ../include/matador/sql/any_type_to_visitor.hpp
../include/matador/sql/query_result_reader.hpp ../include/matador/sql/query_result_reader.hpp
../include/matador/sql/to_value.hpp ../include/matador/sql/to_value.hpp
@@ -69,6 +80,21 @@ set(SQL_HEADER
../include/matador/sql/placeholder_generator.hpp ../include/matador/sql/placeholder_generator.hpp
../include/matador/sql/result_parameter_binder.hpp ../include/matador/sql/result_parameter_binder.hpp
../include/matador/sql/convert.hpp ../include/matador/sql/convert.hpp
../include/matador/sql/query.hpp
../include/matador/sql/query_parts.hpp
../include/matador/sql/query_part_visitor.hpp
../include/matador/sql/query_compiler.hpp
../include/matador/sql/query_data.hpp
../include/matador/sql/table.hpp
../include/matador/sql/noop_connection.hpp
../include/matador/sql/query_part.hpp
../include/matador/sql/any_type_to_string_visitor.hpp
../include/matador/sql/query_helper.hpp
../include/matador/sql/field.hpp
../include/matador/sql/entity_query_builder.hpp
../include/matador/sql/table_definition.hpp
../include/matador/sql/has_many_to_many_relation.hpp
../include/matador/sql/value.hpp
) )
set(UTILS_HEADER set(UTILS_HEADER
@@ -82,7 +108,11 @@ set(UTILS_HEADER
../include/matador/utils/cascade_type.hpp ../include/matador/utils/cascade_type.hpp
../include/matador/utils/logger.hpp ../include/matador/utils/logger.hpp
../include/matador/utils/enum_mapper.hpp ../include/matador/utils/enum_mapper.hpp
../include/matador/utils/types.hpp) ../include/matador/utils/types.hpp
../include/matador/utils/foreign_attributes.hpp
../include/matador/utils/fetch_type.hpp
../include/matador/utils/result.hpp
)
set(UTILS_SOURCES set(UTILS_SOURCES
utils/field_attributes.cpp utils/field_attributes.cpp
@@ -92,8 +122,13 @@ set(UTILS_SOURCES
utils/os.cpp utils/os.cpp
utils/identifier.cpp utils/identifier.cpp
sql/value_extractor.cpp sql/value_extractor.cpp
utils/logger.cpp) utils/logger.cpp
utils/foreign_attributes.cpp
)
add_library(matador STATIC
${SQL_SOURCES} ${SQL_HEADER}
${UTILS_SOURCES} ${UTILS_HEADER}
)
add_library(matador STATIC ${SQL_SOURCES} ${SQL_HEADER} ${UTILS_SOURCES} ${UTILS_HEADER})
target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include)
#set_target_properties(matador PROPERTIES LINKER_LANGUAGE CXX)
+40
View File
@@ -0,0 +1,40 @@
#include "matador/sql/any_type_to_string_visitor.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/string.hpp"
namespace matador::sql {
any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query)
: d(d), query(query)
{}
void any_type_to_string_visitor::to_string(const char *val)
{
result = "'" + d.prepare_literal(val) + "'";
}
void any_type_to_string_visitor::to_string(std::string &val)
{
result = "'" + d.prepare_literal(val) + "'";
}
void any_type_to_string_visitor::to_string(utils::blob &val)
{
// "This is a binary Data string" as binary data:
// MySQL: X'5468697320697320612062616E617279204461746120737472696E67'
// Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67'
// MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67
// Sqlite: X'5468697320697320612062616E617279204461746120737472696E67'
result = d.token_at(dialect::token_t::BEGIN_BINARY_DATA) + utils::to_string(val) + d.token_at(dialect::token_t::END_BINARY_DATA);
}
void any_type_to_string_visitor::to_string(placeholder &/*val*/)
{
query.bind_vars.emplace_back("unknown");
result = d.next_placeholder(query.bind_vars);
}
}
+49 -13
View File
@@ -1,14 +1,15 @@
#include "matador/sql/backend_provider.hpp" #include "matador/sql/backend_provider.hpp"
#include "matador/sql/noop_connection.hpp"
#include "matador/sql/dialect_builder.hpp"
#include "matador/utils/os.hpp" #include <algorithm>
#include <cstdint>
#include <stdexcept> #include <stdexcept>
namespace matador::sql { namespace matador::sql {
backend_provider::backend_provider() backend_provider::backend_provider()
: backends_path_(utils::os::getenv("MATADOR_BACKENDS_PATH")) {
{} backends_.emplace("noop", std::make_unique<noop_backend_context>());
}
backend_provider &backend_provider::instance() { backend_provider &backend_provider::instance() {
static backend_provider provider; static backend_provider provider;
@@ -19,31 +20,30 @@ connection_impl *backend_provider::create_connection(const std::string &connecti
{ {
auto it = backends_.find(connection_type); auto it = backends_.find(connection_type);
if (it == backends_.end()) { if (it == backends_.end()) {
it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type, backends_path_)).first; it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type)).first;
} }
return (*it->second->create_connection)(info); return it->second->create(info);
} }
void backend_provider::destroy_connection(const std::string &connection_type, connection_impl *c) void backend_provider::destroy_connection(const std::string &connection_type, connection_impl *c)
{ {
auto it = backends_.find(connection_type); auto it = backends_.find(connection_type);
if (it == backends_.end()) { if (it == backends_.end()) {
it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type, backends_path_)).first; it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type)).first;
} }
(*it->second->destroy_connection)(c); return it->second->destroy(c);
} }
const dialect &backend_provider::connection_dialect(const std::string &connection_type) const dialect &backend_provider::connection_dialect(const std::string &connection_type)
{ {
auto it = backends_.find(connection_type); auto it = backends_.find(connection_type);
if (it == backends_.end()) { if (it == backends_.end()) {
it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type, backends_path_)).first; it = backends_.emplace(connection_type, std::make_unique<backend_context>(connection_type)).first;
} }
return *(*it->second->get_dialect)(); return *it->second->dialect();
} }
backend_provider::backend_context::backend_context(const std::string &connection_type, backend_provider::backend_context::backend_context(const std::string &connection_type)
const std::string &backends_path)
{ {
if (!lib.load("matador-" + connection_type)) { if (!lib.load("matador-" + connection_type)) {
throw std::runtime_error("couldn't load library '" + connection_type + "'"); throw std::runtime_error("couldn't load library '" + connection_type + "'");
@@ -54,9 +54,45 @@ backend_provider::backend_context::backend_context(const std::string &connection
get_dialect = reinterpret_cast<dialect_func >(reinterpret_cast<std::uintptr_t>(lib.function("get_dialect"))); get_dialect = reinterpret_cast<dialect_func >(reinterpret_cast<std::uintptr_t>(lib.function("get_dialect")));
} }
connection_impl *backend_provider::backend_context::create(const connection_info &info)
{
return (create_connection)(info);
}
void backend_provider::backend_context::destroy(connection_impl *conn)
{
(destroy_connection)(conn);
}
const class dialect *backend_provider::backend_context::dialect() const
{
return (get_dialect)();
}
backend_provider::backend_context::~backend_context() backend_provider::backend_context::~backend_context()
{ {
lib.unload(); lib.unload();
} }
connection_impl *backend_provider::noop_backend_context::create(const connection_info &info)
{
return noop_connections_.insert(std::make_unique<noop_connection>(info)).first->get();
}
void backend_provider::noop_backend_context::destroy(connection_impl *impl)
{
auto it = std::find_if(noop_connections_.begin(), noop_connections_.end(), [impl](const auto &item) {
return impl == item.get();
});
if (it != noop_connections_.end()) {
noop_connections_.erase(it);
}
}
const dialect *backend_provider::noop_backend_context::dialect() const
{
static sql::dialect dialect_ = dialect_builder::builder().create().build();
return &dialect_;
}
} }
+32 -108
View File
@@ -1,129 +1,53 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/table.hpp"
#include <utility>
namespace matador::sql { namespace matador::sql {
column::column(sql_function_t func, std::string name) column operator ""_col(const char *name, size_t len)
: name_(std::move(name)), type_(data_type_t::type_int), attributes_(utils::null_attributes), function_(func)
{}
column::column(const char *name, std::string alias)
: name_(name), attributes_(utils::null_attributes), alias_(std::move(alias))
{}
column::column(std::string name, std::string alias)
: name_(std::move(name)), attributes_(utils::null_attributes), alias_(std::move(alias))
{}
column::column(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index)
: name_(std::move(name)), index_(index), type_(type), attributes_(attr), null_option_(null_opt)
{}
column::column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column,
utils::field_attributes attr, null_option null_opt)
: name_(std::move(name))
, index_(index)
, type_(type)
, attributes_(attr)
, null_option_(null_opt)
, ref_table_(std::move(ref_table))
, ref_column_(std::move(ref_column))
{}
const std::string &column::name() const
{ {
return name_; return {{name, len}};
} }
size_t column::index() const column::column(const char *name) : name(name) {}
column::column(std::string name) : name(std::move(name)) {}
column::column(sql_function_t func, std::string name) : name(std::move(name)), function_(func) {}
column::column(std::string table_name, std::string name, std::string as)
: table(std::move(table_name))
, name(std::move(name))
, alias(std::move(as)) {}
column::column(std::string table_name, const char *name, std::string as)
: table(std::move(table_name))
, name(name)
, alias(std::move(as)) {}
column::column(struct table &t, const char *name, std::string as)
: table(t.name)
, name(name)
, alias(std::move(as))
{ {
return index_; t.columns.push_back(*this);
} }
const utils::field_attributes &column::attributes() const bool column::equals(const column &x) const
{ {
return attributes_; return table == x.table &&
name == x.name &&
alias == x.alias &&
function_ == x.function_;
} }
bool column::is_nullable() const column &column::as(std::string a)
{ {
return null_option_ == null_option::NULLABLE; alias = std::move(a);
} return *this;
data_type_t column::type() const
{
return type_;
}
const std::string &column::alias() const
{
return alias_;
}
const std::string &column::ref_table() const
{
return ref_table_;
}
const std::string &column::ref_column() const
{
return ref_column_;
}
void column::type(data_type_t type)
{
type_ = type;
}
void column::alias(const std::string &as)
{
alias_ = as;
}
std::string column::str() const
{
any_type_to_visitor<std::string> visitor;
std::visit(visitor, const_cast<any_type &>(value_));
return visitor.result;
} }
bool column::is_function() const bool column::is_function() const
{ {
return function_ != sql_function_t::NONE; return function_ != sql_function_t::NONE;
} }
sql_function_t column::function() const
{
return function_;
}
column operator "" _col(const char *name, size_t len)
{
return {std::string(name, len)};
}
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr, null_option null_opt)
{
return {name, type, attr, null_opt};
}
template<>
column make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt)
{
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), attr, null_opt);
}
template<>
column make_pk_column<std::string>(const std::string &name, size_t size)
{
return make_column<std::string>(name, {size, utils::constraints::FOREIGN_KEY});
}
template<>
[[maybe_unused]] column make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table,
const std::string &ref_column)
{
return {name, data_type_traits<std::string>::builtin_type(size), 0, ref_table, ref_column, {size, utils::constraints::FOREIGN_KEY}, null_option::NOT_NULL};
}
} }
+151
View File
@@ -0,0 +1,151 @@
#include "matador/sql/column_definition.hpp"
#include <ostream>
#include <utility>
namespace matador::sql {
column_definition::column_definition(const char *name)
: name_(name), attributes_(utils::null_attributes)
{}
column_definition::column_definition(std::string name)
: name_(std::move(name)), attributes_(utils::null_attributes)
{}
column_definition::column_definition(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index)
: name_(std::move(name)), index_(index), type_(type), attributes_(attr), null_option_(null_opt)
{}
column_definition::column_definition(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column,
utils::field_attributes attr, null_option null_opt)
: name_(std::move(name))
, index_(index)
, type_(type)
, attributes_(attr)
, null_option_(null_opt)
, ref_table_(std::move(ref_table))
, ref_column_(std::move(ref_column))
{}
const std::string &column_definition::name() const
{
return name_;
}
int column_definition::index() const
{
return index_;
}
const utils::field_attributes &column_definition::attributes() const
{
return attributes_;
}
bool column_definition::is_nullable() const
{
return null_option_ == null_option::NULLABLE;
}
data_type_t column_definition::type() const
{
return type_;
}
const std::string &column_definition::ref_table() const
{
return ref_table_;
}
const std::string &column_definition::ref_column() const
{
return ref_column_;
}
bool column_definition::is_integer() const
{
return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long;
}
bool column_definition::is_floating_point() const
{
return type_ == data_type_t::type_float || type_ == data_type_t::type_double;
}
bool column_definition::is_bool() const
{
return type_ == data_type_t::type_bool;
}
bool column_definition::is_string() const
{
return type_ == data_type_t::type_text;
}
bool column_definition::is_varchar() const
{
return type_ == data_type_t::type_varchar;
}
bool column_definition::is_blob() const
{
return type_ == data_type_t::type_blob;
}
bool column_definition::is_null() const
{
return type_ == data_type_t::type_null;
}
bool column_definition::is_unknown() const
{
return type_ == data_type_t::type_unknown;
}
void column_definition::type(data_type_t type)
{
type_ = type;
}
std::string column_definition::str() const
{
if (std::holds_alternative<std::string>(value_)) {
return std::get<std::string>(value_);
}
any_type_to_visitor<std::string> visitor;
std::visit(visitor, const_cast<any_type &>(value_));
return visitor.result;
}
std::ostream& operator<<(std::ostream &out, const column_definition &col)
{
out << col.str();
return out;
}
column_definition make_column(const std::string &name, data_type_t type, utils::field_attributes attr, null_option null_opt)
{
return {name, type, attr, null_opt};
}
template<>
column_definition make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt)
{
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), attr, null_opt);
}
template<>
column_definition make_pk_column<std::string>(const std::string &name, size_t size)
{
return make_column<std::string>(name, {size, utils::constraints::FOREIGN_KEY});
}
template<>
[[maybe_unused]] column_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table,
const std::string &ref_column)
{
return {name, data_type_traits<std::string>::builtin_type(size), 0, ref_table, ref_column, {size, utils::constraints::FOREIGN_KEY}, null_option::NOT_NULL};
}
}
+31
View File
@@ -0,0 +1,31 @@
#include "matador/sql/column_definition_generator.hpp"
#include "matador/sql/schema.hpp"
namespace matador::sql {
column_definition_generator::column_definition_generator(std::vector<column_definition> &columns, const schema &repo)
: columns_(columns)
, repo_(repo)
{}
void column_definition_generator::on_primary_key(const char *id, std::string &pk, size_t size)
{
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY });
}
void column_definition_generator::on_revision(const char *id, unsigned long long int &x)
{
on_attribute(id, x);
}
std::pair<std::string, std::string> column_definition_generator::determine_foreign_ref(const std::type_index &ti)
{
return repo_.reference(ti);
}
void fk_column_generator::on_primary_key(const char *, std::string &, size_t size)
{
type_ = data_type_traits<std::string>::builtin_type(size);
}
}
+16 -14
View File
@@ -1,31 +1,33 @@
#include "matador/sql/column_generator.hpp" #include "matador/sql/column_generator.hpp"
#include "matador/sql/table_repository.hpp"
namespace matador::sql { namespace matador::sql {
column_generator::column_generator(std::vector<column> &columns, const table_repository &repo) column_generator::column_generator(std::vector<column> &column_infos,
: columns_(columns) const sql::schema &ts,
, repo_(repo) const std::string &table_name,
{} bool force_lazy)
: column_infos_(column_infos)
void column_generator::on_primary_key(const char *id, std::string &pk, size_t size) , table_schema_(ts)
, force_lazy_(force_lazy)
{ {
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY }); table_name_stack_.push(table_name);
} }
void column_generator::on_revision(const char *id, unsigned long long int &x) void column_generator::on_primary_key(const char *id, std::string &, size_t)
{ {
on_attribute(id, x); push(id);
} }
std::pair<std::string, std::string> column_generator::determine_foreign_ref(const std::type_index &ti) void column_generator::on_revision(const char *id, unsigned long long int &)
{ {
return repo_.reference(ti); push(id);
} }
void fk_column_generator::on_primary_key(const char *, std::string &, size_t size) void column_generator::push(const std::string &column_name)
{ {
type_ = data_type_traits<std::string>::builtin_type(size); char str[4];
snprintf(str, 4, "c%02d", ++column_index);
column_infos_.emplace_back(table_name_stack_.top(), column_name, str);
} }
} }
-19
View File
@@ -1,19 +0,0 @@
#include "matador/sql/column_name_generator.hpp"
namespace matador::sql {
column_name_generator::column_name_generator(std::vector<std::string> &column_names)
: column_names_(column_names)
{}
void column_name_generator::on_primary_key(const char *id, std::string &, size_t)
{
column_names_.emplace_back(id);
}
void column_name_generator::on_revision(const char *id, unsigned long long int &)
{
column_names_.emplace_back(id);
}
}
+29 -7
View File
@@ -1,26 +1,48 @@
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/query_intermediates.hpp"
namespace matador::sql { namespace matador::sql {
condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val) condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val)
: basic_column_condition(fld, op), value(val) : basic_column_condition(fld, op), value(val)
{} {}
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d, query_context &query) const std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(const dialect &d, query_context &query) const
{ {
query.bind_vars.emplace_back(field_.name()); query.bind_vars.emplace_back(field_.name);
return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder(query.bind_vars); return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars);
} }
condition<column, query_context>::condition(column col, basic_condition::operand_t op, query_context &q) condition<column, query_context>::condition(column col, basic_condition::operand_t op, const query_context &q)
: basic_column_condition(std::move(col), op), query_(q) : basic_column_condition(std::move(col), op), query_(q)
{} {}
std::string condition<column, query_context>::evaluate(dialect &d, query_context &query) const std::string condition<column, query_context>::evaluate(const dialect &d, query_context &query) const
{ {
std::string result(d.prepare_identifier(field_.name()) + " " + operand + " ("); std::string result(d.prepare_identifier(field_) + " " + operand + " (");
result += (")"); result += query_.sql + (")");
return result; return result;
} }
condition<column, query_context> in( const column& col, const query_context &q ) {
return {col, basic_condition::operand_t::IN_LIST, q};
}
condition<column, query_context> in( const column& col, const query_select& q ) {
return in(col, q.build());
}
condition<column, std::string> like( const column& col, const std::string& val ) {
return {col, basic_condition::operand_t::LIKE, val};
}
condition<column, query_context> equals( const column& col, query_context& q ) {
return {col, basic_condition::operand_t::EQUAL, q};
}
condition<column, column> operator==(const column &a, const column &b)
{
return {a, basic_condition::operand_t::EQUAL, b};
}
} }
+25 -45
View File
@@ -2,23 +2,24 @@
#include "matador/sql/backend_provider.hpp" #include "matador/sql/backend_provider.hpp"
#include "matador/sql/connection_impl.hpp" #include "matador/sql/connection_impl.hpp"
#include "matador/sql/schema.hpp"
#include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
namespace matador::sql { namespace matador::sql {
connection::connection(connection_info info, const std::shared_ptr<table_repository> &repo) connection::connection(connection_info info)
: connection_info_(std::move(info)) : connection_info_(std::move(info))
, logger_(stdout, "SQL") , logger_(stdout, "SQL")
, dialect_(backend_provider::instance().connection_dialect(connection_info_.type)) , dialect_(backend_provider::instance().connection_dialect(connection_info_.type))
, table_repository_(repo)
{ {
connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_)); connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_));
} }
connection::connection(const std::string& dns, const std::shared_ptr<table_repository> &repo) connection::connection(const std::string& dns)
: connection(connection_info::parse(dns), repo) : connection(connection_info::parse(dns))
{} {}
connection::connection(const connection &x) connection::connection(const connection &x)
@@ -69,37 +70,7 @@ const connection_info &connection::info() const
return connection_info_; return connection_info_;
} }
query_create_intermediate connection::create() std::vector<sql::column_definition> connection::describe(const std::string &table_name) const
{
return query_create_intermediate(*this);
}
query_drop_intermediate connection::drop()
{
return query_drop_intermediate{*this};
}
query_select_intermediate connection::select(std::initializer_list<column> columns)
{
return {*this, columns};
}
query_insert_intermediate connection::insert()
{
return query_insert_intermediate{*this};
}
query_update_intermediate connection::update(const std::string &table)
{
return query_update_intermediate{*this, table};
}
query_delete_intermediate connection::remove()
{
return query_delete_intermediate{*this};
}
record connection::describe(const std::string &table_name) const
{ {
return std::move(connection_->describe(table_name)); return std::move(connection_->describe(table_name));
} }
@@ -120,15 +91,29 @@ size_t connection::execute(const std::string &sql) const
return connection_->execute(sql); return connection_->execute(sql);
} }
sql::query connection::query(const schema &schema) const
{
return sql::query(*const_cast<connection*>(this), schema);
}
bool is_unknown(const std::vector<sql::column_definition> &columns) {
return std::all_of(std::begin(columns), std::end(columns), [](const auto &col) {
return col.type() == data_type_t::type_unknown;
});
}
query_result<record> connection::fetch(const query_context &q) const query_result<record> connection::fetch(const query_context &q) const
{ {
if (q.prototype.empty() || q.prototype.unknown()) { if (q.prototype.empty() || is_unknown(q.prototype)) {
const auto table_prototype = describe(q.table_name); const auto table_prototype = describe(q.table.name);
for (auto &col : q.prototype) { for (auto &col : q.prototype) {
if (const auto rit = table_prototype.find(col.name()); col.type() == data_type_t::type_unknown && rit != table_prototype.end()) { const auto rit = std::find_if(std::begin(table_prototype), std::end(table_prototype), [&col](const auto &value) {
const_cast<column&>(col).type(rit->type()); return value.name() == col.name();
});
if (col.type() == data_type_t::type_unknown && rit != table_prototype.end()) {
const_cast<column_definition&>(col).type(rit->type());
} }
} }
} }
// auto it = prototypes_.find(q.table_name); // auto it = prototypes_.find(q.table_name);
// if (it == prototypes_.end()) { // if (it == prototypes_.end()) {
@@ -160,9 +145,4 @@ const class dialect &connection::dialect() const
return dialect_; return dialect_;
} }
std::shared_ptr<table_repository> connection::tables() const
{
return table_repository_;
}
} }
+5
View File
@@ -59,4 +59,9 @@ long double to_double(const char *source)
return result; return result;
} }
void convert(utils::blob &dest, const utils::blob &data)
{
dest = data;
}
} }
+15
View File
@@ -6,6 +6,21 @@
namespace matador::sql { namespace matador::sql {
void data_type_traits<nullptr_t>::read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/)
{
// reader.read_value(id, index, value);
}
void data_type_traits<nullptr_t>::bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/)
{
// binder.bind(index, value);
}
void data_type_traits<nullptr_t>::bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/)
{
// binder.bind_result_value(index, value);
}
void data_type_traits<char>::read_value(query_result_reader &reader, const char *id, size_t index, char &value) void data_type_traits<char>::read_value(query_result_reader &reader, const char *id, size_t index, char &value)
{ {
reader.read_value(id, index, value); reader.read_value(id, index, value);
+21 -9
View File
@@ -1,5 +1,4 @@
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/column.hpp"
#include "matador/utils/string.hpp" #include "matador/utils/string.hpp"
@@ -17,19 +16,32 @@ const std::string &dialect::data_type_at(data_type_t type) const
std::string dialect::prepare_identifier(const column &col) const std::string dialect::prepare_identifier(const column &col) const
{ {
std::string result(col.name()); std::string result;
if (!col.is_function()) { if (!col.is_function()) {
escape_quotes_in_identifier(result); if (!col.table.empty()) {
quote_identifier(result); result = prepare_identifier_string(col.table) + ".";
}
result += prepare_identifier_string(col.name);
} else { } else {
result = sql_func_map_.at(col.function()) + "(" + result + ")"; result = sql_func_map_.at(col.function_) + "(" + col.name + ")";
} }
if (!col.alias().empty()) { if (!col.alias.empty()) {
result += " AS " + col.alias(); result += " AS " + col.alias;
} }
return result; return result;
} }
std::string dialect::prepare_identifier_string(const std::string &col) const
{
auto parts = utils::split(col, '.');
for (auto &part : parts) {
escape_quotes_in_identifier(part);
quote_identifier(part);
}
return utils::join(parts, ".");
}
std::string dialect::prepare_literal(const std::string &str) const std::string dialect::prepare_literal(const std::string &str) const
{ {
std::string result(str); std::string result(str);
@@ -39,8 +51,8 @@ std::string dialect::prepare_literal(const std::string &str) const
void dialect::quote_identifier(std::string &str) const void dialect::quote_identifier(std::string &str) const
{ {
str.insert(0, token_at(token_t::START_QUOTE)); // str.insert(0, token_at(token_t::START_QUOTE));
str += token_at(token_t::END_QUOTE); // str += token_at(token_t::END_QUOTE);
} }
void dialect::escape_quotes_in_identifier(std::string &str) const void dialect::escape_quotes_in_identifier(std::string &str) const
+38
View File
@@ -0,0 +1,38 @@
#include "matador/sql/entity_query_builder.hpp"
namespace matador::sql {
void entity_query_builder::on_primary_key(const char *id, std::string &, size_t)
{
push(id);
if (!is_root_entity()) {
auto b = pk_.is_varchar();
std::cout << "is matching primary key: " << std::boolalpha << b << "\n";
}
}
void entity_query_builder::on_revision(const char *id, unsigned long long &/*rev*/)
{
push(id);
}
void entity_query_builder::push(const std::string &column_name)
{
char str[4];
snprintf(str, 4, "c%02d", ++column_index);
entity_query_data_.columns.emplace_back(table_info_stack_.top().name, column_name, str);
}
[[nodiscard]] bool entity_query_builder::is_root_entity() const {
return table_info_stack_.size() == 1;
}
void entity_query_builder::append_join(const column &left, const column &right)
{
entity_query_data_.joins.push_back({
{ right.table },
make_condition(left == right)
});
}
}
+103
View File
@@ -0,0 +1,103 @@
#include "matador/sql/field.hpp"
#include <ostream>
namespace matador::sql {
field::field(std::string name)
: name_(std::move(name))
, value_(nullptr)
{}
field::field(std::string name, data_type_t data_type, size_t size, int index)
: name_(std::move(name))
, index_(index)
, value_(data_type, size) {}
field::field(field &&x) noexcept
: name_(std::move(x.name_))
, index_(x.index_)
, value_(std::move(x.value_))
{
x.value_ = nullptr;
x.index_ = -1;
}
field &field::operator=(field &&x) noexcept
{
name_ = std::move(x.name_);
index_ = x.index_;
value_ = std::move(x.value_);
x.index_ = -1;
x.value_ = nullptr;
return *this;
}
const std::string &field::name() const
{
return name_;
}
size_t field::size() const
{
return value_.size();
}
int field::index() const
{
return index_;
}
std::ostream &operator<<(std::ostream &out, const field &col)
{
out << col.str();
return out;
}
std::string field::str() const
{
return as<std::string>().value();
}
bool field::is_integer() const
{
return value_.is_integer();
}
bool field::is_floating_point() const
{
return value_.is_floating_point();
}
bool field::is_bool() const
{
return value_.is_bool();
}
bool field::is_string() const
{
return value_.is_string();
}
bool field::is_varchar() const
{
return value_.is_varchar();
}
bool field::is_blob() const
{
return value_.is_blob();
}
bool field::is_null() const
{
return value_.is_null();
}
bool field::is_unknown() const
{
return value_.is_unknown();
}
}
+10 -2
View File
@@ -1,8 +1,16 @@
#include <utility>
#include "matador/sql/key_value_pair.hpp" #include "matador/sql/key_value_pair.hpp"
namespace matador::sql { namespace matador::sql {
key_value_pair::key_value_pair(const std::string &name, any_type value)
: name_(name) key_value_pair::key_value_pair(std::string name, any_type value)
: name_(std::move(name))
, value_(std::move(value)) {
}
key_value_pair::key_value_pair(const column &col, any_type value)
: name_(col.name)
, value_(std::move(value)) { , value_(std::move(value)) {
} }
+52
View File
@@ -0,0 +1,52 @@
#include "matador/sql/noop_connection.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/sql/record.hpp"
#include <string>
#include <memory>
namespace matador::sql {
noop_connection::noop_connection(const connection_info &info)
: connection_impl(info) {}
void noop_connection::open()
{
is_open_ = true;
}
void noop_connection::close()
{
is_open_ = false;
}
bool noop_connection::is_open()
{
return is_open_;
}
size_t noop_connection::execute(const std::string &stmt)
{
return 0;
}
std::unique_ptr<query_result_impl> noop_connection::fetch(const std::string &stmt)
{
return {};
}
std::unique_ptr<statement_impl> noop_connection::prepare(query_context context)
{
return {};
}
std::vector<sql::column_definition> noop_connection::describe(const std::string &table)
{
return {};
}
bool noop_connection::exists(const std::string &schema_name, const std::string &table_name)
{
return false;
}
}
+54
View File
@@ -0,0 +1,54 @@
#include "matador/sql/query.hpp"
#include "matador/sql/connection.hpp"
namespace matador::sql {
query::query(connection &db, const sql::schema &schema)
: connection_(db)
, schema_(schema)
{}
query_create_intermediate query::create()
{
return query_create_intermediate(connection_, schema_);
}
query_drop_intermediate query::drop()
{
return query_drop_intermediate{connection_, schema_};
}
query_select_intermediate query::select(std::initializer_list<column> columns)
{
return select(std::vector<column>{columns});
}
query_select_intermediate query::select(const std::vector<column>& columns)
{
return {connection_, schema_, columns};
}
query_select_intermediate query::select(std::vector<column> columns, std::initializer_list<column> additional_columns)
{
for (const auto &col : additional_columns) {
columns.push_back(col);
}
return {connection_, schema_, columns};
}
query_insert_intermediate query::insert()
{
return query_insert_intermediate{connection_, schema_};
}
query_update_intermediate query::update(const sql::table &table)
{
return query_update_intermediate{connection_, schema_, table};
}
query_delete_intermediate query::remove()
{
return query_delete_intermediate{connection_, schema_};
}
}
+58 -56
View File
@@ -1,4 +1,6 @@
#include "matador/sql/query_builder.hpp" #include "matador/sql/query_builder.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/sql/column.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/utils/string.hpp" #include "matador/utils/string.hpp"
@@ -138,19 +140,19 @@ query_builder &query_builder::select(const std::vector<column> &columns)
if (columns.size() < 2) { if (columns.size() < 2) {
for (const auto &col: columns) { for (const auto &col: columns) {
result.append(dialect_.prepare_identifier(col)); result.append(dialect_.prepare_identifier(col));
query_.result_vars.emplace_back(col.name()); query_.result_vars.emplace_back(col.name);
query_.prototype.append(col); query_.prototype.emplace_back(col.name);
} }
} else { } else {
auto it = columns.begin(); auto it = columns.begin();
result.append(dialect_.prepare_identifier(*it)); result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name()); query_.result_vars.emplace_back(it->name);
query_.prototype.append(column{*it++}); query_.prototype.emplace_back((*it++).name);
for (; it != columns.end(); ++it) { for (; it != columns.end(); ++it) {
result.append(", "); result.append(", ");
result.append(dialect_.prepare_identifier(*it)); result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name()); query_.result_vars.emplace_back(it->name);
query_.prototype.append(column{*it}); query_.prototype.emplace_back((*it).name);
} }
} }
@@ -171,7 +173,7 @@ query_builder &query_builder::update(const std::string &table)
{ {
initialize(command_t::UPDATE, state_t::QUERY_UPDATE); initialize(command_t::UPDATE, state_t::QUERY_UPDATE);
query_.table_name = table; query_.table = {table};
query_parts_.emplace_back(dialect::token_t::UPDATE, dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table)); query_parts_.emplace_back(dialect::token_t::UPDATE, dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table));
return *this; return *this;
@@ -186,9 +188,9 @@ query_builder &query_builder::remove()
return *this; return *this;
} }
query_builder &query_builder::table(const std::string &table, std::initializer_list<column> columns) query_builder &query_builder::table(const std::string &table, std::initializer_list<column_definition> columns)
{ {
return this->table(table, std::vector<column>{columns}); return this->table(table, std::vector<column_definition>{columns});
} }
struct fk_context struct fk_context
@@ -204,31 +206,31 @@ struct column_context
std::vector<fk_context> foreign_contexts; std::vector<fk_context> foreign_contexts;
}; };
std::string build_create_column(const column &col, const dialect &d, column_context &context); //std::string build_create_column(const column_definition &col, const dialect &d, column_context &context);
query_builder &query_builder::table(const std::string &table, const std::vector<column> &columns) query_builder &query_builder::table(const std::string &table, const std::vector<column_definition> &columns)
{ {
transition_to(state_t::QUERY_TABLE_CREATE); transition_to(state_t::QUERY_TABLE_CREATE);
query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
query_.table_name = table; query_.table = {table};
std::string result = "("; std::string result = "(";
column_context context; column_context context;
if (columns.size() < 2) { // if (columns.size() < 2) {
for (const auto &col: columns) { // for (const auto &col: columns) {
result.append(build_create_column(col, dialect_, context)); // result.append(build_create_column(col, dialect_, context));
} // }
} else { // } else {
auto it = columns.begin(); // auto it = columns.begin();
result.append(build_create_column(*it++, dialect_, context)); // result.append(build_create_column(*it++, dialect_, context));
for (; it != columns.end(); ++it) { // for (; it != columns.end(); ++it) {
result.append(", "); // result.append(", ");
result.append(build_create_column(*it, dialect_, context)); // result.append(build_create_column(*it, dialect_, context));
} // }
} // }
if (!context.primary_keys.empty()) { if (!context.primary_keys.empty()) {
result.append(", CONSTRAINT PK_" + table + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")"); result.append(", CONSTRAINT PK_" + table + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
@@ -250,34 +252,34 @@ query_builder &query_builder::table(const std::string &table)
transition_to(state_t::QUERY_TABLE_DROP); transition_to(state_t::QUERY_TABLE_DROP);
query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table)); query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
query_.table_name = table; query_.table = {table};
return *this; return *this;
} }
query_builder &query_builder::into(const std::string &table, std::initializer_list<std::string> column_names) query_builder &query_builder::into(const std::string &table, std::initializer_list<column> column_names)
{ {
return into(table, std::vector<std::string>{column_names}); return into(table, std::vector<column>{column_names});
} }
query_builder &query_builder::into(const std::string &table, const std::vector<std::string> &column_names) query_builder &query_builder::into(const std::string &table, const std::vector<column> &column_names)
{ {
transition_to(state_t::QUERY_INTO); transition_to(state_t::QUERY_INTO);
query_parts_.emplace_back(dialect::token_t::INTO, " " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " "); query_parts_.emplace_back(dialect::token_t::INTO, " " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " ");
query_.table_name = table; query_.table = {table};
std::string result{"("}; std::string result{"("};
if (column_names.size() < 2) { if (column_names.size() < 2) {
for (const auto &col: column_names) { for (const auto &col: column_names) {
result.append(dialect_.prepare_identifier(col)); result.append(dialect_.prepare_identifier(col.name));
} }
} else { } else {
auto it = column_names.begin(); auto it = column_names.begin();
result.append(dialect_.prepare_identifier(*it++)); result.append(dialect_.prepare_identifier((it++)->name));
for (; it != column_names.end(); ++it) { for (; it != column_names.end(); ++it) {
result.append(", "); result.append(", ");
result.append(dialect_.prepare_identifier(*it)); result.append(dialect_.prepare_identifier(it->name));
} }
} }
result += (")"); result += (")");
@@ -335,7 +337,7 @@ query_builder &query_builder::from(const std::string &table, const std::string &
"." + dialect_.prepare_identifier(table) + "." + dialect_.prepare_identifier(table) +
(as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as)));
} }
query_.table_name = table; query_.table = {table};
return *this; return *this;
} }
@@ -489,36 +491,36 @@ void query_builder::initialize(query_builder::command_t cmd, query_builder::stat
query_parts_.clear(); query_parts_.clear();
} }
std::string build_create_column(const column &col, const dialect &d, column_context &context) //std::string build_create_column(const column_definition &col, const dialect &d, column_context &context)
{ //{
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type()); // std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
if (col.attributes().size() > 0) { // if (col.attributes().size() > 0) {
result.append("(" + std::to_string(col.attributes().size()) + ")"); // result.append("(" + std::to_string(col.attributes().size()) + ")");
} // }
if (!col.is_nullable()) { // if (!col.is_nullable()) {
result.append(" NOT NULL"); // result.append(" NOT NULL");
} // }
if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) { // if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) {
result.append(" UNIQUE"); // result.append(" UNIQUE");
} // }
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { // if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
context.primary_keys.emplace_back(col.name()); // context.primary_keys.emplace_back(col.name());
} // }
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) { // if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()}); // context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
} // }
//
return result; // return result;
} //}
column alias(const std::string &column, const std::string &as) column alias(const std::string &column, const std::string &as)
{ {
return {column, as}; return {"", column, as};
} }
column alias(column &&col, const std::string &as) column alias(column &&col, const std::string &as)
{ {
col.alias(as); col.as(as);
return std::move(col); return std::move(col);
} }
+324
View File
@@ -0,0 +1,324 @@
#include "matador/sql/query_compiler.hpp"
#include "matador/sql/query_data.hpp"
#include "matador/sql/column_definition.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/any_type_to_string_visitor.hpp"
#include "matador/utils/string.hpp"
namespace matador::sql {
query_compiler::query_compiler(const sql::dialect &d)
: dialect_(d)
{}
query_context query_compiler::compile(const query_data *data)
{
for (const auto &part: data->parts) {
part->accept(*this);
}
return query_;
}
void query_compiler::visit(query_select_part &select_part)
{
query_.sql = dialect_.token_at(dialect::token_t::SELECT) + " ";
query_.prototype.clear();
std::string result;
const auto &columns = select_part.columns();
if (columns.size() < 2) {
for (const auto &col: columns) {
result.append(dialect_.prepare_identifier(col));
query_.result_vars.emplace_back(col.name);
query_.prototype.emplace_back(col.name);
}
} else {
auto it = columns.begin();
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name);
query_.prototype.emplace_back((*it++).name);
for (; it != columns.end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name);
query_.prototype.emplace_back((*it).name);
}
}
query_.sql += result;
}
void query_compiler::visit(query_from_part &from_part)
{
query_.table = from_part.table();
if (!dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " +
dialect_.prepare_identifier(from_part.table().alias));
} else {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
"." + dialect_.prepare_identifier(from_part.table().name) +
(from_part.table().alias.empty() ? "" : " AS " +
dialect_.prepare_identifier(from_part.table().alias));
}
}
void query_compiler::visit(query_join_part &join_part)
{
if (dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(join_part.table().name) +
(join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias));
} else {
query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
"." + dialect_.prepare_identifier(join_part.table().name) +
(join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias));
}
}
void query_compiler::visit(query_on_part &on_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ON) +
" " + on_part.condition().evaluate(const_cast<dialect &>(dialect_), query_);
}
void query_compiler::visit(query_where_part &where_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::WHERE) +
" " + where_part.condition().evaluate(const_cast<dialect &>(dialect_), query_);
}
void query_compiler::visit(query_group_by_part &group_by_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(group_by_part.column());
}
void query_compiler::visit(query_order_by_part &order_by_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ORDER_BY) +
" " + dialect_.prepare_identifier(order_by_part.column());
}
void query_compiler::visit(query_order_by_asc_part &order_by_asc_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::ASC);
}
void query_compiler::visit(query_order_by_desc_part &order_by_desc_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::DESC);
}
void query_compiler::visit(query_offset_part &offset_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(offset_part.offset());
}
void query_compiler::visit(query_limit_part &limit_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(limit_part.limit());
}
void query_compiler::visit(query_insert_part &insert_part)
{
query_.sql = dialect_.token_at(dialect::token_t::INSERT);
}
void query_compiler::visit(query_into_part &into_part)
{
query_.table = into_part.table();
query_.sql += " " + dialect_.token_at(dialect::token_t::INTO) +
" " + dialect_.prepare_identifier(into_part.table().name);
std::string result{"("};
if (into_part.columns().size() < 2) {
for (const auto &col: into_part.columns()) {
result.append(dialect_.prepare_identifier(col.name));
}
} else {
auto it = into_part.columns().begin();
result.append(dialect_.prepare_identifier((it++)->name));
for (; it != into_part.columns().end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(it->name));
}
}
result += (")");
query_.sql += " " + result;
}
void query_compiler::visit(query_values_part &values_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::VALUES);
any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result{"("};
if (values_part.values().size() < 2) {
for (auto val: values_part.values()) {
std::visit(value_to_string, val);
result.append(value_to_string.result);
}
} else {
auto it = values_part.values().begin();
auto val = *it++;
std::visit(value_to_string, val);
result.append(value_to_string.result);
for (; it != values_part.values().end(); ++it) {
result.append(", ");
val = *it;
std::visit(value_to_string, val);
result.append(value_to_string.result);
}
}
result += (")");
query_.sql += " " + result;
}
void query_compiler::visit(query_update_part &update_part)
{
query_.table = update_part.table();
query_.sql = dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(update_part.table().name);
}
void query_compiler::visit(query_delete_part &delete_part)
{
query_.sql = dialect_.token_at(dialect::token_t::REMOVE);
}
void query_compiler::visit(query_delete_from_part &delete_from_part)
{
query_.table = delete_from_part.table();
query_.sql += " " + dialect_.token_at(delete_from_part.token()) +
" " + dialect_.prepare_identifier(delete_from_part.table().name);
}
void query_compiler::visit(query_create_part &create_part)
{
query_.sql = dialect_.token_at(dialect::token_t::CREATE);
}
struct fk_context
{
std::string column;
std::string ref_table;
std::string ref_column;
};
struct column_context
{
std::vector<std::string> primary_keys;
std::vector<fk_context> foreign_contexts;
};
std::string build_create_column(const column_definition &col, const dialect &d, column_context &context);
void query_compiler::visit(query_create_table_part &create_table_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(create_table_part.table().name) + " ";
query_.table = create_table_part.table();
std::string result = "(";
column_context context;
if (create_table_part.columns().size() < 2) {
for (const auto &col: create_table_part.columns()) {
result.append(build_create_column(col, dialect_, context));
}
} else {
auto it = create_table_part.columns().begin();
result.append(build_create_column(*it++, dialect_, context));
for (; it != create_table_part.columns().end(); ++it) {
result.append(", ");
result.append(build_create_column(*it, dialect_, context));
}
}
if (!context.primary_keys.empty()) {
result.append(", CONSTRAINT PK_" + create_table_part.table().name + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
}
for (const auto &fk: context.foreign_contexts) {
result += ", CONSTRAINT FK_" + create_table_part.table().name;
result += "_" + fk.column;
result += " FOREIGN KEY (" + fk.column + ")";
result += " REFERENCES " + fk.ref_table + "(" + fk.ref_column + ")";
}
result += ")";
query_.sql += result;
}
void query_compiler::visit(query_drop_part &drop_part)
{
query_.sql = dialect_.token_at(dialect::token_t::DROP);
}
void query_compiler::visit(query_set_part &set_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::SET) + " ";
any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result;
if (set_part.key_values().size() < 2) {
for (const auto &col: set_part.key_values()) {
result.append(dialect_.prepare_identifier(col.name()) + "=");
auto var = col.value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
}
} else {
auto it = set_part.key_values().begin();
result.append(dialect_.prepare_identifier(it->name()) + "=");
auto var = (it++)->value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
for (; it != set_part.key_values().end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier((*it).name()) + "=");
var = it->value();
std::visit(value_to_string, var);
result.append(value_to_string.result);
}
}
query_.sql += result;
}
void query_compiler::visit(query_drop_table_part &drop_table_part)
{
query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(drop_table_part.table().name);
query_.table = drop_table_part.table();
}
std::string build_create_column(const column_definition &col, const dialect &d, column_context &context)
{
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
if (col.attributes().size() > 0) {
result.append("(" + std::to_string(col.attributes().size()) + ")");
}
if (!col.is_nullable()) {
result.append(" NOT NULL");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) {
result.append(" UNIQUE");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
context.primary_keys.emplace_back(col.name());
}
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
}
return result;
}
}
+178 -77
View File
@@ -1,188 +1,289 @@
#include "matador/sql/query_intermediates.hpp" #include "matador/sql/query_intermediates.hpp"
#include "matador/sql/session.hpp" #include "matador/sql/session.hpp"
#include "matador/sql/query_compiler.hpp"
#include "matador/sql/condition.hpp"
namespace matador::sql { namespace matador::sql {
basic_query_intermediate::basic_query_intermediate(connection &db, const sql::schema & schema)
: connection_(db)
, schema_(schema) {}
query_result<record> query_select_finish::fetch_all() query_result<record> query_select::fetch_all()
{ {
return connection_.fetch(builder_.compile()); query_compiler compiler(connection_.dialect());
return connection_.fetch(compiler.compile(data_.get()));
} }
record query_select_finish::fetch_one() std::optional<record> query_select::fetch_one()
{ {
return *connection_.fetch(builder_.compile()).begin().get(); query_compiler compiler(connection_.dialect());
auto result = connection_.fetch(compiler.compile(data_.get()));
auto first = result.begin();
if (first == result.end()) {
return std::nullopt;
}
return *first.get();
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch() query_context query_select::build() const
{ {
return connection_.fetch(builder_.compile().sql); query_compiler compiler(connection_.dialect());
return compiler.compile(data_.get());
} }
statement query_select_finish::prepare() std::unique_ptr<query_result_impl> query_select::fetch()
{ {
return connection_.prepare(builder_.compile()); query_compiler compiler(connection_.dialect());
return connection_.fetch(compiler.compile(data_.get()).sql);
} }
query_intermediate::query_intermediate(connection &db, query_builder &query) statement query_select::prepare()
: connection_(db), builder_(query) {}
query_offset_intermediate query_order_direction_intermediate::offset(size_t offset)
{ {
return {connection_, builder_}; query_compiler compiler(connection_.dialect());
return connection_.prepare(compiler.compile(data_.get()));
}
query_intermediate::query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr<query_data> &data)
: basic_query_intermediate(db, schema), data_(data) {}
query_offset_intermediate query_limit_intermediate::offset(size_t offset)
{
data_->parts.push_back(std::make_unique<query_offset_part>(offset));
return {connection_, schema_, data_};
} }
query_limit_intermediate query_offset_intermediate::limit(size_t limit) query_limit_intermediate query_offset_intermediate::limit(size_t limit)
{ {
return {connection_, builder_}; data_->parts.push_back(std::make_unique<query_limit_part>(limit));
return {connection_, schema_, data_};
} }
query_limit_intermediate query_order_direction_intermediate::limit(size_t limit) query_limit_intermediate query_order_direction_intermediate::limit(size_t limit)
{ {
return {connection_, builder_}; data_->parts.push_back(std::make_unique<query_limit_part>(limit));
return {connection_, schema_, data_};
} }
query_order_by_intermediate query_group_by_intermediate::order_by(const std::string &name) query_order_by_intermediate query_group_by_intermediate::order_by(const column &col)
{ {
return {connection_, builder_.order_by(name)}; data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_};
} }
query_order_direction_intermediate query_order_by_intermediate::asc() query_order_direction_intermediate query_order_by_intermediate::asc()
{ {
return {connection_, builder_.asc()}; data_->parts.push_back(std::make_unique<query_order_by_asc_part>());
return {connection_, schema_, data_};
} }
query_order_direction_intermediate query_order_by_intermediate::desc() query_order_direction_intermediate query_order_by_intermediate::desc()
{ {
return {connection_, builder_.desc()}; data_->parts.push_back(std::make_unique<query_order_by_desc_part>());
return {connection_, schema_, data_};
} }
query_group_by_intermediate query_from_intermediate::group_by(const std::string &name) query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{ {
return {connection_, builder_.group_by(name)}; if (cond) {
data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
}
return {connection_, schema_, data_};
} }
query_order_by_intermediate query_from_intermediate::order_by(const std::string &name) query_group_by_intermediate query_from_intermediate::group_by(const column &col)
{ {
return {connection_, builder_.order_by(name)}; data_->parts.push_back(std::make_unique<query_group_by_part>(col));
return {connection_, schema_, data_};
} }
query_group_by_intermediate query_where_intermediate::group_by(const std::string &name) query_order_by_intermediate query_from_intermediate::order_by(const column &col)
{ {
return {connection_, builder_.group_by(name)}; data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_};
} }
query_order_by_intermediate query_where_intermediate::order_by(const std::string &name) query_group_by_intermediate query_where_intermediate::group_by(const column &col)
{ {
return {connection_, builder_.order_by(name)}; data_->parts.push_back(std::make_unique<query_group_by_part>(col));
return {connection_, schema_, data_};
} }
query_where_intermediate query_from_intermediate::where(const basic_condition &cond) query_order_by_intermediate query_where_intermediate::order_by(const column &col)
{ {
return query_where_intermediate{connection_, builder_.where(cond)}; data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_};
} }
query_select_intermediate::query_select_intermediate(connection &s, const std::vector<column>& columns) query_on_intermediate query_join_intermediate::on_clause(std::unique_ptr<basic_condition> &&cond)
: query_start_intermediate(s)
{ {
builder_.select(columns); data_->parts.push_back(std::make_unique<query_on_part>(std::move(cond)));
return {connection_, schema_, data_};
} }
query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as) query_join_intermediate query_from_intermediate::join_left(const table &t)
{ {
return {connection_, builder_.from(table, as)}; data_->parts.push_back(std::make_unique<query_join_part>(t));
return {connection_, schema_, data_};
} }
query_insert_intermediate::query_insert_intermediate(connection &s) query_from_intermediate query_from_intermediate::join_left(join_data &data)
: query_start_intermediate(s)
{ {
builder_.insert(); data_->parts.push_back(std::make_unique<query_join_part>(data.join_table));
data_->parts.push_back(std::make_unique<query_on_part>(std::move(data.condition)));
return {connection_, schema_, data_};
} }
query_into_intermediate query_insert_intermediate::into(const std::string &table, std::initializer_list<std::string> column_names) query_from_intermediate query_from_intermediate::join_left(std::vector<join_data> &data_vector)
{ {
return {connection_, builder_.into(table, column_names)}; for (auto &data : data_vector) {
data_->parts.push_back(std::make_unique<query_join_part>(data.join_table));
data_->parts.push_back(std::make_unique<query_on_part>(std::move(data.condition)));
}
return {connection_, schema_, data_};
} }
size_t query_execute_finish::execute() query_select_intermediate::query_select_intermediate(connection &db, const sql::schema &schema, const std::vector<column>& columns)
: query_start_intermediate(db, schema)
{ {
return connection_.execute(builder_.compile().sql); data_->parts.push_back(std::make_unique<query_select_part>(columns));
} }
statement query_execute_finish::prepare() query_from_intermediate query_select_intermediate::from(const table& t)
{ {
return connection_.prepare(builder_.compile()); data_->parts.push_back(std::make_unique<query_from_part>(t));
return {connection_, schema_, data_};
} }
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values) query_insert_intermediate::query_insert_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema)
{ {
return {connection_, builder_.values(values)}; data_->parts.push_back(std::make_unique<query_insert_part>());
} }
query_create_intermediate::query_create_intermediate(connection &db) query_into_intermediate query_insert_intermediate::into(const sql::table &table, std::initializer_list<column> column_names)
: query_start_intermediate(db) {
builder_.create();
}
query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns)
{ {
return {connection_, builder_.table(table, columns)}; return into(table, std::move(std::vector<column>{column_names}));
} }
std::shared_ptr<table_repository> query_create_intermediate::tables() const query_into_intermediate query_insert_intermediate::into(const table &table, std::vector<column> &&column_names)
{ {
return connection_.tables(); data_->parts.push_back(std::make_unique<query_into_part>(table, column_names));
return {connection_, schema_, data_};
} }
query_drop_intermediate::query_drop_intermediate(connection &s) query_into_intermediate query_insert_intermediate::into(const table &table)
: query_start_intermediate(s)
{ {
builder_.drop(); data_->parts.push_back(std::make_unique<query_into_part>(table, table.columns));
return {connection_, schema_, data_};
} }
query_execute_finish query_drop_intermediate::table(const std::string &table) size_t query_execute::execute()
{ {
return {connection_, builder_.table(table)}; query_compiler compiler(connection_.dialect());
return connection_.execute(compiler.compile(data_.get()).sql);
} }
query_execute_finish query_execute_where_intermediate::limit(int limit) statement query_execute::prepare()
{ {
return {connection_, builder_.limit(limit)}; query_compiler compiler(connection_.dialect());
return connection_.prepare(compiler.compile(data_.get()));
} }
query_execute_where_intermediate query_set_intermediate::where(const basic_condition &cond) query_context query_execute::build() const
{ {
return {connection_, builder_.where(cond)}; query_compiler compiler(connection_.dialect());
return compiler.compile(data_.get());
} }
query_update_intermediate::query_update_intermediate(connection &s, const std::string& table_name) query_execute query_into_intermediate::values(std::initializer_list<any_type> values)
: query_start_intermediate(s)
{ {
builder_.update(table_name); return this->values(std::vector<any_type>(values));
}
query_execute query_into_intermediate::values(std::vector<any_type> &&values)
{
data_->parts.push_back(std::make_unique<query_values_part>(std::move(values)));
return {connection_, schema_, data_};
}
query_create_intermediate::query_create_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema) {
data_->parts.push_back(std::make_unique<query_create_part>());
}
query_execute query_create_intermediate::table(const sql::table &table, std::initializer_list<column_definition> columns)
{
return this->table(table, std::vector<column_definition>{columns});
}
query_execute query_create_intermediate::table(const sql::table &table, const std::vector<column_definition> &columns)
{
data_->parts.push_back(std::make_unique<query_create_table_part>(table, columns));
return {connection_, schema_, data_};
}
query_drop_intermediate::query_drop_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema)
{
data_->parts.push_back(std::make_unique<query_drop_part>());
}
query_execute query_drop_intermediate::table(const sql::table &table)
{
data_->parts.push_back(std::make_unique<query_drop_table_part>(table));
return {connection_, schema_, data_};
}
query_order_by_intermediate query_execute_where_intermediate::order_by(const column &col)
{
data_->parts.push_back(std::make_unique<query_order_by_part>(col));
return {connection_, schema_, data_};
}
query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
{
data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
return {connection_, schema_, data_};
}
query_update_intermediate::query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table)
: query_start_intermediate(db, schema)
{
data_->parts.push_back(std::make_unique<query_update_part>(table));
} }
query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns) query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns)
{ {
return {connection_, builder_.set(columns)}; return set(std::vector<key_value_pair>{columns});
} }
query_execute_where_intermediate query_delete_from_intermediate::where(const basic_condition &cond) query_set_intermediate query_update_intermediate::set(std::vector<key_value_pair> &&columns)
{ {
return {connection_, builder_.where(cond)}; data_->parts.push_back(std::make_unique<query_set_part>(std::move(columns)));
return {connection_, schema_, data_};
} }
query_delete_intermediate::query_delete_intermediate(connection &s) query_execute_where_intermediate query_delete_from_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
: query_start_intermediate(s)
{ {
builder_.remove(); data_->parts.push_back(std::make_unique<query_where_part>(std::move(cond)));
return {connection_, schema_, data_};
} }
query_delete_from_intermediate query_delete_intermediate::from(const std::string &table) query_delete_intermediate::query_delete_intermediate(connection &db, const sql::schema &schema)
: query_start_intermediate(db, schema)
{ {
return {connection_, builder_.from(table)}; data_->parts.push_back(std::make_unique<query_delete_part>());
} }
query_start_intermediate::query_start_intermediate(connection &s) query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table)
: connection_(s) {
, builder_(s.dialect()) data_->parts.push_back(std::make_unique<query_delete_from_part>(table));
return {connection_, schema_, data_};
}
query_start_intermediate::query_start_intermediate(connection &db, const sql::schema &schema)
: basic_query_intermediate(db, schema)
{} {}
} }
+13
View File
@@ -0,0 +1,13 @@
#include "matador/sql/query_part.hpp"
namespace matador::sql {
query_part::query_part(sql::dialect::token_t token)
: token_(token) {}
dialect::token_t query_part::token() const
{
return token_;
}
}
+300
View File
@@ -0,0 +1,300 @@
#include <utility>
#include "matador/sql/query_parts.hpp"
#include "matador/sql/basic_condition.hpp"
namespace matador::sql {
query_select_part::query_select_part(std::vector<column> columns)
: query_part(sql::dialect::token_t::SELECT)
, columns_(std::move(columns)) {}
void query_select_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
const std::vector<column>& query_select_part::columns() const
{
return columns_;
}
query_from_part::query_from_part(sql::table t)
: query_part(sql::dialect::token_t::FROM)
, table_(std::move(t)) {}
const sql::table &query_from_part::table() const
{
return table_;
}
void query_from_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_join_part::query_join_part(sql::table t)
: query_part(sql::dialect::token_t::JOIN)
, table_(std::move(t)) {}
const sql::table &query_join_part::table() const
{
return table_;
}
void query_join_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_on_part::query_on_part(std::unique_ptr<basic_condition> &&cond)
: query_part(dialect::token_t::ON)
, condition_(std::move(cond)) {}
const basic_condition &query_on_part::condition() const
{
return *condition_;
}
void query_on_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_where_part::query_where_part(std::unique_ptr<basic_condition> &&cond)
: query_part(dialect::token_t::WHERE)
, condition_(std::move(cond)) {}
void query_where_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
const basic_condition &query_where_part::condition() const
{
return *condition_;
}
query_table_name_part::query_table_name_part(sql::dialect::token_t token, std::string table_name)
: query_part(token)
, table_name_(std::move(table_name)) {}
query_group_by_part::query_group_by_part(sql::column col)
: query_part(dialect::token_t::GROUP_BY)
, column_(std::move(col))
{}
const sql::column &query_group_by_part::column() const
{
return column_;
}
void query_group_by_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_order_by_part::query_order_by_part(sql::column col)
: query_part(dialect::token_t::ORDER_BY)
, column_(std::move(col))
{}
const sql::column &query_order_by_part::column() const
{
return column_;
}
void query_order_by_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_order_by_asc_part::query_order_by_asc_part()
: query_part(dialect::token_t::ASC)
{}
void query_order_by_asc_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_order_by_desc_part::query_order_by_desc_part()
: query_part(dialect::token_t::DESC)
{}
void query_order_by_desc_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_offset_part::query_offset_part(size_t offset)
: query_part(dialect::token_t::OFFSET)
, offset_(offset) {}
size_t query_offset_part::offset() const
{
return offset_;
}
void query_offset_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_limit_part::query_limit_part(size_t limit)
: query_part(dialect::token_t::LIMIT)
, limit_(limit) {}
size_t query_limit_part::limit() const
{
return limit_;
}
void query_limit_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_insert_part::query_insert_part()
: query_part(dialect::token_t::INSERT) {}
void query_insert_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_into_part::query_into_part(sql::table t, std::vector<sql::column> columns)
: query_part(dialect::token_t::INSERT)
, table_(std::move(t))
, columns_(std::move(columns)) {}
const sql::table &query_into_part::table() const
{
return table_;
}
const std::vector<column> &query_into_part::columns() const
{
return columns_;
}
void query_into_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_values_part::query_values_part(std::vector<any_type> &&values)
: query_part(sql::dialect::token_t::VALUES)
, values_(std::move(values)) {}
const std::vector<any_type>& query_values_part::values() const
{
return values_;
}
void query_values_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_update_part::query_update_part(sql::table table)
: query_part(dialect::token_t::UPDATE)
, table_(std::move(table)) {}
const sql::table& query_update_part::table() const
{
return table_;
}
void query_update_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_set_part::query_set_part(const std::vector<sql::key_value_pair>& key_value_pairs)
: query_part(sql::dialect::token_t::SET)
, key_value_pairs_(key_value_pairs) {}
const std::vector<sql::key_value_pair> &query_set_part::key_values() const
{
return key_value_pairs_;
}
void query_set_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_delete_part::query_delete_part()
: query_part(sql::dialect::token_t::REMOVE) {}
void query_delete_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_delete_from_part::query_delete_from_part(sql::table table)
: query_part(sql::dialect::token_t::FROM)
, table_(std::move(table)) {}
const sql::table &query_delete_from_part::table() const
{
return table_;
}
void query_delete_from_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_create_part::query_create_part()
: query_part(sql::dialect::token_t::CREATE) {}
void query_create_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_create_table_part::query_create_table_part(sql::table table, std::vector<sql::column_definition> columns)
: query_part(sql::dialect::token_t::TABLE)
, table_(std::move(table))
, columns_(std::move(columns)) {}
const sql::table &query_create_table_part::table() const
{
return table_;
}
const std::vector<sql::column_definition> &query_create_table_part::columns() const
{
return columns_;
}
void query_create_table_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_drop_part::query_drop_part()
: query_part(sql::dialect::token_t::DROP) {}
void query_drop_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_drop_table_part::query_drop_table_part(sql::table table)
: query_part(sql::dialect::token_t::TABLE)
, table_(std::move(table)) {}
const sql::table &query_drop_table_part::table() const
{
return table_;
}
void query_drop_table_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
}
+8 -2
View File
@@ -1,11 +1,17 @@
#include "matador/sql/query_result.hpp" #include "matador/sql/query_result.hpp"
#include "matador/sql/record.hpp"
namespace matador::sql::detail { namespace matador::sql::detail {
template<> template<>
record *create_prototype<record>(const record &prototype) record *create_prototype<record>(const std::vector<column_definition> &prototype)
{ {
return new record{prototype}; auto result = std::make_unique<record>();
for (const auto &col: prototype) {
result->append({col.name(), col.type(), col.attributes().size(), col.index()});
}
return result.release();
} }
} }
+5 -4
View File
@@ -1,5 +1,6 @@
#include "matador/sql/query_result_impl.hpp" #include "matador/sql/query_result_impl.hpp"
#include "matador/sql/query_result_reader.hpp" #include "matador/sql/query_result_reader.hpp"
#include "matador/sql/value.hpp"
namespace matador::sql { namespace matador::sql {
@@ -11,7 +12,7 @@ void detail::pk_reader::on_primary_key(const char *id, std::string &value, size_
data_type_traits<std::string>::read_value(reader_, id, column_index_++, value, size); data_type_traits<std::string>::read_value(reader_, id, column_index_++, value, size);
} }
query_result_impl::query_result_impl(std::unique_ptr<query_result_reader> &&reader, record prototype) query_result_impl::query_result_impl(std::unique_ptr<query_result_reader> &&reader, std::vector<column_definition> prototype)
: prototype_(std::move(prototype)) : prototype_(std::move(prototype))
, reader_(std::move(reader)) , reader_(std::move(reader))
, pk_reader_(*reader_) , pk_reader_(*reader_)
@@ -39,12 +40,12 @@ void query_result_impl::on_attribute(const char *id, std::string &value, const u
} }
void void
query_result_impl::on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr) query_result_impl::on_attribute(const char *id, value &val, const utils::field_attributes &attr)
{ {
reader_->read_value(id, column_index_++, value, type, attr.size()); reader_->read_value(id, column_index_++, val, attr.size());
} }
const record& query_result_impl::prototype() const const std::vector<column_definition>& query_result_impl::prototype() const
{ {
return prototype_; return prototype_;
} }

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