query builder progress

This commit is contained in:
Sascha Kühl 2025-12-02 16:19:13 +01:00
parent c156ab5e74
commit bfbbd4c589
31 changed files with 348 additions and 164 deletions

View File

@ -240,9 +240,9 @@ utils::result<std::vector<object::attribute>, utils::error> postgres_connection:
// Todo: extract size
auto type = (string2type(reader.column(2)));
end = nullptr;
object::null_option_type null_opt{object::null_option_type::NULLABLE};
object::null_option_type null_opt{object::null_option_type::Nullable};
if (strtoul(reader.column(4), &end, 10) == 0) {
null_opt = object::null_option_type::NOT_NULL;
null_opt = object::null_option_type::NotNull;
}
// f.default_value(res->column(4));
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);

View File

@ -10,14 +10,9 @@
namespace matador::object {
enum class null_option_type : uint8_t {
NULLABLE, NOT_NULL
Nullable, NotNull
};
struct attribute_options {
utils::field_attributes attributes;
null_option_type null_option{null_option_type::NOT_NULL};
int index{-1};
};
class object;
class attribute_generator;
@ -33,20 +28,17 @@ public:
attribute() = default;
attribute(std::string name,
utils::basic_type type,
const utils::field_attributes&,
null_option_type null_opt);
const utils::field_attributes &attr = utils::null_attributes,
null_option_type null_opt = null_option_type::NotNull);
[[nodiscard]] const std::string& name() const;
void name(const std::string& n);
[[nodiscard]] std::string full_name() const;
[[nodiscard]] int index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] utils::field_attributes& attributes();
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] utils::basic_type type() const;
[[nodiscard]] object* owner() const;
// [[nodiscard]] const std::string& table_name() const;
// void table_name(const std::string& name);
void change_type(utils::basic_type type, const utils::field_attributes &attr = utils::null_attributes);
template < typename Type >
void change_type(const utils::field_attributes &attr = utils::null_attributes) {
@ -75,8 +67,9 @@ private:
std::string name_;
object *owner_{nullptr};
attribute_options options_;
utils::basic_type type_{utils::basic_type::type_null};
utils::field_attributes options_{};
null_option_type null_option_{null_option_type::NotNull};
};

View File

