Commit 12d2c4fc authored by Sergei Golubchik's avatar Sergei Golubchik

optimize constant default expressions

to be calculated at the CREATE TABLE time and stored in
the default row image.
parent 4070d557
......@@ -409,7 +409,6 @@ create or replace table t1 (a int not null, b int default (a+1));
create or replace table t1 (a int default a);
ERROR 01000: Expression for field `a` is refering to uninitialized field `a`
create or replace table t1 (a int default b, b int default (1+1));
ERROR 01000: Expression for field `a` is refering to uninitialized field `b`
create or replace table t1 (a int default 1, b int as (c), c int as (a+1));
ERROR 01000: Expression for field `b` is refering to uninitialized field `c`
CREATE TABLE t1 (a INT DEFAULT (DEFAULT(a)));
......@@ -3035,7 +3034,7 @@ t1 CREATE TABLE `t1` (
`a` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT concat('A')
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
create table t1 (a int default 1, b int default (1+1), c int);
create table t1 (a int default 1, b int default (rand()*0+2), c int);
insert t1 (c) values (a);
insert t1 (c) values (b);
select * from t1;
......
......@@ -295,7 +295,6 @@ create or replace table t1 (a int not null, b int default (a+1));
--error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD
create or replace table t1 (a int default a);
--error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD
create or replace table t1 (a int default b, b int default (1+1));
--error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD
create or replace table t1 (a int default 1, b int as (c), c int as (a+1));
......@@ -1810,7 +1809,7 @@ DROP TABLE t1;
#
# Order of evaluation:
#
create table t1 (a int default 1, b int default (1+1), c int);
create table t1 (a int default 1, b int default (rand()*0+2), c int);
insert t1 (c) values (a);
insert t1 (c) values (b);
select * from t1;
......
......@@ -574,7 +574,11 @@ inline bool is_temporal_type_with_time(enum_field_types type)
}
}
/* Bits for type of vcol expression */
/*
Flags for Virtual_column_info. If none is set, the expression must be
a constant with no side-effects, so it's calculated at CREATE TABLE time,
stored in table->record[2], and not recalculated for every statement.
*/
#define VCOL_FIELD_REF 1
#define VCOL_NON_DETERMINISTIC 2
#define VCOL_SESSION_FUNC 4 /* uses session data, e.g. USER or DAYNAME */
......
......@@ -956,8 +956,8 @@ bool Item_field::check_field_expression_processor(void *arg)
{
if (field->flags & NO_DEFAULT_VALUE_FLAG)
return 0;
if ((field->default_value || field->has_insert_default_function() ||
field->vcol_info))
if ((field->default_value && field->default_value->flags)
|| field->has_insert_default_function() || field->vcol_info)
{
Field *org_field= (Field*) arg;
if (field == org_field ||
......@@ -8232,7 +8232,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
set_field(def_field);
if (field->default_value)
{
if (field->default_value->expr_item) // it's NULL during CREATE TABLE
if (thd->mark_used_columns != MARK_COLUMNS_NONE)
field->default_value->expr_item->walk(&Item::register_field_in_read_map, 1, 0);
IF_DBUG(def_field->is_stat_field=1,); // a hack to fool ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED
}
......
......@@ -3255,35 +3255,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 +3320,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, &not_used, &not_used2, &not_found);
}
else /* not NULL */
{
(void) find_set(interval, def->ptr(), def->length(),
cs, &not_used, &not_used2, &not_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 +3327,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 +3346,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, &not_used, &not_used2, &not_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++;
......
......@@ -2593,13 +2593,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table, Field *field,
DBUG_RETURN(1);
}
/* Check that we are not refering to any not yet initialized fields */
if (field)
{
if (func_expr->walk(&Item::check_field_expression_processor, 0, field))
DBUG_RETURN(1);
}
/*
Walk through the Item tree checking if all items are valid
to be part of the virtual column
......@@ -2777,6 +2770,14 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd,
DBUG_RETURN(vcol_info);
}
static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
{
bool res= vcol &&
vcol->expr_item->walk(&Item::check_field_expression_processor, 0,
field);
return res;
}
/*
Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
*/
......@@ -3041,22 +3042,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
}
field->default_value= vcol;
if (is_create_table && !vcol->flags)
{
enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values
my_ptrdiff_t off= share->default_values - outparam->record[0];
field->move_field_offset(off);
int res= vcol->expr_item->save_in_field(field, 1);
field->move_field_offset(-off);
thd->count_cuted_fields= old_count_cuted_fields;
if (res != 0 && res != 3)
{
my_error(ER_INVALID_DEFAULT, MYF(0), field->field_name);
error= OPEN_FRM_CORRUPTED;
goto err;
}
}
*(dfield_ptr++)= *field_ptr;
}
else
......@@ -3068,6 +3053,19 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
*vfield_ptr= 0; // End marker
*dfield_ptr= 0; // End marker
/* Check that expressions aren't refering to not yet initialized fields */
for (field_ptr= outparam->field; *field_ptr; field_ptr++)
{
Field *field= *field_ptr;
if (check_vcol_forward_refs(field, field->vcol_info) ||
check_vcol_forward_refs(field, field->check_constraint) ||
check_vcol_forward_refs(field, field->default_value))
{
error= OPEN_FRM_CORRUPTED;
goto err;
}
}
/* Update to use trigger fields */
switch_defaults_to_nullable_trigger_fields(outparam);
......@@ -7316,7 +7314,8 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors)
{
if (!update_command)
{
if (field->default_value)
if (field->default_value &&
(field->default_value->flags || field->flags & BLOB_FLAG))
res|= (field->default_value->expr_item->save_in_field(field, 0) < 0);
else
res|= field->evaluate_insert_default_function();
......
......@@ -634,7 +634,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
if (add_expr_length(thd, &field->vcol_info, &expression_length))
DBUG_RETURN(1);
if (field->has_default_expression())
if (field->default_value && field->default_value->expr_str.length)
if (add_expr_length(thd, &field->default_value, &expression_length))
DBUG_RETURN(1);
if (add_expr_length(thd, &field->check_constraint, &expression_length))
......@@ -983,7 +983,7 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields,
if (field->vcol_info)
pack_expression(&buff, field->vcol_info, field_nr,
field->vcol_info->stored_in_db ? 1 : 0);
if (field->has_default_expression())
if (field->default_value && field->default_value->expr_str.length)
pack_expression(&buff, field->default_value, field_nr, 2);
if (field->check_constraint)
pack_expression(&buff, field->check_constraint, field_nr, 3);
......
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