summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc1301
1 files changed, 1007 insertions, 294 deletions
diff --git a/sql/table.cc b/sql/table.cc
index 1004f583448..cf5a95e0929 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
- Copyright (c) 2008, 2018, MariaDB
+ Copyright (c) 2008, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
/* Some general useful functions */
-#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "table.h"
#include "key.h" // find_ref_key
@@ -42,6 +42,8 @@
#include "sql_view.h"
#include "rpl_filter.h"
#include "sql_cte.h"
+#include "ha_sequence.h"
+#include "sql_show.h"
/* For MySQL 5.7 virtual fields */
#define MYSQL57_GENERATED_FIELD 128
@@ -60,28 +62,34 @@ public:
static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
TABLE *, String *, Virtual_column_info **, bool *);
-static bool check_vcol_forward_refs(Field *, Virtual_column_info *);
+static bool check_vcol_forward_refs(Field *, Virtual_column_info *,
+ bool check_constraint);
/* INFORMATION_SCHEMA name */
-LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
+LEX_CSTRING INFORMATION_SCHEMA_NAME= {STRING_WITH_LEN("information_schema")};
/* PERFORMANCE_SCHEMA name */
-LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")};
+LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME= {STRING_WITH_LEN("performance_schema")};
/* MYSQL_SCHEMA name */
-LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
+LEX_CSTRING MYSQL_SCHEMA_NAME= {STRING_WITH_LEN("mysql")};
/* GENERAL_LOG name */
-LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")};
+LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")};
/* SLOW_LOG name */
-LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
+LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")};
+
+LEX_CSTRING TRANSACTION_REG_NAME= {STRING_WITH_LEN("transaction_registry")};
+LEX_CSTRING MYSQL_USER_NAME= {STRING_WITH_LEN("user")};
+LEX_CSTRING MYSQL_DB_NAME= {STRING_WITH_LEN("db")};
+LEX_CSTRING MYSQL_PROC_NAME= {STRING_WITH_LEN("proc")};
/*
Keyword added as a prefix when parsing the defining expression for a
virtual column read from the column definition saved in the frm file
*/
-static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
+static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
static int64 last_table_id;
@@ -94,7 +102,7 @@ static bool fix_type_pointers(const char ***typelib_value_names,
static uint find_field(Field **fields, uchar *record, uint start, uint length);
-inline bool is_system_table_name(const char *name, uint length);
+inline bool is_system_table_name(const char *name, size_t length);
/**************************************************************************
Object_creation_ctx implementation.
@@ -173,8 +181,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_NO_CREATION_CTX,
ER_THD(thd, ER_VIEW_NO_CREATION_CTX),
- (const char *) view->db,
- (const char *) view->table_name);
+ view->db.str,
+ view->table_name.str);
ctx->m_client_cs= system_charset_info;
ctx->m_connection_cl= system_charset_info;
@@ -199,16 +207,16 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
{
sql_print_warning("View '%s'.'%s': there is unknown charset/collation "
"names (client: '%s'; connection: '%s').",
- (const char *) view->db,
- (const char *) view->table_name,
+ view->db.str,
+ view->table_name.str,
(const char *) view->view_client_cs_name.str,
(const char *) view->view_connection_cl_name.str);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_INVALID_CREATION_CTX,
ER_THD(thd, ER_VIEW_INVALID_CREATION_CTX),
- (const char *) view->db,
- (const char *) view->table_name);
+ view->db.str,
+ view->table_name.str);
}
return ctx;
@@ -221,8 +229,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
static uchar *get_field_name(Field **buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
- *length= (uint) strlen((*buff)->field_name);
- return (uchar*) (*buff)->field_name;
+ *length= (uint) (*buff)->field_name.length;
+ return (uchar*) (*buff)->field_name.str;
}
@@ -230,63 +238,50 @@ static uchar *get_field_name(Field **buff, size_t *length,
Returns pointer to '.frm' extension of the file name.
SYNOPSIS
- fn_rext()
+ fn_frm_ext()
name file name
DESCRIPTION
Checks file name part starting with the rightmost '.' character,
and returns it if it is equal to '.frm'.
- TODO
- It is a good idea to get rid of this function modifying the code
- to garantee that the functions presently calling fn_rext() always
- get arguments in the same format: either with '.frm' or without '.frm'.
-
RETURN VALUES
- Pointer to the '.frm' extension. If there is no extension,
- or extension is not '.frm', pointer at the end of file name.
+ Pointer to the '.frm' extension or NULL if not a .frm file
*/
-char *fn_rext(char *name)
+const char *fn_frm_ext(const char *name)
{
- char *res= strrchr(name, '.');
+ const char *res= strrchr(name, '.');
if (res && !strcmp(res, reg_ext))
return res;
- return name + strlen(name);
+ return 0;
}
-TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
+
+TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
+ const LEX_CSTRING *name)
{
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
- if (is_infoschema_db(db->str, db->length))
+ if (is_infoschema_db(db))
return TABLE_CATEGORY_INFORMATION;
- if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- PERFORMANCE_SCHEMA_DB_NAME.str,
- db->str) == 0))
+ if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db))
return TABLE_CATEGORY_PERFORMANCE;
- if ((db->length == MYSQL_SCHEMA_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- MYSQL_SCHEMA_NAME.str,
- db->str) == 0))
+ if (lex_string_eq(&MYSQL_SCHEMA_NAME, db))
{
if (is_system_table_name(name->str, name->length))
return TABLE_CATEGORY_SYSTEM;
- if ((name->length == GENERAL_LOG_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- GENERAL_LOG_NAME.str,
- name->str) == 0))
+ if (lex_string_eq(&GENERAL_LOG_NAME, name))
return TABLE_CATEGORY_LOG;
- if ((name->length == SLOW_LOG_NAME.length) &&
- (my_strcasecmp(system_charset_info,
- SLOW_LOG_NAME.str,
- name->str) == 0))
+ if (lex_string_eq(&SLOW_LOG_NAME, name))
+ return TABLE_CATEGORY_LOG;
+
+ if (lex_string_eq(&TRANSACTION_REG_NAME, name))
return TABLE_CATEGORY_LOG;
}
@@ -322,7 +317,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
path_length= build_table_filename(path, sizeof(path) - 1,
db, table_name, "", 0);
- init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&mem_root, "table_share", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (multi_alloc_root(&mem_root,
&share, sizeof(*share),
&key_buff, key_length,
@@ -335,15 +330,22 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
share->path.str= path_buff;
share->path.length= path_length;
- strmov(share->path.str, path);
+ strmov(path_buff, path);
share->normalized_path.str= share->path.str;
share->normalized_path.length= path_length;
share->table_category= get_table_category(& share->db, & share->table_name);
share->open_errno= ENOENT;
- /* The following will be fixed in open_table_from_share */
- share->cached_row_logging_check= 1;
-
- init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ /* The following will be updated in open_table_from_share */
+ share->can_do_row_logging= 1;
+ if (share->table_category == TABLE_CATEGORY_LOG)
+ share->no_replicate= 1;
+ if (key_length > 6 &&
+ my_strnncoll(table_alias_charset, (const uchar*) key, 6,
+ (const uchar*) "mysql", 6) == 0)
+ share->not_usable_by_query_cache= 1;
+
+ init_sql_alloc(&share->stats_cb.mem_root, "share_stats",
+ TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
mysql_mutex_init(key_TABLE_SHARE_LOCK_share,
@@ -404,8 +406,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
This can't be MY_THREAD_SPECIFIC for slaves as they are freed
during cleanup() from Relay_log_info::close_temporary_tables()
*/
- init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
- MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
+ init_sql_alloc(&share->mem_root, "tmp_table_share", TABLE_ALLOC_BLOCK_SIZE,
+ 0, MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
share->table_category= TABLE_CATEGORY_TEMPORARY;
share->tmp_table= INTERNAL_TMP_TABLE;
share->db.str= (char*) key;
@@ -418,8 +420,8 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
share->normalized_path.str= (char*) path;
share->path.length= share->normalized_path.length= strlen(path);
share->frm_version= FRM_VER_CURRENT;
-
- share->cached_row_logging_check= 0; // No row logging
+ share->not_usable_by_query_cache= 1;
+ share->can_do_row_logging= 0; // No row logging
/*
table_map_id is also used for MERGE tables to suppress repeated
@@ -450,6 +452,7 @@ void TABLE_SHARE::destroy()
}
delete_stat_values_for_table_share(this);
+ delete sequence;
free_root(&stats_cb.mem_root, MYF(0));
/* The mutexes are initialized only for shares that are part of the TDC */
@@ -520,7 +523,7 @@ void free_table_share(TABLE_SHARE *share)
and should not contain user tables.
*/
-inline bool is_system_table_name(const char *name, uint length)
+inline bool is_system_table_name(const char *name, size_t length)
{
CHARSET_INFO *ci= system_charset_info;
@@ -634,7 +637,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
share->is_view= 1;
if (flags & GTS_VIEW)
{
- LEX_STRING pathstr= { path, length };
+ LEX_CSTRING pathstr= { path, length };
/*
Create view file parser and hold it in TABLE_SHARE member
view_def.
@@ -682,7 +685,11 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
frmlen= read_length + sizeof(head);
share->init_from_binary_frm_image(thd, false, buf, frmlen);
- error_given= true; // init_from_binary_frm_image has already called my_error()
+ /*
+ Don't give any additional errors. If there would be a problem,
+ init_from_binary_frm_image would call my_error() itself.
+ */
+ error_given= true;
my_free(buf);
goto err_not_open;
@@ -691,7 +698,10 @@ err:
mysql_file_close(file, MYF(MY_WME));
err_not_open:
- if (share->error && !error_given)
+ /* Mark that table was created earlier and thus should have been logged */
+ share->table_creation_was_logged= 1;
+
+ if (unlikely(share->error && !error_given))
{
share->open_errno= my_errno;
open_table_error(share, share->error, share->open_errno);
@@ -1041,7 +1051,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
thd->stmt_arena= table->expr_arena;
thd->update_charset(&my_charset_utf8mb4_general_ci, table->s->table_charset);
expr_str.append(&parse_vcol_keyword);
- thd->variables.sql_mode &= ~MODE_NO_BACKSLASH_ESCAPES;
+ thd->variables.sql_mode &= ~(MODE_NO_BACKSLASH_ESCAPES | MODE_EMPTY_STRING_IS_NULL);
while (pos < end)
{
@@ -1154,9 +1164,9 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
for (field_ptr= table->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))
+ 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))
goto end;
}
@@ -1204,7 +1214,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos, *UNINIT_VAR(mysql57_vcol_null_pos);
const uchar *disk_buff, *strpos;
- ulong pos, record_offset;
+ ulong pos, record_offset;
ulong rec_buff_length;
handler *handler_file= 0;
KEY *keyinfo;
@@ -1215,15 +1225,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum legacy_db_type legacy_db_type;
my_bitmap_map *bitmaps;
bool null_bits_are_used;
- uint vcol_screen_length, UNINIT_VAR(options_len);
+ uint vcol_screen_length;
+ size_t UNINIT_VAR(options_len);
uchar *vcol_screen_pos;
const uchar *options= 0;
- uint UNINIT_VAR(gis_options_len);
+ size_t UNINIT_VAR(gis_options_len);
const uchar *gis_options= 0;
KEY first_keyinfo;
uint len;
uint ext_key_parts= 0;
plugin_ref se_plugin= 0;
+ const uchar *system_period= 0;
+ bool vers_can_native= false;
+ const uchar *extra2_field_flags= 0;
+ size_t extra2_field_flags_length= 0;
+
MEM_ROOT *old_root= thd->mem_root;
Virtual_column_info **table_check_constraints;
DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
@@ -1259,7 +1275,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (*extra2 != '/') // old frm had '/' there
{
const uchar *e2end= extra2 + len;
- while (extra2 + 3 < e2end)
+ while (extra2 + 3 <= e2end)
{
uchar type= *extra2++;
size_t length= *extra2++;
@@ -1300,7 +1316,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
case EXTRA2_DEFAULT_PART_ENGINE:
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
- LEX_STRING name= { (char*)extra2, length };
+ LEX_CSTRING name= { (char*)extra2, length };
share->default_part_plugin= ha_resolve_by_name(NULL, &name, false);
if (!share->default_part_plugin)
goto err;
@@ -1317,6 +1333,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
}
#endif /*HAVE_SPATIAL*/
break;
+ case EXTRA2_PERIOD_FOR_SYSTEM_TIME:
+ if (system_period || length != 2 * sizeof(uint16))
+ goto err;
+ system_period = extra2;
+ break;
+ case EXTRA2_FIELD_FLAGS:
+ if (extra2_field_flags)
+ goto err;
+ extra2_field_flags= extra2;
+ extra2_field_flags_length= length;
+ break;
default:
/* abort frm parsing if it's an unknown but important extra2 value */
if (type >= EXTRA2_ENGINE_IMPORTANT)
@@ -1356,6 +1383,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->db_create_options= db_create_options= uint2korr(frm_image+30);
share->db_options_in_use= share->db_create_options;
share->mysql_version= uint4korr(frm_image+51);
+ share->table_type= TABLE_TYPE_NORMAL;
share->null_field_first= 0;
if (!frm_image[32]) // New frm file in 3.23
{
@@ -1369,6 +1397,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX);
share->page_checksum= (ha_choice)
enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX);
+ if (((ha_choice) enum_value_with_check(thd, share, "sequence",
+ (frm_image[39] >> 4) & 3,
+ HA_CHOICE_MAX)) == HA_CHOICE_YES)
+ {
+ share->table_type= TABLE_TYPE_SEQUENCE;
+ share->sequence= new (&share->mem_root) SEQUENCE();
+ share->non_determinstic_insert= true;
+ }
share->row_type= (enum row_type)
enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX);
@@ -1455,7 +1491,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
- LEX_STRING name;
+ LEX_CSTRING name;
name.str= (char*) next_chunk + 2;
name.length= str_db_type_length;
@@ -1502,7 +1538,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
else if (!tmp_plugin)
{
/* purecov: begin inspected */
- name.str[name.length]=0;
+ ((char*) name.str)[name.length]=0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
goto err;
/* purecov: end */
@@ -1553,7 +1589,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
if (keyinfo->flags & HA_USES_PARSER)
{
- LEX_STRING parser_name;
+ LEX_CSTRING parser_name;
if (next_chunk >= buff_end)
{
DBUG_PRINT("error",
@@ -1620,14 +1656,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length)))
goto err; /* purecov: inspected */
- MEM_NOACCESS(record, rec_buff_length);
- MEM_UNDEFINED(record, share->reclength);
+ /* Mark bytes after record as not accessable to catch overrun bugs */
+ MEM_NOACCESS(record + share->reclength, rec_buff_length - share->reclength);
share->default_values= record;
memcpy(record, frm_image + record_offset, share->reclength);
disk_buff= frm_image + pos + FRM_FORMINFO_SIZE;
-
share->fields= uint2korr(forminfo+258);
+ if (extra2_field_flags && extra2_field_flags_length != share->fields)
+ goto err;
pos= uint2korr(forminfo+260); /* Length of all screens */
n_length= uint2korr(forminfo+268);
interval_count= uint2korr(forminfo+270);
@@ -1638,6 +1675,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
vcol_screen_length= uint2korr(forminfo+286);
share->virtual_fields= share->default_expressions=
share->field_check_constraints= share->default_fields= 0;
+ share->visible_fields= 0;
share->stored_fields= share->fields;
if (forminfo[46] != (uchar)255)
{
@@ -1770,6 +1808,28 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
strpos, vcol_screen_pos);
}
+ /* Set system versioning information. */
+ if (system_period == NULL)
+ {
+ versioned= VERS_UNDEFINED;
+ row_start_field= 0;
+ row_end_field= 0;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Setting system versioning informations"));
+ uint16 row_start= uint2korr(system_period);
+ uint16 row_end= uint2korr(system_period + sizeof(uint16));
+ if (row_start >= share->fields || row_end >= share->fields)
+ goto err;
+ DBUG_PRINT("info", ("Columns with system versioning: [%d, %d]", row_start, row_end));
+ versioned= VERS_TIMESTAMP;
+ vers_can_native= handler_file->vers_can_native(thd);
+ row_start_field= row_start;
+ row_end_field= row_end;
+ status_var_increment(thd->status_var.feature_system_versioning);
+ } // if (system_period == NULL)
+
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
uint pack_flag, interval_nr, unireg_type, recpos, field_length;
@@ -1778,10 +1838,13 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
enum_field_types field_type;
CHARSET_INFO *charset=NULL;
Field::geometry_type geom_type= Field::GEOM_GEOMETRY;
- LEX_STRING comment;
+ LEX_CSTRING comment;
+ LEX_CSTRING name;
Virtual_column_info *vcol_info= 0;
uint gis_length, gis_decimals, srid= 0;
Field::utype unireg_check;
+ const Type_handler *handler;
+ uint32 flags= 0;
if (new_frm_ver >= 3)
{
@@ -1824,7 +1887,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
char tmp[10];
if (!csname || csname[0] =='?')
{
- my_snprintf(tmp, sizeof(tmp), "#%d", cs_new);
+ my_snprintf(tmp, sizeof(tmp), "#%u", cs_new);
csname= tmp;
}
my_printf_error(ER_UNKNOWN_COLLATION,
@@ -1993,14 +2056,47 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
swap_variables(uint, null_bit_pos, mysql57_vcol_null_bit_pos);
}
+ if (versioned)
+ {
+ if (i == row_start_field)
+ flags|= VERS_SYS_START_FLAG;
+ else if (i == row_end_field)
+ flags|= VERS_SYS_END_FLAG;
+
+ if (flags & VERS_SYSTEM_FIELD)
+ {
+ switch (field_type)
+ {
+ case MYSQL_TYPE_TIMESTAMP2:
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ if (vers_can_native)
+ {
+ versioned= VERS_TRX_ID;
+ break;
+ }
+ /* Fallthrough */
+ default:
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), fieldnames.type_names[i],
+ versioned == VERS_TIMESTAMP ? "TIMESTAMP(6)" : "BIGINT(20) UNSIGNED",
+ table_name.str);
+ goto err;
+ }
+ }
+ }
+
/* Convert pre-10.2.2 timestamps to use Field::default_value */
unireg_check= (Field::utype) MTYP_TYPENR(unireg_type);
+ name.str= fieldnames.type_names[i];
+ name.length= strlen(name.str);
+ if (!(handler= Type_handler::get_handler_by_real_type(field_type)))
+ goto err; // Not supported field type
*field_ptr= reg_field=
make_field(share, &share->mem_root, record+recpos, (uint32) field_length,
- null_pos, null_bit_pos, pack_flag, field_type, charset,
+ null_pos, null_bit_pos, pack_flag, handler, charset,
geom_type, srid, unireg_check,
(interval_nr ? share->intervals+interval_nr-1 : NULL),
- share->fieldnames.type_names[i]);
+ &name, flags);
if (!reg_field) // Not supported field type
goto err;
@@ -2016,6 +2112,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
reg_field->field_index= i;
reg_field->comment=comment;
reg_field->vcol_info= vcol_info;
+ reg_field->flags|= flags;
+ if (extra2_field_flags)
+ {
+ uchar flags= *extra2_field_flags++;
+ if (flags & VERS_OPTIMIZED_UPDATE)
+ reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG;
+
+ reg_field->invisible= f_visibility(flags);
+ }
+ if (reg_field->invisible == INVISIBLE_USER)
+ status_var_increment(thd->status_var.feature_invisible_columns);
+ if (!reg_field->invisible)
+ share->visible_fields++;
if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag))
{
null_bits_are_used= 1;
@@ -2033,8 +2142,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (vcol_info)
{
- vcol_info->name.str= const_cast<char*>(reg_field->field_name);
- vcol_info->name.length = strlen(reg_field->field_name);
+ vcol_info->name= reg_field->field_name;
if (mysql57_null_bits && !vcol_info->stored_in_db)
{
/* MySQL 5.7 has null bits last */
@@ -2152,18 +2260,18 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
for (uint key=0 ; key < keys ; key++,keyinfo++)
{
uint usable_parts= 0;
- keyinfo->name=(char*) share->keynames.type_names[key];
- keyinfo->name_length= strlen(keyinfo->name);
+ keyinfo->name.str= share->keynames.type_names[key];
+ keyinfo->name.length= strlen(keyinfo->name.str);
keyinfo->cache_name=
(uchar*) alloc_root(&share->mem_root,
share->table_cache_key.length+
- keyinfo->name_length + 1);
+ keyinfo->name.length + 1);
if (keyinfo->cache_name) // If not out of memory
{
uchar *pos= keyinfo->cache_name;
memcpy(pos, share->table_cache_key.str, share->table_cache_key.length);
- memcpy(pos + share->table_cache_key.length, keyinfo->name,
- keyinfo->name_length+1);
+ memcpy(pos + share->table_cache_key.length, keyinfo->name.str,
+ keyinfo->name.length+1);
}
if (ext_key_parts > share->key_parts && key)
@@ -2266,6 +2374,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
+ if (field->invisible > INVISIBLE_USER && !field->vers_sys_field())
+ keyinfo->flags |= HA_INVISIBLE_KEY;
if (field->null_ptr)
{
key_part->null_offset=(uint) ((uchar*) field->null_ptr -
@@ -2378,7 +2488,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (!(key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
HA_BIT_PART)) &&
key_part->type != HA_KEYTYPE_FLOAT &&
- key_part->type == HA_KEYTYPE_DOUBLE)
+ key_part->type != HA_KEYTYPE_DOUBLE)
key_part->key_part_flag|= HA_CAN_MEMCMP;
}
keyinfo->usable_key_parts= usable_parts; // Filesort
@@ -2465,15 +2575,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE;
vcol_info->set_vcol_type((enum_vcol_info_type) type);
- vcol_info->name.length= name_length;
if (name_length)
+ {
vcol_info->name.str= strmake_root(&share->mem_root,
(char*)vcol_screen_pos, name_length);
- else
- {
- vcol_info->name.str= const_cast<char*>(reg_field->field_name);
- vcol_info->name.length = strlen(reg_field->field_name);
+ vcol_info->name.length= name_length;
}
+ else
+ vcol_info->name= reg_field->field_name;
vcol_screen_pos+= name_length + expr_length;
switch (type) {
@@ -2597,19 +2706,21 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
bitmap_clear_all(share->check_set);
}
- delete handler_file;
#ifndef DBUG_OFF
if (use_hash)
(void) my_hash_check(&share->name_hash);
#endif
share->db_plugin= se_plugin;
+ delete handler_file;
+
share->error= OPEN_FRM_OK;
thd->status_var.opened_shares++;
thd->mem_root= old_root;
DBUG_RETURN(0);
- err:
+err:
+ share->db_plugin= NULL;
share->error= OPEN_FRM_CORRUPTED;
share->open_errno= my_errno;
delete handler_file;
@@ -2631,7 +2742,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
HA_CREATE_INFO *create_info= &lex->create_info;
// ... not CREATE TABLE
- if (lex->sql_command != SQLCOM_CREATE_TABLE)
+ if (lex->sql_command != SQLCOM_CREATE_TABLE &&
+ lex->sql_command != SQLCOM_CREATE_SEQUENCE)
return 1;
// ... create like
if (lex->create_info.like())
@@ -2671,6 +2783,9 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
(create_info->db_type && create_info->db_type != engine))
return 1;
}
+ // ... WITH SYSTEM VERSIONING
+ if (create_info->versioned())
+ return 1;
return 0;
}
@@ -2691,7 +2806,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
uint unused2;
handlerton *hton= plugin_hton(db_plugin);
LEX_CUSTRING frm= {0,0};
- LEX_STRING db_backup= { thd->db, thd->db_length };
+ LEX_CSTRING db_backup= thd->db;
DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string");
/*
@@ -2718,11 +2833,11 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
else
thd->set_n_backup_active_arena(arena, &backup);
- thd->reset_db(db.str, db.length);
+ thd->reset_db(&db);
lex_start(thd);
- if ((error= parse_sql(thd, & parser_state, NULL) ||
- sql_unusable_for_discovery(thd, hton, sql_copy)))
+ if (unlikely((error= parse_sql(thd, & parser_state, NULL) ||
+ sql_unusable_for_discovery(thd, hton, sql_copy))))
goto ret;
thd->lex->create_info.db_type= hton;
@@ -2734,7 +2849,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
thd->lex->create_info.tabledef_version= tabledef_version;
promote_first_timestamp_column(&thd->lex->alter_info.create_list);
- file= mysql_create_frm_image(thd, db.str, table_name.str,
+ file= mysql_create_frm_image(thd, &db, &table_name,
&thd->lex->create_info, &thd->lex->alter_info,
C_ORDINARY_CREATE, &unused1, &unused2, &frm);
error|= file == 0;
@@ -2750,21 +2865,22 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
ret:
my_free(const_cast<uchar*>(frm.str));
lex_end(thd->lex);
- thd->reset_db(db_backup.str, db_backup.length);
+ thd->reset_db(&db_backup);
thd->lex= old_lex;
if (arena)
thd->restore_active_arena(arena, &backup);
reenable_binlog(thd);
thd->variables.sql_mode= saved_mode;
thd->variables.character_set_client= old_cs;
- if (thd->is_error() || error)
+ if (unlikely(thd->is_error() || error))
{
thd->clear_error();
- my_error(ER_SQL_DISCOVER_ERROR, MYF(0),
- plugin_name(db_plugin)->str, db.str, table_name.str,
- sql_copy);
+ my_error(ER_SQL_DISCOVER_ERROR, MYF(0), hton_name(hton)->str,
+ db.str, table_name.str, sql_copy);
DBUG_RETURN(HA_ERR_GENERIC);
}
+ /* Treat the table as normal table from binary logging point of view */
+ table_creation_was_logged= 1;
DBUG_RETURN(0);
}
@@ -2805,12 +2921,12 @@ static bool fix_vcol_expr(THD *thd, Virtual_column_info *vcol)
{
DBUG_ENTER("fix_vcol_expr");
- const enum enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
- thd->mark_used_columns= MARK_COLUMNS_NONE;
+ 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);
- thd->mark_used_columns= save_mark_used_columns;
+ thd->column_usage= saved_column_usage;
if (unlikely(error))
{
@@ -2917,14 +3033,14 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
res.errors= 0;
int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
- if (error || (res.errors & VCOL_IMPOSSIBLE))
+ 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);
DBUG_RETURN(1);
}
- else if (res.errors & VCOL_AUTO_INC)
+ else if (unlikely(res.errors & VCOL_AUTO_INC))
{
/*
An auto_increment field may not be used in an expression for
@@ -3005,9 +3121,17 @@ unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table,
lex.last_field= &vcol_storage;
error= parse_sql(thd, &parser_state, NULL);
- if (error)
+ if (unlikely(error))
goto end;
+ if (lex.current_select->table_list.first[0].next_global)
+ {
+ /* We are using NEXT VALUE FOR sequence. Remember table name for open */
+ TABLE_LIST *sequence= lex.current_select->table_list.first[0].next_global;
+ sequence->next_global= table->internal_tables;
+ table->internal_tables= sequence;
+ }
+
vcol_storage.vcol_info->set_vcol_type(vcol->get_vcol_type());
vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db;
vcol_storage.vcol_info->name= vcol->name;
@@ -3026,11 +3150,19 @@ end:
DBUG_RETURN(vcol_info);
}
-static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
+static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol,
+ bool check_constraint)
{
- bool res= vcol &&
- vcol->expr->walk(&Item::check_field_expression_processor, 0,
- field);
+ 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;
}
@@ -3048,6 +3180,7 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
prgflag READ_ALL etc..
ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
outparam result table
+ partitions_to_open open only these partitions.
RETURN VALUES
0 ok
@@ -3060,13 +3193,12 @@ static bool check_vcol_forward_refs(Field *field, Virtual_column_info *vcol)
*/
enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
- const char *alias, uint db_stat, uint prgflag,
+ const LEX_CSTRING *alias, uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
- bool is_create_table)
+ bool is_create_table, List<String> *partitions_to_open)
{
enum open_frm_error error;
uint records, i, bitmap_size, bitmap_count;
- size_t tmp_length;
const char *tmp_alias;
bool error_reported= FALSE;
uchar *record, *bitmaps;
@@ -3093,17 +3225,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
error= OPEN_FRM_NEEDS_REBUILD;
goto err;
}
- init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(0));
/*
We have to store the original alias in mem_root as constraints and virtual
functions may store pointers to it
*/
- tmp_length= strlen(alias);
- if (!(tmp_alias= strmake_root(&outparam->mem_root, alias, tmp_length)))
+ if (!(tmp_alias= strmake_root(&outparam->mem_root, alias->str, alias->length)))
goto err;
- outparam->alias.set(tmp_alias, tmp_length, table_alias_charset);
+ outparam->alias.set(tmp_alias, alias->length, table_alias_charset);
outparam->quick_keys.init();
outparam->covering_keys.init();
outparam->intersect_keys.init();
@@ -3125,34 +3257,51 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
DBUG_ASSERT(!db_stat);
}
+ if (share->sequence && outparam->file)
+ {
+ ha_sequence *file;
+ /* SEQUENCE table. Create a sequence handler over the original handler */
+ if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share,
+ &outparam->mem_root)))
+ goto err;
+ file->register_original_handler(outparam->file);
+ outparam->file= file;
+ }
+
outparam->reginfo.lock_type= TL_UNLOCK;
outparam->current_lock= F_UNLCK;
records=0;
if ((db_stat & HA_OPEN_KEYFILE) || (prgflag & DELAYED_OPEN))
records=1;
- if (prgflag & (READ_ALL+EXTRA_RECORD))
+ if (prgflag & (READ_ALL + EXTRA_RECORD))
+ {
records++;
-
- if (!(record= (uchar*) alloc_root(&outparam->mem_root,
- share->rec_buff_length * records)))
- goto err; /* purecov: inspected */
- MEM_NOACCESS(record, share->rec_buff_length * records);
+ if (share->versioned)
+ records++;
+ }
if (records == 0)
{
/* We are probably in hard repair, and the buffers should not be used */
- outparam->record[0]= outparam->record[1]= share->default_values;
+ record= share->default_values;
}
else
{
- outparam->record[0]= record;
- if (records > 1)
- outparam->record[1]= record+ share->rec_buff_length;
- else
- outparam->record[1]= outparam->record[0]; // Safety
+ if (!(record= (uchar*) alloc_root(&outparam->mem_root,
+ share->rec_buff_length * records)))
+ goto err; /* purecov: inspected */
}
- MEM_UNDEFINED(outparam->record[0], share->reclength);
- MEM_UNDEFINED(outparam->record[1], share->reclength);
+
+ for (i= 0; i < 3;)
+ {
+ outparam->record[i]= record;
+ if (++i < records)
+ record+= share->rec_buff_length;
+ }
+ /* Mark bytes between records as not accessable to catch overrun bugs */
+ for (i= 0; i < records; i++)
+ MEM_NOACCESS(outparam->record[i] + share->reclength,
+ share->rec_buff_length - share->reclength);
if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->fields+1)*
@@ -3178,6 +3327,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
DEBUG_SYNC(thd, "TABLE_after_field_clone");
+ outparam->vers_write= share->versioned;
+
if (share->found_next_number_field)
outparam->found_next_number_field=
outparam->field[(uint) (share->found_next_number_field - share->field)];
@@ -3275,6 +3426,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
break;
}
#endif
+
if (parse_vcol_defs(thd, &outparam->mem_root, outparam,
&error_reported, mode))
{
@@ -3287,6 +3439,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
+ bool work_part_info_used;
if (share->partition_info_str_len && outparam->file)
{
/*
@@ -3307,7 +3460,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
thd->set_n_backup_active_arena(&part_func_arena, &backup_arena);
thd->stmt_arena= &part_func_arena;
bool tmp;
- bool work_part_info_used;
tmp= mysql_unpack_partition(thd, share->partition_info_str,
share->partition_info_str_len,
@@ -3421,14 +3573,15 @@ partititon_err:
int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str,
(db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
- ha_open_flags);
+ ha_open_flags, 0, partitions_to_open);
if (ha_err)
{
share->open_errno= ha_err;
/* Set a flag if the table is crashed and it can be auto. repaired */
share->crashed= (outparam->file->auto_repair(ha_err) &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
- outparam->file->print_error(ha_err, MYF(0));
+ if (!thd->is_error())
+ outparam->file->print_error(ha_err, MYF(0));
error_reported= TRUE;
if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
@@ -3462,24 +3615,20 @@ partititon_err:
}
}
- if (share->table_category == TABLE_CATEGORY_LOG)
- {
- outparam->no_replicate= TRUE;
- }
- else if (outparam->file)
+ if (db_stat)
{
+ /* Set some flags in share on first open of the table */
handler::Table_flags flags= outparam->file->ha_table_flags();
- outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE
- | HA_BINLOG_ROW_CAPABLE))
- || MY_TEST(flags & HA_HAS_OWN_BINLOGGING);
- }
- else
- {
- outparam->no_replicate= FALSE;
+ if (! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE |
+ HA_BINLOG_ROW_CAPABLE)) ||
+ MY_TEST(flags & HA_HAS_OWN_BINLOGGING))
+ share->no_replicate= TRUE;
+ if (outparam->file->table_cache_type() & HA_CACHE_TBL_NOCACHE)
+ share->not_usable_by_query_cache= TRUE;
}
- if (outparam->no_replicate || !binlog_filter->db_ok(outparam->s->db.str))
- outparam->s->cached_row_logging_check= 0; // No row based replication
+ if (share->no_replicate || !binlog_filter->db_ok(share->db.str))
+ share->can_do_row_logging= 0; // No row based replication
/* Increment the opened_tables counter, only when open flags set. */
if (db_stat)
@@ -3735,30 +3884,6 @@ fix_type_pointers(const char ***typelib_value_names,
} /* fix_type_pointers */
-TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings)
-{
- TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB));
- if (!result)
- return 0;
- result->count=strings.elements;
- result->name="";
- uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1);
- if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes)))
- return 0;
- result->type_lengths= (uint*) (result->type_names + result->count + 1);
- List_iterator<String> it(strings);
- String *tmp;
- for (uint i=0; (tmp=it++) ; i++)
- {
- result->type_names[i]= tmp->ptr();
- result->type_lengths[i]= tmp->length();
- }
- result->type_names[result->count]= 0; // End marker
- result->type_lengths[result->count]= 0;
- return result;
-}
-
-
/*
Search after a field with given start & length
If an exact field isn't found, return longest field with starts
@@ -3807,24 +3932,13 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length)
May fail with some multibyte charsets though.
*/
-void append_unescaped(String *res, const char *pos, uint length)
+void append_unescaped(String *res, const char *pos, size_t length)
{
const char *end= pos+length;
res->append('\'');
for (; pos != end ; pos++)
{
-#if defined(USE_MB) && MYSQL_VERSION_ID < 40100
- uint mblen;
- if (use_mb(default_charset_info) &&
- (mblen= my_ismbchar(default_charset_info, pos, end)))
- {
- res->append(pos, mblen);
- pos+= mblen;
- continue;
- }
-#endif
-
switch (*pos) {
case 0: /* Must be escaped for 'mysql' */
res->append('\\');
@@ -3858,7 +3972,7 @@ void append_unescaped(String *res, const char *pos, uint length)
void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
{
- ulong key_comment_total_bytes= 0;
+ size_t key_comment_total_bytes= 0;
uint i;
DBUG_ENTER("prepare_frm_header");
@@ -3868,7 +3982,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- uint key_length, tmp_key_length, tmp, csid;
+ size_t key_length, tmp_key_length, tmp, csid;
bzero((char*) fileinfo, FRM_HEADER_SIZE);
/* header */
fileinfo[0]=(uchar) 254;
@@ -3920,7 +4034,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
create_info->default_table_charset->number : 0);
fileinfo[38]= (uchar) csid;
fileinfo[39]= (uchar) ((uint) create_info->transactional |
- ((uint) create_info->page_checksum << 2));
+ ((uint) create_info->page_checksum << 2) |
+ ((create_info->sequence ? HA_CHOICE_YES : 0) << 4));
fileinfo[40]= (uchar) create_info->row_type;
/* Bytes 41-46 were for RAID support; now reused for other purposes */
fileinfo[41]= (uchar) (csid >> 8);
@@ -3958,6 +4073,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->transactional= share->transactional;
create_info->page_checksum= share->page_checksum;
create_info->option_list= share->option_list;
+ create_info->sequence= MY_TEST(share->sequence);
DBUG_VOID_RETURN;
}
@@ -4093,7 +4209,7 @@ bool ok_for_lower_case_names(const char *name)
bool check_db_name(LEX_STRING *org_name)
{
char *name= org_name->str;
- uint name_length= org_name->length;
+ size_t name_length= org_name->length;
bool check_for_path_chars;
if ((check_for_path_chars= check_mysql50_prefix(name)))
@@ -4130,7 +4246,6 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
size_t name_length= 0;
const char *end= name+length;
-
if (!check_for_path_chars &&
(check_for_path_chars= check_mysql50_prefix(name)))
{
@@ -4282,7 +4397,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
{
Field *field= table->field[i];
- if (strncmp(field->field_name, field_def->name.str,
+ if (strncmp(field->field_name.str, field_def->name.str,
field_def->name.length))
{
/*
@@ -4294,7 +4409,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
"expected column '%s' at position %d, found '%s'.",
table->s->db.str, table->alias.c_ptr(),
field_def->name.str, i,
- field->field_name);
+ field->field_name.str);
}
field->sql_type(sql_type);
/*
@@ -4400,7 +4515,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
}
- if (! error)
+ if (likely(! error))
table->s->table_field_def_cache= table_def;
end:
@@ -4628,10 +4743,10 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
if (thd->lex->need_correct_ident())
alias_name_used= my_strcasecmp(table_alias_charset,
s->table_name.str,
- tl->alias);
+ tl->alias.str);
/* Fix alias if table name changes. */
- if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias))
- alias.copy(tl->alias, strlen(tl->alias), alias.charset());
+ if (!alias.alloced_length() || strcmp(alias.c_ptr(), tl->alias.str))
+ alias.copy(tl->alias.str, tl->alias.length, alias.charset());
tablenr= thd->current_tablenr++;
used_fields= 0;
@@ -4649,7 +4764,9 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
+ vers_write= s->versioned;
quick_condition_rows=0;
+ no_cache= false;
initialize_quick_structures();
#ifdef HAVE_REPLICATION
/* used in RBR Triggers */
@@ -4725,10 +4842,13 @@ bool TABLE::fill_item_list(List<Item> *item_list) const
is the same as the number of columns in the table.
*/
-void TABLE::reset_item_list(List<Item> *item_list) const
+void TABLE::reset_item_list(List<Item> *item_list, uint skip) const
{
List_iterator_fast<Item> it(*item_list);
- for (Field **ptr= field; *ptr; ptr++)
+ Field **ptr= field;
+ for ( ; skip && *ptr; skip--)
+ ptr++;
+ for (; *ptr; ptr++)
{
Item_field *item_field= (Item_field*) it++;
DBUG_ASSERT(item_field != 0);
@@ -4744,7 +4864,7 @@ void TABLE::reset_item_list(List<Item> *item_list) const
buffer buffer for md5 writing
*/
-void TABLE_LIST::calc_md5(char *buffer)
+void TABLE_LIST::calc_md5(const char *buffer)
{
uchar digest[16];
compute_md5_hash(digest, select_stmt.str,
@@ -4782,7 +4902,7 @@ bool TABLE_LIST::create_field_translation(THD *thd)
bool res= FALSE;
DBUG_ENTER("TABLE_LIST::create_field_translation");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
- (alias ? alias : "<NULL>"),
+ (alias.str ? alias.str : "<NULL>"),
get_unit()));
if (thd->stmt_arena->is_conventional() ||
@@ -4810,16 +4930,20 @@ bool TABLE_LIST::create_field_translation(THD *thd)
*/
if (is_view() && get_unit()->prepared && !field_translation_updated)
{
+ field_translation_updated= TRUE;
+ if (static_cast<uint>(field_translation_end - field_translation) <
+ select->item_list.elements)
+ goto allocate;
while ((item= it++))
{
field_translation[field_count++].item= item;
}
- field_translation_updated= TRUE;
}
DBUG_RETURN(FALSE);
}
+allocate:
arena= thd->activate_stmt_arena_if_needed(&backup);
/* Create view fields translation table */
@@ -4835,8 +4959,9 @@ bool TABLE_LIST::create_field_translation(THD *thd)
while ((item= it++))
{
- DBUG_ASSERT(item->name && item->name[0]);
- transl[field_count].name= thd->strdup(item->name);
+ DBUG_ASSERT(item->name.str && item->name.str[0]);
+ transl[field_count].name.str= thd->strmake(item->name.str, item->name.length);
+ transl[field_count].name.length= item->name.length;
transl[field_count++].item= item;
}
field_translation= transl;
@@ -4928,10 +5053,8 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
{
if (where->fixed)
where->update_used_tables();
- if (!where->fixed && where->fix_fields(thd, &where))
- {
+ else if (where->fix_fields(thd, &where))
DBUG_RETURN(TRUE);
- }
/*
check that it is not VIEW in which we insert with INSERT SELECT
@@ -4961,12 +5084,12 @@ bool TABLE_LIST::prep_where(THD *thd, Item **conds,
}
if (tbl == 0)
{
- if (*conds && !(*conds)->fixed)
- res= (*conds)->fix_fields(thd, conds);
+ if (*conds)
+ res= (*conds)->fix_fields_if_needed_for_bool(thd, conds);
if (!res)
*conds= and_conds(thd, *conds, where->copy_andor_structure(thd));
- if (*conds && !(*conds)->fixed && !res)
- res= (*conds)->fix_fields(thd, conds);
+ if (*conds && !res)
+ res= (*conds)->fix_fields_if_needed_for_bool(thd, conds);
}
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -5026,7 +5149,7 @@ merge_on_conds(THD *thd, TABLE_LIST *table, bool is_cascaded)
DBUG_ENTER("merge_on_conds");
Item *cond= NULL;
- DBUG_PRINT("info", ("alias: %s", table->alias));
+ DBUG_PRINT("info", ("alias: %s", table->alias.str));
if (table->on_expr)
cond= table->on_expr->copy_andor_structure(thd);
if (!table->view)
@@ -5113,12 +5236,8 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
{
const char *save_where= thd->where;
thd->where= "check option";
- if ((!check_option->fixed &&
- check_option->fix_fields(thd, &check_option)) ||
- check_option->check_cols(1))
- {
+ if (check_option->fix_fields_if_needed_for_bool(thd, &check_option))
DBUG_RETURN(TRUE);
- }
thd->where= save_where;
}
DBUG_RETURN(FALSE);
@@ -5254,9 +5373,9 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
{
TABLE_LIST *main_view= top_table();
const char *name_db= (main_view->view ? main_view->view_db.str :
- main_view->db);
+ main_view->db.str);
const char *name_table= (main_view->view ? main_view->view_name.str :
- main_view->table_name);
+ main_view->table_name.str);
my_error(ER_VIEW_CHECK_FAILED, MYF(ignore_failure ? ME_JUST_WARNING : 0),
name_db, name_table);
return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
@@ -5279,6 +5398,8 @@ int TABLE::verify_constraints(bool ignore_failure)
if (check_constraints &&
!(in_use->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS))
{
+ if (versioned() && !vers_end_field()->is_max())
+ return VIEW_CHECK_OK;
for (Virtual_column_info **chk= check_constraints ; *chk ; chk++)
{
/*
@@ -5312,7 +5433,6 @@ int TABLE::verify_constraints(bool ignore_failure)
return(!in_use->is_error() ? VIEW_CHECK_OK : VIEW_CHECK_ERROR);
}
-
/*
Find table in underlying tables by mask and check that only this
table belong to given mask
@@ -5582,7 +5702,7 @@ void TABLE_LIST::register_want_access(ulong want_access)
bool TABLE_LIST::prepare_view_security_context(THD *thd)
{
DBUG_ENTER("TABLE_LIST::prepare_view_security_context");
- DBUG_PRINT("enter", ("table: %s", alias));
+ DBUG_PRINT("enter", ("table: %s", alias.str));
DBUG_ASSERT(!prelocking_placeholder && view);
if (view_suid)
@@ -5590,7 +5710,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd)
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
- definer.host.str, thd->db))
+ definer.host.str, thd->db.str))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -5655,7 +5775,7 @@ Security_context *TABLE_LIST::find_view_security_context(THD *thd)
if (upper_view)
{
DBUG_PRINT("info", ("Securety context of view %s will be used",
- upper_view->alias));
+ upper_view->alias.str));
sctx= upper_view->view_sctx;
DBUG_ASSERT(sctx);
}
@@ -5696,7 +5816,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
while ((tbl= tb++))
{
DBUG_ASSERT(tbl->referencing_view);
- char *local_db, *local_table_name;
+ const char *local_db, *local_table_name;
if (tbl->view)
{
local_db= tbl->view_db.str;
@@ -5704,8 +5824,8 @@ bool TABLE_LIST::prepare_security(THD *thd)
}
else
{
- local_db= tbl->db;
- local_table_name= tbl->table_name;
+ local_db= tbl->db.str;
+ local_table_name= tbl->table_name.str;
}
fill_effective_table_privileges(thd, &tbl->grant, local_db,
local_table_name);
@@ -5821,15 +5941,15 @@ Natural_join_column::Natural_join_column(Item_field *field_param,
}
-const char *Natural_join_column::name()
+LEX_CSTRING *Natural_join_column::name()
{
if (view_field)
{
DBUG_ASSERT(table_field == NULL);
- return view_field->name;
+ return &view_field->name;
}
- return table_field->field_name;
+ return &table_field->field_name;
}
@@ -5839,7 +5959,7 @@ Item *Natural_join_column::create_item(THD *thd)
{
DBUG_ASSERT(table_field == NULL);
return create_view_field(thd, table_ref, &view_field->item,
- view_field->name);
+ &view_field->name);
}
return table_field;
}
@@ -5856,30 +5976,29 @@ Field *Natural_join_column::field()
}
-const char *Natural_join_column::table_name()
+const char *Natural_join_column::safe_table_name()
{
DBUG_ASSERT(table_ref);
- return table_ref->alias;
+ return table_ref->alias.str ? table_ref->alias.str : "";
}
-const char *Natural_join_column::db_name()
+const char *Natural_join_column::safe_db_name()
{
if (view_field)
- return table_ref->view_db.str;
+ return table_ref->view_db.str ? table_ref->view_db.str : "";
/*
Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
- DBUG_ASSERT(!strcmp(table_ref->db,
- table_ref->table->s->db.str) ||
+ DBUG_ASSERT(!cmp(&table_ref->db,
+ &table_ref->table->s->db) ||
(table_ref->schema_table &&
- is_infoschema_db(table_ref->table->s->db.str,
- table_ref->table->s->db.length)) ||
- table_ref->is_materialized_derived());
- return table_ref->db;
+ is_infoschema_db(&table_ref->table->s->db)) ||
+ table_ref->is_materialized_derived());
+ return table_ref->db.str ? table_ref->db.str : "";
}
@@ -5908,9 +6027,9 @@ void Field_iterator_view::set(TABLE_LIST *table)
}
-const char *Field_iterator_table::name()
+LEX_CSTRING *Field_iterator_table::name()
{
- return (*ptr)->field_name;
+ return &(*ptr)->field_name;
}
@@ -5919,6 +6038,7 @@ Item *Field_iterator_table::create_item(THD *thd)
SELECT_LEX *select= thd->lex->current_select;
Item_field *item= new (thd->mem_root) Item_field(thd, &select->context, *ptr);
+ DBUG_ASSERT(strlen(item->name.str) == item->name.length);
if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
!thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS &&
select->join)
@@ -5931,19 +6051,19 @@ Item *Field_iterator_table::create_item(THD *thd)
}
-const char *Field_iterator_view::name()
+LEX_CSTRING *Field_iterator_view::name()
{
- return ptr->name;
+ return &ptr->name;
}
Item *Field_iterator_view::create_item(THD *thd)
{
- return create_view_field(thd, view, &ptr->item, ptr->name);
+ return create_view_field(thd, view, &ptr->item, &ptr->name);
}
Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
- const char *name)
+ LEX_CSTRING *name)
{
bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
Item *field= *field_ref;
@@ -5979,7 +6099,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
Name_resolution_context *context= view->view ? &view->view->select_lex.context :
&thd->lex->select_lex.context;
Item *item= (new (thd->mem_root)
- Item_direct_view_ref(thd, context, field_ref, view->alias,
+ Item_direct_view_ref(thd, context, field_ref, view->alias.str,
name, view));
if (!item)
return NULL;
@@ -6045,7 +6165,7 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->table->s->fields))));
field_it= &natural_join_it;
DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
- table_ref->alias));
+ table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
else if (table_ref->field_translation)
@@ -6053,7 +6173,7 @@ void Field_iterator_table_ref::set_field_iterator()
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
- table_ref->alias));
+ table_ref->alias.str));
}
/* This is a base table or stored view. */
else
@@ -6061,7 +6181,7 @@ void Field_iterator_table_ref::set_field_iterator()
DBUG_ASSERT(table_ref->table || table_ref->view);
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
- table_ref->alias));
+ table_ref->alias.str));
}
field_it->set(table_ref);
DBUG_VOID_RETURN;
@@ -6103,12 +6223,12 @@ const char *Field_iterator_table_ref::get_table_name()
if (table_ref->is_derived())
return table_ref->table->s->table_name.str;
else if (table_ref->is_natural_join)
- return natural_join_it.column_ref()->table_name();
+ return natural_join_it.column_ref()->safe_table_name();
- DBUG_ASSERT(!strcmp(table_ref->table_name,
+ DBUG_ASSERT(!strcmp(table_ref->table_name.str,
table_ref->table->s->table_name.str) ||
table_ref->schema_table);
- return table_ref->table_name;
+ return table_ref->table_name.str;
}
@@ -6117,19 +6237,18 @@ const char *Field_iterator_table_ref::get_db_name()
if (table_ref->view)
return table_ref->view_db.str;
else if (table_ref->is_natural_join)
- return natural_join_it.column_ref()->db_name();
+ return natural_join_it.column_ref()->safe_db_name();
/*
Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
- DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) ||
+ DBUG_ASSERT(!cmp(&table_ref->db, &table_ref->table->s->db) ||
(table_ref->schema_table &&
- is_infoschema_db(table_ref->table->s->db.str,
- table_ref->table->s->db.length)));
+ is_infoschema_db(&table_ref->table->s->db)));
- return table_ref->db;
+ return table_ref->db.str;
}
@@ -6480,9 +6599,12 @@ void TABLE::mark_columns_needed_for_delete()
need_signal= true;
}
}
- if (check_constraints)
+
+ if (s->versioned)
{
- mark_check_constraint_columns_for_read();
+ bitmap_set_bit(read_set, s->vers_start_field()->field_index);
+ bitmap_set_bit(read_set, s->vers_end_field()->field_index);
+ bitmap_set_bit(write_set, s->vers_end_field()->field_index);
need_signal= true;
}
@@ -6500,7 +6622,7 @@ void TABLE::mark_columns_needed_for_delete()
updated columns to be read.
If this is no the case, we do like in the delete case and mark
- if neeed, either the primary key column or all columns to be read.
+ if needed, either the primary key column or all columns to be read.
(see mark_columns_needed_for_delete() for details)
If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will
@@ -6528,7 +6650,7 @@ void TABLE::mark_columns_needed_for_update()
for (KEY *k= key_info; k < end; k++)
{
KEY_PART_INFO *kpend= k->key_part + k->ext_key_parts;
- bool any_written= false, all_read= true;
+ int any_written= 0, all_read= 1;
for (KEY_PART_INFO *kp= k->key_part; kp < kpend; kp++)
{
int idx= kp->fieldnr - 1;
@@ -6570,6 +6692,15 @@ void TABLE::mark_columns_needed_for_update()
need_signal= true;
}
}
+ if (s->versioned)
+ {
+ /*
+ For System Versioning we have to read all columns since we store
+ a copy of previous row with modified row_end back to a table.
+ */
+ bitmap_union(read_set, &s->all_set);
+ need_signal= true;
+ }
if (check_constraints)
{
mark_check_constraint_columns_for_read();
@@ -6730,8 +6861,16 @@ void TABLE::mark_columns_per_binlog_row_image()
binary log will include all columns read anyway.
*/
mark_columns_used_by_index_no_reset(s->primary_key, read_set);
- /* Only write columns that have changed */
- rpl_write_set= write_set;
+ if (versioned())
+ {
+ // TODO: After MDEV-18432 we don't pass history rows, so remove this:
+ rpl_write_set= &s->all_set;
+ }
+ else
+ {
+ /* Only write columns that have changed */
+ rpl_write_set= write_set;
+ }
break;
default:
@@ -6837,6 +6976,58 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl
DBUG_RETURN(bitmap_updated);
}
+
+/**
+ Check if a virtual not stored column field is in read set
+
+ @retval FALSE No virtual not stored column is used
+ @retval TRUE At least one virtual not stored column is used
+*/
+
+bool TABLE::check_virtual_columns_marked_for_read()
+{
+ if (vfield)
+ {
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ Field *tmp_vfield= *vfield_ptr;
+ if (bitmap_is_set(read_set, tmp_vfield->field_index) &&
+ !tmp_vfield->vcol_info->stored_in_db)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/**
+ Check if a stored virtual column field is marked for write
+
+ This can be used to check if any column that is part of a virtual
+ stored column is changed
+
+ @retval FALSE No stored virtual column is used
+ @retval TRUE At least one stored virtual column is used
+*/
+
+bool TABLE::check_virtual_columns_marked_for_write()
+{
+ if (vfield)
+ {
+ Field **vfield_ptr;
+ for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++)
+ {
+ Field *tmp_vfield= *vfield_ptr;
+ if (bitmap_is_set(write_set, tmp_vfield->field_index) &&
+ tmp_vfield->vcol_info->stored_in_db)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/*
Mark fields used by check constraints into s->check_set.
Mark all fields used in an expression that is part of an index
@@ -6935,6 +7126,7 @@ void TABLE::mark_default_fields_for_write(bool is_insert)
DBUG_VOID_RETURN;
}
+
void TABLE::move_fields(Field **ptr, const uchar *to, const uchar *from)
{
my_ptrdiff_t diff= to - from;
@@ -7196,7 +7388,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
if (unique)
keyinfo->flags|= HA_NOSAME;
sprintf(buf, "key%i", key);
- if (!(keyinfo->name= strdup_root(&mem_root, buf)))
+ keyinfo->name.length= strlen(buf);
+ if (!(keyinfo->name.str= strmake_root(&mem_root, buf, keyinfo->name.length)))
return TRUE;
keyinfo->rec_per_key= (ulong*) alloc_root(&mem_root,
sizeof(ulong)*key_parts);
@@ -7220,6 +7413,26 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
key_part_info++;
}
+ /*
+ For the case when there is a derived table that would give distinct rows,
+ the index statistics are passed to the join optimizer to tell that a ref
+ access to all the fields of the derived table will produce only one row.
+ */
+
+ st_select_lex_unit* derived= pos_in_table_list ?
+ pos_in_table_list->derived: NULL;
+ if (derived)
+ {
+ st_select_lex* first= derived->first_select();
+ uint select_list_items= first->get_item_list()->elements;
+ if (key_parts == select_list_items)
+ {
+ if ((!first->is_part_of_union() && (first->options & SELECT_DISTINCT)) ||
+ derived->check_distinct_in_union())
+ keyinfo->rec_per_key[key_parts - 1]= 1;
+ }
+ }
+
set_if_bigger(s->max_key_length, keyinfo->key_length);
s->keys++;
return FALSE;
@@ -7475,7 +7688,7 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
(pos= find_type(&tbl->s->keynames, hint->key_name.str,
hint->key_name.length, 1)) <= 0)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias);
+ my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias.str);
return 1;
}
@@ -7581,7 +7794,7 @@ void init_mdl_requests(TABLE_LIST *table_list)
{
for ( ; table_list ; table_list= table_list->next_global)
table_list->mdl_request.init(MDL_key::TABLE,
- table_list->db, table_list->table_name,
+ table_list->db.str, table_list->table_name.str,
table_list->lock_type >= TL_WRITE_ALLOW_WRITE ?
MDL_SHARED_WRITE : MDL_SHARED_READ,
MDL_TRANSACTION);
@@ -7765,7 +7978,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
if (vcol_info->expr->save_in_field(vf, 0))
field_error= error= 1;
DBUG_PRINT("info", ("field '%s' - updated error: %d",
- vf->field_name, field_error));
+ vf->field_name.str, field_error));
if (swap_values && (vf->flags & BLOB_FLAG))
{
/*
@@ -7779,7 +7992,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
}
else
{
- DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name));
+ DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name.str));
}
}
if (handler_pushed)
@@ -7851,7 +8064,7 @@ int TABLE::update_default_fields(bool ignore_errors)
res|= (field->default_value->expr->save_in_field(field, 0) < 0);
if (!ignore_errors && res)
{
- my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name);
+ my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str);
break;
}
res= 0;
@@ -7861,6 +8074,7 @@ int TABLE::update_default_fields(bool ignore_errors)
DBUG_RETURN(res);
}
+
void TABLE::evaluate_update_default_function()
{
DBUG_ENTER("TABLE::evaluate_update_default_function");
@@ -7876,6 +8090,39 @@ void TABLE::evaluate_update_default_function()
}
+void TABLE::vers_update_fields()
+{
+ bitmap_set_bit(write_set, vers_start_field()->field_index);
+ bitmap_set_bit(write_set, vers_end_field()->field_index);
+
+ if (!vers_write)
+ {
+ file->column_bitmaps_signal();
+ return;
+ }
+
+ if (versioned(VERS_TIMESTAMP) &&
+ vers_start_field()->store_timestamp(in_use->query_start(),
+ in_use->query_start_sec_part()))
+ {
+ DBUG_ASSERT(0);
+ }
+
+ vers_end_field()->set_max();
+ bitmap_set_bit(read_set, vers_end_field()->field_index);
+ file->column_bitmaps_signal();
+ if (vfield)
+ update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
+}
+
+
+void TABLE::vers_update_end()
+{
+ if (vers_end_field()->store_timestamp(in_use->query_start(),
+ in_use->query_start_sec_part()))
+ DBUG_ASSERT(0);
+}
+
/**
Reset markers that fields are being updated
*/
@@ -7984,7 +8231,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const
for (Field **fld= field; *fld; fld++)
{
if (!bitmap_is_set(write_set, (*fld)->field_index) &&
- !((*fld)->flags & NO_DEFAULT_VALUE_FLAG))
+ !((*fld)->flags & (NO_DEFAULT_VALUE_FLAG | VERS_SYSTEM_FIELD)))
{
if (!(*fld)->is_null_in_record(s->default_values) &&
(*fld)->validate_value_in_record_with_warn(thd, s->default_values) &&
@@ -8023,7 +8270,7 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
tmp_table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL);
file->ha_index_or_rnd_end();
- if (file->ha_rnd_init_with_error(1))
+ if (unlikely(file->ha_rnd_init_with_error(1)))
DBUG_RETURN(1);
if (tmp_table->no_rows)
@@ -8035,10 +8282,10 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
tmp_table->file->ha_start_bulk_insert(file->stats.records);
}
- while (!file->ha_rnd_next(tmp_table->record[0]))
+ while (likely(!file->ha_rnd_next(tmp_table->record[0])))
{
write_err= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]);
- if (write_err)
+ if (unlikely(write_err))
{
bool is_duplicate;
if (tmp_table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
@@ -8049,11 +8296,8 @@ bool TABLE::insert_all_rows_into_tmp_table(THD *thd,
DBUG_RETURN(1);
}
- if (thd->check_killed())
- {
- thd->send_kill_message();
+ if (unlikely(thd->check_killed()))
goto err_killed;
- }
}
if (!tmp_table->no_rows && tmp_table->file->ha_end_bulk_insert())
goto err;
@@ -8220,7 +8464,21 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
(first_table && first_table->is_multitable()))
set_multitable();
- unit->derived= this;
+ if (!unit->derived)
+ unit->derived= this;
+ else if (!is_with_table_recursive_reference() && unit->derived != this)
+ {
+ if (unit->derived->is_with_table_recursive_reference())
+ unit->derived= this;
+ else if (vers_conditions.eq(unit->derived->vers_conditions))
+ vers_conditions.empty();
+ else
+ {
+ my_error(ER_CONFLICTING_FOR_SYSTEM_TIME, MYF(0));
+ return TRUE;
+ }
+ }
+
if (init_view && !view)
{
/* This is all what we can do for a derived table for now. */
@@ -8294,9 +8552,8 @@ int TABLE_LIST::fetch_number_of_rows()
return 0;
}
if (is_materialized_derived() && !fill_me)
-
{
- table->file->stats.records= ((select_union*)derived->result)->records;
+ table->file->stats.records= get_unit()->result->est_records;
set_if_bigger(table->file->stats.records, 2);
table->used_stat_records= table->file->stats.records;
}
@@ -8356,7 +8613,10 @@ bool TABLE_LIST::change_refs_to_fields()
if (!used_items.elements)
return FALSE;
- materialized_items= (Item**)thd->calloc(sizeof(void*) * table->s->fields);
+ Item **materialized_items=
+ (Item **)thd->calloc(sizeof(void *) * table->s->fields);
+ if (!materialized_items)
+ return TRUE;
while ((ref= (Item_direct_ref*)li++))
{
@@ -8413,7 +8673,6 @@ bool TABLE_LIST::is_with_table()
return derived && derived->with_element;
}
-
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
@@ -8590,8 +8849,8 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
if (!(item->used_tables() == tab_map))
continue;
Item_func_eq *eq= 0;
- Item *left_item_clone= left_item->build_clone(thd, thd->mem_root);
- Item *right_item_clone= item->build_clone(thd, thd->mem_root);
+ Item *left_item_clone= left_item->build_clone(thd);
+ Item *right_item_clone= item->build_clone(thd);
if (left_item_clone && right_item_clone)
{
left_item_clone->set_item_equal(NULL);
@@ -8621,7 +8880,7 @@ Item* TABLE_LIST::build_pushable_cond_for_table(THD *thd, Item *cond)
return new_cond;
}
else if (cond->get_extraction_flag() != NO_EXTRACTION_FL)
- return cond->build_clone(thd, thd->mem_root);
+ return cond->build_clone(thd);
return 0;
}
@@ -8645,6 +8904,460 @@ bool fk_modifies_child(enum_fk_option opt)
return can_write[opt];
}
+enum TR_table::enabled TR_table::use_transaction_registry= TR_table::MAYBE;
+
+TR_table::TR_table(THD* _thd, bool rw) :
+ thd(_thd), open_tables_backup(NULL)
+{
+ init_one_table(&MYSQL_SCHEMA_NAME, &TRANSACTION_REG_NAME,
+ NULL, rw ? TL_WRITE : TL_READ);
+}
+
+bool TR_table::open()
+{
+ DBUG_ASSERT(!table);
+ open_tables_backup= new Open_tables_backup;
+ if (!open_tables_backup)
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return true;
+ }
+
+ All_tmp_tables_list *temporary_tables= thd->temporary_tables;
+ bool error= !open_log_table(thd, this, open_tables_backup);
+ thd->temporary_tables= temporary_tables;
+
+ if (use_transaction_registry == MAYBE)
+ error= check(error);
+
+ use_transaction_registry= error ? NO : YES;
+
+ return error;
+}
+
+TR_table::~TR_table()
+{
+ if (table)
+ {
+ thd->temporary_tables= NULL;
+ close_log_table(thd, open_tables_backup);
+ }
+ delete open_tables_backup;
+}
+
+void TR_table::store(uint field_id, ulonglong val)
+{
+ table->field[field_id]->store(val, true);
+ table->field[field_id]->set_notnull();
+}
+
+void TR_table::store(uint field_id, timeval ts)
+{
+ table->field[field_id]->store_timestamp(ts.tv_sec, ts.tv_usec);
+ table->field[field_id]->set_notnull();
+}
+
+enum_tx_isolation TR_table::iso_level() const
+{
+ enum_tx_isolation res= (enum_tx_isolation) ((*this)[FLD_ISO_LEVEL]->val_int() - 1);
+ DBUG_ASSERT(res <= ISO_SERIALIZABLE);
+ return res;
+}
+
+bool TR_table::update(ulonglong start_id, ulonglong end_id)
+{
+ if (!table && open())
+ return true;
+
+ store(FLD_BEGIN_TS, thd->transaction_time());
+ thd->set_time();
+ timeval end_time= {thd->query_start(), int(thd->query_start_sec_part())};
+ store(FLD_TRX_ID, start_id);
+ store(FLD_COMMIT_ID, end_id);
+ store(FLD_COMMIT_TS, end_time);
+ store_iso_level(thd->tx_isolation);
+
+ int error= table->file->ha_write_row(table->record[0]);
+ if (unlikely(error))
+ table->file->print_error(error, MYF(0));
+ return error;
+}
+
+#define newx new (thd->mem_root)
+bool TR_table::query(ulonglong trx_id)
+{
+ if (!table && open())
+ return false;
+ SQL_SELECT_auto select;
+ READ_RECORD info;
+ int error;
+ List<TABLE_LIST> dummy;
+ SELECT_LEX &slex= thd->lex->select_lex;
+ Name_resolution_context_backup backup(slex.context, *this);
+ Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_TRX_ID]);
+ Item *value= newx Item_int(thd, trx_id);
+ COND *conds= newx Item_func_eq(thd, field, value);
+ if (unlikely((error= setup_conds(thd, this, dummy, &conds))))
+ return false;
+ select= make_select(table, 0, 0, conds, NULL, 0, &error);
+ if (unlikely(error || !select))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return false;
+ }
+ // FIXME: (performance) force index 'transaction_id'
+ error= init_read_record(&info, thd, table, select, NULL,
+ 1 /* use_record_cache */, true /* print_error */,
+ false /* disable_rr_cache */);
+ while (!(error= info.read_record()) && !thd->killed && !thd->is_error())
+ {
+ if (select->skip_record(thd) > 0)
+ return true;
+ }
+ my_error(ER_VERS_NO_TRX_ID, MYF(0), (longlong) trx_id);
+ return false;
+}
+
+bool TR_table::query(MYSQL_TIME &commit_time, bool backwards)
+{
+ if (!table && open())
+ return false;
+ SQL_SELECT_auto select;
+ READ_RECORD info;
+ int error;
+ List<TABLE_LIST> dummy;
+ SELECT_LEX &slex= thd->lex->select_lex;
+ Name_resolution_context_backup backup(slex.context, *this);
+ Item *field= newx Item_field(thd, &slex.context, (*this)[FLD_COMMIT_TS]);
+ Item *value= newx Item_datetime_literal(thd, &commit_time, 6);
+ COND *conds;
+ if (backwards)
+ conds= newx Item_func_ge(thd, field, value);
+ else
+ conds= newx Item_func_le(thd, field, value);
+ if (unlikely((error= setup_conds(thd, this, dummy, &conds))))
+ return false;
+ // FIXME: (performance) force index 'commit_timestamp'
+ select= make_select(table, 0, 0, conds, NULL, 0, &error);
+ if (unlikely(error || !select))
+ return false;
+ error= init_read_record(&info, thd, table, select, NULL,
+ 1 /* use_record_cache */, true /* print_error */,
+ false /* disable_rr_cache */);
+
+ // With PK by transaction_id the records are ordered by PK, so we have to
+ // scan TRT fully and collect min (backwards == true)
+ // or max (backwards == false) stats.
+ bool found= false;
+ MYSQL_TIME found_ts;
+ while (!(error= info.read_record()) && !thd->killed && !thd->is_error())
+ {
+ int res= select->skip_record(thd);
+ if (res > 0)
+ {
+ MYSQL_TIME commit_ts;
+ if ((*this)[FLD_COMMIT_TS]->get_date(&commit_ts, 0))
+ {
+ found= false;
+ break;
+ }
+ int c;
+ if (!found || ((c= my_time_compare(&commit_ts, &found_ts)) &&
+ (backwards ? c < 0 : c > 0)))
+ {
+ found_ts= commit_ts;
+ found= true;
+ // TODO: (performance) make ORDER DESC and break after first found.
+ // Otherwise it is O(n) scan (+copy)!
+ store_record(table, record[1]);
+ }
+ }
+ else if (res < 0)
+ {
+ found= false;
+ break;
+ }
+ }
+ if (found)
+ restore_record(table, record[1]);
+ return found;
+}
+#undef newx
+
+bool TR_table::query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0,
+ ulonglong commit_id1, enum_tx_isolation iso_level1,
+ ulonglong commit_id0)
+{
+ if (trx_id1 == trx_id0)
+ {
+ return false;
+ }
+
+ if (trx_id1 == ULONGLONG_MAX || trx_id0 == 0)
+ {
+ result= true;
+ return false;
+ }
+
+ if (trx_id0 == ULONGLONG_MAX || trx_id1 == 0)
+ {
+ result= false;
+ return false;
+ }
+
+ if (!commit_id1)
+ {
+ if (!query(trx_id1))
+ return true;
+
+ commit_id1= (*this)[FLD_COMMIT_ID]->val_int();
+ iso_level1= iso_level();
+ }
+
+ if (!commit_id0)
+ {
+ if (!query(trx_id0))
+ return true;
+
+ commit_id0= (*this)[FLD_COMMIT_ID]->val_int();
+ }
+
+ // Trivial case: TX1 started after TX0 committed
+ if (trx_id1 > commit_id0
+ // Concurrent transactions: TX1 committed after TX0 and TX1 is read (un)committed
+ || (commit_id1 > commit_id0 && iso_level1 < ISO_REPEATABLE_READ))
+ {
+ result= true;
+ }
+ else // All other cases: TX1 does not see TX0
+ {
+ result= false;
+ }
+
+ return false;
+}
+
+void TR_table::warn_schema_incorrect(const char *reason)
+{
+ if (MYSQL_VERSION_ID == table->s->mysql_version)
+ {
+ sql_print_error("%`s.%`s schema is incorrect: %s.",
+ db.str, table_name.str, reason);
+ }
+ else
+ {
+ sql_print_error("%`s.%`s schema is incorrect: %s. Created with MariaDB %d, "
+ "now running %d.",
+ db.str, table_name.str, reason, MYSQL_VERSION_ID,
+ static_cast<int>(table->s->mysql_version));
+ }
+}
+
+bool TR_table::check(bool error)
+{
+ if (error)
+ {
+ sql_print_warning("%`s.%`s does not exist (open failed).", db.str,
+ table_name.str);
+ return true;
+ }
+
+ if (table->file->ht->db_type != DB_TYPE_INNODB)
+ {
+ warn_schema_incorrect("Wrong table engine (expected InnoDB)");
+ return true;
+ }
+
+#define WARN_SCHEMA(...) \
+ char reason[128]; \
+ snprintf(reason, 128, __VA_ARGS__); \
+ warn_schema_incorrect(reason);
+
+ if (table->s->fields != FIELD_COUNT)
+ {
+ WARN_SCHEMA("Wrong field count (expected %d)", FIELD_COUNT);
+ return true;
+ }
+
+ if (table->field[FLD_TRX_ID]->type() != MYSQL_TYPE_LONGLONG)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_TRX_ID);
+ return true;
+ }
+
+ if (table->field[FLD_COMMIT_ID]->type() != MYSQL_TYPE_LONGLONG)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected BIGINT UNSIGNED)", FLD_COMMIT_ID);
+ return true;
+ }
+
+ if (table->field[FLD_BEGIN_TS]->type() != MYSQL_TYPE_TIMESTAMP)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_BEGIN_TS);
+ return true;
+ }
+
+ if (table->field[FLD_COMMIT_TS]->type() != MYSQL_TYPE_TIMESTAMP)
+ {
+ WARN_SCHEMA("Wrong field %d type (expected TIMESTAMP(6))", FLD_COMMIT_TS);
+ return true;
+ }
+
+ if (table->field[FLD_ISO_LEVEL]->type() != MYSQL_TYPE_STRING ||
+ !(table->field[FLD_ISO_LEVEL]->flags & ENUM_FLAG))
+ {
+ wrong_enum:
+ WARN_SCHEMA("Wrong field %d type (expected ENUM('READ-UNCOMMITTED', "
+ "'READ-COMMITTED', 'REPEATABLE-READ', 'SERIALIZABLE'))",
+ FLD_ISO_LEVEL);
+ return true;
+ }
+
+ Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]);
+ st_typelib *typelib= iso_level->typelib;
+
+ if (typelib->count != 4)
+ goto wrong_enum;
+
+ if (strcmp(typelib->type_names[0], "READ-UNCOMMITTED") ||
+ strcmp(typelib->type_names[1], "READ-COMMITTED") ||
+ strcmp(typelib->type_names[2], "REPEATABLE-READ") ||
+ strcmp(typelib->type_names[3], "SERIALIZABLE"))
+ {
+ goto wrong_enum;
+ }
+
+ if (!table->key_info || !table->key_info->key_part)
+ goto wrong_pk;
+
+ if (strcmp(table->key_info->key_part->field->field_name.str, "transaction_id"))
+ {
+ wrong_pk:
+ WARN_SCHEMA("Wrong PRIMARY KEY (expected `transaction_id`)");
+ return true;
+ }
+
+ return false;
+}
+
+bool vers_select_conds_t::resolve_units(THD *thd)
+{
+ DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED);
+ DBUG_ASSERT(start.item);
+ return start.resolve_unit(thd) ||
+ end.resolve_unit(thd);
+}
+
+bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const
+{
+ if (type != conds.type)
+ return false;
+ switch (type) {
+ case SYSTEM_TIME_UNSPECIFIED:
+ case SYSTEM_TIME_ALL:
+ return true;
+ case SYSTEM_TIME_BEFORE:
+ break;
+ case SYSTEM_TIME_HISTORY:
+ break;
+ case SYSTEM_TIME_AS_OF:
+ return start.eq(conds.start);
+ case SYSTEM_TIME_FROM_TO:
+ case SYSTEM_TIME_BETWEEN:
+ return start.eq(conds.start) && end.eq(conds.end);
+ }
+ DBUG_ASSERT(0);
+ return false;
+}
+
+
+bool Vers_history_point::resolve_unit(THD *thd)
+{
+ if (!item)
+ return false;
+ if (item->fix_fields_if_needed(thd, &item))
+ return true;
+ return item->this_item()->type_handler_for_system_time()->
+ Vers_history_point_resolve_unit(thd, this);
+}
+
+
+void Vers_history_point::bad_expression_data_type_error(const char *type) const
+{
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ type, "FOR SYSTEM_TIME");
+}
+
+
+void Vers_history_point::fix_item()
+{
+ if (item && item->decimals == 0 && item->type() == Item::FUNC_ITEM &&
+ ((Item_func*)item)->functype() == Item_func::NOW_FUNC)
+ item->decimals= 6;
+}
+
+
+bool Vers_history_point::eq(const vers_history_point_t &point) const
+{
+ return unit == point.unit && item->eq(point.item, false);
+}
+
+void Vers_history_point::print(String *str, enum_query_type query_type,
+ const char *prefix, size_t plen) const
+{
+ const static LEX_CSTRING unit_type[]=
+ {
+ { STRING_WITH_LEN("") },
+ { STRING_WITH_LEN("TIMESTAMP ") },
+ { STRING_WITH_LEN("TRANSACTION ") }
+ };
+ str->append(prefix, plen);
+ str->append(unit_type + unit);
+ item->print(str, query_type);
+}
+
+Field *TABLE::find_field_by_name(LEX_CSTRING *str) const
+{
+ Field **tmp;
+ size_t length= str->length;
+ if (s->name_hash.records)
+ {
+ tmp= (Field**) my_hash_search(&s->name_hash, (uchar*) str->str, length);
+ return tmp ? field[tmp - s->field] : NULL;
+ }
+ else
+ {
+ for (tmp= field; *tmp; tmp++)
+ {
+ if ((*tmp)->field_name.length == length &&
+ !lex_string_cmp(system_charset_info, &(*tmp)->field_name, str))
+ return *tmp;
+ }
+ }
+ return NULL;
+}
+
+
+bool TABLE::export_structure(THD *thd, Row_definition_list *defs)
+{
+ for (Field **src= field; *src; src++)
+ {
+ uint offs;
+ if (defs->find_row_field_by_name(&src[0]->field_name, &offs))
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name.str);
+ return true;
+ }
+ Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src);
+ if (!def)
+ return true;
+ def->flags&= (uint) ~NOT_NULL_FLAG;
+ if ((def->sp_prepare_create_field(thd, thd->mem_root)) ||
+ (defs->push_back(def, thd->mem_root)))
+ return true;
+ }
+ return false;
+}
/*
@brief
@@ -8658,10 +9371,10 @@ bool fk_modifies_child(enum_fk_option opt)
void TABLE::initialize_quick_structures()
{
- bzero(quick_rows, sizeof(quick_rows));
- bzero(quick_key_parts, sizeof(quick_key_parts));
- bzero(quick_costs, sizeof(quick_costs));
- bzero(quick_n_ranges, sizeof(quick_n_ranges));
+ TRASH_ALLOC(quick_rows, sizeof(quick_rows));
+ TRASH_ALLOC(quick_key_parts, sizeof(quick_key_parts));
+ TRASH_ALLOC(quick_costs, sizeof(quick_costs));
+ TRASH_ALLOC(quick_n_ranges, sizeof(quick_n_ranges));
}
/*