diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2022-04-21 15:33:50 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2022-04-21 15:33:50 +0300 |
commit | 620c55e708b9ea94ef8ba309267a6f2f32ed8104 (patch) | |
tree | 434f8bbb36626d16e8438b82da1618bd8313f6a2 /sql/table.cc | |
parent | fdec842fd7f82b6ed9aec54f82ac50b5eea925b3 (diff) | |
parent | aec856073df12e95b68667587bfd8e469b60e7d4 (diff) | |
download | mariadb-git-620c55e708b9ea94ef8ba309267a6f2f32ed8104.tar.gz |
Merge 10.4 into 10.5
Diffstat (limited to 'sql/table.cc')
-rw-r--r-- | sql/table.cc | 275 |
1 files changed, 194 insertions, 81 deletions
diff --git a/sql/table.cc b/sql/table.cc index 0bc43dcb31d..e2b4b62d243 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -76,10 +76,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")}; @@ -1106,9 +1104,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 @@ -1134,6 +1129,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; @@ -1214,7 +1234,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); @@ -1234,7 +1254,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; @@ -1242,13 +1262,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; @@ -1300,7 +1320,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; @@ -1318,7 +1338,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; @@ -1340,16 +1360,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(); @@ -3504,21 +3519,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); } @@ -3531,36 +3546,131 @@ 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->is_fixed()); - DBUG_RETURN(fix_vcol_expr(thd, vcol)); + DBUG_ASSERT(!expr->is_fixed()); + return fix_expr(thd); } -/** invoke fix_session_vcol_expr for a vcol +bool Virtual_column_info::cleanup_session_expr() +{ + DBUG_ASSERT(need_refix()); + if (expr->walk(&Item::cleanup_excluding_fields_processor, 0, 0)) + return true; + return false; +} - @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_WRITE_ALLOW_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->is_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; } @@ -3585,28 +3695,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->is_fixed()) + /* NOTE: constants are fixed when constructed */ + if (expr->is_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); @@ -3618,12 +3725,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)) @@ -3638,14 +3745,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); } @@ -3683,7 +3790,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) { @@ -3722,7 +3829,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); @@ -3736,22 +3843,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) { @@ -3952,6 +4043,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; @@ -8604,7 +8696,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 */ @@ -8675,10 +8776,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)); @@ -8712,6 +8815,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); @@ -8752,6 +8860,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 */ |