Compare commits

..

2 Commits

26 changed files with 391 additions and 282 deletions

View File

@ -234,19 +234,15 @@ sql::record mysql_connection::describe(const std::string &table)
while (reader.fetch()) { while (reader.fetch()) {
char *end = nullptr; char *end = nullptr;
// Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10);
std::string name = reader.column(0); std::string name = reader.column(0);
// Todo: extract size
auto typeinfo = determine_type_info(reader.column(1)); auto typeinfo = determine_type_info(reader.column(1));
end = nullptr; end = nullptr;
sql::null_option null_opt{sql::null_option::NULLABLE}; sql::null_option null_opt{sql::null_option::NULLABLE};
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;
} }
// f.default_value(res->column(4)); prototype.append({name, typeinfo.type, {typeinfo.size}, null_opt, prototype.size()});
prototype.append({name, typeinfo.type, {typeinfo.size}, null_opt});
} }
return prototype; return prototype;

View File

@ -24,7 +24,8 @@ set(TEST_SOURCES
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/SessionRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp) ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp)
set(LIBRARY_TEST_TARGET mysql_tests) set(LIBRARY_TEST_TARGET mysql_tests)

View File

@ -22,8 +22,11 @@ void throw_postgres_error(PGconn *db, const std::string &source)
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql) void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql)
{ {
if (res == nullptr || if (res == nullptr) {
(PQresultStatus(res) != PGRES_COMMAND_OK && std::stringstream msg;
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
throw std::logic_error(msg.str());
} else if ((PQresultStatus(res) != PGRES_COMMAND_OK &&
PQresultStatus(res) != PGRES_TUPLES_OK)) { PQresultStatus(res) != PGRES_TUPLES_OK)) {
std::stringstream msg; std::stringstream msg;
msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql; msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql;

View File

@ -24,7 +24,8 @@ set(TEST_SOURCES
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/SessionRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp) ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp)
set(LIBRARY_TEST_TARGET postgres_tests) set(LIBRARY_TEST_TARGET postgres_tests)

View File

@ -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}); prototype.append({name, type, utils::null_attributes, null_opt, index});
} }
return std::move(prototype); return std::move(prototype);

View File

@ -24,7 +24,8 @@ set(TEST_SOURCES
../../tests/ConnectionTest.cpp ../../tests/ConnectionTest.cpp
../../tests/SessionRecordTest.cpp ../../tests/SessionRecordTest.cpp
../../tests/StatementTest.cpp ../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp) ../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp)
set(LIBRARY_TEST_TARGET sqlite_tests) set(LIBRARY_TEST_TARGET sqlite_tests)

View File

