Skip to content

Commit 1825f17

Browse files
Dmitry ShulgaDmitry Shulga
Dmitry Shulga
authored and
Dmitry Shulga
committed
This is core patch for WL#6030.
This patch does the following things: - changes Field class so that it is possible to set NOT NULL fields temporary to NULL; - allows NOT NULL fields to accept NULL values temporarily during BEFORE-trigger processing; - checks that the NOT NULL constraint is enforced after BEFORE-trigger processing. - adds support for handling NULL fields in trigger BEFORE-insert during execution of statement LOAD DATA INFILE. Details: This patch - changes the class Field to allow set the field in temporary value NULL; - extends interface of class Field by method check_constraints in order to check NOT NULL constraint for the field; - adds static overloaded functions check_record() into sql_base.cc that iterates over table's fields and checks NOT NULL constraint. These functions are called after trigger processing has been finished as part of execution function fill_records_n_invoke_before_triggers; - extends interface of class Table_triggers_list by two additional methods enable_fields_temporary_nullability() and disable_fields_temporary_nullability(). These methods are used in the function fill_record_n_invoke_before_triggers() to enable/disable temporary nullability for table's fields during execution of BEFORE trigger. - adds function check_that_all_fields_are_given_values(). This function is called after all fields that are used (explicitly or implicitly as part BEFORE INSERT trigger) in the statement INSERT have been prepared. - turns on temporary nullability for table's fields before start loading of file records during handling statement LOAD DATA INFILE. After records loading has been finished iterates over table's fields and checks NOT NULL constraint.
1 parent 5ad944e commit 1825f17

13 files changed

+448
-75
lines changed

mysql-test/suite/funcs_1/r/innodb_trig_09.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
198198
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
199199
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
200200
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
201-
a NULL Test 3.5.9.4-trig 0 999 NULL
201+
a NULL Test 3.5.9.4-trig NULL 999 NULL
202202
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
203203
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
204204
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163

mysql-test/suite/funcs_1/r/memory_trig_09.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
199199
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
200200
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
201201
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
202-
a NULL Test 3.5.9.4-trig 0 999 NULL
202+
a NULL Test 3.5.9.4-trig NULL 999 NULL
203203
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
204204
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
205205
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163

mysql-test/suite/funcs_1/r/myisam_trig_09.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
199199
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
200200
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
201201
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
202-
a NULL Test 3.5.9.4-trig 0 999 NULL
202+
a NULL Test 3.5.9.4-trig NULL 999 NULL
203203
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
204204
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
205205
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163

sql/field.cc

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,10 @@ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val)
13511351
Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
13521352
uchar null_bit_arg,
13531353
utype unireg_check_arg, const char *field_name_arg)
1354-
:ptr(ptr_arg), m_null_ptr(null_ptr_arg),
1354+
:ptr(ptr_arg),
1355+
m_null_ptr(null_ptr_arg),
1356+
m_is_tmp_nullable(false),
1357+
m_is_tmp_null(false),
13551358
table(0), orig_table(0), table_name(0),
13561359
field_name(field_name_arg),
13571360
unireg_check(unireg_check_arg),
@@ -1365,6 +1368,104 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
13651368
}
13661369

13671370

