← Back to team overview

maria-developers team mailing list archive

MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function

 

Hello Marko,

please review a patch for MDEV-14038.

Thanks!
commit 472c814c1e7bacd3b23e05a2b58f455ce161b786
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Wed Oct 11 09:18:32 2017 +0400

    MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function
    
    Changing Field::set_default from void to int.
    It now uses the same return value notation with Field::store*:
      0 OK
      1 truncated (e.g. to a smaller number on numeric overflow)
      2 truncated (e.g. to a shorter string), or converted some characters to '?'
      3 minor acceptable truncation happened (such as TIMESTAMP->DATE)
     -1 OEM, or a completely bad value (no truncated replacement value possible)

diff --git a/mysql-test/suite/innodb/r/innodb-alter.result b/mysql-test/suite/innodb/r/innodb-alter.result
index aa78f55..b06c606 100644
--- a/mysql-test/suite/innodb/r/innodb-alter.result
+++ b/mysql-test/suite/innodb/r/innodb-alter.result
@@ -857,3 +857,33 @@ DROP TABLE dest_db.t1;
 DROP TABLE source_db.t1;
 DROP DATABASE source_db;
 DROP DATABASE dest_db;
+USE test;
+#
+# MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function
+#
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+ALTER TABLE t1 ADD b TINYINT NOT NULL DEFAULT if(unix_timestamp()>1,1000,0);
+ERROR 22003: Out of range value for column 'b' at row 1
+SELECT * FROM t1;
+a
+10
+DROP TABLE t1;
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+ALTER TABLE t1 ADD b DATE NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0);
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
+SELECT * FROM t1;
+a	b
+10	2001-01-01
+DROP TABLE t1;
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+ALTER TABLE t1 ADD b TIME NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0);
+affected rows: 0
+info: Records: 0  Duplicates: 0  Warnings: 0
+SELECT * FROM t1;
+a	b
+10	10:20:30
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/innodb-online-alter-gis.result b/mysql-test/suite/innodb/r/innodb-online-alter-gis.result
index c7daac4..b534597 100644
--- a/mysql-test/suite/innodb/r/innodb-online-alter-gis.result
+++ b/mysql-test/suite/innodb/r/innodb-online-alter-gis.result
@@ -37,3 +37,13 @@ Level	Code	Message
 show errors;
 Level	Code	Message
 drop table t1;
+#
+# MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function
+#
+CREATE OR REPLACE TABLE t1 (a INT) ENGINE=InnoDB;
+ALTER TABLE t1 ADD COLUMN b LINESTRING DEFAULT POINT(1,1);
+ERROR 22007: Incorrect LINESTRING value: 'POINT' for column 'b' at row 1
+DESCRIBE t1;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/innodb-alter.test b/mysql-test/suite/innodb/t/innodb-alter.test
index 5e681f9..d936dca 100644
--- a/mysql-test/suite/innodb/t/innodb-alter.test
+++ b/mysql-test/suite/innodb/t/innodb-alter.test
@@ -494,6 +494,34 @@ eval ALTER TABLE $source_db.t1 DROP INDEX index2, algorithm=inplace;
 eval DROP TABLE $source_db.t1;
 eval DROP DATABASE $source_db;
 eval DROP DATABASE $dest_db;