@ -2,7 +2,7 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/session.hpp" #include "matador/sql/connection.hpp"
#include "connection.hpp" #include "connection.hpp"
@ -12,8 +12,10 @@ class SessionRecordFixture
{ {
public: public:
SessionRecordFixture() SessionRecordFixture()
: pool(matador::test::connection::dns, 4), ses(pool) : db(matador::test::connection::dns)
{} {
db.open();
}
~SessionRecordFixture() { ~SessionRecordFixture() {
drop_table_if_exists("flight"); drop_table_if_exists("flight");
drop_table_if_exists("airplane"); drop_table_if_exists("airplane");
@ -22,13 +24,12 @@ public:
} }
protected: protected:
matador::sql::connection_pool<matador::sql::connection> pool; matador::sql::connection db;
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 (ses.table_exists(table_name)) { if (db.exists(table_name)) {
ses.drop().table(table_name).execute(); db.drop().table(table_name).execute();
} }
} }
}; };
@ -37,8 +38,8 @@ using namespace matador::sql;
TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement", "[session][record]")
{ {
REQUIRE(!ses.table_exists("person")); REQUIRE(!db.exists("person"));
ses.create() db.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),
@ -46,18 +47,18 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement", "[ses
}) })
.execute(); .execute();
REQUIRE(ses.table_exists("person")); REQUIRE(db.exists("person"));
ses.drop() db.drop()
.table("person") .table("person")
.execute(); .execute();
REQUIRE(!ses.table_exists("person")); REQUIRE(!db.exists("person"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with foreign key", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with foreign key", "[session][record]")
{ {
ses.create() db.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),
@ -65,9 +66,9 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with fo
}) })
.execute(); .execute();
REQUIRE(ses.table_exists("airplane")); REQUIRE(db.exists("airplane"));
ses.create() db.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"),
@ -75,24 +76,24 @@ TEST_CASE_METHOD(SessionRecordFixture, " Create and drop table statement with fo
}) })
.execute(); .execute();
REQUIRE(ses.table_exists("flight")); REQUIRE(db.exists("flight"));
ses.drop() db.drop()
.table("flight") .table("flight")
.execute(); .execute();
REQUIRE(!ses.table_exists("flight")); REQUIRE(!db.exists("flight"));
ses.drop() db.drop()
.table("airplane") .table("airplane")
.execute(); .execute();
REQUIRE(!ses.table_exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[session][record]")
{ {
ses.create() db.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),
@ -100,14 +101,14 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement", "[ses
}) })
.execute(); .execute();
auto res = ses.insert() auto res = db.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 = ses.select({"id", "name", "age"}) auto result = db.select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
@ -124,14 +125,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);
} }
ses.drop() db.drop()
.table("person") .table("person")
.execute(); .execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with foreign key", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with foreign key", "[session][record]")
{ {
ses.create() db.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),
@ -139,7 +140,7 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with fo
}) })
.execute(); .execute();
ses.create() db.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"),
@ -147,31 +148,31 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute insert record statement with fo
}) })
.execute(); .execute();
auto res = ses.insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute(); auto res = db.insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute(); res = db.insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute(); res = db.insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto count = ses.select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3); REQUIRE(count == 3);
res = ses.insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute(); res = db.insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
ses.drop().table("flight").execute(); db.drop().table("flight").execute();
ses.drop().table("airplane").execute(); db.drop().table("airplane").execute();
REQUIRE(!ses.table_exists("flight")); REQUIRE(!db.exists("flight"));
REQUIRE(!ses.table_exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[session][record]")
{ {
ses.create() db.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),
@ -179,14 +180,14 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[ses
}) })
.execute(); .execute();
auto res = ses.insert() auto res = db.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 = ses.update("person") res = db.update("person")
.set({{"id", 7}, .set({{"id", 7},
{"name", "jane"}, {"name", "jane"},
{"age", 35}}) {"age", 35}})
@ -195,7 +196,7 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[ses
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select({"id", "name", "age"}) auto result = db.select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
@ -212,12 +213,12 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute update record statement", "[ses
REQUIRE(i.at(2).as<int>() == 35); REQUIRE(i.at(2).as<int>() == 35);
} }
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][record]")
{ {
ses.create() db.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),
@ -225,16 +226,16 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][r
}) })
.execute(); .execute();
auto res = ses.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select({"id", "name", "age"}) auto result = db.select({"id", "name", "age"})
.from("person") .from("person")
.fetch_all(); .fetch_all();
@ -245,22 +246,22 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement", "[session][r
} }
REQUIRE(expected_names.empty()); REQUIRE(expected_names.empty());
auto rec = ses.select({"id", "name", "age"}) auto rec = db.select({"id", "name", "age"})
.from("person") .from("person")
.fetch_one(); .fetch_one();
REQUIRE(rec.at(1).str() == "george"); REQUIRE(rec.at(1).str() == "george");
auto name = ses.select({"name"}) auto name = db.select({"name"})
.from("person") .from("person")
.fetch_value<std::string>(); .fetch_value<std::string>();
REQUIRE(name == "george"); REQUIRE(name == "george");
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by", "[session][record]")
{ {
ses.create() db.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),
@ -268,16 +269,16 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by"
}) })
.execute(); .execute();
auto res = ses.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select({"id", "name", "age"}) auto result = db.select({"id", "name", "age"})
.from("person") .from("person")
.order_by("name").asc() .order_by("name").asc()
.fetch_all(); .fetch_all();
@ -289,12 +290,12 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with order by"
} }
REQUIRE(expected_names.empty()); REQUIRE(expected_names.empty());
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by and order by", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by and order by", "[session][record]")
{ {
ses.create() db.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),
@ -302,18 +303,18 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by
}) })
.execute(); .execute();
auto res = ses.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select({alias(count("age"), "age_count"), "age"}) auto result = db.select({alias(count("age"), "age_count"), "age"})
.from("person") .from("person")
.group_by("age") .group_by("age")
.order_by("age_count").desc() .order_by("age_count").desc()
@ -328,41 +329,41 @@ TEST_CASE_METHOD(SessionRecordFixture, " Execute select statement with group by
expected_values.pop_front(); expected_values.pop_front();
} }
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Execute delete statement", "[session][record]") TEST_CASE_METHOD(SessionRecordFixture, " Execute delete statement", "[session][record]")
{ {
ses.create() db.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 = ses.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); auto res = db.insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
res = ses.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); res = db.insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto count = ses.select({count_all()}).from("person").fetch_value<int>(); auto count = db.select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 2); REQUIRE(count == 2);
res = ses.remove() res = db.remove()
.from("person") .from("person")
.where("id"_col == 1) .where("id"_col == 1)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
count = ses.select({count_all()}).from("person").fetch_value<int>(); count = db.select({count_all()}).from("person").fetch_value<int>();
REQUIRE(count == 1); REQUIRE(count == 1);
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionRecordFixture, " Test quoted identifier", "[session][record]") { TEST_CASE_METHOD(SessionRecordFixture, " Test quoted identifier", "[session][record]") {
ses.create() db.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)
@ -371,26 +372,26 @@ TEST_CASE_METHOD(SessionRecordFixture, " Test quoted identifier", "[session][rec
// check table description // check table description
std::vector<std::string> columns = { "from", "to"}; std::vector<std::string> columns = { "from", "to"};
std::vector<data_type_t> types = {data_type_t::type_varchar, data_type_t::type_varchar}; std::vector<data_type_t> types = {data_type_t::type_varchar, data_type_t::type_varchar};
auto fields = ses.describe_table("quotes"); auto fields = db.describe("quotes");
for (const auto &field : fields) { for (const auto &field : fields) {
REQUIRE(field.name() == columns[field.index()]); REQUIRE(field.name() == columns[field.index()]);
REQUIRE(field.type() == types[field.index()]); REQUIRE(field.type() == types[field.index()]);
} }
ses.insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute(); db.insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute();
auto res = ses.select({"from", "to"}).from("quotes").fetch_one(); auto res = db.select({"from", "to"}).from("quotes").fetch_one();
REQUIRE("Berlin" == res.at("from").str()); REQUIRE("Berlin" == res.at("from").str());
REQUIRE("London" == res.at("to").str()); REQUIRE("London" == res.at("to").str());
ses.update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute(); db.update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute();
res = ses.select({"from", "to"}).from("quotes").fetch_one(); res = db.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());
ses.drop().table("quotes").execute(); db.drop().table("quotes").execute();
} }

View File