1371+
/**
1372+
Check NOT NULL constraint on the field after temporary nullability is
1373+
disabled.
1374+
1375+
@param warning_no Warning to report.
1376+
1377+
@return TYPE_OK if the value is Ok, or corresponding error code from
1378+
the type_conversion_status enum.
1379+
*/
1380+
type_conversion_status Field::check_constraints(int warning_no) const
1381+
{
1382+
/*
1383+
Ensure that Field::check_constraints() is called only when temporary
1384+
nullability is disabled.
1385+
*/
1386+
1387+
DBUG_ASSERT(!is_tmp_nullable());
1388+
1389+
if (real_maybe_null())
1390+
return TYPE_OK; // If the field is nullable, we're Ok.
1391+
1392+
if (!m_is_tmp_null)
1393+
return TYPE_OK; // If the field was not NULL, we're Ok.
1394+
1395+
// The field has been set to NULL.
1396+
1397+
/*
1398+
If the field is of AUTO_INCREMENT, and the next number
1399+
has been assigned to it, we're Ok.
1400+
*/
1401+
1402+
if (this == table->next_number_field)
1403+
return TYPE_OK;
1404+
1405+
/*
1406+
If the field is of TIMESTAMP its default value is CURRENT_TIMESTAMP,
1407+
so we're OK.
1408+
*/
1409+
1410+
if (type() == MYSQL_TYPE_TIMESTAMP)
1411+
return TYPE_OK;
1412+
1413+
switch (m_count_cuted_fields_saved) {
1414+
case CHECK_FIELD_WARN:
1415+
set_warning(Sql_condition::SL_WARNING, warning_no, 1);
1416+
/* fall through */
1417+
case CHECK_FIELD_IGNORE:
1418+
return TYPE_OK;
1419+
case CHECK_FIELD_ERROR_FOR_NULL:
1420+
if (!table->in_use->no_errors)
1421+
my_error(ER_BAD_NULL_ERROR, MYF(0), field_name);
1422+
return TYPE_ERR_NULL_CONSTRAINT_VIOLATION;
1423+
}
1424+
1425+
DBUG_ASSERT(0); // impossible
1426+
return TYPE_ERR_NULL_CONSTRAINT_VIOLATION;
1427+
}
1428+
1429+
1430+
/**
1431+
Set field to value NULL.
1432+
1433+
@param row_offset This is the offset between the row being updated
1434+
and table->record[0]
1435+
*/
1436+
void Field::set_null(my_ptrdiff_t row_offset)
1437+
{
1438+
if (real_maybe_null())
1439+
{
1440+
m_null_ptr[row_offset]|= null_bit;
1441+
}
1442+
else if (is_tmp_nullable())
1443+
{
1444+
m_is_tmp_null= true;
1445+
m_count_cuted_fields_saved= table->in_use->count_cuted_fields;
1446+
}
1447+
}
1448+
1449+
1450+
/**
1451+
Set field to value NOT NULL.
1452+
1453+
@param row_offset This is the offset between the row being updated
1454+
and table->record[0]
1455+
*/
1456+
void Field::set_notnull(my_ptrdiff_t row_offset)
1457+
{
1458+
if (real_maybe_null())
1459+
{
1460+
m_null_ptr[row_offset]&= (uchar) ~null_bit;
1461+
}
1462+
else if (is_tmp_nullable())
1463+
{
1464+
m_is_tmp_null= false;
1465+
}
1466+
}
1467+
1468+
13681469
void Field::hash(ulong *nr, ulong *nr2)
13691470
{
13701471
if (is_null())
@@ -1395,7 +1496,8 @@ void Field::copy_data(my_ptrdiff_t src_record_offset)
13951496
// Set to NULL if the source record is NULL, otherwise set to NOT-NULL.
13961497
m_null_ptr[0]= (m_null_ptr[0] & ~null_bit) |
13971498
(m_null_ptr[src_record_offset] & null_bit);
1398-
}
1499+
} else if (is_tmp_nullable())
1500+
m_is_tmp_null= false;
13991501
}
14001502

14011503

sql/field.h

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,27 @@ class Field
489489
*/
490490
uchar *m_null_ptr;
491491

492+
/**
493+
Flag: if the NOT-NULL field can be temporary NULL.
494+
*/
495+
bool m_is_tmp_nullable;
496+
497+
/**
498+
This is a flag with the following semantics:
499+
- it can be changed only when m_is_tmp_nullable is true;
500+
- it specifies if this field in the first current record
501+
(TABLE::record[0]) was set to NULL (temporary NULL).
502+
503+
This flag is used for trigger handling.
504+
*/
505+
bool m_is_tmp_null;
506+
507+
/**
508+
The value of THD::count_cuted_fields at the moment of setting
509+
m_is_tmp_null attribute.
510+
*/
511+
enum_check_fields m_count_cuted_fields_saved;
512+
492513
protected:
493514
const uchar *get_null_ptr() const
494515
{ return m_null_ptr; }
@@ -551,6 +572,34 @@ class Field
551572
const char *field_name_arg);
552573
virtual ~Field() {}
553574

