summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2016-07-23 16:55:52 +0200
committerSergei Golubchik <serg@mariadb.org>2016-08-27 16:59:12 +0200
commit12d2c4fcd0bc3fbe74759e8285f2c93ad348e749 (patch)
treee8f0ec7fab2a8ec1365e10a5ddde62e7af2e885d /sql
parent4070d55735f1642e563b8d60fc2e9771f4963a3f (diff)
downloadmariadb-git-12d2c4fcd0bc3fbe74759e8285f2c93ad348e749.tar.gz
optimize constant default expressions
to be calculated at the CREATE TABLE time and stored in the default row image.
Diffstat (limited to 'sql')
-rw-r--r--sql/field.h6
-rw-r--r--sql/item.cc6
-rw-r--r--sql/sql_table.cc190
-rw-r--r--sql/table.cc47
-rw-r--r--sql/unireg.cc4
5 files changed, 139 insertions, 114 deletions
diff --git a/sql/field.h b/sql/field.h
index f3328d560b8..45d2c3a7f00 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -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 */
diff --git a/sql/item.cc b/sql/item.cc
index 2fb507cd720..420e0df71bd 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -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
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 3ca2783f773..364d8eda773 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -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++;
diff --git a/sql/table.cc b/sql/table.cc
index ca6a5a1b5a6..77736430fa3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -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 @@ end:
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();
diff --git a/sql/unireg.cc b/sql/unireg.cc
index ff46e03ab3e..add09411acb 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -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);