mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Merge branch '10.2' into bb-10.2-jan
This commit is contained in:
375
sql/sql_table.cc
375
sql/sql_table.cc
@@ -55,6 +55,7 @@
|
||||
#include "transaction.h"
|
||||
#include "sql_audit.h"
|
||||
|
||||
|
||||
#ifdef __WIN__
|
||||
#include <io.h>
|
||||
#endif
|
||||
@@ -2686,14 +2687,15 @@ bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
|
||||
*/
|
||||
|
||||
bool quick_rm_table(THD *thd, handlerton *base, const char *db,
|
||||
const char *table_name, uint flags)
|
||||
const char *table_name, uint flags, const char *table_path)
|
||||
{
|
||||
char path[FN_REFLEN + 1];
|
||||
bool error= 0;
|
||||
DBUG_ENTER("quick_rm_table");
|
||||
|
||||
uint path_length= build_table_filename(path, sizeof(path) - 1,
|
||||
db, table_name, reg_ext, flags);
|
||||
uint path_length= table_path ?
|
||||
(strxnmov(path, sizeof(path) - 1, table_path, reg_ext, NullS) - path) :
|
||||
build_table_filename(path, sizeof(path)-1, db, table_name, reg_ext, flags);
|
||||
if (mysql_file_delete(key_file_frm, path, MYF(0)))
|
||||
error= 1; /* purecov: inspected */
|
||||
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
|
||||
@@ -3255,35 +3257,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
!(sql_field->charset= find_bin_collation(sql_field->charset)))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/*
|
||||
Convert the default value from client character
|
||||
set into the column character set if necessary.
|
||||
We can only do this for constants as we have not yet run fix_fields.
|
||||
*/
|
||||
if (sql_field->default_value &&
|
||||
sql_field->default_value->expr_item->basic_const_item() &&
|
||||
save_cs != sql_field->default_value->expr_item->collation.collation &&
|
||||
(sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
|
||||
sql_field->sql_type == MYSQL_TYPE_STRING ||
|
||||
sql_field->sql_type == MYSQL_TYPE_SET ||
|
||||
sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_ENUM))
|
||||
{
|
||||
Item *item;
|
||||
if (!(item= sql_field->default_value->expr_item->
|
||||
safe_charset_converter(thd, save_cs)))
|
||||
{
|
||||
/* Could not convert */
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/* Fix for prepare statement */
|
||||
thd->change_item_tree(&sql_field->default_value->expr_item, item);
|
||||
}
|
||||
|
||||
if (sql_field->sql_type == MYSQL_TYPE_SET ||
|
||||
sql_field->sql_type == MYSQL_TYPE_ENUM)
|
||||
{
|
||||
@@ -3349,37 +3322,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
if (sql_field->sql_type == MYSQL_TYPE_SET)
|
||||
{
|
||||
uint32 field_length;
|
||||
if (sql_field->default_value &&
|
||||
sql_field->default_value->expr_item->basic_const_item())
|
||||
{
|
||||
char *not_used;
|
||||
uint not_used2;
|
||||
bool not_found= 0;
|
||||
String str, *def= sql_field->default_value->expr_item->val_str(&str);
|
||||
if (def == NULL) /* SQL "NULL" maps to NULL */
|
||||
{
|
||||
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
|
||||
{
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* else, NULL is an allowed value */
|
||||
(void) find_set(interval, NULL, 0,
|
||||
cs, ¬_used, ¬_used2, ¬_found);
|
||||
}
|
||||
else /* not NULL */
|
||||
{
|
||||
(void) find_set(interval, def->ptr(), def->length(),
|
||||
cs, ¬_used, ¬_used2, ¬_found);
|
||||
}
|
||||
|
||||
if (not_found)
|
||||
{
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
calculate_interval_lengths(cs, interval, &dummy, &field_length);
|
||||
sql_field->length= field_length + (interval->count - 1);
|
||||
}
|
||||
@@ -3387,30 +3329,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
{
|
||||
uint32 field_length;
|
||||
DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM);
|
||||
if (sql_field->default_value &&
|
||||
sql_field->default_value->expr_item->basic_const_item())
|
||||
{
|
||||
String str, *def= sql_field->default_value->expr_item->val_str(&str);
|
||||
if (def == NULL) /* SQL "NULL" maps to NULL */
|
||||
{
|
||||
if ((sql_field->flags & NOT_NULL_FLAG) != 0)
|
||||
{
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* else, the defaults yield the correct length for NULLs. */
|
||||
}
|
||||
else /* not NULL */
|
||||
{
|
||||
def->length(cs->cset->lengthsp(cs, def->ptr(), def->length()));
|
||||
if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
|
||||
{
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
calculate_interval_lengths(cs, interval, &field_length, &dummy);
|
||||
sql_field->length= field_length;
|
||||
}
|
||||
@@ -3430,6 +3348,112 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
if (prepare_blob_field(thd, sql_field))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (sql_field->default_value)
|
||||
{
|
||||
Virtual_column_info *def= sql_field->default_value;
|
||||
|
||||
if (!sql_field->has_default_expression())
|
||||
def->expr_str= null_lex_str;
|
||||
|
||||
if (!def->expr_item->basic_const_item() && !def->flags)
|
||||
{
|
||||
Item *expr= def->expr_item;
|
||||
int err= !expr->fixed && // may be already fixed if ALTER TABLE
|
||||
expr->fix_fields(thd, &expr);
|
||||
if (!err)
|
||||
{
|
||||
if (expr->result_type() == REAL_RESULT)
|
||||
{ // don't convert floats to string and back, it can be lossy
|
||||
double res= expr->val_real();
|
||||
if (expr->null_value)
|
||||
expr= new (thd->mem_root) Item_null(thd);
|
||||
else
|
||||
expr= new (thd->mem_root) Item_float(thd, res, expr->decimals);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuffer<MAX_FIELD_WIDTH> buf;
|
||||
String *res= expr->val_str(&buf);
|
||||
if (expr->null_value)
|
||||
expr= new (thd->mem_root) Item_null(thd);
|
||||
else
|
||||
{
|
||||
char *str= (char*) thd->strmake(res->ptr(), res->length());
|
||||
expr= new (thd->mem_root) Item_string(thd, str, res->length(), res->charset());
|
||||
}
|
||||
}
|
||||
thd->change_item_tree(&def->expr_item, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Convert the default value from client character
|
||||
set into the column character set if necessary.
|
||||
We can only do this for constants as we have not yet run fix_fields.
|
||||
*/
|
||||
if (sql_field->default_value &&
|
||||
sql_field->default_value->expr_item->basic_const_item() &&
|
||||
save_cs != sql_field->default_value->expr_item->collation.collation &&
|
||||
(sql_field->sql_type == MYSQL_TYPE_VAR_STRING ||
|
||||
sql_field->sql_type == MYSQL_TYPE_STRING ||
|
||||
sql_field->sql_type == MYSQL_TYPE_SET ||
|
||||
sql_field->sql_type == MYSQL_TYPE_TINY_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_LONG_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_BLOB ||
|
||||
sql_field->sql_type == MYSQL_TYPE_ENUM))
|
||||
{
|
||||
Item *item;
|
||||
if (!(item= sql_field->default_value->expr_item->
|
||||
safe_charset_converter(thd, save_cs)))
|
||||
{
|
||||
/* Could not convert */
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
/* Fix for prepare statement */
|
||||
thd->change_item_tree(&sql_field->default_value->expr_item, item);
|
||||
}
|
||||
|
||||
if (sql_field->default_value &&
|
||||
sql_field->default_value->expr_item->basic_const_item() &&
|
||||
(sql_field->sql_type == MYSQL_TYPE_SET ||
|
||||
sql_field->sql_type == MYSQL_TYPE_ENUM))
|
||||
{
|
||||
StringBuffer<MAX_FIELD_WIDTH> str;
|
||||
String *def= sql_field->default_value->expr_item->val_str(&str);
|
||||
bool not_found;
|
||||
if (def == NULL) /* SQL "NULL" maps to NULL */
|
||||
{
|
||||
not_found= sql_field->flags & NOT_NULL_FLAG;
|
||||
}
|
||||
else
|
||||
{
|
||||
not_found= false;
|
||||
if (sql_field->sql_type == MYSQL_TYPE_SET)
|
||||
{
|
||||
char *not_used;
|
||||
uint not_used2;
|
||||
find_set(sql_field->interval, def->ptr(), def->length(),
|
||||
sql_field->charset, ¬_used, ¬_used2, ¬_found);
|
||||
}
|
||||
else /* MYSQL_TYPE_ENUM */
|
||||
{
|
||||
def->length(sql_field->charset->cset->lengthsp(sql_field->charset,
|
||||
def->ptr(), def->length()));
|
||||
not_found= !find_type2(sql_field->interval, def->ptr(),
|
||||
def->length(), sql_field->charset);
|
||||
}
|
||||
}
|
||||
|
||||
if (not_found)
|
||||
{
|
||||
my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(sql_field->flags & NOT_NULL_FLAG))
|
||||
null_fields++;
|
||||
|
||||
@@ -4178,6 +4202,22 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
|
||||
make_unique_constraint_name(thd, &check->name,
|
||||
&alter_info->check_constraint_list,
|
||||
&nr);
|
||||
{
|
||||
/* Check that there's no repeating constraint names. */
|
||||
List_iterator_fast<Virtual_column_info>
|
||||
dup_it(alter_info->check_constraint_list);
|
||||
Virtual_column_info *dup_check;
|
||||
while ((dup_check= dup_it++) && dup_check != check)
|
||||
{
|
||||
if (check->name.length == dup_check->name.length &&
|
||||
my_strcasecmp(system_charset_info,
|
||||
check->name.str, dup_check->name.str) == 0)
|
||||
{
|
||||
my_error(ER_DUP_CONSTRAINT_NAME, MYF(0), "CHECK", check->name.str);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (check_string_char_length(&check->name, 0, NAME_CHAR_LEN,
|
||||
system_charset_info, 1))
|
||||
@@ -6130,6 +6170,39 @@ remove_key:
|
||||
}
|
||||
#endif /*WITH_PARTITION_STORAGE_ENGINE*/
|
||||
|
||||
/* ADD CONSTRAINT IF NOT EXISTS. */
|
||||
{
|
||||
List_iterator<Virtual_column_info> it(alter_info->check_constraint_list);
|
||||
Virtual_column_info *check;
|
||||
TABLE_SHARE *share= table->s;
|
||||
uint c;
|
||||
while ((check=it++))
|
||||
{
|
||||
if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
|
||||
check->name.length)
|
||||
continue;
|
||||
check->flags= 0;
|
||||
for (c= share->field_check_constraints;
|
||||
c < share->table_check_constraints ; c++)
|
||||
{
|
||||
Virtual_column_info *dup= table->check_constraints[c];
|
||||
if (dup->name.length == check->name.length &&
|
||||
my_strcasecmp(system_charset_info,
|
||||
check->name.str, dup->name.str) == 0)
|
||||
{
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_DUP_CONSTRAINT_NAME, ER_THD(thd, ER_DUP_CONSTRAINT_NAME),
|
||||
"CHECK", check->name.str);
|
||||
it.remove();
|
||||
if (alter_info->check_constraint_list.elements == 0)
|
||||
alter_info->flags&= ~Alter_info::ALTER_ADD_CHECK_CONSTRAINT;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@@ -8257,6 +8330,72 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
/**
|
||||
Rename temporary table and/or turn indexes on/off without touching .FRM.
|
||||
Its a variant of simple_rename_or_index_change() to be used exclusively
|
||||
for temporary tables.
|
||||
|
||||
@param thd Thread handler
|
||||
@param table_list TABLE_LIST for the table to change
|
||||
@param keys_onoff ENABLE or DISABLE KEYS?
|
||||
@param alter_ctx ALTER TABLE runtime context.
|
||||
|
||||
@return Operation status
|
||||
@retval false Success
|
||||
@retval true Failure
|
||||
*/
|
||||
static bool
|
||||
simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
|
||||
Alter_info::enum_enable_or_disable keys_onoff,
|
||||
Alter_table_ctx *alter_ctx)
|
||||
{
|
||||
DBUG_ENTER("simple_tmp_rename_or_index_change");
|
||||
|
||||
TABLE *table= table_list->table;
|
||||
bool error= false;
|
||||
|
||||
DBUG_ASSERT(table->s->tmp_table);
|
||||
|
||||
if (keys_onoff != Alter_info::LEAVE_AS_IS)
|
||||
{
|
||||
THD_STAGE_INFO(thd, stage_manage_keys);
|
||||
error= alter_table_manage_keys(table, table->file->indexes_are_disabled(),
|
||||
keys_onoff);
|
||||
}
|
||||
|
||||
if (!error && alter_ctx->is_table_renamed())
|
||||
{
|
||||
THD_STAGE_INFO(thd, stage_rename);
|
||||
|
||||
/*
|
||||
If THD::rename_temporary_table() fails, there is no need to rename it
|
||||
back to the original name (unlike the case for non-temporary tables),
|
||||
as it was an allocation error and the table was not renamed.
|
||||
*/
|
||||
error= thd->rename_temporary_table(table, alter_ctx->new_db,
|
||||
alter_ctx->new_alias);
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
int res= 0;
|
||||
/*
|
||||
We do not replicate alter table statement on temporary tables under
|
||||
ROW-based replication.
|
||||
*/
|
||||
if (!thd->is_current_stmt_binlog_format_row())
|
||||
{
|
||||
res= write_bin_log(thd, true, thd->query(), thd->query_length());
|
||||
}
|
||||
if (res != 0)
|
||||
error= true;
|
||||
else
|
||||
my_ok(thd);
|
||||
}
|
||||
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Rename table and/or turn indexes on/off without touching .FRM
|
||||
@@ -8293,6 +8432,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
|
||||
if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
|
||||
DBUG_RETURN(true);
|
||||
|
||||
THD_STAGE_INFO(thd, stage_manage_keys);
|
||||
error= alter_table_manage_keys(table,
|
||||
table->file->indexes_are_disabled(),
|
||||
keys_onoff);
|
||||
@@ -8671,29 +8811,48 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
thd->get_stmt_da()->current_statement_warn_count());
|
||||
my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
|
||||
|
||||
if (write_bin_log(thd, true, thd->query(), thd->query_length()))
|
||||
DBUG_RETURN(true);
|
||||
/* We don't replicate alter table statement on temporary tables */
|
||||
if (table->s->tmp_table == NO_TMP_TABLE ||
|
||||
!thd->is_current_stmt_binlog_format_row())
|
||||
{
|
||||
if (write_bin_log(thd, true, thd->query(), thd->query_length()))
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
/*
|
||||
Test if we are only doing RENAME or KEYS ON/OFF. This works
|
||||
as we are testing if flags == 0 above.
|
||||
*/
|
||||
if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
|
||||
Alter_info::ALTER_KEYS_ONOFF)) &&
|
||||
alter_info->requested_algorithm !=
|
||||
Alter_info::ALTER_TABLE_ALGORITHM_COPY &&
|
||||
!table->s->tmp_table) // no need to touch frm
|
||||
Alter_info::ALTER_TABLE_ALGORITHM_COPY) // No need to touch frm.
|
||||
{
|
||||
// This requires X-lock, no other lock levels supported.
|
||||
if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
|
||||
alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
|
||||
bool res;
|
||||
|
||||
if (!table->s->tmp_table)
|
||||
{
|
||||
my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
|
||||
"LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
|
||||
DBUG_RETURN(true);
|
||||
// This requires X-lock, no other lock levels supported.
|
||||
if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
|
||||
alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
|
||||
{
|
||||
my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
|
||||
"LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
res= simple_rename_or_index_change(thd, table_list,
|
||||
alter_info->keys_onoff,
|
||||
&alter_ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
res= simple_tmp_rename_or_index_change(thd, table_list,
|
||||
alter_info->keys_onoff,
|
||||
&alter_ctx);
|
||||
}
|
||||
bool res= simple_rename_or_index_change(thd, table_list,
|
||||
alter_info->keys_onoff,
|
||||
&alter_ctx);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
@@ -9123,6 +9282,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
||||
{
|
||||
goto err_new_table_cleanup;
|
||||
}
|
||||
/* in case of alter temp table send the tracker in OK packet */
|
||||
SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -9383,7 +9544,8 @@ err_new_table_cleanup:
|
||||
else
|
||||
(void) quick_rm_table(thd, new_db_type,
|
||||
alter_ctx.new_db, alter_ctx.tmp_name,
|
||||
(FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)));
|
||||
(FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)),
|
||||
alter_ctx.get_tmp_path());
|
||||
|
||||
/*
|
||||
No default value was provided for a DATE/DATETIME field, the
|
||||
@@ -9424,6 +9586,9 @@ err_new_table_cleanup:
|
||||
|
||||
err_with_mdl_after_alter:
|
||||
/* the table was altered. binlog the operation */
|
||||
DBUG_ASSERT(!(mysql_bin_log.is_open() &&
|
||||
thd->is_current_stmt_binlog_format_row() &&
|
||||
(create_info->tmp_table())));
|
||||
write_bin_log(thd, true, thd->query(), thd->query_length());
|
||||
|
||||
err_with_mdl:
|
||||
@@ -9562,8 +9727,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
||||
Old fields keep their current values, and therefore should not be
|
||||
present in the set of autoupdate fields.
|
||||
*/
|
||||
if ((*ptr)->default_value ||
|
||||
((*ptr)->has_insert_default_function()))
|
||||
if ((*ptr)->default_value)
|
||||
{
|
||||
*(dfield_ptr++)= *ptr;
|
||||
++to->s->default_fields;
|
||||
@@ -9740,7 +9904,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
||||
THD_STAGE_INFO(thd, stage_enabling_keys);
|
||||
thd_progress_next_stage(thd);
|
||||
|
||||
if (error > 0)
|
||||
if (error > 0 && !from->s->tmp_table)
|
||||
{
|
||||
/* We are going to drop the temporary table */
|
||||
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
|
||||
@@ -9769,7 +9933,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
|
||||
to->file->ha_release_auto_increment();
|
||||
if (to->file->ha_external_lock(thd,F_UNLCK))
|
||||
error=1;
|
||||
if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
|
||||
if (error < 0 && !from->s->tmp_table &&
|
||||
to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
|
||||
error= 1;
|
||||
thd_progress_end(thd);
|
||||
DBUG_RETURN(error > 0 ? -1 : 0);
|
||||
|
Reference in New Issue
Block a user