Commit ca7fbcea authored by Alexander Barkov's avatar Alexander Barkov

MDEV-19317 TEXT column accepts too long literals as a default value

Adding new virtual methods in Field:
- make_empty_rec_store_default_value()
- make_empty_rec_reset()

This simplifies the logic for every Field type,
and makes the code more friendly to pluggable data types.
parent baadbe96
...@@ -1093,3 +1093,34 @@ drop table t1; ...@@ -1093,3 +1093,34 @@ drop table t1;
# #
# End of 10.2 test # End of 10.2 test
# #
#
# Start of 10.4 test
#
#
# MDEV-19317 TEXT column accepts too long literals as a default value
#
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT ?)' USING REPEAT('a', 255);
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
LENGTH(a) LENGTH(DEFAULT(a))
255 255
DROP TABLE t1;
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT ?)' USING REPEAT('a', 256);
ERROR 42000: Invalid default value for 'a'
CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
ERROR 42000: Invalid default value for 'a'
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TEXT DEFAULT ?)' USING REPEAT('a', 256);
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
LENGTH(a) LENGTH(DEFAULT(a))
256 256
DROP TABLE t1;
CREATE OR REPLACE TABLE t1 (a TEXT DEFAULT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
LENGTH(a) LENGTH(DEFAULT(a))
256 256
DROP TABLE t1;
#
# End of 10.4 test
#
...@@ -702,3 +702,39 @@ drop table t1; ...@@ -702,3 +702,39 @@ drop table t1;
--echo # --echo #
--echo # End of 10.2 test --echo # End of 10.2 test
--echo # --echo #
--echo #
--echo # Start of 10.4 test
--echo #
--echo #
--echo # MDEV-19317 TEXT column accepts too long literals as a default value
--echo #
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT ?)' USING REPEAT('a', 255);
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
DROP TABLE t1;
--error ER_INVALID_DEFAULT
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT ?)' USING REPEAT('a', 256);
--error ER_INVALID_DEFAULT
CREATE OR REPLACE TABLE t1 (a TINYTEXT DEFAULT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
EXECUTE IMMEDIATE 'CREATE OR REPLACE TABLE t1 (a TEXT DEFAULT ?)' USING REPEAT('a', 256);
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
DROP TABLE t1;
CREATE OR REPLACE TABLE t1 (a TEXT DEFAULT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
INSERT INTO t1 VALUES ();
SELECT LENGTH(a), LENGTH(DEFAULT(a)) FROM t1;
DROP TABLE t1;
--echo #
--echo # End of 10.4 test
--echo #
...@@ -1382,6 +1382,14 @@ bool Field::sp_prepare_and_store_item(THD *thd, Item **value) ...@@ -1382,6 +1382,14 @@ bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
} }
bool Field::make_empty_rec_store_default_value(THD *thd, Item *item)
{
DBUG_ASSERT(!(flags & BLOB_FLAG));
int res= item->save_in_field(this, true);
return res != 0 && res != 3;
}
/** /**
Numeric fields base class constructor. Numeric fields base class constructor.
*/ */
...@@ -8773,6 +8781,18 @@ void Field_blob::make_send_field(Send_field *field) ...@@ -8773,6 +8781,18 @@ void Field_blob::make_send_field(Send_field *field)
} }
bool Field_blob::make_empty_rec_store_default_value(THD *thd, Item *item)
{
DBUG_ASSERT(flags & BLOB_FLAG);
int res= item->save_in_field(this, true);
DBUG_ASSERT(res != 3); // Field_blob never returns 3
if (res)
return true; // E.g. truncation happened
reset(); // Clear the pointer to a String, it should not be written to frm
return false;
}
int Field_blob_compressed::store(const char *from, size_t length, int Field_blob_compressed::store(const char *from, size_t length,
CHARSET_INFO *cs) CHARSET_INFO *cs)
{ {
......
...@@ -782,6 +782,11 @@ class Field: public Value_source ...@@ -782,6 +782,11 @@ class Field: public Value_source
@retval false - conversion is needed @retval false - conversion is needed
*/ */
virtual bool memcpy_field_possible(const Field *from) const= 0; virtual bool memcpy_field_possible(const Field *from) const= 0;
virtual bool make_empty_rec_store_default_value(THD *thd, Item *item);
virtual void make_empty_rec_reset(THD *thd)
{
reset();
}
virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0; virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0;
virtual int store_hex_hybrid(const char *str, size_t length); virtual int store_hex_hybrid(const char *str, size_t length);
virtual int store(double nr)=0; virtual int store(double nr)=0;
...@@ -3895,6 +3900,7 @@ class Field_blob :public Field_longstr { ...@@ -3895,6 +3900,7 @@ class Field_blob :public Field_longstr {
!compression_method() == !from->compression_method() && !compression_method() == !from->compression_method() &&
!table->copy_blobs; !table->copy_blobs;
} }
bool make_empty_rec_store_default_value(THD *thd, Item *item);
int store(const char *to, size_t length, CHARSET_INFO *charset); int store(const char *to, size_t length, CHARSET_INFO *charset);
using Field_str::store; using Field_str::store;
double val_real(void); double val_real(void);
...@@ -4212,6 +4218,16 @@ class Field_enum :public Field_str { ...@@ -4212,6 +4218,16 @@ class Field_enum :public Field_str {
return save_in_field_str(to); return save_in_field_str(to);
} }
bool memcpy_field_possible(const Field *from) const { return false; } bool memcpy_field_possible(const Field *from) const { return false; }
void make_empty_rec_reset(THD *thd)
{
if (flags & NOT_NULL_FLAG)
{
set_notnull();
store((longlong) 1, true);
}
else
reset();
}
int store(const char *to,size_t length,CHARSET_INFO *charset); int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr); int store(double nr);
int store(longlong nr, bool unsigned_val); int store(longlong nr, bool unsigned_val);
...@@ -4278,6 +4294,11 @@ class Field_set :public Field_enum { ...@@ -4278,6 +4294,11 @@ class Field_set :public Field_enum {
{ {
flags=(flags & ~ENUM_FLAG) | SET_FLAG; flags=(flags & ~ENUM_FLAG) | SET_FLAG;
} }
void make_empty_rec_reset(THD *thd)
{
Field::make_empty_rec_reset(thd);
}
int store_field(Field *from) { return from->save_in_field(this); } int store_field(Field *from) { return from->save_in_field(this); }
int store(const char *to,size_t length,CHARSET_INFO *charset); int store(const char *to,size_t length,CHARSET_INFO *charset);
int store(double nr) { return Field_set::store((longlong) nr, FALSE); } int store(double nr) { return Field_set::store((longlong) nr, FALSE); }
......
...@@ -974,13 +974,36 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, ...@@ -974,13 +974,36 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
DBUG_RETURN(0); DBUG_RETURN(0);
} }
static bool make_empty_rec_store_default(THD *thd, Field *regfield,
Virtual_column_info *default_value)
{
if (default_value && !default_value->flags)
{
Item *expr= default_value->expr;
// may be already fixed if ALTER TABLE
if (expr->fix_fields_if_needed(thd, &expr))
return true;
DBUG_ASSERT(expr == default_value->expr); // Should not change
if (regfield->make_empty_rec_store_default_value(thd, expr))
{
my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str);
return true;
}
return false;
}
regfield->make_empty_rec_reset(thd);
return false;
}
/* save an empty record on start of formfile */ /* save an empty record on start of formfile */
static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
List<Create_field> &create_fields, List<Create_field> &create_fields,
uint reclength, ulong data_offset) uint reclength, ulong data_offset)
{ {
int error= 0; int error= false;
uint null_count; uint null_count;
uchar *null_pos; uchar *null_pos;
TABLE table; TABLE table;
...@@ -1020,7 +1043,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, ...@@ -1020,7 +1043,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
field->flags); field->flags);
if (!regfield) if (!regfield)
{ {
error= 1; error= true;
goto err; // End of memory goto err; // End of memory
} }
...@@ -1037,36 +1060,10 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, ...@@ -1037,36 +1060,10 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
!f_bit_as_char(field->pack_flag)) !f_bit_as_char(field->pack_flag))
null_count+= field->length & 7; null_count+= field->length & 7;
if (field->default_value && !field->default_value->flags && error= make_empty_rec_store_default(thd, regfield, field->default_value);
(!(field->flags & BLOB_FLAG) || delete regfield; // Avoid memory leaks
field->real_field_type() == MYSQL_TYPE_GEOMETRY)) if (error)
{ goto err;
Item *expr= field->default_value->expr;
// may be already fixed if ALTER TABLE
int res= expr->fix_fields_if_needed(thd, &expr);
if (!res)
res= expr->save_in_field(regfield, 1);
if (!res && (field->flags & BLOB_FLAG))
regfield->reset();
/* If not ok or warning of level 'note' */
if (res != 0 && res != 3)
{
my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str);
error= 1;
delete regfield; //To avoid memory leak
goto err;
}
delete regfield; //To avoid memory leak
}
else if (regfield->real_type() == MYSQL_TYPE_ENUM &&
(field->flags & NOT_NULL_FLAG))
{
regfield->set_notnull();
regfield->store((longlong) 1, TRUE);
}
else
regfield->reset();
} }
DBUG_ASSERT(data_offset == ((null_count + 7) / 8)); DBUG_ASSERT(data_offset == ((null_count + 7) / 8));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment