extract not null constraint into enum

This commit is contained in:
Sascha Kuehl 2024-01-19 07:58:37 +01:00
parent ed6366fef6
commit 9355f7ea27
13 changed files with 90 additions and 87 deletions

View File

@ -90,12 +90,9 @@ sql::data_type_t to_type(enum_field_types type, unsigned int flags)
}
}
utils::constraints to_options(unsigned int flags)
utils::constraints to_constraints(unsigned int flags)
{
utils::constraints options{utils::constraints::NONE};
if (flags & NOT_NULL_FLAG) {
options |= utils::constraints::NOT_NULL;
}
if (flags & PRI_KEY_FLAG) {
options |= utils::constraints::PRIMARY_KEY;
}
@ -106,6 +103,11 @@ utils::constraints to_options(unsigned int flags)
return options;
}
sql::null_option to_null_option(unsigned int flags)
{
return flags & NOT_NULL_FLAG ? sql::null_option::NOT_NULL : sql::null_option::NULLABLE;
}
sql::data_type_t string2type(const std::string &type_string)
{
// if (strcmp(type_string.c_str(), "int")
@ -149,9 +151,10 @@ std::unique_ptr<sql::query_result_impl> mysql_connection::fetch(const std::strin
sql::record prototype;
for (unsigned i = 0; i < field_count; ++i) {
auto type = to_type(fields[i].type, fields[i].flags);
auto options = to_options(fields[i].flags);
auto options = to_constraints(fields[i].flags);
auto null_opt = to_null_option(fields[i].flags);
prototype.append({fields[i].name, type, options});
prototype.append({fields[i].name, type, options, null_opt});
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_result_reader>(result, field_count), std::move(prototype)));
@ -200,17 +203,17 @@ sql::record mysql_connection::describe(const std::string &table)
char *end = nullptr;
// Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10);
std::string name = reader.column(1);
std::string name = reader.column(0);
// Todo: extract size
auto typeinfo = determine_type_info(reader.column(2));
auto typeinfo = determine_type_info(reader.column(1));
end = nullptr;
utils::constraints options{};
if (strtoul(reader.column(4), &end, 10) == 0) {
options = utils::constraints::NOT_NULL;
sql::null_option null_opt{sql::null_option::NULLABLE};
if (strtoul(reader.column(2), &end, 10) == 0) {
null_opt = sql::null_option::NOT_NULL;
}
// f.default_value(res->column(4));
prototype.append({name, typeinfo.type, {typeinfo.size, options}});
prototype.append({name, typeinfo.type, {typeinfo.size}, null_opt});
}
return prototype;

View File

@ -150,12 +150,12 @@ sql::record postgres_connection::describe(const std::string &table)
// Todo: extract size
auto type = (string2type(reader.column(2)));
end = nullptr;
utils::constraints options{};
sql::null_option null_opt{sql::null_option::NULLABLE};
if (strtoul(reader.column(4), &end, 10) == 0) {
options = utils::constraints::NOT_NULL;
null_opt = sql::null_option::NOT_NULL;
}
// f.default_value(res->column(4));
prototype.append({name, type, {options}});
prototype.append({name, type, utils::null_attributes, null_opt});
}
return std::move(prototype);

View File

@ -158,12 +158,12 @@ sql::record sqlite_connection::describe(const std::string& table)
auto type = (string2type(reader.column(2)));
end = nullptr;
utils::constraints options{};
sql::null_option null_opt{sql::null_option::NULLABLE};
if (strtoul(reader.column(3), &end, 10) == 0) {
options = utils::constraints::NOT_NULL;
null_opt = sql::null_option::NOT_NULL;
}
// f.default_value(res->column(4));
prototype.append({name, type, {options}});
prototype.append({name, type, utils::null_attributes, null_opt});
}
return std::move(prototype);

View File

