summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc273
1 files changed, 192 insertions, 81 deletions
diff --git a/sql/table.cc b/sql/table.cc
index f33dacc8d8c..b9f0e7596aa 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -88,10 +88,8 @@ struct extra2_fields
{ bzero((void*)this, sizeof(*this)); }
};
-static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
+static Virtual_column_info * unpack_vcol_info_from_frm(THD *,
TABLE *, String *, Virtual_column_info **, bool *);
-static bool check_vcol_forward_refs(Field *, Virtual_column_info *,
- bool check_constraint);
/* INFORMATION_SCHEMA name */
LEX_CSTRING INFORMATION_SCHEMA_NAME= {STRING_WITH_LEN("information_schema")};
@@ -1119,9 +1117,6 @@ static void mysql57_calculate_null_position(TABLE_SHARE *share,
}
}
-static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
- Virtual_column_info *vcol);
-
/** Parse TABLE_SHARE::vcol_defs
unpack_vcol_info_from_frm
@@ -1147,6 +1142,31 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
bool *error_reported, vcol_init_mode mode)
{
+ struct check_vcol_forward_refs
+ {
+ static bool check(Field *field, Virtual_column_info *vcol)
+ {
+ return vcol &&
+ vcol->expr->walk(&Item::check_field_expression_processor, 0, field);
+ }
+ static bool check_constraint(Field *field, Virtual_column_info *vcol)
+ {
+ uint32 flags= field->flags;
+ /* Check constraints can refer it itself */
+ field->flags|= NO_DEFAULT_VALUE_FLAG;
+ const bool res= check(field, vcol);
+ field->flags= flags;
+ return res;
+ }
+ static bool check(Field *field)
+ {
+ if (check(field, field->vcol_info) ||
+ check_constraint(field, field->check_constraint) ||
+ check(field, field->default_value))
+ return true;
+ return false;
+ }
+ };
CHARSET_INFO *save_character_set_client= thd->variables.character_set_client;
CHARSET_INFO *save_collation= thd->variables.collation_connection;
Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
@@ -1221,7 +1241,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
switch (type) {
case VCOL_GENERATED_VIRTUAL:
case VCOL_GENERATED_STORED:
- vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ vcol= unpack_vcol_info_from_frm(thd, table, &expr_str,
&((*field_ptr)->vcol_info), error_reported);
*(vfield_ptr++)= *field_ptr;
DBUG_ASSERT(table->map == 0);
@@ -1241,7 +1261,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
table->map= 0;
break;
case VCOL_DEFAULT:
- vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ vcol= unpack_vcol_info_from_frm(thd, table, &expr_str,
&((*field_ptr)->default_value),
error_reported);
*(dfield_ptr++)= *field_ptr;
@@ -1249,13 +1269,13 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
table->s->non_determinstic_insert= true;
break;
case VCOL_CHECK_FIELD:
- vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ vcol= unpack_vcol_info_from_frm(thd, table, &expr_str,
&((*field_ptr)->check_constraint),
error_reported);
*check_constraint_ptr++= (*field_ptr)->check_constraint;
break;
case VCOL_CHECK_TABLE:
- vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ vcol= unpack_vcol_info_from_frm(thd, table, &expr_str,
check_constraint_ptr, error_reported);
check_constraint_ptr++;
break;
@@ -1307,7 +1327,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
field->vcol_info= v;
field->vcol_info->expr= hash_item;
field->vcol_info->set_vcol_type(VCOL_USING_HASH);
- if (fix_and_check_vcol_expr(thd, table, v))
+ if (v->fix_and_check_expr(thd, table))
goto end;
key->user_defined_key_parts= key->ext_key_parts= key->usable_key_parts= 1;
key->key_part+= parts;
@@ -1325,7 +1345,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
expr_str.append(STRING_WITH_LEN("current_timestamp("));
expr_str.append_ulonglong(field->decimals());
expr_str.append(')');
- vcol= unpack_vcol_info_from_frm(thd, mem_root, table, &expr_str,
+ vcol= unpack_vcol_info_from_frm(thd, table, &expr_str,
&((*field_ptr)->default_value),
error_reported);
*(dfield_ptr++)= *field_ptr;
@@ -1347,16 +1367,11 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
/* Check that expressions aren't referring to not yet initialized fields */
for (field_ptr= table->field; *field_ptr; field_ptr++)
- {
- Field *field= *field_ptr;
- if (check_vcol_forward_refs(field, field->vcol_info, 0) ||
- check_vcol_forward_refs(field, field->check_constraint, 1) ||
- check_vcol_forward_refs(field, field->default_value, 0))
+ if (check_vcol_forward_refs::check(*field_ptr))
{
*error_reported= true;
goto end;
}
- }
table->find_constraint_correlated_indexes();
@@ -3594,21 +3609,21 @@ void TABLE_SHARE::free_frm_image(const uchar *frm)
}
-static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
+bool Virtual_column_info::fix_expr(THD *thd)
{
DBUG_ENTER("fix_vcol_expr");
const enum enum_column_usage saved_column_usage= thd->column_usage;
thd->column_usage= COLUMNS_WRITE;
- int error= vcol->expr->fix_fields(thd, &vcol->expr);
+ int error= expr->fix_fields(thd, &expr);
thd->column_usage= saved_column_usage;
if (unlikely(error))
{
StringBuffer<MAX_FIELD_WIDTH> str;
- vcol->print(&str);
+ print(&str);
my_error(ER_ERROR_EVALUATING_EXPRESSION, MYF(0), str.c_ptr_safe());
DBUG_RETURN(1);
}
@@ -3621,36 +3636,129 @@ static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
@note this is done for all vcols for INSERT/UPDATE/DELETE,
and only as needed for SELECTs.
*/
-bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol)
+bool Virtual_column_info::fix_session_expr(THD *thd)
{
- DBUG_ENTER("fix_session_vcol_expr");
- if (!(vcol->flags & (VCOL_TIME_FUNC|VCOL_SESSION_FUNC)))
- DBUG_RETURN(0);
+ if (!need_refix())
+ return false;
- vcol->expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0);
- DBUG_ASSERT(!vcol->expr->fixed());
- DBUG_RETURN(fix_vcol_expr(thd, vcol));
+ DBUG_ASSERT(!expr->fixed());
+ return fix_expr(thd);
}
-/** invoke fix_session_vcol_expr for a vcol
+bool Virtual_column_info::cleanup_session_expr()
+{
+ DBUG_ASSERT(need_refix());
+ return expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0);
+}
- @note this is called for generated column or a DEFAULT expression from
- their corresponding fix_fields on SELECT.
-*/
-bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
- Virtual_column_info *vcol)
+
+
+class Vcol_expr_context
{
- DBUG_ENTER("fix_session_vcol_expr_for_read");
- TABLE_LIST *tl= field->table->pos_in_table_list;
- if (!tl || tl->lock_type >= TL_FIRST_WRITE)
- DBUG_RETURN(0);
- Security_context *save_security_ctx= thd->security_ctx;
- if (tl->security_ctx)
+ bool inited;
+ THD *thd;
+ TABLE *table;
+ LEX *old_lex;
+ LEX lex;
+ table_map old_map;
+ Security_context *save_security_ctx;
+ sql_mode_t save_sql_mode;
+
+public:
+ Vcol_expr_context(THD *_thd, TABLE *_table) :
+ inited(false),
+ thd(_thd),
+ table(_table),
+ old_lex(thd->lex),
+ old_map(table->map),
+ save_security_ctx(thd->security_ctx),
+ save_sql_mode(thd->variables.sql_mode) {}
+ bool init();
+
+ ~Vcol_expr_context();
+};
+
+
+bool Vcol_expr_context::init()
+{
+ /*
+ As this is vcol expression we must narrow down name resolution to
+ single table.
+ */
+ if (init_lex_with_single_table(thd, table, &lex))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ table->map= old_map;
+ return true;
+ }
+
+ lex.sql_command= old_lex->sql_command;
+ thd->variables.sql_mode= 0;
+
+ TABLE_LIST const *tl= table->pos_in_table_list;
+ DBUG_ASSERT(table->pos_in_table_list);
+
+ if (table->pos_in_table_list->security_ctx)
thd->security_ctx= tl->security_ctx;
- bool res= fix_session_vcol_expr(thd, vcol);
+
+ inited= true;
+ return false;
+}
+
+Vcol_expr_context::~Vcol_expr_context()
+{
+ if (!inited)
+ return;
+ end_lex_with_single_table(thd, table, old_lex);
+ table->map= old_map;
thd->security_ctx= save_security_ctx;
- DBUG_RETURN(res);
+ thd->variables.sql_mode= save_sql_mode;
+}
+
+
+bool TABLE::vcol_fix_expr(THD *thd)
+{
+ if (pos_in_table_list->placeholder() || vcol_refix_list.is_empty())
+ return false;
+
+ if (!thd->stmt_arena->is_conventional() &&
+ vcol_refix_list.head()->expr->fixed())
+ {
+ /* NOTE: Under trigger we already have fixed expressions */
+ return false;
+ }
+
+ Vcol_expr_context expr_ctx(thd, this);
+ if (expr_ctx.init())
+ return true;
+
+ List_iterator_fast<Virtual_column_info> it(vcol_refix_list);
+ while (Virtual_column_info *vcol= it++)
+ if (vcol->fix_session_expr(thd))
+ goto error;
+
+ return false;
+
+error:
+ DBUG_ASSERT(thd->get_stmt_da()->is_error());
+ return true;
+}
+
+
+bool TABLE::vcol_cleanup_expr(THD *thd)
+{
+ if (vcol_refix_list.is_empty())
+ return false;
+
+ List_iterator<Virtual_column_info> it(vcol_refix_list);
+ bool result= false;
+
+ while (Virtual_column_info *vcol= it++)
+ result|= vcol->cleanup_session_expr();
+
+ DBUG_ASSERT(!result || thd->get_stmt_da()->is_error());
+ return result;
}
@@ -3675,28 +3783,25 @@ bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
FALSE Otherwise
*/
-static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
- Virtual_column_info *vcol)
+bool Virtual_column_info::fix_and_check_expr(THD *thd, TABLE *table)
{
- Item* func_expr= vcol->expr;
- DBUG_ENTER("fix_and_check_vcol_expr");
- DBUG_PRINT("info", ("vcol: %p", vcol));
- DBUG_ASSERT(func_expr);
+ DBUG_ENTER("Virtual_column_info::fix_and_check_expr");
+ DBUG_PRINT("info", ("vcol: %p", this));
+ DBUG_ASSERT(expr);
- if (func_expr->fixed())
+ /* NOTE: constants are fixed when constructed */
+ if (expr->fixed())
DBUG_RETURN(0); // nothing to do
- if (fix_vcol_expr(thd, vcol))
+ if (fix_expr(thd))
DBUG_RETURN(1);
- if (vcol->flags)
+ if (flags)
DBUG_RETURN(0); // already checked, no need to do it again
- /* fix_fields could've changed the expression */
- func_expr= vcol->expr;
/* this was checked in check_expression(), but the frm could be mangled... */
- if (unlikely(func_expr->result_type() == ROW_RESULT))
+ if (unlikely(expr->result_type() == ROW_RESULT))
{
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
DBUG_RETURN(1);
@@ -3708,12 +3813,12 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
*/
Item::vcol_func_processor_result res;
- int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
+ int error= expr->walk(&Item::check_vcol_func_processor, 0, &res);
if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE)))
{
// this can only happen if the frm was corrupted
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name,
- vcol->get_vcol_type_name(), vcol->name.str);
+ get_vcol_type_name(), name.str);
DBUG_RETURN(1);
}
else if (unlikely(res.errors & VCOL_AUTO_INC))
@@ -3728,14 +3833,14 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
*/
myf warn= table->s->frm_version < FRM_VER_EXPRESSSIONS ? ME_WARNING : 0;
my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(warn),
- "AUTO_INCREMENT", vcol->get_vcol_type_name(), res.name);
+ "AUTO_INCREMENT", get_vcol_type_name(), res.name);
if (!warn)
DBUG_RETURN(1);
}
- vcol->flags= res.errors;
+ flags= res.errors;
- if (vcol->flags & VCOL_SESSION_FUNC)
- table->s->vcols_need_refixing= true;
+ if (!table->s->tmp_table && need_refix())
+ table->vcol_refix_list.push_back(this, &table->mem_root);
DBUG_RETURN(0);
}
@@ -3773,7 +3878,7 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
*/
static Virtual_column_info *
-unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table,
+unpack_vcol_info_from_frm(THD *thd, TABLE *table,
String *expr_str, Virtual_column_info **vcol_ptr,
bool *error_reported)
{
@@ -3812,7 +3917,7 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table,
vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
vcol_storage.vcol_info->name= vcol->name;
vcol_storage.vcol_info->utf8= vcol->utf8;
- if (!fix_and_check_vcol_expr(thd, table, vcol_storage.vcol_info))
+ if (!vcol_storage.vcol_info->fix_and_check_expr(thd, table))
{
*vcol_ptr= vcol_info= vcol_storage.vcol_info; // Expression ok
DBUG_ASSERT(vcol_info->expr);
@@ -3826,22 +3931,6 @@ end:
DBUG_RETURN(vcol_info);
}
-static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol,
- bool check_constraint)
-{
- bool res;
- uint32 flags= field->flags;
- if (check_constraint)
- {
- /* Check constraints can refer it itself */
- field->flags|= NO_DEFAULT_VALUE_FLAG;
- }
- res= (vcol &&
- vcol->expr->walk(&Item::check_field_expression_processor, 0, field));
- field->flags= flags;
- return res;
-}
-
#ifndef DBUG_OFF
static void print_long_unique_table(TABLE *table)
{
@@ -4042,6 +4131,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
outparam->alias.set(tmp_alias, alias->length, table_alias_charset);
+ outparam->vcol_refix_list.empty();
/* Allocate handler */
outparam->file= 0;
@@ -8688,7 +8778,16 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
if (h->keyread_enabled())
DBUG_RETURN(0);
-
+ /*
+ TODO: this imposes memory leak until table flush when save_in_field()
+ does expr_arena allocation. F.ex. case in
+ gcol.gcol_supported_sql_funcs_innodb (see CONVERT_TZ):
+
+ create table t1 (
+ a datetime, b datetime generated always as
+ (convert_tz(a, 'MET', 'UTC')) virtual);
+ insert into t1 values ('2008-08-31', default);
+ */
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
/* When reading or deleting row, ignore errors from virtual columns */
@@ -8759,10 +8858,12 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
if (update)
{
- int field_error __attribute__((unused)) = 0;
/* Compute the actual value of the virtual fields */
DBUG_FIX_WRITE_SET(vf);
- field_error= vcol_info->expr->save_in_field(vf, 0);
+# ifndef DBUG_OFF
+ int field_error=
+# endif
+ vcol_info->expr->save_in_field(vf, 0);
DBUG_RESTORE_WRITE_SET(vf);
DBUG_PRINT("info", ("field '%s' - updated error: %d",
vf->field_name.str, field_error));
@@ -8796,6 +8897,11 @@ int TABLE::update_virtual_field(Field *vf)
Query_arena backup_arena;
Counting_error_handler count_errors;
in_use->push_internal_handler(&count_errors);
+ /*
+ TODO: this may impose memory leak until table flush.
+ See comment in
+ TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
+ */
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
bitmap_clear_all(&tmp_set);
vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set);
@@ -8836,6 +8942,11 @@ int TABLE::update_default_fields(bool ignore_errors)
DBUG_ENTER("TABLE::update_default_fields");
DBUG_ASSERT(default_field);
+ /*
+ TODO: this may impose memory leak until table flush.
+ See comment in
+ TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
+ */
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
/* Iterate over fields with default functions in the table */