@ -13,65 +13,66 @@
using namespace matador::sql; using namespace matador::sql;
using namespace matador::test; using namespace matador::test;
class SessionFixture class QueryFixture
{ {
public: public:
SessionFixture() QueryFixture()
: pool(matador::test::connection::dns, 4), ses(pool) : db(matador::test::connection::dns)
{} {
~SessionFixture() { db.open();
}
~QueryFixture() {
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");
} }
protected: protected:
matador::sql::connection_pool<matador::sql::connection> pool; matador::sql::connection db;
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 (ses.table_exists(table_name)) { if (db.exists(table_name)) {
ses.drop().table(table_name).execute(); db.drop().table(table_name).execute();
} }
} }
}; };
TEST_CASE_METHOD(SessionFixture, " Create table with foreign key relation", "[session]") { TEST_CASE_METHOD(QueryFixture, " Create table with foreign key relation", "[session]") {
ses.create() db.create()
.table<airplane>("airplane") .table<airplane>("airplane")
.execute(); .execute();
REQUIRE(ses.table_exists("airplane")); REQUIRE(db.exists("airplane"));
ses.create() db.create()
.table<flight>("flight") .table<flight>("flight")
.execute(); .execute();
REQUIRE(ses.table_exists("flight")); REQUIRE(db.exists("flight"));
ses.drop().table("flight").execute(); db.drop().table("flight").execute();
ses.drop().table("airplane").execute(); db.drop().table("airplane").execute();
REQUIRE(!ses.table_exists("flight")); REQUIRE(!db.exists("flight"));
REQUIRE(!ses.table_exists("airplane")); REQUIRE(!db.exists("airplane"));
} }
TEST_CASE_METHOD(SessionFixture, " Execute select statement with where clause", "[session]") { TEST_CASE_METHOD(QueryFixture, " Execute select statement with where clause", "[session]") {
ses.create() db.create()
.table<person>("person") .table<person>("person")
.execute(); .execute();
person george{7, "george", 45}; person george{7, "george", 45};
george.image.push_back(37); george.image.push_back(37);
auto res = ses.insert() auto res = db.insert()
.into("person", george) .into("person", george)
.execute(); .execute();
REQUIRE(res == 1); REQUIRE(res == 1);
// fetch person as record // fetch person as record
auto result_record = ses.select<person>() auto result_record = db.select<person>()
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
@ -90,7 +91,7 @@ TEST_CASE_METHOD(SessionFixture, " Execute select statement with where clause",
} }
// fetch person as person // fetch person as person
auto result_person = ses.select<person>() auto result_person = db.select<person>()
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all<person>(); .fetch_all<person>();
@ -101,11 +102,11 @@ TEST_CASE_METHOD(SessionFixture, " Execute select statement with where clause",
REQUIRE(i.age == 45); REQUIRE(i.age == 45);
} }
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionFixture, " Execute insert statement", "[session]") { TEST_CASE_METHOD(QueryFixture, " Execute insert statement", "[session]") {
ses.create() db.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),
@ -113,7 +114,7 @@ TEST_CASE_METHOD(SessionFixture, " Execute insert statement", "[session]") {
}) })
.execute(); .execute();
auto res = ses.insert() auto res = db.insert()
.into("person", {"id", "name", "color"}) .into("person", {"id", "name", "color"})
.values({7, "george", "green"}) .values({7, "george", "green"})
.execute(); .execute();
@ -121,7 +122,7 @@ TEST_CASE_METHOD(SessionFixture, " Execute insert statement", "[session]") {
REQUIRE(res == 1); REQUIRE(res == 1);
// fetch person as record // fetch person as record
auto result_record = ses.select({"id", "name", "color"}) auto result_record = db.select({"id", "name", "color"})
.from("person") .from("person")
.where("id"_col == 7) .where("id"_col == 7)
.fetch_all(); .fetch_all();
@ -139,15 +140,15 @@ TEST_CASE_METHOD(SessionFixture, " Execute insert statement", "[session]") {
REQUIRE(i.at(2).as<std::string>() == "green"); REQUIRE(i.at(2).as<std::string>() == "green");
} }
ses.drop().table("person").execute(); db.drop().table("person").execute();
} }
TEST_CASE_METHOD(SessionFixture, " Select statement with foreign key", "[session]") { TEST_CASE_METHOD(QueryFixture, " Select statement with foreign key", "[session]") {
ses.create() db.create()
.table<airplane>("airplane") .table<airplane>("airplane")
.execute(); .execute();
ses.create() db.create()
.table<flight>("flight") .table<flight>("flight")
.execute(); .execute();
@ -158,24 +159,24 @@ TEST_CASE_METHOD(SessionFixture, " Select statement with foreign key", "[session
}; };
for (const auto &plane : planes) { for (const auto &plane : planes) {
auto res = ses.insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.insert().into<airplane>("airplane").values(*plane).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto count = ses.select({count_all()}).from("airplane").fetch_value<int>(); auto count = db.select({count_all()}).from("airplane").fetch_value<int>();
REQUIRE(count == 3); REQUIRE(count == 3);
flight f4711{4, planes.at(1), "hans"}; flight f4711{4, planes.at(1), "hans"};
auto res = ses.insert().into<flight>("flight").values(f4711).execute(); auto res = db.insert().into<flight>("flight").values(f4711).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto f = *ses.select<flight>().from("flight").fetch_all<flight>().begin(); auto f = *db.select<flight>().from("flight").fetch_all<flight>().begin();
REQUIRE(f.id == 4); REQUIRE(f.id == 4);
REQUIRE(f.pilot_name == "hans"); REQUIRE(f.pilot_name == "hans");
REQUIRE(f.airplane.get() != nullptr); REQUIRE(f.airplane.get() != nullptr);
REQUIRE(f.airplane->id == 2); REQUIRE(f.airplane->id == 2);
ses.drop().table("flight").execute(); db.drop().table("flight").execute();
ses.drop().table("airplane").execute(); db.drop().table("airplane").execute();
} }

View File

@ -5,18 +5,28 @@
#include "matador/sql/session.hpp" #include "matador/sql/session.hpp"
#include "matador/sql/statement_cache.hpp" #include "matador/sql/statement_cache.hpp"
#include "connection.hpp"
using namespace matador; using namespace matador;
TEST_CASE("Acquire prepared statement", "[statement cache]") { class StatementCacheFixture
sql::statement_cache cache; {
sql::connection_pool<sql::connection> pool("sqlite://sqlite.db", 4); public:
StatementCacheFixture()
: pool(matador::test::connection::dns, 4), ses(pool)
{}
~StatementCacheFixture() = default;
protected:
matador::sql::connection_pool<matador::sql::connection> pool;
matador::sql::session ses;
};
TEST_CASE_METHOD(StatementCacheFixture, " Acquire prepared statement", "[statement cache]") {
sql::statement_cache cache;
sql::session s(pool);
auto conn = pool.acquire(); auto conn = pool.acquire();
std::string sql = R"(SELECT * FROM person WHERE name = 'george')"; std::string sql = R"(SELECT * FROM person WHERE name = 'george')";
// auto &stmt = cache.acquire(sql, *conn); // auto &stmt = cache.acquire(sql, *conn);
} }

View File

@ -2,9 +2,7 @@
#include "matador/sql/column.hpp" #include "matador/sql/column.hpp"
#include "matador/sql/condition.hpp" #include "matador/sql/condition.hpp"
#include "matador/sql/connection_info.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp"
#include "matador/sql/session.hpp"
#include "connection.hpp" #include "connection.hpp"
@ -17,9 +15,10 @@ class StatementTestFixture
{ {
public: public:
StatementTestFixture() StatementTestFixture()
: pool(matador::test::connection::dns, 4), ses(pool) : db(matador::test::connection::dns)
{ {
ses.create().table<airplane>("airplane").execute(); db.open();
db.create().table<airplane>("airplane").execute();
} }
~StatementTestFixture() ~StatementTestFixture()
@ -28,8 +27,7 @@ public:
} }
protected: protected:
matador::sql::connection_pool<matador::sql::connection> pool; matador::sql::connection db;
matador::sql::session ses;
std::vector<entity<airplane>> planes{ std::vector<entity<airplane>> planes{
make_entity<airplane>(1, "Airbus", "A380"), make_entity<airplane>(1, "Airbus", "A380"),
@ -39,8 +37,8 @@ protected:
private: private:
void drop_table_if_exists(const std::string &table_name) { void drop_table_if_exists(const std::string &table_name) {
if (ses.table_exists(table_name)) { if (db.exists(table_name)) {
ses.drop().table(table_name).execute(); db.drop().table(table_name).execute();
} }
} }
@ -49,7 +47,7 @@ private:
TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement]") TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement]")
{ {
SECTION("Insert with prepared statement and placeholder") { SECTION("Insert with prepared statement and placeholder") {
auto stmt = ses.insert() auto stmt = db.insert()
.into<airplane>("airplane") .into<airplane>("airplane")
.values<airplane>().prepare(); .values<airplane>().prepare();
@ -59,7 +57,7 @@ TEST_CASE_METHOD(StatementTestFixture, " Create prepared statement", "[statement
stmt.reset(); stmt.reset();
} }
auto result = ses.select<airplane>().from("airplane").fetch_all<airplane>(); auto result = db.select<airplane>().from("airplane").fetch_all<airplane>();
size_t index{0}; size_t index{0};
for (const auto &i: result) { for (const auto &i: result) {
@ -71,11 +69,11 @@ 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 = ses.insert().into<airplane>("airplane").values(*plane).execute(); auto res = db.insert().into<airplane>("airplane").values(*plane).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
} }
auto stmt = ses.select<airplane>().from("airplane").where("brand"_col == _).prepare(); auto stmt = db.select<airplane>().from("airplane").where("brand"_col == _).prepare();
stmt.bind(0, "Airbus"); stmt.bind(0, "Airbus");

View File

@ -1,6 +1,6 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include "matador/sql/session.hpp" #include "matador/sql/connection.hpp"
#include "matador/utils/enum_mapper.hpp" #include "matador/utils/enum_mapper.hpp"
@ -14,26 +14,26 @@ class TypeTraitsTestFixture
{ {
public: public:
TypeTraitsTestFixture() TypeTraitsTestFixture()
: pool(matador::test::connection::dns, 4), ses(pool) : db(matador::test::connection::dns)
{ {
ses.create() db.open();
db.create()
.table<location>("location") .table<location>("location")
.execute(); .execute();
} }
~TypeTraitsTestFixture() ~TypeTraitsTestFixture()
{ {
ses.drop().table("location").execute(); db.drop().table("location").execute();
} }
protected: protected:
matador::sql::connection_pool<matador::sql::connection> pool; matador::sql::connection db;
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 (ses.table_exists(table_name)) { if (db.exists(table_name)) {
ses.drop().table(table_name).execute(); db.drop().table(table_name).execute();
} }
} }
}; };
@ -85,10 +85,10 @@ TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with typ
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 = ses.insert().into<location>("location").values(loc).execute(); auto res = db.insert().into<location>("location").values(loc).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select<location>().from("location").fetch_all<location>(); auto result = db.select<location>().from("location").fetch_all<location>();
for (const auto &l: result) { for (const auto &l: result) {
REQUIRE(l.name == "center"); REQUIRE(l.name == "center");
@ -98,11 +98,11 @@ 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 = ses.insert().into<location>("location").values<location>().prepare(); auto stmt = db.insert().into<location>("location").values<location>().prepare();
auto res = stmt.bind(loc).execute(); auto res = stmt.bind(loc).execute();
REQUIRE(res == 1); REQUIRE(res == 1);
auto result = ses.select<location>().from("location"). auto result = db.select<location>().from("location").
template fetch_all<location>(); template fetch_all<location>();
for (const auto &l: result) { for (const auto &l: result) {

View File

@ -4,6 +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_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"
@ -18,12 +19,11 @@ namespace matador::sql {
class connection class connection
{ {
public: public:
explicit connection(connection_info info); explicit connection(connection_info info, const std::shared_ptr<table_repository> &repo = std::make_shared<table_repository>());
explicit connection(const std::string& dns); explicit connection(const std::string& dns, const std::shared_ptr<table_repository> &repo = std::make_shared<table_repository>());
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;
connection& operator=(connection &&x) noexcept = default;
~connection(); ~connection();
void open(); void open();
@ -31,22 +31,41 @@ 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();
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]] 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;
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;
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

View File

@ -30,6 +30,8 @@ public:
COLUMNS, COLUMNS,
COLUMN, COLUMN,
FROM, FROM,
JOIN,
ON,
INTO, INTO,
WHERE, WHERE,
WHERE_CLAUSE, WHERE_CLAUSE,
@ -161,6 +163,8 @@ private:
{token_t::COLUMNS, "COLUMNS"}, {token_t::COLUMNS, "COLUMNS"},
{token_t::COLUMN, "COLUMN"}, {token_t::COLUMN, "COLUMN"},
{token_t::FROM, "FROM"}, {token_t::FROM, "FROM"},
{token_t::JOIN, "INNER JOIN"},
{token_t::ON, "ON"},
{token_t::WHERE, "WHERE"}, {token_t::WHERE, "WHERE"},
{token_t::AND, "AND"}, {token_t::AND, "AND"},
{token_t::OR, "OR"}, {token_t::OR, "OR"},

View File

@ -80,6 +80,8 @@ private:
QUERY_DELETE, QUERY_DELETE,
QUERY_SET, QUERY_SET,
QUERY_FROM, QUERY_FROM,
QUERY_JOIN,
QUERY_ON,
QUERY_INTO, QUERY_INTO,
QUERY_WHERE, QUERY_WHERE,
QUERY_VALUES, QUERY_VALUES,
@ -129,7 +131,7 @@ public:
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 = "");
query_builder& join(const std::string &table, join_type_t); query_builder& join(const std::string &table, join_type_t, const std::string &as = "");
query_builder& on(const std::string &column, const std::string &join_column); query_builder& on(const std::string &column, const std::string &join_column);
query_builder& set(std::initializer_list<key_value_pair> key_values); query_builder& set(std::initializer_list<key_value_pair> key_values);
query_builder& set(const std::vector<key_value_pair> &key_values); query_builder& set(const std::vector<key_value_pair> &key_values);

View File

@ -19,15 +19,15 @@
namespace matador::sql { namespace matador::sql {
class basic_condition; class basic_condition;
class session; class connection;
class query_intermediate class query_intermediate
{ {
public: public:
query_intermediate(session &db, query_builder &query); query_intermediate(connection &db, query_builder &query);
protected: protected:
session &session_; connection &connection_;
query_builder &builder_; query_builder &builder_;
}; };
@ -130,17 +130,17 @@ public:
class query_start_intermediate class query_start_intermediate
{ {
public: public:
explicit query_start_intermediate(session &s); explicit query_start_intermediate(connection &s);
protected: protected:
session &session_; connection &connection_;
query_builder builder_; query_builder builder_;
}; };
class query_select_intermediate : public query_start_intermediate class query_select_intermediate : public query_start_intermediate
{ {
public: public:
query_select_intermediate(session &s, const std::vector<column>& columns); query_select_intermediate(connection &s, const std::vector<column>& columns);
query_from_intermediate from(const std::string &table, const std::string &as = ""); query_from_intermediate from(const std::string &table, const std::string &as = "");
}; };
@ -164,36 +164,38 @@ public:
query_execute_finish values() query_execute_finish values()
{ {
Type obj; Type obj;
return {session_, builder_.values(as_placeholder(obj))}; return {connection_, builder_.values(as_placeholder(obj))};
} }
template<class Type> template<class Type>
query_execute_finish values(const Type &obj) query_execute_finish values(const Type &obj)
{ {
return {session_, builder_.values(value_extractor::extract(obj))}; return {connection_, builder_.values(value_extractor::extract(obj))};
} }
}; };
class query_create_intermediate : query_start_intermediate class query_create_intermediate : public query_start_intermediate
{ {
public: public:
query_create_intermediate(session &s, table_repository &repo); explicit query_create_intermediate(connection &db);
query_execute_finish table(const std::string &table, std::initializer_list<column> columns); query_execute_finish table(const std::string &table, std::initializer_list<column> columns);
template<class Type> template<class Type>
query_execute_finish table(const std::string &table_name) query_execute_finish table(const std::string &table_name)
{ {
const auto &info = repository_.attach<Type>(table_name); if (!tables()->exists<Type>()) {
return {session_, builder_.table(table_name, info.prototype.columns())}; tables()->attach<Type>(table_name);
}
return {connection_, builder_.table(table_name, column_generator::generate<Type>(*tables()))};
} }
private: private:
table_repository &repository_; 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(session &s); explicit query_drop_intermediate(connection &s);
query_execute_finish table(const std::string &table); query_execute_finish table(const std::string &table);
}; };
@ -201,18 +203,18 @@ public:
class query_insert_intermediate : public query_start_intermediate class query_insert_intermediate : public query_start_intermediate
{ {
public: public:
explicit query_insert_intermediate(session &s); explicit query_insert_intermediate(connection &s);
query_into_intermediate into(const std::string &table, std::initializer_list<std::string> column_names); query_into_intermediate into(const std::string &table, std::initializer_list<std::string> column_names);
template<class Type> template<class Type>
query_into_intermediate into(const std::string &table) query_into_intermediate into(const std::string &table)
{ {
return {session_, builder_.into(table, column_name_generator::generate<Type>())}; return {connection_, builder_.into(table, column_name_generator::generate<Type>())};
} }
template<class Type> template<class Type>
query_execute_finish into(const std::string &table, const Type &obj) query_execute_finish into(const std::string &table, const Type &obj)
{ {
return {session_, builder_.into(table, column_name_generator::generate<Type>()) return {connection_, builder_.into(table, column_name_generator::generate<Type>())
.values(value_extractor::extract(obj))}; .values(value_extractor::extract(obj))};
} }
}; };
@ -236,13 +238,13 @@ public:
class query_update_intermediate : public query_start_intermediate class query_update_intermediate : public query_start_intermediate
{ {
public: public:
query_update_intermediate(session &s, const std::string& table_name); query_update_intermediate(connection &s, const std::string& table_name);
query_set_intermediate set(std::initializer_list<key_value_pair> columns); query_set_intermediate set(std::initializer_list<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 {session_, builder_.set(key_value_generator::generate(obj))}; return {connection_, builder_.set(key_value_generator::generate(obj))};
} }
}; };
@ -257,7 +259,7 @@ public:
class query_delete_intermediate : public query_start_intermediate class query_delete_intermediate : public query_start_intermediate
{ {
public: public:
explicit query_delete_intermediate(session &s); explicit query_delete_intermediate(connection &s);
query_delete_from_intermediate from(const std::string &table); query_delete_from_intermediate from(const std::string &table);
}; };

View File

@ -68,6 +68,8 @@ public:
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty() const;
void clear(); void clear();
[[nodiscard]] bool unknown() const;
private: private:
void init(); void init();
void add_to_map(column &col, size_t index); void add_to_map(column &col, size_t index);

View File

@ -20,15 +20,6 @@ class session
public: public:
explicit session(connection_pool<connection> &pool); explicit session(connection_pool<connection> &pool);
query_create_intermediate create();
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]] 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;
@ -60,11 +51,5 @@ private:
mutable std::unordered_map<std::string, record> prototypes_; mutable std::unordered_map<std::string, record> prototypes_;
}; };
template<class Type>
query_select_intermediate session::select()
{
return query_select_intermediate{*this, column_generator::generate<Type>(table_repository_)};
}
} }
#endif //QUERY_SESSION_HPP #endif //QUERY_SESSION_HPP