+USE test;
 
 
+--echo #
+--echo # MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function
+--echo #
 
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+--error ER_WARN_DATA_OUT_OF_RANGE
+ALTER TABLE t1 ADD b TINYINT NOT NULL DEFAULT if(unix_timestamp()>1,1000,0);
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# DATETIME-to-DATE truncation is OK
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+--enable_info
+ALTER TABLE t1 ADD b DATE NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0);
+--disable_info
+SELECT * FROM t1;
+DROP TABLE t1;
+
+# DATETIME-to-TIME truncation is OK
+CREATE TABLE t1 (a INT NOT NULL DEFAULT 0) ENGINE=InnoDB;
+iNSERT INTO t1 VALUES (10);
+--enable_info
+ALTER TABLE t1 ADD b TIME NOT NULL DEFAULT if(unix_timestamp()>1,TIMESTAMP'2001-01-01 10:20:30',0);
+--disable_info
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/t/innodb-online-alter-gis.test b/mysql-test/suite/innodb/t/innodb-online-alter-gis.test
index 64d07ba..3f39750 100644
--- a/mysql-test/suite/innodb/t/innodb-online-alter-gis.test
+++ b/mysql-test/suite/innodb/t/innodb-online-alter-gis.test
@@ -19,3 +19,13 @@ ALTER ONLINE TABLE t1 ADD PRIMARY KEY(a),DROP INDEX d, LOCK=SHARED;
 show warnings;
 show errors;
 drop table t1;
+
+--echo #
+--echo # MDEV-14038 ALTER TABLE does not exit on error with InnoDB + bad default function
+--echo #
+
+CREATE OR REPLACE TABLE t1 (a INT) ENGINE=InnoDB;
+--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD
+ALTER TABLE t1 ADD COLUMN b LINESTRING DEFAULT POINT(1,1);
+DESCRIBE t1;
+DROP TABLE t1;
diff --git a/sql/field.cc b/sql/field.cc
index c9cf3c3..6b3f836 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2335,15 +2335,15 @@ Field *Field::clone(MEM_ROOT *root, my_ptrdiff_t diff)
   return tmp;
 }
 
-void Field::set_default()
+int Field::set_default()
 {
   if (default_value)
   {
     Query_arena backup_arena;
     table->in_use->set_n_backup_active_arena(table->expr_arena, &backup_arena);
-    (void) default_value->expr->save_in_field(this, 0);
+    int rc= default_value->expr->save_in_field(this, 0);
     table->in_use->restore_active_arena(table->expr_arena, &backup_arena);
-    return;
+    return rc;
   }
   /* Copy constant value stored in s->default_values */
   my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
@@ -2352,6 +2352,7 @@ void Field::set_default()
   if (maybe_null_in_table())
     *null_ptr= ((*null_ptr & (uchar) ~null_bit) |
                 (null_ptr[l_offset] & null_bit));
+  return 0;
 }
 
 
@@ -9665,7 +9666,7 @@ Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
 }
 
 
-void Field_bit::set_default()
+int Field_bit::set_default()
 {
   if (bit_len > 0)
   {
@@ -9673,7 +9674,7 @@ void Field_bit::set_default()
     uchar bits= get_rec_bits(bit_ptr + col_offset, bit_ofs, bit_len);
     set_rec_bits(bits, bit_ptr, bit_ofs, bit_len);
   }
-  Field::set_default();
+  return Field::set_default();
 }
 
 /*
diff --git a/sql/field.h b/sql/field.h
index 91e97c8..1d0ce8c 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -953,7 +953,7 @@ class Field: public Value_source
     my_ptrdiff_t l_offset= (my_ptrdiff_t) (record -  table->record[0]);
     return ptr + l_offset;
   }
-  virtual void set_default();
+  virtual int set_default();
 
   bool has_update_default_function() const
   {
@@ -3715,7 +3715,7 @@ class Field_bit :public Field {
   virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
   virtual const uchar *unpack(uchar *to, const uchar *from,
                               const uchar *from_end, uint param_data);
-  virtual void set_default();
+  virtual int set_default();
 
   Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
                        uchar *new_ptr, uint32 length,
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 084fc80..6a6295e 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -1069,8 +1069,9 @@ ha_innobase::check_if_supported_inplace_alter(
 
 			/* Compute the DEFAULT values of non-constant columns
 			(VCOL_SESSION_FUNC | VCOL_TIME_FUNC). */
-			(*af)->set_default();
-			goto next_column;
+			int rc= (*af)->set_default();
+			if (rc == 0 || rc == 3)
+				goto next_column;
 		}
 
 		DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);

Follow ups