query progress

This commit is contained in:
Sascha Kuehl 2024-02-28 18:02:12 +01:00
parent b9709d14c2
commit 7e1713ddd3
25 changed files with 405 additions and 221 deletions

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 {

View File

@ -66,7 +66,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()) {

View File

@ -12,7 +12,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest) include(CTest)
include(Catch) include(Catch)
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test") set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:5432/matador_test")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)

View File

@ -90,7 +90,7 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[s
for (const auto &i: result_record) { for (const auto &i: result_record) {
REQUIRE(i.size() == 4); REQUIRE(i.size() == 4);
REQUIRE(i.at(0).name() == "id"); REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_unsigned_long); REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).as<long long>() == george.id); REQUIRE(i.at(0).as<long long>() == george.id);
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).type() == data_type_t::type_varchar);

View File

@ -6,7 +6,7 @@
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "query_helper.hpp" #include "matador/sql/query_helper.hpp"
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -21,7 +21,8 @@ struct author
bool distinguished{false}; bool distinguished{false};
template<typename Operator> template<typename Operator>
void process(Operator &op) { void process(Operator &op)
{
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "first_name", first_name, 63); field::attribute(op, "first_name", first_name, 63);
@ -40,7 +41,8 @@ struct book
unsigned short published_in{}; unsigned short published_in{};
template<typename Operator> template<typename Operator>
void process(Operator &op) { void process(Operator &op)
{
namespace field = matador::utils::access; namespace field = matador::utils::access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "title", title, 511); field::attribute(op, "title", title, 511);
@ -60,7 +62,6 @@ int main()
const std::string env_var{"MATADOR_BACKENDS_PATH"}; const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"}; std::string dns{"sqlite://demo.db"};
// std::string dns{"memory://test"};
auto s = std::make_shared<schema>("main"); auto s = std::make_shared<schema>("main");
s->attach<author>("authors"); s->attach<author>("authors");
s->attach<book>("books"); s->attach<book>("books");
@ -71,10 +72,12 @@ int main()
auto create_authors_sql = c.query(s) auto create_authors_sql = c.query(s)
.create() .create()
.table<author>(qh::authors) .table<author>(qh::authors)
// .str();
.execute(); .execute();
c.query(s).create().table<book>(qh::books).execute(); c.query(s)
.create()
.table<book>(qh::books)
.execute();
std::cout << "SQL: " << create_authors_sql << "\n"; std::cout << "SQL: " << create_authors_sql << "\n";
@ -90,7 +93,6 @@ int main()
.into<author>(qh::authors) .into<author>(qh::authors)
.values(*mc) .values(*mc)
.execute(); .execute();
// .str();
std::cout << "SQL: " << insert_authors_sql << "\n"; std::cout << "SQL: " << insert_authors_sql << "\n";
@ -105,10 +107,10 @@ int main()
auto update_authors_sql = c.query(s) auto update_authors_sql = c.query(s)
.update(qh::authors) .update(qh::authors)
.set({{qh::authors.first_name, "Stephen"}, {qh::authors.last_name, "King"}}) .set({{qh::authors.first_name, "Stephen"},
{qh::authors.last_name, "King"}})
.where(qh::authors.last_name == "Crichton") .where(qh::authors.last_name == "Crichton")
.execute(); .execute();
// .str();
std::cout << "SQL: " << update_authors_sql << "\n"; std::cout << "SQL: " << update_authors_sql << "\n";
@ -143,11 +145,7 @@ int main()
.order_by(qh::books.title).asc() .order_by(qh::books.title).asc()
.limit(5) .limit(5)
.offset(2) .offset(2)
// .str();
.fetch_all(); .fetch_all();
// .fetch_all<book>();
// std::cout << "SQL: " << select_books_sql << "\n";
for (const auto &r: select_books_sql) { for (const auto &r: select_books_sql) {
std::cout << "R: " << r.at(qh::books.title) << ", " << r.at(qh::authors.last_name) << "\n"; std::cout << "R: " << r.at(qh::books.title) << ", " << r.at(qh::authors.last_name) << "\n";
@ -159,21 +157,11 @@ int main()
// ORDER BY "book.title" ASC // ORDER BY "book.title" ASC
// OFFSET 2 LIMIT 5 // OFFSET 2 LIMIT 5
// char var[1024];
// size_t len{};
// const auto error = getenv_s(&len, var, 1024, env_var.c_str());
// if (error > 0) {
// std::cout << "error: unknown env var " << env_var << "\n";
// } else {
// std::cout << "env var: " << var << "\n";
// }
c.query(s).drop().table(qh::books).execute(); c.query(s).drop().table(qh::books).execute();
auto drop_authors_sql = c.query(s) auto drop_authors_sql = c.query(s)
.drop() .drop()
.table(qh::authors) .table(qh::authors)
// .str();
.execute(); .execute();
std::cout << "SQL: " << drop_authors_sql << "\n"; std::cout << "SQL: " << drop_authors_sql << "\n";

View File

@ -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

View File

@ -41,6 +41,7 @@ private:
void visit(query_set_part &set_part) override; void visit(query_set_part &set_part) override;
void visit(query_delete_part &delete_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_part &create_part) override;
void visit(query_create_table_part &create_table_part) override; void visit(query_create_table_part &create_table_part) override;

View File

@ -2,6 +2,7 @@
#define QUERY_QUERY_CONTEXT_HPP #define QUERY_QUERY_CONTEXT_HPP
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include "matador/sql/table.hpp"
namespace matador::sql { namespace matador::sql {
@ -9,7 +10,7 @@ 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; record 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;

View File

@ -47,7 +47,7 @@ public:
size_t execute(); size_t execute();
statement prepare(); statement prepare();
[[nodiscard]] std::string str() const; [[nodiscard]] query_context build() const;
}; };
class query_select_finish : public query_intermediate class query_select_finish : public query_intermediate
@ -72,7 +72,7 @@ public:
statement prepare(); statement prepare();
[[nodiscard]] std::string str() const; [[nodiscard]] query_context build() const;
private: private:
std::unique_ptr<query_result_impl> fetch(); std::unique_ptr<query_result_impl> fetch();
@ -275,7 +275,7 @@ class query_execute_where_intermediate : public query_execute_finish
public: public:
using query_execute_finish::query_execute_finish; using query_execute_finish::query_execute_finish;
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_finish

View File

@ -16,6 +16,8 @@ public:
virtual ~query_part() = default; virtual ~query_part() = default;
virtual void accept(query_part_visitor &visitor) = 0; virtual void accept(query_part_visitor &visitor) = 0;
[[nodiscard]] dialect::token_t token() const;
protected: protected:
sql::dialect::token_t token_; sql::dialect::token_t token_;
}; };

View File

@ -20,6 +20,7 @@ class query_values_part;
class query_update_part; class query_update_part;
class query_set_part; class query_set_part;
class query_delete_part; class query_delete_part;
class query_delete_from_part;
class query_create_part; class query_create_part;
class query_create_table_part; class query_create_table_part;
class query_drop_part; class query_drop_part;
@ -50,6 +51,7 @@ public:
virtual void visit(query_set_part &set_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_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_part &create_part) = 0;
virtual void visit(query_create_table_part &create_table_part) = 0; virtual void visit(query_create_table_part &create_table_part) = 0;

View File

@ -157,7 +157,7 @@ class query_offset_part : public query_part
public: public:
explicit query_offset_part(size_t offset); explicit query_offset_part(size_t offset);
size_t offset() const; [[nodiscard]] size_t offset() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
@ -171,7 +171,7 @@ class query_limit_part : public query_part
public: public:
explicit query_limit_part(size_t limit); explicit query_limit_part(size_t limit);
size_t limit() const; [[nodiscard]] size_t limit() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;
@ -210,7 +210,7 @@ private:
class query_values_part : public query_part class query_values_part : public query_part
{ {
public: public:
query_values_part(std::vector<any_type> &&values); explicit query_values_part(std::vector<any_type> &&values);
[[nodiscard]] const std::vector<any_type>& values() const; [[nodiscard]] const std::vector<any_type>& values() const;
@ -258,6 +258,20 @@ private:
void accept(query_part_visitor &visitor) override; 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 class query_create_part : public query_part
{ {
public: public:
@ -273,7 +287,7 @@ public:
query_create_table_part(sql::table table, std::vector<sql::column_definition> columns); query_create_table_part(sql::table table, std::vector<sql::column_definition> columns);
[[nodiscard]] const sql::table& table() const; [[nodiscard]] const sql::table& table() const;
const std::vector<sql::column_definition>& columns() const; [[nodiscard]] const std::vector<sql::column_definition>& columns() const;
private: private:
void accept(query_part_visitor &visitor) override; void accept(query_part_visitor &visitor) override;

View File

@ -17,8 +17,8 @@ struct table_info
{ {
std::string name; std::string name;
record prototype; record prototype;
void create(connection &conn) const; void create(connection &conn, schema &scm) const;
void drop(connection &conn) const; void drop(connection &conn, schema &scm) const;
}; };
class schema class schema

View File

@ -34,6 +34,7 @@ set(SQL_SOURCES
sql/query_compiler.cpp sql/query_compiler.cpp
sql/noop_connection.cpp sql/noop_connection.cpp
sql/query_part.cpp sql/query_part.cpp
sql/any_type_to_string_visitor.cpp
) )
set(SQL_HEADER set(SQL_HEADER
@ -83,6 +84,8 @@ set(SQL_HEADER
../include/matador/sql/table.hpp ../include/matador/sql/table.hpp
../include/matador/sql/noop_connection.hpp ../include/matador/sql/noop_connection.hpp
../include/matador/sql/query_part.hpp ../include/matador/sql/query_part.hpp
../include/matador/sql/any_type_to_string_visitor.hpp
../include/matador/sql/query_helper.hpp
) )
set(QUERY_SOURCES set(QUERY_SOURCES

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);
}
}

View File

@ -97,7 +97,7 @@ sql::query connection::query(const std::shared_ptr<sql::schema> &schema) const
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() || q.prototype.unknown()) {
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()) { if (const auto rit = table_prototype.find(col.name()); col.type() == data_type_t::type_unknown && rit != table_prototype.end()) {
const_cast<column_definition&>(col).type(rit->type()); const_cast<column_definition&>(col).type(rit->type());

View File

@ -173,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;
@ -213,7 +213,7 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
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 = "(";
@ -252,7 +252,7 @@ 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;
} }
@ -267,7 +267,7 @@ query_builder &query_builder::into(const std::string &table, const std::vector<c
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) {
@ -337,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;
} }

View File

@ -2,7 +2,7 @@
#include "matador/sql/query_data.hpp" #include "matador/sql/query_data.hpp"
#include "matador/sql/column_definition.hpp" #include "matador/sql/column_definition.hpp"
#include "matador/sql/dialect.hpp" #include "matador/sql/dialect.hpp"
#include "matador/sql/query_builder.hpp" #include "matador/sql/any_type_to_string_visitor.hpp"
#include "matador/utils/string.hpp" #include "matador/utils/string.hpp"
@ -53,7 +53,7 @@ void query_compiler::visit(query_select_part &select_part)
void query_compiler::visit(query_from_part &from_part) void query_compiler::visit(query_from_part &from_part)
{ {
query_.table_name = from_part.table().name; query_.table = from_part.table();
if (dialect_.default_schema_name().empty()) { if (dialect_.default_schema_name().empty()) {
query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) + query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(from_part.table().name) + " " + dialect_.prepare_identifier(from_part.table().name) +
@ -125,7 +125,7 @@ void query_compiler::visit(query_insert_part &insert_part)
void query_compiler::visit(query_into_part &into_part) void query_compiler::visit(query_into_part &into_part)
{ {
query_.table_name = into_part.table().name; query_.table = into_part.table();
query_.sql += " " + dialect_.token_at(dialect::token_t::INTO) + query_.sql += " " + dialect_.token_at(dialect::token_t::INTO) +
" " + dialect_.prepare_identifier(into_part.table().name); " " + dialect_.prepare_identifier(into_part.table().name);
@ -150,7 +150,7 @@ void query_compiler::visit(query_values_part &values_part)
{ {
query_.sql += " " + dialect_.token_at(dialect::token_t::VALUES); query_.sql += " " + dialect_.token_at(dialect::token_t::VALUES);
detail::any_type_to_string_visitor value_to_string(dialect_, query_); any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result{"("}; std::string result{"("};
if (values_part.values().size() < 2) { if (values_part.values().size() < 2) {
@ -177,13 +177,20 @@ void query_compiler::visit(query_values_part &values_part)
void query_compiler::visit(query_update_part &update_part) void query_compiler::visit(query_update_part &update_part)
{ {
query_.table_name = update_part.table().name; query_.table = update_part.table();
query_.sql = dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(update_part.table().name); 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) 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) void query_compiler::visit(query_create_part &create_part)
@ -209,7 +216,7 @@ std::string build_create_column(const column_definition &col, const dialect &d,
void query_compiler::visit(query_create_table_part &create_table_part) 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_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(create_table_part.table().name) + " ";
query_.table_name = create_table_part.table().name; query_.table = create_table_part.table();
std::string result = "("; std::string result = "(";
@ -251,7 +258,7 @@ void query_compiler::visit(query_set_part &set_part)
{ {
query_.sql += " " + dialect_.token_at(dialect::token_t::SET) + " "; query_.sql += " " + dialect_.token_at(dialect::token_t::SET) + " ";
detail::any_type_to_string_visitor value_to_string(dialect_, query_); any_type_to_string_visitor value_to_string(dialect_, query_);
std::string result; std::string result;
if (set_part.key_values().size() < 2) { if (set_part.key_values().size() < 2) {
for (const auto &col: set_part.key_values()) { for (const auto &col: set_part.key_values()) {
@ -281,7 +288,7 @@ void query_compiler::visit(query_set_part &set_part)
void query_compiler::visit(query_drop_table_part &drop_table_part) 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_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(drop_table_part.table().name);
query_.table_name = 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 build_create_column(const column_definition &col, const dialect &d, column_context &context)

View File

@ -20,10 +20,10 @@ record query_select_finish::fetch_one()
return *connection_.fetch(compiler.compile(&data_)).begin().get(); return *connection_.fetch(compiler.compile(&data_)).begin().get();
} }
std::string query_select_finish::str() const query_context query_select_finish::build() const
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return compiler.compile(&data_).sql; return compiler.compile(&data_);
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch() std::unique_ptr<query_result_impl> query_select_finish::fetch()
@ -184,10 +184,10 @@ statement query_execute_finish::prepare()
return connection_.prepare(compiler.compile(&data_)); return connection_.prepare(compiler.compile(&data_));
} }
std::string query_execute_finish::str() const query_context query_execute_finish::build() const
{ {
query_compiler compiler(connection_.dialect()); query_compiler compiler(connection_.dialect());
return compiler.compile(&data_).sql; return compiler.compile(&data_);
} }
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values) query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values)
@ -229,10 +229,10 @@ query_execute_finish query_drop_intermediate::table(const sql::table &table)
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }
query_execute_finish query_execute_where_intermediate::limit(int limit) 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_}; return {connection_, schema_, data_};
// return {connection_, builder_.limit(limit)};
} }
query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond) query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr<basic_condition> &&cond)
@ -273,6 +273,7 @@ query_delete_intermediate::query_delete_intermediate(connection &db, const std::
query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table) query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table)
{ {
data_.parts.push_back(std::make_unique<query_delete_from_part>(table));
return {connection_, schema_, data_}; return {connection_, schema_, data_};
} }

View File

@ -5,5 +5,9 @@ namespace matador::sql {
query_part::query_part(sql::dialect::token_t token) query_part::query_part(sql::dialect::token_t token)
: token_(token) {} : token_(token) {}
dialect::token_t query_part::token() const
{
return token_;
}
} }

View File

@ -233,6 +233,20 @@ void query_delete_part::accept(query_part_visitor &visitor)
visitor.visit(*this); 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_create_part::query_create_part()
: query_part(sql::dialect::token_t::CREATE) {} : query_part(sql::dialect::token_t::CREATE) {}

View File

@ -5,12 +5,12 @@
namespace matador::sql { namespace matador::sql {
void table_info::create(connection &conn) const void table_info::create(connection &conn, schema &scm) const
{ {
// conn.query().create().table(name, prototype.columns()).execute(); // conn.query().create().table(name, prototype.columns()).execute();
} }
void table_info::drop(connection &conn) const void table_info::drop(connection &conn, schema &scm) const
{ {
// conn.query().drop().table(name).execute(); // conn.query().drop().table(name).execute();
} }

View File

@ -15,7 +15,7 @@ void session::create_schema()
{ {
auto c = pool_.acquire(); auto c = pool_.acquire();
for (const auto &t : *schema_) { for (const auto &t : *schema_) {
t.second.create(*c); t.second.create(*c, *schema_);
} }
} }
@ -35,9 +35,9 @@ query_result<record> session::fetch(const query_context &q) const
if (!c.valid()) { if (!c.valid()) {
throw std::logic_error("no database connection available"); throw std::logic_error("no database connection available");
} }
auto it = prototypes_.find(q.table_name); auto it = prototypes_.find(q.table.name);
if (it == prototypes_.end()) { if (it == prototypes_.end()) {
it = prototypes_.emplace(q.table_name, c->describe(q.table_name)).first; it = prototypes_.emplace(q.table.name, c->describe(q.table.name)).first;
} }
// adjust columns from given query // adjust columns from given query
for (auto &col : q.prototype) { for (auto &col : q.prototype) {

View File

@ -10,190 +10,242 @@
using namespace matador::sql; using namespace matador::sql;
using namespace matador::utils; using namespace matador::utils;
TEST_CASE("Create table sql statement string", "[query]") { TEST_CASE("Create table sql statement string", "[query]")
{
connection noop("noop://noop.db"); connection noop("noop://noop.db");
auto scm = std::make_shared<schema>("noop"); auto scm = std::make_shared<schema>("noop");
query qu(noop, scm); query q(noop, scm);
auto s = qu.create().table({"person"}, { auto result = q.create().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")
}).str(); }).build();
dialect d = dialect_builder::builder().create().build(); REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
query_builder query(d); REQUIRE(result.table.name == "person");
auto q = query.create().table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
}).compile();
REQUIRE(s == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); result = q.create().table("person", {
// REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
REQUIRE(q.table_name == "person");
q = query.create().table("person", {
make_pk_column<unsigned long>("id"), make_pk_column<unsigned long>("id"),
make_column<std::string>("name", {255, constraints::UNIQUE}, null_option::NOT_NULL), make_column<std::string>("name", {255, constraints::UNIQUE}, null_option::NOT_NULL),
make_column<unsigned short>("age"), make_column<unsigned short>("age"),
make_fk_column<unsigned long>("address", "address", "id") make_fk_column<unsigned long>("address", "address", "id")
}).compile(); }).build();
REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##"); REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Drop table sql statement string", "[query]") { TEST_CASE("Drop table sql statement string", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.drop().table("person").compile(); auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.drop().table("person").build();
REQUIRE(q.sql == R"(DROP TABLE "person")"); REQUIRE(result.sql == R"(DROP TABLE "person")");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Select sql statement string", "[query]") { TEST_CASE("Select sql statement string", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.select({"id", "name", "age"}).from("person").compile(); auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.select({"id", "name", "age"}).from("person").build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person")"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person")");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Insert sql statement string", "[query]") { TEST_CASE("Insert sql statement string", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.insert().into("person", { auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.insert().into("person", {
"id", "name", "age" "id", "name", "age"
}).values({7UL, "george", 65U}).compile(); }).values({7UL, "george", 65U}).build();
REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))"); REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Update sql statement string", "[query]") { TEST_CASE("Update sql statement string", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.update("person").set({ auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.update("person").set({
{"id", 7UL}, {"id", 7UL},
{"name", "george"}, {"name", "george"},
{"age", 65U} {"age", 65U}
}).compile(); }).build();
REQUIRE(q.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)"); REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Delete sql statement string", "[query]") { TEST_CASE("Update limit sql statement", "[query][update][limit]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.remove().from("person").compile(); auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.update("person")
.set({{"id", 7UL}, {"name", "george"}, {"age", 65U}})
.where("name"_col == "george")
.order_by("id"_col).asc()
.limit(2)
.build();
REQUIRE(q.sql == R"(DELETE FROM "person")"); REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Select sql statement string with where clause", "[query]") { TEST_CASE("Delete sql statement string", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
auto q = query.select({"id", "name", "age"}) auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.remove().from("person").build();
REQUIRE(result.sql == R"(DELETE FROM "person")");
REQUIRE(result.table.name == "person");
}
TEST_CASE("Delete limit sql statement", "[query][delete][limit]")
{
connection noop("noop://noop.db");
auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.remove()
.from("person")
.where("name"_col == "george")
.order_by("id"_col).asc()
.limit(2)
.build();
REQUIRE(result.sql == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
REQUIRE(result.table.name == "person");
}
TEST_CASE("Select sql statement string with where clause", "[query]")
{
connection noop("noop://noop.db");
auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
auto result = q.select({"id", "name", "age"})
.from("person") .from("person")
.where("id"_col == 8 && "age"_col > 50) .where("id"_col == 8 && "age"_col > 50)
.compile(); .build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
q = query.select({"id", "name", "age"}) result = q.select({"id", "name", "age"})
.from("person") .from("person")
.where("id"_col == _ && "age"_col > 50) .where("id"_col == _ && "age"_col > 50)
.compile(); .build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Insert sql statement with placeholder", "[query]") { TEST_CASE("Insert sql statement with placeholder", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.insert().into("person", { auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.insert().into("person", {
"id", "name", "age" "id", "name", "age"
}).values({_, _, _}).compile(); }).values({_, _, _}).build();
REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))"); REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
REQUIRE(q.bind_vars.size() == 3); REQUIRE(result.bind_vars.size() == 3);
} }
TEST_CASE("Select sql statement string with order by", "[query]") { TEST_CASE("Select sql statement string with order by", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.select({"id", "name", "age"}) auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.select({"id", "name", "age"})
.from("person") .from("person")
.order_by("name").asc() .order_by("name").asc()
.compile(); .build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Select sql statement string with group by", "[query]") { TEST_CASE("Select sql statement string with group by", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.select({"id", "name", "age"}) auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.select({"id", "name", "age"})
.from("person") .from("person")
.group_by("age") .group_by("age")
.compile(); .build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Select sql statement string with offset and limit", "[query]") { TEST_CASE("Select sql statement string with offset and limit", "[query]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
const auto q = query.select({"id", "name", "age"}) auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
const auto result = q.select({"id", "name", "age"})
.from("person") .from("person")
.offset(10) .order_by("id"_col).asc()
.limit(20) .limit(20)
.compile(); .offset(10)
.build();
REQUIRE(q.sql == R"(SELECT "id", "name", "age" FROM "person" OFFSET 10 LIMIT 20)"); REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Create, insert and select a blob column", "[query][blob]") { TEST_CASE("Create, insert and select a blob column", "[query][blob]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
auto q = query.create().table("person", { auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
auto result = q.create().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<blob>("data") make_column<blob>("data")
}).compile(); }).build();
REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
q = query.insert().into("person", { result = q.insert().into("person", {
{ "id", "name", "data" } "id", "name", "data"
}).values({7UL, "george", blob{1, 'A', 3, 4}}).compile(); }).values({7UL, "george", blob{1, 'A', 3, 4}}).build();
REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))"); REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
q = query.select({"id", "name", "data"}).from("person").compile(); result = q.select({"id", "name", "data"}).from("person").build();
REQUIRE(q.sql == R"(SELECT "id", "name", "data" FROM "person")"); REQUIRE(result.sql == R"(SELECT "id", "name", "data" FROM "person")");
REQUIRE(q.table_name == "person"); REQUIRE(result.table.name == "person");
} }
TEST_CASE("Select statement with join_left", "[query][join_left]") { TEST_CASE("Select statement with join_left", "[query][join_left]")
dialect d = dialect_builder::builder().create().build(); {
query_builder query(d); connection noop("noop://noop.db");
auto scm = std::make_shared<schema>("noop");
query q(noop, scm);
auto result = q.select({"f.id", "ap.brand", "f.pilot_name"})
.from({"flight", "f"})
.join_left({"airplane", "ap"})
.on("f.airplane_id"_col == "ap.id"_col)
.build();
auto q = query.select({"f.id", "ap.brand", "f.pilot_name"}).from("flight", "f").join("airplane", join_type_t::INNER, "ap").on("f.airplane_id", "ap.id").compile(); REQUIRE(result.sql == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" AS "f" INNER JOIN "airplane" AS "ap" ON "f"."airplane_id" = "ap"."id")");
REQUIRE(result.table.name == "flight");
REQUIRE(q.sql == R"(SELECT "f.id", "ap.brand", "f.pilot_name" FROM "flight" AS "f" INNER JOIN "airplane" AS "ap" ON "f.airplane_id"="ap.id")");
REQUIRE(q.table_name == "flight");
} }