575+
/**
576+
Turn on/off temporary nullability for the field.
577+
578+
@param is_tmp_nullable NULLability flag:
579+
true - turn on temporary NULLability
580+
false - turn off temporary NULLability
581+
*/
582+
void set_tmp_nullable(bool is_tmp_nullable)
583+
{ m_is_tmp_nullable= is_tmp_nullable; }
584+
585+
/**
586+
Return temporary NULLability flag.
587+
588+
@return true - if NULL can be assigned temporary to the Field
589+
false - if NULL can not be assigned temporary to the Field
590+
*/
591+
bool is_tmp_nullable() const
592+
{ return m_is_tmp_nullable; }
593+
594+
/**
595+
Check whether Field has temporary value NULL.
596+
597+
@return true - if the Field has temporary value NULL
598+
false - if the Field's value is NOT NULL
599+
*/
600+
bool is_tmp_null() const
601+
{ return is_tmp_nullable() && m_is_tmp_null; }
602+
554603
/* Store functions returns 1 on overflow and -1 on fatal error */
555604
virtual type_conversion_status store(const char *to, uint length,
556605
const CHARSET_INFO *cs)=0;
@@ -869,6 +918,12 @@ class Field
869918
bool is_temporal_with_date_and_time() const
870919
{ return is_temporal_type_with_date_and_time(type()); }
871920

921+
/**
922+
Check whether the full table's row is NULL or the Field has value NULL.
923+
924+
@return true if the full table's row is NULL or the Field has value NULL
925+
false if neither table's row nor the Field has value NULL
926+
*/
872927
bool is_null(my_ptrdiff_t row_offset= 0) const
873928
{
874929
/*
@@ -883,27 +938,62 @@ class Field
883938
pointer, and its NULLity is recorded in the "null_bit" bit of
884939
m_null_ptr[row_offset].
885940
*/
886-
return table->null_row ? true : is_real_null(row_offset);
941+
return table->null_row ?
942+
true :
943+
is_real_null(row_offset);
887944
}
888945

889-
bool is_real_null(my_ptrdiff_t row_offset= 0) const
890-
{ return real_maybe_null() ? test(m_null_ptr[row_offset] & null_bit) : false; }
891-
892-
bool is_null_in_record(const uchar *record) const
893-
{ return real_maybe_null() ? test(record[null_offset()] & null_bit) : false; }
946+
/**
947+
Check whether the Field has value NULL (temporary or actual).
894948
895-
void set_null(my_ptrdiff_t row_offset= 0)
949+
@return true if the Field has value NULL (temporary or actual)
950+
false if the Field has value NOT NULL.
951+
*/
952+
bool is_real_null(my_ptrdiff_t row_offset= 0) const
896953
{
897954
if (real_maybe_null())
898-
m_null_ptr[row_offset]|= null_bit;
955+
return test(m_null_ptr[row_offset] & null_bit);
956+
957+
if (is_tmp_nullable())
958+
return m_is_tmp_null;
959+
960+
return false;
899961
}
900962

901-
void set_notnull(my_ptrdiff_t row_offset= 0)
963+
/**
964+
Check if the Field has value NULL or the record specified by argument
965+
has value NULL for this Field.
966+
967+
@return true if the Field has value NULL or the record has value NULL
968+
for thois Field.
969+
*/
970+
bool is_null_in_record(const uchar *record) const
902971
{
903972
if (real_maybe_null())
904-
m_null_ptr[row_offset]&= (uchar) ~null_bit;
973+
return test(record[null_offset()] & null_bit);
974+
975+
if (is_tmp_nullable())
976+
return m_is_tmp_null;
977+
978+
return false;
905979
}
906980