@ -12,6 +12,10 @@
namespace matador::sql {
enum class null_option : uint8_t {
NULLABLE, NOT_NULL
};
class column {
public:
column(sql_function_t func, std::string name);
@ -24,27 +28,28 @@ public:
column& operator=(column&&) noexcept = default;
template<typename Type>
explicit column(std::string name, utils::field_attributes attr = utils::null_attributes)
explicit column(std::string name, utils::field_attributes attr)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
{}
template<typename Type>
column(std::string name, const Type &, utils::field_attributes attr = utils::null_attributes)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr)
column(std::string name, const Type &, utils::field_attributes attr, null_option null_opt)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), attr, null_opt)
{}
column(std::string name, data_type_t type, utils::field_attributes attr = utils::null_attributes);
column(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt);
template<typename Type>
column(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr)
column(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt)
: column(std::move(name), data_type_traits<Type>::builtin_type(attr.size()), ref_table, ref_column, attr, null_opt)
{}
column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr = utils::null_attributes);
column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] size_t index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] data_type_t type() const;
[[nodiscard]] const std::string& alias() const;
[[nodiscard]] const std::string& ref_table() const;
@ -114,6 +119,7 @@ private:
std::string name_;
size_t index_{};
utils::field_attributes attributes_;
null_option null_option_{null_option::NOT_NULL};
data_type_t type_{data_type_t::type_unknown};
any_type value_;
sql_function_t function_{sql_function_t::NONE};
@ -130,20 +136,20 @@ private:
*/
column operator "" _col(const char *name, size_t len);
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::not_null_attributes);
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL);
template < typename Type >
column make_column(const std::string &name, utils::field_attributes attr = utils::not_null_attributes)
column make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL)
{
return make_column(name, data_type_traits<Type>::builtin_type(0), attr);
return make_column(name, data_type_traits<Type>::builtin_type(0), attr, null_opt);
}
template <>
column make_column<std::string>(const std::string &name, utils::field_attributes attr);
column make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt);
template < typename Type >
column make_pk_column(const std::string &name, size_t size = 0)
{
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL});
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY });
}
template <>
@ -158,7 +164,7 @@ column make_fk_column(const std::string &name, size_t size, const std::string &r
template < typename Type >
[[maybe_unused]] column make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column)
{
return {name, data_type_traits<Type>::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }};
return {name, data_type_traits<Type>::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template <>

View File

@ -23,7 +23,7 @@ public:
column generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
{
utils::access::process(*this, x);
return column{id, type_, 0, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }};
return column{id, type_, 0, ref_table, ref_column, { utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template<typename ValueType>
@ -73,7 +73,7 @@ public:
void on_revision(const char *id, unsigned long long &rev);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::not_null_attributes);
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes);
template<typename Type>
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
@ -109,23 +109,19 @@ private:
template<typename V>
void column_generator::on_primary_key(const char *id, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type*)
{
on_attribute(id, x, { utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL });
on_attribute(id, x, { utils::constraints::PRIMARY_KEY });
}
template<typename Type>
void column_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
{
if (attr.options() == utils::constraints::NONE) {
columns_.push_back(column{id, x, { attr.size(), utils::constraints::NOT_NULL}});
} else {
columns_.emplace_back(id, x, attr);
}
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
}
template<typename Type>
void column_generator::on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr)
{
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr);
columns_.emplace_back(id, data_type_traits<Type>::builtin_type(attr.size()), attr, null_option::NULLABLE);
}
}

View File

@ -5,14 +5,12 @@ namespace matador::utils {
enum class constraints : unsigned char {
NONE = 0,
NOT_NULL = 1 << 0,
INDEX = 1 << 1,
UNIQUE = 1 << 2,
PRIMARY_KEY = 1 << 3,
FOREIGN_KEY = 1 << 4,
DEFAULT = 1 << 5,
AUTO_INCREMENT = 1 << 6,
UNIQUE_NOT_NULL = UNIQUE | NOT_NULL
AUTO_INCREMENT = 1 << 6
};
//static std::unordered_map<constraints, std::string> constraints_to_name_map();

View File

@ -30,7 +30,6 @@ private:
};
const field_attributes null_attributes {};
const field_attributes not_null_attributes { constraints::NOT_NULL };
}
#endif //QUERY_FIELD_ATTRIBUTES_HPP