@ -27,7 +27,7 @@ public:
template<class Type>
attribute generate(const char *id, Type &x) {
access::process(*this, x);
return attribute{id, type_, {utils::constraints::ForeignKey }, null_option_type::NOT_NULL};
return attribute{id, type_, {utils::constraints::ForeignKey }, null_option_type::NotNull};
}
template<typename ValueType>
@ -139,18 +139,18 @@ private:
template<typename ValueType>
void attribute_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NOT_NULL);
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NotNull);
prepare_primary_key(ref, utils::identifier(x));
}
template<typename Type>
void attribute_generator::on_attribute(const char *id, Type &/*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NOT_NULL);
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NotNull);
}
template<typename Type>
void attribute_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NULLABLE);
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::Nullable);
}
}

View File

@ -2,6 +2,7 @@
#define BASIC_PROTOTYPE_INFO_HPP
#include "matador/object/attribute.hpp"
#include "matador/object/constraint.hpp"
#include "matador/object/relation_endpoint.hpp"
#include "matador/utils/identifier.hpp"
@ -26,6 +27,7 @@ public:
[[nodiscard]] std::type_index type_index() const;
[[nodiscard]] std::string name() const;
[[nodiscard]] const std::list<attribute>& attributes() const;
[[nodiscard]] const std::list<class constraint>& constraints() const;
[[nodiscard]] bool has_primary_key() const;
[[nodiscard]] const utils::identifier& primary_key() const;

View File

@ -4,6 +4,7 @@
#include "matador/utils/constraints.hpp"
#include <string>
#include <variant>
namespace matador::object {
@ -18,6 +19,14 @@ public:
explicit constraint(std::string name);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] const class attribute* attribute() const;
[[nodiscard]] std::string column_name() const;
[[nodiscard]] const object* owner() const;
[[nodiscard]] bool is_primary_key_constraint() const;
[[nodiscard]] bool is_foreign_key_constraint() const;
[[nodiscard]] bool is_unique_constraint() const;
[[nodiscard]] const std::string& ref_table_name() const;
[[nodiscard]] const std::string& ref_column_name() const;
private:
friend class constraint_builder;
@ -26,7 +35,8 @@ private:
friend class object;
std::string name_;
attribute* attr_{nullptr};
std::variant<class attribute*, std::string> attr_;
// class attribute* attr_{nullptr};
object *owner_{nullptr};
utils::constraints options_{utils::constraints::None};
std::string ref_table_name_{};

View File

@ -88,7 +88,7 @@ public:
template <class Pointer>
void on_foreign_key(const char *id, Pointer &/*x*/) {
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NOT_NULL);
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
ref.owner_ = object_.get();
}
template<class ContainerType>
@ -121,19 +121,19 @@ private:
template<typename ValueType>
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NOT_NULL);
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NotNull);
prepare_primary_key(ref, utils::identifier(x));
create_pk_constraint(id);
}
template<typename Type>
void object_generator::on_attribute(const char *id, Type &/*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NOT_NULL);
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NotNull);
}
template<typename Type>
void object_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NULLABLE);
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::Nullable);
}
}

View File

@ -272,7 +272,7 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
append_join(
query::column{table_info_stack_.top().table, id},
query::column{next->second, info->get().reference_column()->name()}
query::column{next->second, info->get().primary_key_attribute()->name()}
);
} else {
push(id);
@ -281,7 +281,7 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
// create select query
auto result = matador::query::query::select(generator::columns<typename Pointer::value_type>(schema_, generator::column_generator_options::ForceLazy))
.from(*foreign_table)
.where(column(foreign_table, info->get().reference_column()->name(), "") == _)
.where(column(foreign_table, info->get().primary_key_attribute()->name(), "") == _)
.prepare(executor_);
if (!result) {
throw query_builder_exception(query_build_error::QueryError, result.release_error());

View File

@ -26,10 +26,8 @@ private:
class data_type {
public:
template<typename Type>
data_type() : type_(utils::data_type_traits<Type>()) {}
template<typename Type>
explicit data_type(const size_t size) : type_(utils::data_type_traits<Type>()), size_(size) {}
explicit data_type(const utils::basic_type type, const size_t size = 0)
: type_(type), size_(size) {}
[[nodiscard]] const utils::basic_type& type() const { return type_; }
[[nodiscard]] size_t size() const { return size_; }
@ -39,10 +37,28 @@ private:
size_t size_{0};
};
// using BigInt = data_type<int64_t>();
// using Real = data_type<float>;
// using double_ = data_type<double>;
// using string = data_type<std::string>;
template<typename Type>
class typed_data_type final : public data_type {
public:
typed_data_type()
: data_type(utils::data_type_traits<Type>::type()) {}
};
template<typename Type>
class sized_typed_data_type final : public data_type {
public:
explicit sized_typed_data_type(size_t size)
: data_type(utils::data_type_traits<Type>::type(size), size) {}
};
using BigInt = typed_data_type<int64_t>;
using Integer = typed_data_type<int64_t>;
using Real = typed_data_type<float>;
using Double_ = typed_data_type<double>;
using String = typed_data_type<std::string>;
using Boolean = typed_data_type<bool>;
using Varchar = sized_typed_data_type<std::string>;
class constraint {
public:
@ -50,7 +66,7 @@ public:
: name_(std::move(name)) {}
[[nodiscard]] const std::string& name() const;
[[nodiscard]] const std::string& column_name() const;
[[nodiscard]] std::string column_name() const;
[[nodiscard]] const utils::constraints& type() const;
[[nodiscard]] bool is_primary_key_constraint() const;
[[nodiscard]] bool is_foreign_key_constraint() const;
@ -64,6 +80,7 @@ private:
utils::constraints type_{};
std::string referenced_table_;
std::string referenced_column_;
data_type dt = Varchar{255};
};
}
namespace matador::query {

View File

@ -5,16 +5,32 @@
#include "matador/query/intermediates/executable_query.hpp"
#include "matador/object/attribute_generator.hpp"
#include "matador/object/attribute.hpp"
#include "matador/object/constraint.hpp"
namespace matador::query {
class query_create_table_columns_intermediate : public executable_query {
public:
using executable_query::executable_query;
executable_query constraints(std::initializer_list<class object::constraint> constraints);
executable_query constraints(const std::list<class object::constraint> &constraints);
};
class query_create_table_intermediate : public query_intermediate {
public:
using query_intermediate::query_intermediate;
query_create_table_columns_intermediate columns(std::initializer_list<object::attribute> columns);
query_create_table_columns_intermediate columns(const std::list<object::attribute> &columns);
};
class query_create_intermediate : public query_intermediate {
public:
query_create_intermediate();
executable_query table(const table &tab, std::initializer_list<object::attribute> columns);
executable_query table(const query::table &tab, const std::vector<object::attribute> &columns);
query_create_table_intermediate table(const table &tab);
executable_query schema(const std::string &schema_name);
};

View File

@ -10,6 +10,7 @@
#include "matador/query/table.hpp"
#include "matador/object/attribute.hpp"
#include "matador/object/constraint.hpp"
#include "matador/utils/placeholder.hpp"
@ -364,17 +365,41 @@ private:
class query_create_table_part final : public query_part
{
public:
query_create_table_part(class table tab, std::vector<object::attribute> columns);
explicit query_create_table_part(class table tab);
[[nodiscard]] const class table& table() const;
[[nodiscard]] const std::vector<object::attribute>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
class table table_;
std::vector<object::attribute> columns_;
};
class query_create_table_columns_part final : public query_part {
public:
explicit query_create_table_columns_part(const std::list<object::attribute> &columns);
[[nodiscard]] const std::list<object::attribute>& columns() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::list<object::attribute> columns_;
};
class query_create_table_constraints_part final : public query_part {
public:
explicit query_create_table_constraints_part(const std::list<class object::constraint> &constraints);
[[nodiscard]] const std::list<class object::constraint>& constraints() const;
private:
void accept(query_part_visitor &visitor) override;
private:
std::list<class object::constraint> constraints_;
};
class query_create_schema_part final : public query_part {

View File

@ -8,6 +8,7 @@
#include "matador/utils/placeholder.hpp"
#include <functional>
#include <optional>
namespace matador::sql {
@ -64,6 +65,8 @@ protected:
void visit(internal::query_create_part &part) override;
void visit(internal::query_create_table_part &part) override;
void visit(internal::query_create_table_columns_part& part) override;
void visit(internal::query_create_table_constraints_part& part) override;
void visit(internal::query_create_schema_part& part) override;
void visit(internal::query_drop_part &part) override;
@ -79,6 +82,8 @@ protected:
size_t table_index{0};
const sql::dialect *dialect_{nullptr};
std::optional<std::reference_wrapper<const sql::connection_impl>> connection_{};
std::function<void(sql::query_context&)> finisher_ = [](sql::query_context&) {};
};
}

View File

@ -32,6 +32,8 @@ class query_delete_part;
class query_delete_from_part;
class query_create_part;
class query_create_table_part;
class query_create_table_columns_part;
class query_create_table_constraints_part;
class query_create_schema_part;
class query_drop_part;
class query_drop_table_part;
@ -75,6 +77,8 @@ public:
virtual void visit(internal::query_create_part &part) = 0;
virtual void visit(internal::query_create_table_part &part) = 0;
virtual void visit(internal::query_create_table_columns_part &part) = 0;
virtual void visit(internal::query_create_table_constraints_part &part) = 0;
virtual void visit(internal::query_create_schema_part &part) = 0;
virtual void visit(internal::query_drop_part &part) = 0;

View File

@ -137,6 +137,7 @@ public:
[[nodiscard]] const std::string& column() const;
[[nodiscard]] const std::string& columns() const;
[[nodiscard]] const std::string& commit() const;
[[nodiscard]] const std::string& constraint() const;
[[nodiscard]] const std::string& create() const;
[[nodiscard]] const std::string& desc() const;
[[nodiscard]] const std::string& distinct() const;
@ -201,6 +202,7 @@ private:
{dialect_token::Column, "COLUMN"},
{dialect_token::Columns, "COLUMNS"},
{dialect_token::Commit, "COMMIT TRANSACTION"},
{dialect_token::Constraint, "CONSTRAINT"},
{dialect_token::Create, "CREATE"},
{dialect_token::Desc, "DESC"},
{dialect_token::Distinct, "DISTINCT"},

View File

@ -19,6 +19,7 @@ enum class dialect_token : uint8_t {
Column,
Columns,
Commit,
Constraint,
Create,
Database,
Desc,

View File

@ -25,7 +25,8 @@ enum class basic_type : uint8_t {
type_date, /*!< Data type date */
type_time, /*!< Data type time */
type_blob, /*!< Data type blob */
type_null /*!< Data type null */
type_null, /*!< Data type null */
type_unknown /*!< Data type unknown */
};
}

