Commit 90aa0271 authored by evgen@moonbone.local's avatar evgen@moonbone.local
Browse files

Bug#27507: Wrong DATETIME value was allowed by ALTER TABLE in the NO_ZERO_DATE

mode.

When a new DATE/DATETIME field without default value is being added by the
ALTER TABLE the '0000-00-00' value is used as the default one. But it wasn't
checked whether such value was allowed by the set sql mode. Due to this
'0000-00-00' values was allowed for DATE/DATETIME fields even in the
NO_ZERO_DATE mode.

Now the mysql_alter_table() function checks whether the '0000-00-00' value
is allowed for DATE/DATETIME fields by the set sql mode.
The new error_if_not_empty flag is used in the mysql_alter_table() function
to indicate that it should abort if the table being altered isn't empty.
The new new_datetime_field field is used in the mysql_alter_table() function
for error throwing purposes. 
The new error_if_not_empty parameter is added to the copy_data_between_tables()
function to indicate the it should return error if the source table isn't empty.
parent bb708f49
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -884,3 +884,22 @@ id
50
51
drop table t1;
set @orig_sql_mode = @@sql_mode;
set sql_mode="no_zero_date";
create table t1(f1 int);
alter table t1 add column f2 datetime not null, add column f21 date not null;
insert into t1 values(1,'2000-01-01','2000-01-01');
alter table t1 add column f3 datetime not null;
ERROR 22007: Incorrect datetime value: '0000-00-00 00:00:00' for column 'f3' at row 1
alter table t1 add column f3 date not null;
ERROR 22007: Incorrect date value: '0000-00-00' for column 'f3' at row 1
alter table t1 add column f4 datetime not null default '2002-02-02',
add column f41 date not null;
ERROR 22007: Incorrect date value: '0000-00-00' for column 'f41' at row 1
alter table t1 add column f4 datetime not null default '2002-02-02',
add column f41 date not null default '2002-02-02';
select * from t1;
f1	f2	f21	f4	f41
1	2000-01-01 00:00:00	2000-01-01	2002-02-02 00:00:00	2002-02-02
drop table t1;
set sql_mode= @orig_sql_mode;
+22 −0
Original line number Diff line number Diff line
@@ -662,3 +662,25 @@ insert into t1 values (null);
select * from t1;

drop table t1;

#
# Bug#27507: Wrong DATETIME value was allowed by ALTER TABLE in the
#            NO_ZERO_DATE mode.
#
set @orig_sql_mode = @@sql_mode;
set sql_mode="no_zero_date";
create table t1(f1 int);
alter table t1 add column f2 datetime not null, add column f21 date not null;
insert into t1 values(1,'2000-01-01','2000-01-01');
--error 1292
alter table t1 add column f3 datetime not null;
--error 1292
alter table t1 add column f3 date not null;
--error 1292
alter table t1 add column f4 datetime not null default '2002-02-02',
  add column f41 date not null;
alter table t1 add column f4 datetime not null default '2002-02-02',
  add column f41 date not null default '2002-02-02';
select * from t1;
drop table t1;
set sql_mode= @orig_sql_mode;
+69 −3
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ static int copy_data_between_tables(TABLE *from,TABLE *to,
                                    List<create_field> &create, bool ignore,
				    uint order_num, ORDER *order,
				    ha_rows *copied,ha_rows *deleted,
                                    enum enum_enable_or_disable keys_onoff);
                                    enum enum_enable_or_disable keys_onoff,
                                    bool error_if_not_empty);

static bool prepare_blob_field(THD *thd, create_field *sql_field);
static bool check_engine(THD *thd, const char *table_name,
@@ -3077,6 +3078,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
  bool need_copy_table;
  bool no_table_reopen= FALSE, varchar= FALSE;
  frm_type_enum frm_type;
  /*
    Throw an error if the table to be altered isn't empty.
    Used in DATE/DATETIME fields default value checking.
  */
  bool error_if_not_empty= FALSE;
  /*
    A field used for error reporting in DATE/DATETIME fields default
    value checking.
  */
  create_field *new_datetime_field= 0;
  DBUG_ENTER("mysql_alter_table");

  thd->proc_info="init";
@@ -3445,6 +3456,22 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
      DBUG_RETURN(TRUE);
    }
    /*
      Check that the DATE/DATETIME not null field we are going to add is
      either has a default value or the '0000-00-00' is allowed by the
      set sql mode.
      If the '0000-00-00' value isn't allowed then raise the error_if_not_empty
      flag to allow ALTER TABLE only if the table to be altered is empty.
    */
    if ((def->sql_type == MYSQL_TYPE_DATE ||
         def->sql_type == MYSQL_TYPE_NEWDATE ||
         def->sql_type == MYSQL_TYPE_DATETIME) && !new_datetime_field &&
         !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
         thd->variables.sql_mode & MODE_NO_ZERO_DATE)
    {
        new_datetime_field= def;
        error_if_not_empty= TRUE;
    }
    if (!def->after)
      new_info.create_list.push_back(def);
    else if (def->after == first_keyword)
@@ -3765,7 +3792,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
    new_table->next_number_field=new_table->found_next_number_field;
    error= copy_data_between_tables(table, new_table, new_info.create_list,
                                    ignore, order_num, order,
                                    &copied, &deleted, alter_info->keys_onoff);
                                    &copied, &deleted, alter_info->keys_onoff,
                                    error_if_not_empty);
  }
  else if (!new_table)
  {
@@ -3999,6 +4027,37 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
  DBUG_RETURN(FALSE);

err:
  /*
    No default value was provided for a DATE/DATETIME field, the
    current sql_mode doesn't allow the '0000-00-00' value and
    the table to be altered isn't empty.
    Report error here.
  */
  if (error_if_not_empty && thd->row_count)
  {
    const char *f_val;
    enum enum_mysql_timestamp_type t_type;
    switch (new_datetime_field->sql_type)
    {
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_NEWDATE:
        f_val= "0000-00-00";
        t_type= MYSQL_TIMESTAMP_DATE;
        break;
      case MYSQL_TYPE_DATETIME:
        f_val= "0000-00-00 00:00:00";
        t_type= MYSQL_TIMESTAMP_DATETIME;
        break;
      default:
        /* Shouldn't get here. */
        DBUG_ASSERT(0);
    }
    bool save_abort_on_warning= thd->abort_on_warning;
    thd->abort_on_warning= TRUE;
    make_truncated_value_warning(thd, f_val, strlength(f_val), t_type,
                                 new_datetime_field->field_name);
    thd->abort_on_warning= save_abort_on_warning;
  }
  DBUG_RETURN(TRUE);
}

@@ -4010,7 +4069,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
			 uint order_num, ORDER *order,
			 ha_rows *copied,
			 ha_rows *deleted,
                         enum enum_enable_or_disable keys_onoff)
                         enum enum_enable_or_disable keys_onoff,
                         bool error_if_not_empty)
{
  int error;
  Copy_field *copy,*copy_end;
@@ -4125,6 +4185,12 @@ copy_data_between_tables(TABLE *from,TABLE *to,
      break;
    }
    thd->row_count++;
    /* Return error if source table isn't empty. */
    if (error_if_not_empty)
    {
      error= 1;
      break;
    }
    if (to->next_number_field)
    {
      if (auto_increment_field_copied)