View File

@ -44,11 +44,18 @@ public:
[[nodiscard]] std::pair<std::string, std::string> reference(const std::type_index &ti) const; [[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: private:
using repository = std::unordered_map<std::type_index, table_info>; using repository = std::unordered_map<std::type_index, table_info>;
repository repository_; repository repository_;
}; };
;
} }

View File

@ -8,16 +8,17 @@
namespace matador::sql { namespace matador::sql {
connection::connection(connection_info info) connection::connection(connection_info info, const std::shared_ptr<table_repository> &repo)
: connection_info_(std::move(info)) : connection_info_(std::move(info))
, logger_(stdout, "SQL") , logger_(stdout, "SQL")
, dialect_(backend_provider::instance().connection_dialect(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) connection::connection(const std::string& dns, const std::shared_ptr<table_repository> &repo)
: connection(connection_info::parse(dns)) : connection(connection_info::parse(dns), repo)
{} {}
connection::connection(const connection &x) connection::connection(const connection &x)
@ -68,6 +69,36 @@ const connection_info &connection::info() const
return connection_info_; return connection_info_;
} }
query_create_intermediate connection::create()
{
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 record connection::describe(const std::string &table_name) const
{ {
return std::move(connection_->describe(table_name)); return std::move(connection_->describe(table_name));
@ -78,12 +109,41 @@ bool connection::exists(const std::string &schema_name, const std::string &table
return connection_->exists(schema_name, table_name); return connection_->exists(schema_name, table_name);
} }
bool connection::exists(const std::string &table_name) const
{
return connection_->exists(dialect_.default_schema_name(), table_name);
}
size_t connection::execute(const std::string &sql) const size_t connection::execute(const std::string &sql) const
{ {
logger_.debug(sql); logger_.debug(sql);
return connection_->execute(sql); return connection_->execute(sql);
} }
query_result<record> connection::fetch(const query_context &q) const
{
if (q.prototype.empty() || q.prototype.unknown()) {
const auto table_prototype = describe(q.table_name);
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_cast<column&>(col).type(rit->type());
}
}
}
// auto it = prototypes_.find(q.table_name);
// if (it == prototypes_.end()) {
// it = prototypes_.emplace(q.table_name, describe(q.table_name)).first;
// }
// // adjust columns from given query
// for (auto &col : q.prototype) {
// if (const auto rit = it->second.find(col.name()); col.type() == data_type_t::type_unknown && rit != it->second.end()) {
// const_cast<column&>(col).type(rit->type());
// }
// }
auto res = fetch(q.sql);
return query_result<record>{std::move(res), q.prototype};
}
std::unique_ptr<query_result_impl> connection::fetch(const std::string &sql) const std::unique_ptr<query_result_impl> connection::fetch(const std::string &sql) const
{ {
logger_.debug(sql); logger_.debug(sql);
@ -100,4 +160,9 @@ const class dialect &connection::dialect() const
return dialect_; return dialect_;
} }
std::shared_ptr<table_repository> connection::tables() const
{
return table_repository_;
}
} }