View File

@ -16,14 +16,19 @@ column::column(std::string name, std::string alias)
: name_(std::move(name)), attributes_(utils::null_attributes), alias_(std::move(alias))
{}
column::column(std::string name, data_type_t type, utils::field_attributes attr)
: name_(std::move(name)), type_(type), attributes_(attr)
column::column(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt)
: name_(std::move(name)), type_(type), attributes_(attr), null_option_(null_opt)
{}
column::column(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column,
utils::field_attributes attr)
: name_(std::move(name)), index_(index), type_(type), attributes_(attr), ref_table_(std::move(ref_table)),
ref_column_(std::move(ref_column))
utils::field_attributes attr, null_option null_opt)
: name_(std::move(name))
, index_(index)
, type_(type)
, attributes_(attr)
, null_option_(null_opt)
, ref_table_(std::move(ref_table))
, ref_column_(std::move(ref_column))
{}
const std::string &column::name() const
@ -41,6 +46,11 @@ const utils::field_attributes &column::attributes() const
return attributes_;
}
bool column::is_nullable() const
{
return null_option_ == null_option::NULLABLE;
}
data_type_t column::type() const
{
return type_;
@ -93,18 +103,15 @@ column operator "" _col(const char *name, size_t len)
return {std::string(name, len)};
}
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr)
column make_column(const std::string &name, data_type_t type, utils::field_attributes attr, null_option null_opt)
{
return {name, type, attr};
return {name, type, attr, null_opt};
}
template<>
column make_column<std::string>(const std::string &name, utils::field_attributes attr)
column make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt)
{
if (attr.options() == utils::constraints::NONE) {
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), { attr.size(), utils::constraints::NOT_NULL});
}
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), attr);
return make_column(name, data_type_traits<std::string>::builtin_type(attr.size()), attr, null_opt);
}
template<>
@ -117,6 +124,6 @@ template<>
[[maybe_unused]] column make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table,
const std::string &ref_column)
{
return {name, data_type_traits<std::string>::builtin_type(size), 0, ref_table, ref_column, {size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL}};
return {name, data_type_traits<std::string>::builtin_type(size), 0, ref_table, ref_column, {size, utils::constraints::FOREIGN_KEY}, null_option::NOT_NULL};
}
}

View File

@ -10,7 +10,7 @@ column_generator::column_generator(std::vector<column> &columns, const table_rep
void column_generator::on_primary_key(const char *id, std::string &pk, size_t size)
{
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY | utils::constraints::NOT_NULL });
on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY });
}
void column_generator::on_revision(const char *id, unsigned long long int &x)

View File