981+
void set_null(my_ptrdiff_t row_offset= 0);
982+
983+
void set_notnull(my_ptrdiff_t row_offset= 0);
984+
985+
type_conversion_status check_constraints(int warning_no) const;
986+
987+
/**
988+
Remember the value of THD::count_cuted_fields to handle possible
989+
NOT-NULL constraint errors after BEFORE-trigger execution is finished.
990+
We should save the value of THD::count_cuted_fields before starting
991+
BEFORE-trigger processing since during triggers execution the
992+
value of THD::count_cuted_fields could be changed.
993+
*/
994+
void set_count_cuted_fields(enum_check_fields count_cuted_fields)
995+
{ m_count_cuted_fields_saved= count_cuted_fields; }
996+
907997
bool maybe_null(void) const
908998
{ return real_maybe_null() || table->maybe_null; }
909999

@@ -1017,7 +1107,7 @@ class Field
10171107
virtual void move_field_offset(my_ptrdiff_t ptr_diff)
10181108
{
10191109
ptr= ADD_TO_PTR(ptr, ptr_diff, uchar*);
1020-
if (m_null_ptr)
1110+
if (real_maybe_null())
10211111
m_null_ptr= ADD_TO_PTR(m_null_ptr, ptr_diff, uchar*);
10221112
}
10231113

sql/field_conv.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ static void do_field_to_null_str(Copy_field *copy)
100100

101101
type_conversion_status set_field_to_null(Field *field)
102102
{
103-
if (field->real_maybe_null())
103+
if (field->real_maybe_null() || field->is_tmp_nullable())
104104
{
105105
field->set_null();
106106
field->reset();
@@ -149,8 +149,20 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
149149
field->reset();
150150
return TYPE_OK;
151151
}
152+
152153
if (no_conversions)
153-
return TYPE_ERR_NULL_CONSTRAINT_VIOLATION;
154+
{
155+
if (field->is_tmp_nullable())
156+
{
157+
field->set_null();
158+
field->reset();
159+
return TYPE_OK;
160+
}
161+
else
162+
{
163+
return TYPE_ERR_NULL_CONSTRAINT_VIOLATION;
164+
}
165+
}
154166

155167
/*
156168
Check if this is a special type, which will get a special walue
@@ -166,7 +178,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
166178
Item_func_now_local::store_in(field);
167179
return TYPE_OK; // Ok to set time to NULL
168180
}
169-
181+
170182
// Note: we ignore any potential failure of reset() here.
171183
field->reset();
172184

@@ -175,6 +187,14 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
175187
field->table->auto_increment_field_not_null= FALSE;
176188
return TYPE_OK; // field is set in fill_record()
177189
}
190+
191+
if (field->is_tmp_nullable())
192+
{
193+
field->set_null();
194+
field->reset();
195+
return TYPE_OK;
196+
}
197+
178198
switch (field->table->in_use->count_cuted_fields) {
179199
case CHECK_FIELD_WARN:
180200
field->set_warning(Sql_condition::SL_WARNING, ER_BAD_NULL_ERROR, 1);

sql/item.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2534,7 +2534,7 @@ adjust_max_effective_column_length(Field *field_par, uint32 max_length)
25342534
void Item_field::set_field(Field *field_par)
25352535
{
25362536
field=result_field=field_par; // for easy coding with fields
2537-
maybe_null=field->maybe_null();
2537+
maybe_null= field->maybe_null() || field->is_tmp_nullable();
25382538
decimals= field->decimals();
25392539
table_name= *field_par->table_name;
25402540
field_name= field_par->field_name;

sql/item_sum.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,7 @@ bool Aggregator_distinct::add()
971971
return TRUE;
972972

973973
for (Field **field=table->field ; *field ; field++)
974-
if ((*field)->is_real_null(0))
974+
if ((*field)->is_real_null())
975975
return 0; // Don't count NULL
976976

977977
if (tree)
@@ -3253,7 +3253,7 @@ bool Item_func_group_concat::add()
32533253
if (!show_item->const_item())
32543254
{
32553255
Field *f= show_item->get_tmp_table_field();
3256-
if (f->is_real_null())
3256+
if (f->is_null_in_record((const uchar*) table->record[0]))
32573257
return 0; // Skip row if it contains null
32583258
}
32593259
}

0 commit comments

Comments
 (0)