View File

@ -10,7 +10,8 @@ namespace matador::sql {
namespace detail { namespace detail {
any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query) any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query)
: d(d), query(query) {} : d(d), query(query)
{}
void any_type_to_string_visitor::to_string(const char *val) void any_type_to_string_visitor::to_string(const char *val)
{ {
@ -52,8 +53,10 @@ query_builder::query_state_transition_map query_builder::transitions_{
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}}, {state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
{state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}}, {state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}},
{state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}}, {state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}},
{state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}}, {state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_JOIN, state_t::QUERY_FINISH}},
{state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}}, {state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}},
{state_t::QUERY_JOIN, {state_t::QUERY_ON}},
{state_t::QUERY_ON, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_JOIN, state_t::QUERY_FINISH}},
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}}, {state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
{state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, {state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
{state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}}, {state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}},
@ -325,25 +328,37 @@ query_builder &query_builder::from(const std::string &table, const std::string &
if (dialect_.default_schema_name().empty()) { if (dialect_.default_schema_name().empty()) {
query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(table) + " " + dialect_.prepare_identifier(table) +
(as.empty() ? "" : " " + as)); (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as)));
} else { } else {
query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) +
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) + " " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
"." + dialect_.prepare_identifier(table) + "." + dialect_.prepare_identifier(table) +
(as.empty() ? "" : " " + as)); (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as)));
} }
query_.table_name = table; query_.table_name = table;
return *this; return *this;
} }
query_builder &query_builder::join(const std::string &table, join_type_t) query_builder &query_builder::join(const std::string &table, join_type_t, const std::string &as)
{ {
transition_to(state_t::QUERY_JOIN);
query_parts_.emplace_back(dialect::token_t::JOIN, " " + dialect_.token_at(dialect::token_t::JOIN) +
" " + dialect_.prepare_identifier(table) +
(as.empty() ? "" : " AS " + dialect_.prepare_identifier(as)));
return *this; return *this;
} }
query_builder &query_builder::on(const std::string &column, const std::string &join_column) query_builder &query_builder::on(const std::string &column, const std::string &join_column)
{ {
transition_to(state_t::QUERY_ON);
query_parts_.emplace_back(dialect::token_t::ON, " " + dialect_.token_at(dialect::token_t::ON) +
" " + dialect_.prepare_identifier(column) +
"=" + dialect_.prepare_identifier(join_column));
return *this; return *this;
} }

