mirror of
https://github.com/getml/sqlgen.git
synced 2025-12-31 22:49:35 -06:00
Adapted the postgres connector
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include "../dynamic/Statement.hpp"
|
||||
#include "Credentials.hpp"
|
||||
#include "exec.hpp"
|
||||
#include "to_sql.hpp"
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
@@ -37,11 +38,11 @@ class Connection : public sqlgen::Connection {
|
||||
return exec(conn_, _sql).transform([](auto&&) { return Nothing{}; });
|
||||
}
|
||||
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final {
|
||||
return error("TODO");
|
||||
}
|
||||
Result<Ref<IteratorBase>> read(const dynamic::SelectFrom& _query) final;
|
||||
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept final;
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept final {
|
||||
return postgres::to_sql(_stmt);
|
||||
}
|
||||
|
||||
Result<Nothing> start_write(const dynamic::Insert& _stmt) final {
|
||||
return execute(to_sql(_stmt));
|
||||
@@ -53,97 +54,17 @@ class Connection : public sqlgen::Connection {
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) final;
|
||||
|
||||
private:
|
||||
std::string add_not_null_if_necessary(
|
||||
const dynamic::types::Properties& _p) const noexcept;
|
||||
|
||||
std::string column_or_value_to_sql(
|
||||
const dynamic::ColumnOrValue& _col) const noexcept;
|
||||
|
||||
std::string column_to_sql_definition(
|
||||
const dynamic::Column& _col) const noexcept;
|
||||
|
||||
std::string condition_to_sql(
|
||||
const dynamic::Condition& _condition) const noexcept;
|
||||
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(
|
||||
const ConditionType& _condition) const noexcept;
|
||||
|
||||
std::string create_table_to_sql(
|
||||
const dynamic::CreateTable& _stmt) const noexcept;
|
||||
|
||||
static std::string get_name(const dynamic::Column& _col) { return _col.name; }
|
||||
|
||||
std::vector<std::string> get_primary_keys(
|
||||
const dynamic::CreateTable& _stmt) const noexcept;
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) const noexcept;
|
||||
|
||||
static ConnPtr make_conn(const std::string& _conn_str);
|
||||
|
||||
std::string select_from_to_sql(
|
||||
const dynamic::SelectFrom& _stmt) const noexcept;
|
||||
|
||||
std::string to_buffer(
|
||||
const std::vector<std::optional<std::string>>& _line) const noexcept;
|
||||
|
||||
std::string type_to_sql(const dynamic::Type& _type) const noexcept;
|
||||
|
||||
static std::string wrap_in_quotes(const std::string& _name) noexcept {
|
||||
return "\"" + _name + "\"";
|
||||
}
|
||||
|
||||
private:
|
||||
ConnPtr conn_;
|
||||
|
||||
Credentials credentials_;
|
||||
};
|
||||
|
||||
template <class ConditionType>
|
||||
std::string Connection::condition_to_sql_impl(
|
||||
const ConditionType& _condition) const noexcept {
|
||||
using C = std::remove_cvref_t<ConditionType>;
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<C, dynamic::Condition::And>) {
|
||||
stream << "(" << condition_to_sql(*_condition.cond1) << ") AND ("
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Equal>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " = "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " >= " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " > "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " != " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " <= " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " < "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Or>) {
|
||||
stream << "(" << condition_to_sql(*_condition.cond1) << ") OR ("
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<C>, "Not all cases where covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace sqlgen::postgres
|
||||
|
||||
#endif
|
||||
|
||||
15
include/sqlgen/postgres/to_sql.hpp
Normal file
15
include/sqlgen/postgres/to_sql.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef SQLGEN_POSTGRES_TO_SQL_HPP_
|
||||
#define SQLGEN_POSTGRES_TO_SQL_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../dynamic/Statement.hpp"
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
/// Transpiles a dynamic general SQL statement to the postgres dialect.
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept;
|
||||
|
||||
} // namespace sqlgen::postgres
|
||||
|
||||
#endif
|
||||
@@ -7,84 +7,10 @@
|
||||
|
||||
#include "sqlgen/internal/collect/vector.hpp"
|
||||
#include "sqlgen/internal/strings/strings.hpp"
|
||||
#include "sqlgen/postgres/Iterator.hpp"
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
std::string Connection::add_not_null_if_necessary(
|
||||
const dynamic::types::Properties& _p) const noexcept {
|
||||
return std::string(_p.nullable ? "" : " NOT NULL");
|
||||
}
|
||||
|
||||
std::string Connection::column_or_value_to_sql(
|
||||
const dynamic::ColumnOrValue& _col) const noexcept {
|
||||
const auto handle_value = [](const auto& _v) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::String>) {
|
||||
return "'" + _v.val + "'";
|
||||
} else {
|
||||
return std::to_string(_v.val);
|
||||
}
|
||||
};
|
||||
|
||||
return _col.visit([&](const auto& _c) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_c)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
return wrap_in_quotes(_c.name);
|
||||
} else {
|
||||
return _c.visit(handle_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string Connection::condition_to_sql(
|
||||
const dynamic::Condition& _cond) const noexcept {
|
||||
return _cond.val.visit(
|
||||
[&](const auto& _c) { return condition_to_sql_impl(_c); });
|
||||
}
|
||||
|
||||
std::string Connection::column_to_sql_definition(
|
||||
const dynamic::Column& _col) const noexcept {
|
||||
return wrap_in_quotes(_col.name) + " " + type_to_sql(_col.type) +
|
||||
add_not_null_if_necessary(
|
||||
_col.type.visit([](const auto& _t) { return _t.properties; }));
|
||||
}
|
||||
|
||||
std::string Connection::create_table_to_sql(
|
||||
const dynamic::CreateTable& _stmt) const noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto col_to_sql = [&](const auto& _col) {
|
||||
return column_to_sql_definition(_col);
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "CREATE TABLE ";
|
||||
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "IF NOT EXISTS ";
|
||||
}
|
||||
|
||||
if (_stmt.table.schema) {
|
||||
stream << wrap_in_quotes(*_stmt.table.schema) << ".";
|
||||
}
|
||||
stream << wrap_in_quotes(_stmt.table.name) << " ";
|
||||
|
||||
stream << "(";
|
||||
stream << internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.columns | transform(col_to_sql)));
|
||||
|
||||
const auto primary_keys = get_primary_keys(_stmt);
|
||||
|
||||
if (primary_keys.size() != 0) {
|
||||
stream << ", PRIMARY KEY (" << internal::strings::join(", ", primary_keys)
|
||||
<< ")";
|
||||
}
|
||||
|
||||
stream << ");";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::end_write() {
|
||||
if (PQputCopyEnd(conn_.get(), NULL) == -1) {
|
||||
return error(PQerrorMessage(conn_.get()));
|
||||
@@ -96,32 +22,6 @@ Result<Nothing> Connection::end_write() {
|
||||
return Nothing{};
|
||||
}
|
||||
|
||||
std::vector<std::string> Connection::get_primary_keys(
|
||||
const dynamic::CreateTable& _stmt) const noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto is_primary_key = [](const auto& _col) -> bool {
|
||||
return _col.type.visit(
|
||||
[](const auto& _t) -> bool { return _t.properties.primary; });
|
||||
};
|
||||
|
||||
return internal::collect::vector(_stmt.columns | filter(is_primary_key) |
|
||||
transform(get_name) |
|
||||
transform(wrap_in_quotes));
|
||||
}
|
||||
|
||||
std::string Connection::insert_to_sql(
|
||||
const dynamic::Insert& _stmt) const noexcept {
|
||||
using namespace std::ranges::views;
|
||||
const auto schema = wrap_in_quotes(_stmt.table.schema.value_or("public"));
|
||||
const auto table = wrap_in_quotes(_stmt.table.name);
|
||||
const auto colnames = internal::strings::join(
|
||||
", ",
|
||||
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
|
||||
return "COPY " + schema + "." + table + "(" + colnames +
|
||||
") FROM STDIN WITH DELIMITER '\t' NULL '\e' QUOTE '\a';";
|
||||
}
|
||||
|
||||
rfl::Result<Ref<sqlgen::Connection>> Connection::make(
|
||||
const Credentials& _credentials) noexcept {
|
||||
try {
|
||||
@@ -145,44 +45,13 @@ typename Connection::ConnPtr Connection::make_conn(
|
||||
return ConnPtr::make(std::shared_ptr<PGconn>(raw_ptr, &PQfinish)).value();
|
||||
}
|
||||
|
||||
std::string Connection::select_from_to_sql(
|
||||
const dynamic::SelectFrom& _stmt) const noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto order_by_to_str = [](const auto& _w) -> std::string {
|
||||
return "\"" + _w.column.name + "\"" + (_w.desc ? " DESC" : "");
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "SELECT ";
|
||||
stream << internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.columns | transform(get_name) |
|
||||
transform(wrap_in_quotes)));
|
||||
stream << " FROM ";
|
||||
if (_stmt.table.schema) {
|
||||
stream << wrap_in_quotes(*_stmt.table.schema) << ".";
|
||||
Result<Ref<IteratorBase>> Connection::read(const dynamic::SelectFrom& _query) {
|
||||
const auto sql = postgres::to_sql(_query);
|
||||
try {
|
||||
return Ref<IteratorBase>(Ref<Iterator>::make(sql, conn_));
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
stream << wrap_in_quotes(_stmt.table.name);
|
||||
|
||||
if (_stmt.where) {
|
||||
stream << " WHERE " << condition_to_sql(*_stmt.where);
|
||||
}
|
||||
|
||||
if (_stmt.order_by) {
|
||||
stream << " ORDER BY "
|
||||
<< internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.order_by->columns |
|
||||
transform(order_by_to_str)));
|
||||
}
|
||||
|
||||
if (_stmt.limit) {
|
||||
stream << " LIMIT " << _stmt.limit->val;
|
||||
}
|
||||
|
||||
stream << ";";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string Connection::to_buffer(
|
||||
@@ -205,57 +74,6 @@ std::string Connection::to_buffer(
|
||||
"\n";
|
||||
}
|
||||
|
||||
std::string Connection::to_sql(const dynamic::Statement& _stmt) noexcept {
|
||||
return _stmt.visit([&](const auto& _s) -> std::string {
|
||||
using S = std::remove_cvref_t<decltype(_s)>;
|
||||
if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
|
||||
return create_table_to_sql(_s);
|
||||
} else if constexpr (std::is_same_v<S, dynamic::Insert>) {
|
||||
return insert_to_sql(_s);
|
||||
} else if constexpr (std::is_same_v<S, dynamic::SelectFrom>) {
|
||||
return select_from_to_sql(_s);
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<S>, "Unsupported type.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string Connection::type_to_sql(const dynamic::Type& _type) const noexcept {
|
||||
return _type.visit([](const auto _t) -> std::string {
|
||||
using T = std::remove_cvref_t<decltype(_t)>;
|
||||
if constexpr (std::is_same_v<T, dynamic::types::Boolean>) {
|
||||
return "BOOLEAN";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int8> ||
|
||||
std::is_same_v<T, dynamic::types::Int16> ||
|
||||
std::is_same_v<T, dynamic::types::UInt8> ||
|
||||
std::is_same_v<T, dynamic::types::UInt16>) {
|
||||
return "SMALLINT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int32> ||
|
||||
std::is_same_v<T, dynamic::types::UInt32>) {
|
||||
return "INTEGER";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int64> ||
|
||||
std::is_same_v<T, dynamic::types::UInt64>) {
|
||||
return "BIGINT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32>) {
|
||||
return "REAL";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "DOUBLE PRECISION";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Text>) {
|
||||
return "TEXT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::VarChar>) {
|
||||
return "VARCHAR(" + std::to_string(_t.length) + ")";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Timestamp>) {
|
||||
return "TIMESTAMP";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::TimestampWithTZ>) {
|
||||
return "TIMESTAMP WITH TIME ZONE";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Unknown>) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Not all cases were covered.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Result<Nothing> Connection::write(
|
||||
const std::vector<std::vector<std::optional<std::string>>>& _data) {
|
||||
for (const auto& line : _data) {
|
||||
|
||||
277
src/sqlgen/postgres/to_sql.cpp
Normal file
277
src/sqlgen/postgres/to_sql.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#include "sqlgen/postgres/to_sql.hpp"
|
||||
|
||||
#include <ranges>
|
||||
#include <rfl.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "sqlgen/internal/collect/vector.hpp"
|
||||
#include "sqlgen/internal/strings/strings.hpp"
|
||||
|
||||
namespace sqlgen::postgres {
|
||||
|
||||
std::string add_not_null_if_necessary(
|
||||
const dynamic::types::Properties& _p) noexcept;
|
||||
|
||||
std::string column_or_value_to_sql(const dynamic::ColumnOrValue& _col) noexcept;
|
||||
|
||||
std::string condition_to_sql(const dynamic::Condition& _cond) noexcept;
|
||||
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(const ConditionType& _condition) noexcept;
|
||||
|
||||
std::string column_to_sql_definition(const dynamic::Column& _col) noexcept;
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::vector<std::string> get_primary_keys(
|
||||
const dynamic::CreateTable& _stmt) noexcept;
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept;
|
||||
|
||||
std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept;
|
||||
|
||||
std::string type_to_sql(const dynamic::Type& _type) noexcept;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
inline std::string get_name(const dynamic::Column& _col) { return _col.name; }
|
||||
|
||||
inline std::string wrap_in_quotes(const std::string& _name) noexcept {
|
||||
return "\"" + _name + "\"";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
std::string add_not_null_if_necessary(
|
||||
const dynamic::types::Properties& _p) noexcept {
|
||||
return std::string(_p.nullable ? "" : " NOT NULL");
|
||||
}
|
||||
|
||||
std::string column_or_value_to_sql(
|
||||
const dynamic::ColumnOrValue& _col) noexcept {
|
||||
const auto handle_value = [](const auto& _v) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::String>) {
|
||||
return "'" + _v.val + "'";
|
||||
} else {
|
||||
return std::to_string(_v.val);
|
||||
}
|
||||
};
|
||||
|
||||
return _col.visit([&](const auto& _c) -> std::string {
|
||||
using Type = std::remove_cvref_t<decltype(_c)>;
|
||||
if constexpr (std::is_same_v<Type, dynamic::Column>) {
|
||||
return wrap_in_quotes(_c.name);
|
||||
} else {
|
||||
return _c.visit(handle_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string condition_to_sql(const dynamic::Condition& _cond) noexcept {
|
||||
return _cond.val.visit(
|
||||
[&](const auto& _c) { return condition_to_sql_impl(_c); });
|
||||
}
|
||||
|
||||
template <class ConditionType>
|
||||
std::string condition_to_sql_impl(const ConditionType& _condition) noexcept {
|
||||
using C = std::remove_cvref_t<ConditionType>;
|
||||
std::stringstream stream;
|
||||
|
||||
if constexpr (std::is_same_v<C, dynamic::Condition::And>) {
|
||||
stream << "(" << condition_to_sql(*_condition.cond1) << ") AND ("
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Equal>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " = "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " >= " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::GreaterThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " > "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::NotEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " != " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserEqual>) {
|
||||
stream << column_or_value_to_sql(_condition.op1)
|
||||
<< " <= " << column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::LesserThan>) {
|
||||
stream << column_or_value_to_sql(_condition.op1) << " < "
|
||||
<< column_or_value_to_sql(_condition.op2);
|
||||
|
||||
} else if constexpr (std::is_same_v<C, dynamic::Condition::Or>) {
|
||||
stream << "(" << condition_to_sql(*_condition.cond1) << ") OR ("
|
||||
<< condition_to_sql(*_condition.cond2) << ")";
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<C>, "Not all cases where covered.");
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string column_to_sql_definition(const dynamic::Column& _col) noexcept {
|
||||
return wrap_in_quotes(_col.name) + " " + type_to_sql(_col.type) +
|
||||
add_not_null_if_necessary(
|
||||
_col.type.visit([](const auto& _t) { return _t.properties; }));
|
||||
}
|
||||
|
||||
std::string create_table_to_sql(const dynamic::CreateTable& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto col_to_sql = [&](const auto& _col) {
|
||||
return column_to_sql_definition(_col);
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "CREATE TABLE ";
|
||||
|
||||
if (_stmt.if_not_exists) {
|
||||
stream << "IF NOT EXISTS ";
|
||||
}
|
||||
|
||||
if (_stmt.table.schema) {
|
||||
stream << wrap_in_quotes(*_stmt.table.schema) << ".";
|
||||
}
|
||||
stream << wrap_in_quotes(_stmt.table.name) << " ";
|
||||
|
||||
stream << "(";
|
||||
stream << internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.columns | transform(col_to_sql)));
|
||||
|
||||
const auto primary_keys = get_primary_keys(_stmt);
|
||||
|
||||
if (primary_keys.size() != 0) {
|
||||
stream << ", PRIMARY KEY (" << internal::strings::join(", ", primary_keys)
|
||||
<< ")";
|
||||
}
|
||||
|
||||
stream << ");";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::vector<std::string> get_primary_keys(
|
||||
const dynamic::CreateTable& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto is_primary_key = [](const auto& _col) -> bool {
|
||||
return _col.type.visit(
|
||||
[](const auto& _t) -> bool { return _t.properties.primary; });
|
||||
};
|
||||
|
||||
return internal::collect::vector(_stmt.columns | filter(is_primary_key) |
|
||||
transform(get_name) |
|
||||
transform(wrap_in_quotes));
|
||||
}
|
||||
|
||||
std::string insert_to_sql(const dynamic::Insert& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
const auto schema = wrap_in_quotes(_stmt.table.schema.value_or("public"));
|
||||
const auto table = wrap_in_quotes(_stmt.table.name);
|
||||
const auto colnames = internal::strings::join(
|
||||
", ",
|
||||
internal::collect::vector(_stmt.columns | transform(wrap_in_quotes)));
|
||||
return "COPY " + schema + "." + table + "(" + colnames +
|
||||
") FROM STDIN WITH DELIMITER '\t' NULL '\e' QUOTE '\a';";
|
||||
}
|
||||
|
||||
std::string select_from_to_sql(const dynamic::SelectFrom& _stmt) noexcept {
|
||||
using namespace std::ranges::views;
|
||||
|
||||
const auto order_by_to_str = [](const auto& _w) -> std::string {
|
||||
return "\"" + _w.column.name + "\"" + (_w.desc ? " DESC" : "");
|
||||
};
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "SELECT ";
|
||||
stream << internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.columns | transform(get_name) |
|
||||
transform(wrap_in_quotes)));
|
||||
stream << " FROM ";
|
||||
if (_stmt.table.schema) {
|
||||
stream << wrap_in_quotes(*_stmt.table.schema) << ".";
|
||||
}
|
||||
stream << wrap_in_quotes(_stmt.table.name);
|
||||
|
||||
if (_stmt.where) {
|
||||
stream << " WHERE " << condition_to_sql(*_stmt.where);
|
||||
}
|
||||
|
||||
if (_stmt.order_by) {
|
||||
stream << " ORDER BY "
|
||||
<< internal::strings::join(
|
||||
", ", internal::collect::vector(_stmt.order_by->columns |
|
||||
transform(order_by_to_str)));
|
||||
}
|
||||
|
||||
if (_stmt.limit) {
|
||||
stream << " LIMIT " << _stmt.limit->val;
|
||||
}
|
||||
|
||||
stream << ";";
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string to_sql(const dynamic::Statement& _stmt) noexcept {
|
||||
return _stmt.visit([&](const auto& _s) -> std::string {
|
||||
using S = std::remove_cvref_t<decltype(_s)>;
|
||||
if constexpr (std::is_same_v<S, dynamic::CreateTable>) {
|
||||
return create_table_to_sql(_s);
|
||||
} else if constexpr (std::is_same_v<S, dynamic::Insert>) {
|
||||
return insert_to_sql(_s);
|
||||
} else if constexpr (std::is_same_v<S, dynamic::SelectFrom>) {
|
||||
return select_from_to_sql(_s);
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<S>, "Unsupported type.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::string type_to_sql(const dynamic::Type& _type) noexcept {
|
||||
return _type.visit([](const auto _t) -> std::string {
|
||||
using T = std::remove_cvref_t<decltype(_t)>;
|
||||
if constexpr (std::is_same_v<T, dynamic::types::Boolean>) {
|
||||
return "BOOLEAN";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int8> ||
|
||||
std::is_same_v<T, dynamic::types::Int16> ||
|
||||
std::is_same_v<T, dynamic::types::UInt8> ||
|
||||
std::is_same_v<T, dynamic::types::UInt16>) {
|
||||
return "SMALLINT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int32> ||
|
||||
std::is_same_v<T, dynamic::types::UInt32>) {
|
||||
return "INTEGER";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Int64> ||
|
||||
std::is_same_v<T, dynamic::types::UInt64>) {
|
||||
return "BIGINT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float32>) {
|
||||
return "REAL";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Float64>) {
|
||||
return "DOUBLE PRECISION";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Text>) {
|
||||
return "TEXT";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::VarChar>) {
|
||||
return "VARCHAR(" + std::to_string(_t.length) + ")";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Timestamp>) {
|
||||
return "TIMESTAMP";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::TimestampWithTZ>) {
|
||||
return "TIMESTAMP WITH TIME ZONE";
|
||||
} else if constexpr (std::is_same_v<T, dynamic::types::Unknown>) {
|
||||
return "TEXT";
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<T>, "Not all cases were covered.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace sqlgen::postgres
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "sqlgen/postgres/Connection.cpp"
|
||||
#include "sqlgen/postgres/Iterator.cpp"
|
||||
#include "sqlgen/postgres/exec.cpp"
|
||||
#include "sqlgen/postgres/to_sql.cpp"
|
||||
|
||||
Reference in New Issue
Block a user