View File

@ -19,7 +19,11 @@ class attribute_writer;
* for a data type
*/
template < class Type, class Enable = void >
struct data_type_traits;
struct data_type_traits {
static basic_type type(std::size_t /*size*/) { return basic_type::type_unknown; }
static void read_value(attribute_reader &/*reader*/, const char *id, size_t index, nullptr_t &/*value*/, size_t /*size*/ = 0) {}
static void bind_value(attribute_writer &/*binder*/, size_t index, nullptr_t &/*value*/, size_t /*size*/ = 0) {}
};
}
#endif //BASIC_TYPE_TRAITS_HPP

View File

@ -6,7 +6,7 @@
namespace matador::object {
attribute::attribute(std::string name)
: attribute(std::move(name), utils::basic_type::type_null, {}, null_option_type::NOT_NULL) {
: attribute(std::move(name), utils::basic_type::type_null, {}, null_option_type::NotNull) {
}
attribute::attribute(std::string name,
@ -14,8 +14,9 @@ attribute::attribute(std::string name,
const utils::field_attributes &attr,
const null_option_type null_opt)
: name_(std::move(name))
, options_{attr, null_opt}
, type_(type)
, options_(attr)
, null_option_(null_opt)
{}
const std::string &attribute::name() const {
@ -30,20 +31,16 @@ std::string attribute::full_name() const {
return owner_ ? owner_->name() + "." + name_ : name_;
}
int attribute::index() const {
return options_.index;
}
const utils::field_attributes &attribute::attributes() const {
return options_.attributes;
return options_;
}
utils::field_attributes& attribute::attributes() {
return options_.attributes;
return options_;
}
bool attribute::is_nullable() const {
return options_.null_option == null_option_type::NULLABLE;
return null_option_ == null_option_type::Nullable;
}
utils::basic_type attribute::type() const {
@ -54,16 +51,8 @@ object* attribute::owner() const {
return owner_;
}
// const std::string& attribute::table_name() const {
// return owner_ ? owner_->name() : "";
// }
//
// void attribute::table_name( const std::string& name ) {
// table_name_ = name;
// }
void attribute::change_type(const utils::basic_type type, const utils::field_attributes& attr) {
options_.attributes = attr;
options_ = attr;
type_ = type;
}

View File

@ -36,6 +36,10 @@ const std::list<attribute>& basic_object_info::attributes() const {
return object_->attributes();
}
const std::list<class constraint>& basic_object_info::constraints() const {
return object_->constraints();
}
bool basic_object_info::has_primary_key() const {
return object_->has_primary_key();
}

View File

@ -1,4 +1,5 @@
#include "matador/object/constraint.hpp"
#include "matador/object/attribute.hpp"
namespace matador::object {
constraint::constraint(std::string name)
@ -8,6 +9,49 @@ const std::string & constraint::name() const {
return name_;
}
const class attribute* constraint::attribute() const {
if (std::holds_alternative<class attribute*>(attr_)) {
return std::get<class attribute*>(attr_);
}
return nullptr;
}
std::string constraint::column_name() const {
if (std::holds_alternative<class attribute*>(attr_)) {
return std::get<class attribute*>(attr_)->name();
}
if (std::holds_alternative<std::string>(attr_)) {
return std::get<std::string>(attr_);
}
return "";
}
const object* constraint::owner() const {
return owner_;
}
bool constraint::is_primary_key_constraint() const {
return utils::is_constraint_set(options_, utils::constraints::PrimaryKey);
}
bool constraint::is_foreign_key_constraint() const {
return utils::is_constraint_set(options_, utils::constraints::ForeignKey);
}
bool constraint::is_unique_constraint() const {
return utils::is_constraint_set(options_, utils::constraints::Unique);
}
const std::string& constraint::ref_table_name() const {
return ref_table_name_;
}
const std::string& constraint::ref_column_name() const {
return ref_column_name_;
}
constraint_builder & constraint_builder::constraint(std::string name) {
constraint_name = std::move(name);
return *this;
@ -35,10 +79,11 @@ constraint_builder & constraint_builder::references(std::string table, std::stri
constraint_builder::operator class constraint() const {
class constraint c;
c.name_ = constraint_name;
c.attr_ = column_name;
c.options_ = options_;
c.ref_column_name_ = ref_column_name;
c.ref_table_name_ = ref_table_name;
return {};
return c;
}
constraint_builder constraint(std::string name) {

View File

@ -110,7 +110,7 @@ attribute* repository_node::determine_reference_column(const std::type_index& ti
repository& repo) {
const auto it = repo.missing_references_.find(ti);
if (it == repo.missing_references_.end()) {
return new attribute(pk_info.pk_column_name, pk_info.type, {utils::constraints::ForeignKey}, null_option_type::NOT_NULL);
return new attribute(pk_info.pk_column_name, pk_info.type, {utils::constraints::ForeignKey}, null_option_type::NotNull);
}
auto ref_column = it->second;

View File

@ -57,7 +57,9 @@ matador::utils::result<void, matador::utils::error> matador::orm::schema::create
// std::cout << result.sql << std::endl;
for (const auto &node: repo_) {
auto ctx = query::query::create()
.table(node->name(), node->info().attributes())
.table(node->name())
.columns(node->info().attributes())
.constraints(node->info().constraints())
.compile(*c);
for ( const auto& [sql, command] : ctx.additional_commands ) {

View File

@ -52,7 +52,9 @@ utils::result<void, utils::error> session::create_schema() const {
auto c = cache_.pool().acquire();
for (const auto &node: *schema_) {
auto ctx = query::query::create()
.table(node->name(), node->info().attributes())
.table(node->name())
.columns(node->info().attributes())
.constraints(node->info().constraints())
.compile(*c);
for ( const auto& [sql, command] : ctx.additional_commands ) {

View File

@ -9,13 +9,14 @@ namespace matador::query {
namespace detail {
sql::record *create_prototype(const std::vector<object::attribute> &prototype) {
auto result = std::make_unique<sql::record>();
int index{0};
for (const auto &col: prototype) {
result->append({
col.name(),
col.type(),
col.attributes().options(),
col.attributes().size(),
col.index()
index++
});
}
return result.release();

View File

@ -8,12 +8,8 @@ query_create_intermediate::query_create_intermediate() {
context_->parts.push_back(std::make_unique<internal::query_create_part>());
}
executable_query query_create_intermediate::table(const class table &tab, const std::initializer_list<object::attribute> columns) {
return this->table(tab, std::vector<object::attribute>{columns});
}
executable_query query_create_intermediate::table(const class table &tab, const std::vector<object::attribute> &columns) {
context_->parts.push_back(std::make_unique<internal::query_create_table_part>(tab, columns));
query_create_table_intermediate query_create_intermediate::table(const class table &tab) {
context_->parts.push_back(std::make_unique<internal::query_create_table_part>(tab));
return {context_};
}
@ -21,4 +17,24 @@ executable_query query_create_intermediate::schema( const std::string& schema_na
context_->parts.push_back(std::make_unique<internal::query_create_schema_part>(schema_name));
return {context_};
}
executable_query query_create_table_columns_intermediate::constraints( std::initializer_list<class object::constraint> constraints ) {
return this->constraints(std::list(constraints));
}
executable_query query_create_table_columns_intermediate::constraints( const std::list<class object::constraint>& constraints ) {
context_->parts.push_back(std::make_unique<internal::query_create_table_constraints_part>(constraints));
return {context_};
}
query_create_table_columns_intermediate query_create_table_intermediate::columns( std::initializer_list<object::attribute> columns ) {
context_->parts.push_back(std::make_unique<internal::query_create_table_columns_part>(columns));
return {context_};
}
query_create_table_columns_intermediate query_create_table_intermediate::columns( const std::list<object::attribute>& columns ) {
context_->parts.push_back(std::make_unique<internal::query_create_table_columns_part>(columns));
return {context_};
}
}

View File

@ -344,23 +344,41 @@ void query_create_part::accept(query_part_visitor &visitor)
visitor.visit(*this);
}
query_create_table_part::query_create_table_part(class table tab, std::vector<object::attribute> columns)
query_create_table_part::query_create_table_part(class table tab)
: query_part(sql::dialect_token::Table)
, table_(std::move(tab))
, columns_(std::move(columns)) {}
, table_(std::move(tab)) {}
const table &query_create_table_part::table() const
{
return table_;
}
const std::vector<object::attribute> &query_create_table_part::columns() const
void query_create_table_part::accept(query_part_visitor &visitor)
{
visitor.visit(*this);
}
query_create_table_columns_part::query_create_table_columns_part(const std::list<object::attribute>& columns)
: query_part( sql::dialect_token::Columns )
, columns_(columns){}
const std::list<object::attribute>& query_create_table_columns_part::columns() const {
return columns_;
}
void query_create_table_part::accept(query_part_visitor &visitor)
{
void query_create_table_columns_part::accept(query_part_visitor& visitor) {
visitor.visit(*this);
}
query_create_table_constraints_part::query_create_table_constraints_part(const std::list<class object::constraint>& constraints)
: query_part( sql::dialect_token::Constraint )
, constraints_(constraints) {}
const std::list<class object::constraint>& query_create_table_constraints_part::constraints() const {
return constraints_;
}
void query_create_table_constraints_part::accept( query_part_visitor& visitor ) {
visitor.visit(*this);
}

View File

@ -27,6 +27,7 @@ sql::query_context query_compiler::compile(const query_data &data,
for (const auto &part: data.parts) {
part->accept(*this);
}
finisher_(query_);
connection_ = std::nullopt;
dialect_ = nullptr;
data_ = nullptr;
@ -289,55 +290,55 @@ void query_compiler::visit(internal::query_create_part &/*create_part*/)
query_.sql = dialect_->token_at(sql::dialect_token::Create);
}
struct fk_context {
std::string column;
std::shared_ptr<object::attribute> reference_column;
};
struct column_context
{
std::vector<std::string> primary_keys;
std::vector<fk_context> foreign_contexts;
};
std::string build_create_column(const object::attribute &col, const sql::dialect &d, column_context &context);
std::string build_create_column(const object::attribute &col, const sql::dialect &d);
std::string build_constraint(const class object::constraint &cons, const sql::dialect &d);
void query_compiler::visit(internal::query_create_table_part &part)
{
query_.sql += " " + dialect_->token_at(sql::dialect_token::Table) + " " + dialect_->prepare_identifier_string(part.table().name()) + " ";
query_.sql += " " + dialect_->token_at(sql::dialect_token::Table) + " " + dialect_->prepare_identifier_string(part.table().name()) + " (";
query_.table_name = part.table().name();
std::string result = "(";
// if (!context.primary_keys.empty()) {
// result.append(", CONSTRAINT PK_" + part.table().name() + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
// }
// for (const auto &[column, reference_column]: context.foreign_contexts) {
// // ALTER TABLE Orders ADD CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID);
// std::string fk_cmd = "ALTER TABLE " + dialect_->prepare_identifier_string(query_.table_name) + " ADD";
// fk_cmd += " CONSTRAINT FK_" + query_.table_name;
// fk_cmd += "_" + column;
// fk_cmd += " FOREIGN KEY (" + dialect_->prepare_identifier_string(column) + ")";
// fk_cmd += " REFERENCES " + reference_column->table_name() + "(" + reference_column->name() + ")";
// query_.additional_commands.push_back({fk_cmd, sql::sql_command::SQL_ALTER_TABLE});
// }
column_context context;
finisher_ = [](sql::query_context &ctx) { ctx.sql += ")"; };
}
void query_compiler::visit(internal::query_create_table_columns_part& part) {
std::string result = "(";
if (part.columns().size() < 2) {
for (const auto &col: part.columns()) {
result.append(build_create_column(col, *dialect_, context));
result.append(build_create_column(col, *dialect_));
}
} else {
auto it = part.columns().begin();
result.append(build_create_column(*it++, *dialect_, context));
result.append(build_create_column(*it++, *dialect_));
for (; it != part.columns().end(); ++it) {
result.append(", ");
result.append(build_create_column(*it, *dialect_, context));
result.append(build_create_column(*it, *dialect_));
}
}
if (!context.primary_keys.empty()) {
result.append(", CONSTRAINT PK_" + part.table().name() + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
}
for (const auto &[column, reference_column]: context.foreign_contexts) {
// ALTER TABLE Orders ADD CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID);
std::string fk_cmd = "ALTER TABLE " + dialect_->prepare_identifier_string(query_.table_name) + " ADD";
fk_cmd += " CONSTRAINT FK_" + query_.table_name;
fk_cmd += "_" + column;
fk_cmd += " FOREIGN KEY (" + dialect_->prepare_identifier_string(column) + ")";
fk_cmd += " REFERENCES " + reference_column->table_name() + "(" + reference_column->name() + ")";
query_.additional_commands.push_back({fk_cmd, sql::sql_command::SQL_ALTER_TABLE});
}
query_.sql += result;
}
result += ")";
void query_compiler::visit(internal::query_create_table_constraints_part& part) {
std::string result;
for (const auto& c : part.constraints()) {
result.append(", ");
result.append(build_constraint(c, *dialect_));
}
query_.sql += result;
}
@ -387,7 +388,7 @@ void query_compiler::visit(internal::query_drop_table_part &part)
query_.sql += " " + build_table_name(part.token(), *dialect_, query_.table_name);
}
std::string build_create_column(const object::attribute &col, const sql::dialect &d, column_context &context)
std::string build_create_column(const object::attribute &col, const sql::dialect &d)
{
std::string result = d.prepare_identifier_string(col.name()) + " " + d.data_type_at(col.type());
if (col.attributes().size() > 0) {
@ -399,16 +400,26 @@ std::string build_create_column(const object::attribute &col, const sql::dialect
if (is_constraint_set(col.attributes().options(), utils::constraints::Unique)) {
result.append(" UNIQUE");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::PrimaryKey)) {
context.primary_keys.emplace_back(col.name());
}
if (is_constraint_set(col.attributes().options(), utils::constraints::ForeignKey)) {
context.foreign_contexts.push_back({col.name(), col.reference_column()});
}
return result;
}
std::string build_constraint(const class object::constraint& cons, const sql::dialect& d) {
std::string result;
if (!cons.name().empty()) {
result.append(d.constraint()).append(" ").append(d.prepare_identifier_string(cons.name())).append(" ");
}
if (cons.is_primary_key_constraint()) {
result.append(d.primary_key());
} else if (cons.is_foreign_key_constraint()) {
result.append(d.foreign_key());
} else {
// handle error
}
result.append("(").append(cons.attribute()->full_name()).append(")");
return result;
}
std::string query_compiler::build_table_name(const sql::dialect_token token, const sql::dialect &d, const table& t)
{
return d.token_at(token) + " " +

View File

@ -172,6 +172,10 @@ const std::string& dialect::commit() const {
return token_at(dialect_token::Commit);
}
const std::string& dialect::constraint() const {
return token_at(dialect_token::Constraint);
}
const std::string& dialect::create() const {
return token_at(dialect_token::Create);
}

View File

@ -8,13 +8,14 @@ namespace matador::sql::detail {
template<>
record *create_prototype<record>(const std::vector<object::attribute> &prototype) {
auto result = std::make_unique<record>();
int index{0};
for (const auto &col: prototype) {
result->append({
col.name(),
col.type(),
col.attributes().options(),
col.attributes().size(),
col.index()
index++
});
}
return result.release();

View File

@ -22,15 +22,15 @@ TEST_CASE("Generate column definitions from object", "[column][definition][gener
auto columns = attribute_generator::generate<matador::test::product>(repo, obj);
const std::vector expected_columns = {
attribute{"product_name", basic_type::type_varchar, constraints::PrimaryKey, null_option_type::NOT_NULL },
attribute{"supplier_id", basic_type::type_uint32, constraints::ForeignKey, null_option_type::NOT_NULL },
attribute{"category_id", basic_type::type_uint32, constraints::ForeignKey, null_option_type::NOT_NULL },
attribute{"quantity_per_unit", basic_type::type_varchar, null_attributes, null_option_type::NOT_NULL },
attribute{"unit_price", basic_type::type_uint32, null_attributes, null_option_type::NOT_NULL },
attribute{"units_in_stock", basic_type::type_uint32, null_attributes, null_option_type::NOT_NULL },
attribute{"units_in_order", basic_type::type_uint32, null_attributes, null_option_type::NOT_NULL },
attribute{"reorder_level", basic_type::type_uint32, null_attributes, null_option_type::NOT_NULL },
attribute{"discontinued", basic_type::type_bool, null_attributes, null_option_type::NOT_NULL }
attribute{"product_name", basic_type::type_varchar, constraints::PrimaryKey, null_option_type::NotNull },
attribute{"supplier_id", basic_type::type_uint32, constraints::ForeignKey, null_option_type::NotNull },
attribute{"category_id", basic_type::type_uint32, constraints::ForeignKey, null_option_type::NotNull },
attribute{"quantity_per_unit", basic_type::type_varchar, null_attributes, null_option_type::NotNull },
attribute{"unit_price", basic_type::type_uint32, null_attributes, null_option_type::NotNull },
attribute{"units_in_stock", basic_type::type_uint32, null_attributes, null_option_type::NotNull },
attribute{"units_in_order", basic_type::type_uint32, null_attributes, null_option_type::NotNull },
attribute{"reorder_level", basic_type::type_uint32, null_attributes, null_option_type::NotNull },
attribute{"discontinued", basic_type::type_bool, null_attributes, null_option_type::NotNull }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
@ -49,9 +49,9 @@ TEST_CASE("Generate columns from object with nullable columns", "[column generat
auto columns = attribute_generator::generate<matador::test::optional>(repo, obj);
const std::vector expected_columns = {
attribute{"id", basic_type::type_uint32, constraints::PrimaryKey, null_option_type::NOT_NULL },
attribute{"name", basic_type::type_varchar, null_attributes, null_option_type::NOT_NULL },
attribute{"age", basic_type::type_uint32, null_attributes, null_option_type::NOT_NULL }
attribute{"id", basic_type::type_uint32, constraints::PrimaryKey, null_option_type::NotNull },
attribute{"name", basic_type::type_varchar, null_attributes, null_option_type::NotNull },
attribute{"age", basic_type::type_uint32, null_attributes, null_option_type::NotNull }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());

View File

@ -6,8 +6,6 @@
#include <matador/query/query.hpp>
#include "matador/query/table.hpp"
#include "matador/object/attribute_generator.hpp"
#include "matador/sql/connection.hpp"
#include "matador/utils/placeholder.hpp"
@ -37,21 +35,32 @@ TEST_CASE_METHOD(QueryFixture, "Test alter table sql statement", "[query][alter]
TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query]") {
auto result = query::create()
.table({"person"}, {
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("age")
}).str(*db);
.table({"person"})
.columns({
attribute("id", basic_type::type_uint32),
attribute("name", basic_type::type_varchar, 255),
attribute("age", basic_type::type_uint16)
})
.constraints({
constraint("PK_person").primary_key({"id"})
})
.str(*db);
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
auto ctx = query::create()
.table("person", {
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", {255, constraints::Unique}, null_option_type::NOT_NULL),
make_column<unsigned short>("age"),
make_fk_column<uint32_t>("address", "address", "id")
}).compile(*db);
.table("person")
.columns({
attribute("id", basic_type::type_uint32),
attribute("name", basic_type::type_varchar, 255),
attribute("age", basic_type::type_uint16),
attribute("address", basic_type::type_uint32)
})
.constraints({
constraint("PK_person").primary_key({"id"}),
constraint("FK_person_address").foreign_key({"address"}).references("address", {"id"})
})
.compile(*db);
REQUIRE(ctx.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
REQUIRE(ctx.additional_commands.size() == 1);
@ -210,10 +219,14 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with offset and
TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", "[query][blob]") {
auto result = query::create()
.table("person", {
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", 255),
make_column<blob>("data")
.table("person")
.columns({
attribute("id", basic_type::type_uint32),
attribute("name", basic_type::type_varchar, 255),
attribute("data", basic_type::type_blob)
})
.constraints({
constraint("PK_person").primary_key({"id"})
})
.str(*db);

View File

@ -9,9 +9,7 @@ TEST_CASE("Test create empty column", "[column]") {
attribute c("name");
REQUIRE(c.name() == "name");
REQUIRE(c.index() == -1);
REQUIRE(c.type() == basic_type::type_null);
REQUIRE(!c.reference_column());
c.change_type<std::string>(255);
REQUIRE(c.type() == basic_type::type_varchar);
@ -24,39 +22,39 @@ TEST_CASE("Test copy and move column", "[column]") {
attribute c(
"name",
basic_type::type_varchar,
2,
std::make_shared<attribute>("author", basic_type::type_uint32, "books", attribute_options{constraints::ForeignKey}),
// 2,
// std::make_shared<attribute>("author", basic_type::type_uint32, "books", attribute_options{constraints::ForeignKey}),
{255, constraints::ForeignKey},
null_option_type::NOT_NULL
null_option_type::NotNull
);
REQUIRE(c.name() == "name");
REQUIRE(c.index() == 2);
REQUIRE(c.reference_column());
REQUIRE(c.reference_column()->name() == "author");
REQUIRE(c.reference_column()->table_name() == "books");
// REQUIRE(c.index() == 2);
// REQUIRE(c.reference_column());
// REQUIRE(c.reference_column()->name() == "author");
// REQUIRE(c.reference_column()->table_name() == "books");
REQUIRE(c.type() == basic_type::type_varchar);
REQUIRE(c.attributes().size() == 255);
auto c2 = c;
REQUIRE(c2.name() == "name");
REQUIRE(c2.index() == 2);
REQUIRE(c2.reference_column());
REQUIRE(c2.reference_column()->name() == "author");
REQUIRE(c2.reference_column()->table_name() == "books");
// REQUIRE(c2.index() == 2);
// REQUIRE(c2.reference_column());
// REQUIRE(c2.reference_column()->name() == "author");
// REQUIRE(c2.reference_column()->table_name() == "books");
REQUIRE(c2.type() == basic_type::type_varchar);
REQUIRE(c2.attributes().size() == 255);
auto c3 = std::move(c2);
REQUIRE(c3.name() == "name");
REQUIRE(c3.index() == 2);
REQUIRE(c3.reference_column());
REQUIRE(c3.reference_column()->name() == "author");
REQUIRE(c3.reference_column()->table_name() == "books");
// REQUIRE(c3.index() == 2);
// REQUIRE(c3.reference_column());
// REQUIRE(c3.reference_column()->name() == "author");
// REQUIRE(c3.reference_column()->table_name() == "books");
REQUIRE(c3.type() == basic_type::type_varchar);
REQUIRE(c3.attributes().size() == 255);
REQUIRE(c2.name().empty());
REQUIRE(c2.index() == 2);
REQUIRE(!c2.reference_column());
// REQUIRE(c2.index() == 2);
// REQUIRE(!c2.reference_column());
REQUIRE(c2.attributes().size() == 255);
}