renamed column_generator to column_definition_generator and column_name_generator to column_generator
This commit is contained in:
@@ -16,26 +16,17 @@ enum class sql_function_t {
|
||||
|
||||
struct column
|
||||
{
|
||||
column(const char *name) : name(name) {} // NOLINT(*-explicit-constructor)
|
||||
column(std::string name) : name(std::move(name)) {} // NOLINT(*-explicit-constructor)
|
||||
column(sql_function_t func, std::string name) : name(std::move(name)), function_(func) {} // NOLINT(*-explicit-constructor)
|
||||
column(std::string table_name, std::string name, std::string as)
|
||||
: table(std::move(table_name))
|
||||
, name(std::move(name))
|
||||
, alias(std::move(as)) {}
|
||||
column(std::string table_name, const char* name, std::string as)
|
||||
: table(std::move(table_name))
|
||||
, name(name)
|
||||
, alias(std::move(as)) {}
|
||||
column(const char *name); // NOLINT(*-explicit-constructor)
|
||||
column(std::string name); // NOLINT(*-explicit-constructor)
|
||||
column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor)
|
||||
column(std::string table_name, std::string name, std::string as);
|
||||
column(std::string table_name, const char* name, std::string as);
|
||||
|
||||
column& as(std::string a) {
|
||||
alias = std::move(a);
|
||||
return *this;
|
||||
}
|
||||
bool equals(const column &x) const;
|
||||
|
||||
[[nodiscard]] bool is_function() const {
|
||||
return function_ != sql_function_t::NONE;
|
||||
}
|
||||
column& as(std::string a);
|
||||
|
||||
[[nodiscard]] bool is_function() const;
|
||||
|
||||
std::string table;
|
||||
std::string name;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/data_type_traits.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class schema;
|
||||
|
||||
class fk_column_generator
|
||||
{
|
||||
public:
|
||||
fk_column_generator() = default;
|
||||
|
||||
template<class Type>
|
||||
column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
|
||||
{
|
||||
utils::access::process(*this, x);
|
||||
return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
|
||||
{
|
||||
type_ = data_type_traits<ValueType>::builtin_type(0);
|
||||
}
|
||||
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
|
||||
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
|
||||
template < class Type >
|
||||
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
data_type_t type_{};
|
||||
};
|
||||
|
||||
class column_definition_generator
|
||||
{
|
||||
private:
|
||||
column_definition_generator(std::vector<column_definition> &columns, const schema &repo);
|
||||
|
||||
public:
|
||||
~column_definition_generator() = default;
|
||||
|
||||
template < class Type >
|
||||
static std::vector<column_definition> generate(const schema &repo)
|
||||
{
|
||||
std::vector<column_definition> columns;
|
||||
column_definition_generator gen(columns, repo);
|
||||
Type obj;
|
||||
matador::utils::access::process(gen, obj);
|
||||
return std::move(columns);
|
||||
}
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0);
|
||||
void on_primary_key(const char *id, std::string &pk, size_t size);
|
||||
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::null_attributes);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
|
||||
{
|
||||
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
||||
}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
|
||||
{
|
||||
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
|
||||
|
||||
private:
|
||||
size_t index_ = 0;
|
||||
std::vector<column_definition> &columns_;
|
||||
const schema &repo_;
|
||||
|
||||
fk_column_generator fk_column_generator_;
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
void column_definition_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 });
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void column_definition_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr)
|
||||
{
|
||||
columns_.emplace_back(id, x, attr, null_option::NOT_NULL);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void column_definition_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, null_option::NULLABLE);
|
||||
}
|
||||
|
||||
}
|
||||
#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP
|
||||
@@ -1,129 +1,119 @@
|
||||
#ifndef QUERY_COLUMN_GENERATOR_HPP
|
||||
#define QUERY_COLUMN_GENERATOR_HPP
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/data_type_traits.hpp"
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include <typeindex>
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class schema;
|
||||
|
||||
class fk_column_generator
|
||||
{
|
||||
public:
|
||||
fk_column_generator() = default;
|
||||
|
||||
template<class Type>
|
||||
column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column)
|
||||
{
|
||||
utils::access::process(*this, x);
|
||||
return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
|
||||
{
|
||||
type_ = data_type_traits<ValueType>::builtin_type(0);
|
||||
}
|
||||
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size);
|
||||
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
|
||||
template < class Type >
|
||||
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
data_type_t type_{};
|
||||
};
|
||||
|
||||
class column_generator
|
||||
{
|
||||
private:
|
||||
column_generator(std::vector<column_definition> &columns, const schema &repo);
|
||||
column_generator(std::vector<column> &column_infos, const sql::schema &ts, const std::string &table_name);
|
||||
|
||||
public:
|
||||
~column_generator() = default;
|
||||
|
||||
template < class Type >
|
||||
static std::vector<column_definition> generate(const schema &repo)
|
||||
static std::vector<column> generate(const sql::schema &ts)
|
||||
{
|
||||
std::vector<column_definition> columns;
|
||||
column_generator gen(columns, repo);
|
||||
const auto info = ts.info<Type>();
|
||||
if (!info) {
|
||||
return {};
|
||||
}
|
||||
std::vector<column> columns;
|
||||
column_generator gen(columns, ts, info.value().name);
|
||||
Type obj;
|
||||
matador::utils::access::process(gen, obj);
|
||||
return std::move(columns);
|
||||
}
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *, V &x, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0);
|
||||
void on_primary_key(const char *id, std::string &pk, size_t size);
|
||||
void on_revision(const char *id, unsigned long long &rev);
|
||||
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
void on_primary_key(const char *id, std::string &, size_t);
|
||||
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::null_attributes);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr = utils::null_attributes);
|
||||
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
|
||||
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
{
|
||||
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
||||
if (attr.fetch() == utils::fetch_type::LAZY) {
|
||||
push(id);
|
||||
} else {
|
||||
const auto info = table_schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_name_stack_.push(info.value().name);
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_name_stack_.pop();
|
||||
}
|
||||
}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/)
|
||||
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
{
|
||||
const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
||||
columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
||||
if (attr.fetch() == utils::fetch_type::LAZY) {
|
||||
push(id);
|
||||
} else {
|
||||
const auto info = table_schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_name_stack_.push(info.value().name);
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_name_stack_.pop();
|
||||
}
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::LAZY) {
|
||||
return;
|
||||
}
|
||||
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
table_name_stack_.push(info.value().name);
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_name_stack_.pop();
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
{
|
||||
on_has_many(id, c, "", "", attr);
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> determine_foreign_ref(const std::type_index &ti);
|
||||
void push(const std::string &column_name);
|
||||
|
||||
private:
|
||||
size_t index_ = 0;
|
||||
std::vector<column_definition> &columns_;
|
||||
const schema &repo_;
|
||||
|
||||
fk_column_generator fk_column_generator_;
|
||||
std::stack<std::string> table_name_stack_;
|
||||
std::vector<column> &column_infos_;
|
||||
const sql::schema &table_schema_;
|
||||
int column_index{0};
|
||||
};
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void column_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &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, null_option::NULLABLE);
|
||||
}
|
||||
|
||||
}
|
||||
#endif //QUERY_COLUMN_GENERATOR_HPP
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#ifndef QUERY_COLUMN_NAME_GENERATOR_HPP
|
||||
#define QUERY_COLUMN_NAME_GENERATOR_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
#include "matador/utils/foreign_attributes.hpp"
|
||||
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/schema.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class column_name_generator
|
||||
{
|
||||
private:
|
||||
column_name_generator(std::vector<column> &column_infos, const sql::schema &ts, const std::string &table_name);
|
||||
|
||||
public:
|
||||
~column_name_generator() = default;
|
||||
|
||||
template < class Type >
|
||||
static std::vector<column> generate(const sql::schema &ts)
|
||||
{
|
||||
const auto info = ts.info<Type>();
|
||||
if (!info) {
|
||||
return {};
|
||||
}
|
||||
std::vector<column> columns;
|
||||
column_name_generator gen(columns, ts, info.value().name);
|
||||
Type obj;
|
||||
matador::utils::access::process(gen, obj);
|
||||
return std::move(columns);
|
||||
}
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *id, V &, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
void on_primary_key(const char *id, std::string &, size_t);
|
||||
void on_revision(const char *id, unsigned long long &/*rev*/);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &/*attr*/)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &/*attr*/)
|
||||
{
|
||||
push(id);
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::LAZY) {
|
||||
return;
|
||||
}
|
||||
const auto info = table_schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
table_name_stack_.push(info.value().name);
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_name_stack_.pop();
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr)
|
||||
{
|
||||
on_has_many(id, c, "", "", attr);
|
||||
}
|
||||
|
||||
private:
|
||||
void push(const std::string &column_name);
|
||||
|
||||
private:
|
||||
std::stack<std::string> table_name_stack_;
|
||||
std::vector<column> &column_infos_;
|
||||
const sql::schema &table_schema_;
|
||||
int column_index{0};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_COLUMN_NAME_GENERATOR_HPP
|
||||
@@ -34,13 +34,13 @@ private:
|
||||
template<class Type>
|
||||
query_select_intermediate query::select()
|
||||
{
|
||||
return select(column_name_generator::generate<Type>(schema_));
|
||||
return select(column_generator::generate<Type>(schema_));
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
query_select_intermediate query::select(std::initializer_list<column> columns)
|
||||
{
|
||||
auto cols = column_name_generator::generate<Type>(schema_);
|
||||
auto cols = column_generator::generate<Type>(schema_);
|
||||
cols.insert(cols.end(), columns);
|
||||
return select(cols);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define QUERY_QUERY_INTERMEDIATES_HPP
|
||||
|
||||
#include "matador/sql/column_definition.hpp"
|
||||
#include "matador/sql/column_definition_generator.hpp"
|
||||
#include "matador/sql/column_generator.hpp"
|
||||
#include "matador/sql/column_name_generator.hpp"
|
||||
#include "matador/sql/key_value_generator.hpp"
|
||||
#include "matador/sql/key_value_pair.hpp"
|
||||
#include "matador/sql/placeholder_generator.hpp"
|
||||
@@ -260,7 +260,7 @@ public:
|
||||
// if (!schema_.exists<Type>()) {
|
||||
// schema_.attach<Type>(table.name);
|
||||
// }
|
||||
return this->table(table, column_generator::generate<Type>(schema_));
|
||||
return this->table(table, column_definition_generator::generate<Type>(schema_));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
template<class Type>
|
||||
query_into_intermediate into(const sql::table &table)
|
||||
{
|
||||
return into(table, column_name_generator::generate<Type>(schema_));
|
||||
return into(table, column_generator::generate<Type>(schema_));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef QUERY_SCHEMA_HPP
|
||||
#define QUERY_SCHEMA_HPP
|
||||
|
||||
#include "matador/sql/column_generator.hpp"
|
||||
#include "matador/sql/column_definition_generator.hpp"
|
||||
#include "matador/sql/table_definition.hpp"
|
||||
|
||||
#include <optional>
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
template<typename Type>
|
||||
const table_info& attach(const std::string &table_name)
|
||||
{
|
||||
return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_generator::generate<Type>(*this)}});
|
||||
return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_definition_generator::generate<Type>(*this)}});
|
||||
}
|
||||
|
||||
const table_info& attach(std::type_index ti, const table_info& table);
|
||||
|
||||
Reference in New Issue
Block a user