View File

@ -5,83 +5,83 @@ namespace matador::sql {
query_result<record> query_select_finish::fetch_all() query_result<record> query_select_finish::fetch_all()
{ {
return session_.fetch(builder_.compile()); return connection_.fetch(builder_.compile());
} }
record query_select_finish::fetch_one() record query_select_finish::fetch_one()
{ {
return *session_.fetch(builder_.compile()).begin().get(); return *connection_.fetch(builder_.compile()).begin().get();
} }
std::unique_ptr<query_result_impl> query_select_finish::fetch() std::unique_ptr<query_result_impl> query_select_finish::fetch()
{ {
return session_.fetch(builder_.compile().sql); return connection_.fetch(builder_.compile().sql);
} }
statement query_select_finish::prepare() statement query_select_finish::prepare()
{ {
return session_.prepare(builder_.compile()); return connection_.prepare(builder_.compile());
} }
query_intermediate::query_intermediate(session &db, query_builder &query) query_intermediate::query_intermediate(connection &db, query_builder &query)
: session_(db), builder_(query) {} : connection_(db), builder_(query) {}
query_offset_intermediate query_order_direction_intermediate::offset(size_t offset) query_offset_intermediate query_order_direction_intermediate::offset(size_t offset)
{ {
return {session_, builder_}; return {connection_, builder_};
} }
query_limit_intermediate query_offset_intermediate::limit(size_t limit) query_limit_intermediate query_offset_intermediate::limit(size_t limit)
{ {
return {session_, builder_}; return {connection_, builder_};
} }
query_limit_intermediate query_order_direction_intermediate::limit(size_t limit) query_limit_intermediate query_order_direction_intermediate::limit(size_t limit)
{ {
return {session_, builder_}; return {connection_, builder_};
} }
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 std::string &name)
{ {
return {session_, builder_.order_by(name)}; return {connection_, builder_.order_by(name)};
} }
query_order_direction_intermediate query_order_by_intermediate::asc() query_order_direction_intermediate query_order_by_intermediate::asc()
{ {
return {session_, builder_.asc()}; return {connection_, builder_.asc()};
} }
query_order_direction_intermediate query_order_by_intermediate::desc() query_order_direction_intermediate query_order_by_intermediate::desc()
{ {
return {session_, builder_.desc()}; return {connection_, builder_.desc()};
} }
query_group_by_intermediate query_from_intermediate::group_by(const std::string &name) query_group_by_intermediate query_from_intermediate::group_by(const std::string &name)
{ {
return {session_, builder_.group_by(name)}; return {connection_, builder_.group_by(name)};
} }
query_order_by_intermediate query_from_intermediate::order_by(const std::string &name) query_order_by_intermediate query_from_intermediate::order_by(const std::string &name)
{ {
return {session_, builder_.order_by(name)}; return {connection_, builder_.order_by(name)};
} }
query_group_by_intermediate query_where_intermediate::group_by(const std::string &name) query_group_by_intermediate query_where_intermediate::group_by(const std::string &name)
{ {
return {session_, builder_.group_by(name)}; return {connection_, builder_.group_by(name)};
} }
query_order_by_intermediate query_where_intermediate::order_by(const std::string &name) query_order_by_intermediate query_where_intermediate::order_by(const std::string &name)
{ {
return {session_, builder_.order_by(name)}; return {connection_, builder_.order_by(name)};
} }
query_where_intermediate query_from_intermediate::where(const basic_condition &cond) query_where_intermediate query_from_intermediate::where(const basic_condition &cond)
{ {
return query_where_intermediate{session_, builder_.where(cond)}; return query_where_intermediate{connection_, builder_.where(cond)};
} }
query_select_intermediate::query_select_intermediate(session &s, const std::vector<column>& columns) query_select_intermediate::query_select_intermediate(connection &s, const std::vector<column>& columns)
: query_start_intermediate(s) : query_start_intermediate(s)
{ {
builder_.select(columns); builder_.select(columns);
@ -89,10 +89,10 @@ query_select_intermediate::query_select_intermediate(session &s, const std::vect
query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as) query_from_intermediate query_select_intermediate::from(const std::string &table, const std::string &as)
{ {
return {session_, builder_.from(table, as)}; return {connection_, builder_.from(table, as)};
} }
query_insert_intermediate::query_insert_intermediate(session &s) query_insert_intermediate::query_insert_intermediate(connection &s)
: query_start_intermediate(s) : query_start_intermediate(s)
{ {
builder_.insert(); builder_.insert();
@ -100,36 +100,40 @@ query_insert_intermediate::query_insert_intermediate(session &s)
query_into_intermediate query_insert_intermediate::into(const std::string &table, std::initializer_list<std::string> column_names) query_into_intermediate query_insert_intermediate::into(const std::string &table, std::initializer_list<std::string> column_names)
{ {
return {session_, builder_.into(table, column_names)}; return {connection_, builder_.into(table, column_names)};
} }
size_t query_execute_finish::execute() size_t query_execute_finish::execute()
{ {
return session_.execute(builder_.compile().sql); return connection_.execute(builder_.compile().sql);
} }
statement query_execute_finish::prepare() statement query_execute_finish::prepare()
{ {
return session_.prepare(builder_.compile()); return connection_.prepare(builder_.compile());
} }
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)
{ {
return {session_, builder_.values(values)}; return {connection_, builder_.values(values)};
} }
query_create_intermediate::query_create_intermediate(session &s, table_repository &repo) query_create_intermediate::query_create_intermediate(connection &db)
: query_start_intermediate(s) : query_start_intermediate(db) {
, repository_(repo) {
builder_.create(); builder_.create();
} }
query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns) query_execute_finish query_create_intermediate::table(const std::string &table, std::initializer_list<column> columns)
{ {
return {session_, builder_.table(table, columns)}; return {connection_, builder_.table(table, columns)};
} }
query_drop_intermediate::query_drop_intermediate(session &s) std::shared_ptr<table_repository> query_create_intermediate::tables() const
{
return connection_.tables();
}
query_drop_intermediate::query_drop_intermediate(connection &s)
: query_start_intermediate(s) : query_start_intermediate(s)
{ {
builder_.drop(); builder_.drop();
@ -137,20 +141,20 @@ query_drop_intermediate::query_drop_intermediate(session &s)
query_execute_finish query_drop_intermediate::table(const std::string &table) query_execute_finish query_drop_intermediate::table(const std::string &table)
{ {
return {session_, builder_.table(table)}; return {connection_, builder_.table(table)};
} }
query_execute_finish query_execute_where_intermediate::limit(int limit) query_execute_finish query_execute_where_intermediate::limit(int limit)
{ {
return {session_, builder_.limit(limit)}; return {connection_, builder_.limit(limit)};
} }
query_execute_where_intermediate query_set_intermediate::where(const basic_condition &cond) query_execute_where_intermediate query_set_intermediate::where(const basic_condition &cond)
{ {
return {session_, builder_.where(cond)}; return {connection_, builder_.where(cond)};
} }
query_update_intermediate::query_update_intermediate(session &s, const std::string& table_name) query_update_intermediate::query_update_intermediate(connection &s, const std::string& table_name)
: query_start_intermediate(s) : query_start_intermediate(s)
{ {
builder_.update(table_name); builder_.update(table_name);
@ -158,15 +162,15 @@ query_update_intermediate::query_update_intermediate(session &s, const std::stri
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 {session_, builder_.set(columns)}; return {connection_, builder_.set(columns)};
} }
query_execute_where_intermediate query_delete_from_intermediate::where(const basic_condition &cond) query_execute_where_intermediate query_delete_from_intermediate::where(const basic_condition &cond)
{ {
return {session_, builder_.where(cond)}; return {connection_, builder_.where(cond)};
} }
query_delete_intermediate::query_delete_intermediate(session &s) query_delete_intermediate::query_delete_intermediate(connection &s)
: query_start_intermediate(s) : query_start_intermediate(s)
{ {
builder_.remove(); builder_.remove();
@ -174,11 +178,11 @@ query_delete_intermediate::query_delete_intermediate(session &s)
query_delete_from_intermediate query_delete_intermediate::from(const std::string &table) query_delete_from_intermediate query_delete_intermediate::from(const std::string &table)
{ {
return {session_, builder_.from(table)}; return {connection_, builder_.from(table)};
} }
query_start_intermediate::query_start_intermediate(session &s) query_start_intermediate::query_start_intermediate(connection &s)
: session_(s) : connection_(s)
, builder_(s.dialect()) , builder_(s.dialect())
{} {}
} }

View File

@ -1,5 +1,6 @@
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include <algorithm>
#include <stdexcept> #include <stdexcept>
namespace matador::sql { namespace matador::sql {
@ -133,6 +134,13 @@ void record::clear()
columns_by_name_.clear(); columns_by_name_.clear();
} }
bool record::unknown() const
{
return std::all_of(std::begin(columns_), std::end(columns_), [](const auto &col) {
return col.type() == data_type_t::type_unknown;
});
}
void record::init() void record::init()
{ {
size_t index{0}; size_t index{0};

View File

@ -10,36 +10,6 @@ session::session(connection_pool<connection> &pool)
: pool_(pool) : pool_(pool)
, dialect_(backend_provider::instance().connection_dialect(pool_.info().type)) {} , dialect_(backend_provider::instance().connection_dialect(pool_.info().type)) {}
query_create_intermediate session::create()
{
return query_create_intermediate{*this, table_repository_};
}
query_drop_intermediate session::drop()
{
return query_drop_intermediate{*this};
}
query_select_intermediate session::select(std::initializer_list<column> columns)
{
return {*this, columns};
}
query_insert_intermediate session::insert()
{
return query_insert_intermediate{*this};
}
query_update_intermediate session::update(const std::string &table)
{
return query_update_intermediate{*this, table};
}
query_delete_intermediate session::remove()
{
return query_delete_intermediate{*this};
}
query_result<record> session::fetch(const query_context &q) const query_result<record> session::fetch(const query_context &q) const
{ {
auto c = pool_.acquire(); auto c = pool_.acquire();

View File

@ -31,4 +31,9 @@ std::pair<std::string, std::string> table_repository::reference(const std::type_
return {}; return {};
} }
bool table_repository::exists(const std::type_index &ti) const
{
return repository_.count(ti) > 0;
}
} }

View File

@ -23,7 +23,6 @@ add_executable(tests QueryBuilderTest.cpp
models/person.hpp models/person.hpp
AnyTypeToVisitorTest.cpp AnyTypeToVisitorTest.cpp
ColumnTest.cpp ColumnTest.cpp
StatementCacheTest.cpp
models/coordinate.hpp models/coordinate.hpp
models/location.hpp models/location.hpp
models/optional.hpp models/optional.hpp

View File

@ -175,3 +175,13 @@ TEST_CASE("Create, insert and select a blob column", "[query][blob]") {
REQUIRE(q.sql == R"(SELECT "id", "name", "data" FROM "person")"); REQUIRE(q.sql == R"(SELECT "id", "name", "data" FROM "person")");
REQUIRE(q.table_name == "person"); REQUIRE(q.table_name == "person");
} }
TEST_CASE("Select statement with join", "[query][join]") {
dialect d = dialect_builder::builder().create().build();
query_builder query(d);
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(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");
}