@ -473,7 +473,7 @@ std::string build_create_column(const column &col, const dialect &d, column_cont
if (col.attributes().size() > 0) {
result.append("(" + std::to_string(col.attributes().size()) + ")");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
if (!col.is_nullable()) {
result.append(" NOT NULL");
}
if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) {

View File

@ -15,15 +15,15 @@ TEST_CASE("Generate columns from object", "[column generator]") {
auto columns = column_generator::generate<matador::test::product>(repo);
const std::vector<column> expected_columns = {
column{ "product_name", data_type_t::type_varchar, { constraints::PRIMARY_KEY | constraints::NOT_NULL } },
column{ "supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY },
column{ "category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY },
column{ "quantity_per_unit", data_type_t::type_varchar, constraints::NOT_NULL },
column{ "unit_price", data_type_t::type_unsigned_int, constraints::NOT_NULL },
column{ "units_in_stock", data_type_t::type_unsigned_int, constraints::NOT_NULL },
column{ "units_in_order", data_type_t::type_unsigned_int, constraints::NOT_NULL },
column{ "reorder_level", data_type_t::type_unsigned_int, constraints::NOT_NULL },
column{ "discontinued", data_type_t::type_bool, constraints::NOT_NULL }
column{ "product_name", data_type_t::type_varchar, constraints::PRIMARY_KEY, null_option::NOT_NULL },
column{ "supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
column{ "category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL },
column{ "quantity_per_unit", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
column{ "unit_price", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column{ "units_in_stock", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column{ "units_in_order", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column{ "reorder_level", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL },
column{ "discontinued", data_type_t::type_bool, null_attributes, null_option::NOT_NULL }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());
@ -41,9 +41,9 @@ TEST_CASE("Generate columns from object with nullable columns", "[column generat
auto columns = column_generator::generate<matador::test::optional>(repo);
const std::vector<column> expected_columns = {
column{ "id", data_type_t::type_unsigned_long, { constraints::PRIMARY_KEY | constraints::NOT_NULL } },
column{ "name", data_type_t::type_varchar, null_attributes },
column{ "age", data_type_t::type_unsigned_int, null_attributes }
column{ "id", data_type_t::type_unsigned_long, constraints::PRIMARY_KEY, null_option::NOT_NULL },
column{ "name", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL },
column{ "age", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }
};
REQUIRE(!columns.empty());
REQUIRE(columns.size() == expected_columns.size());

View File

@ -22,12 +22,12 @@ TEST_CASE("Create table sql statement string", "[query]") {
q = query.create().table("person", {
make_pk_column<unsigned long>("id"),
make_column<std::string>("name", { 255, constraints::UNIQUE | constraints::NOT_NULL }),
make_column<std::string>("name", { 255, constraints::UNIQUE }, null_option::NOT_NULL),
make_column<unsigned short>("age"),
make_fk_column<unsigned long>("address", "address", "id")
}).compile();
REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
REQUIRE(q.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##");
REQUIRE(q.table_name == "person");
}

View File

@ -49,10 +49,9 @@ TEMPLATE_TEST_CASE_METHOD(SessionRecordTestFixture, "Create and drop table state
REQUIRE(!s.table_exists("person"));
}
TEST_CASE("Create and drop table statement with foreign key", "[session record]")
TEMPLATE_TEST_CASE_METHOD(SessionRecordTestFixture, "Create and drop table statement with foreign key", "[session record]", Sqlite, Postgres, MySql)
{
connection_pool<connection> pool("sqlite://sqlite.db", 4);
session s(pool);
auto &s = SessionRecordTestFixture<TestType>::session();
s.create()
.table("airplane", {
@ -87,14 +86,9 @@ TEST_CASE("Create and drop table statement with foreign key", "[session record]"
REQUIRE(!s.table_exists("airplane"));
}
TEST_CASE("Execute insert record statement", "[session record]")
TEMPLATE_TEST_CASE_METHOD(SessionRecordTestFixture, "Execute insert record statement", "[session record]", MySql)
{
auto dns = GENERATE(as < std::string > {},
"sqlite://sqlite.db",
"postgres://test:test123@127.0.0.1:5432/matador_test");
connection_pool<connection> pool(dns, 4);
session s(pool);
auto &s = SessionRecordTestFixture<TestType>::session();
s.create()
.table("person", {
@ -119,13 +113,13 @@ TEST_CASE("Execute insert record statement", "[session record]")
REQUIRE(i.size() == 3);
REQUIRE(i.at(0).name() == "id");
REQUIRE(i.at(0).type() == data_type_t::type_long_long);
REQUIRE(i.at(0).as<long long>() == 7);
REQUIRE(i.at(0).template as<long long>() == 7);
REQUIRE(i.at(1).name() == "name");
REQUIRE(i.at(1).type() == data_type_t::type_varchar);
REQUIRE(i.at(1).as<std::string>() == "george");
REQUIRE(i.at(1).template as<std::string>() == "george");
REQUIRE(i.at(2).name() == "age");
REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int);
REQUIRE(i.at(2).as<int>() == 45);
REQUIRE(i.at(2).template as<int>() == 45);
}
s.drop()