summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_sys.h11
-rw-r--r--mysql-test/main/ps_ddl.result12
-rw-r--r--mysql-test/main/ps_ddl.test14
-rw-r--r--mysql-test/main/view.result28
-rw-r--r--mysql-test/main/view.test40
-rw-r--r--sql/parse_file.cc21
-rw-r--r--sql/sql_base.cc18
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_prepare.cc4
-rw-r--r--sql/sql_show.cc22
-rw-r--r--sql/sql_trigger.cc26
-rw-r--r--sql/sql_trigger.h4
-rw-r--r--sql/sql_view.cc67
-rw-r--r--sql/table.cc67
-rw-r--r--sql/table.h66
16 files changed, 329 insertions, 74 deletions
diff --git a/include/my_sys.h b/include/my_sys.h
index 5cabfc25c12..a32a0c09234 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -964,6 +964,17 @@ extern ulonglong my_getcputime(void);
#define hrtime_sec_part(X) ((ulong)((X).val % HRTIME_RESOLUTION))
#define my_time(X) hrtime_to_time(my_hrtime_coarse())
+/**
+ Make high resolution time from two parts.
+*/
+
+static inline my_hrtime_t make_hr_time(my_time_t time, ulong time_sec_part)
+{
+ my_hrtime_t res= {((ulonglong) time)*1000000 + time_sec_part};
+ return res;
+}
+
+
#if STACK_DIRECTION < 0
#define available_stack_size(CUR,END) (long) ((char*)(CUR) - (char*)(END))
#else
diff --git a/mysql-test/main/ps_ddl.result b/mysql-test/main/ps_ddl.result
index 68acf50aee1..ffbc20f2c0b 100644
--- a/mysql-test/main/ps_ddl.result
+++ b/mysql-test/main/ps_ddl.result
@@ -214,7 +214,7 @@ new trigger: 10
drop trigger t1_bd;
set @val=11;
execute stmt using @val;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
SUCCESS
select @message;
@@ -224,7 +224,7 @@ Test 6-e: removing a relevant trigger
drop trigger t1_bi;
set @val=12;
execute stmt using @val;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
SUCCESS
select @message;
@@ -384,7 +384,7 @@ a
flush table t1;
set @var=9;
execute stmt using @var;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
SUCCESS
select * from t2;
@@ -830,7 +830,7 @@ a b c
10 20 50
20 40 100
30 60 150
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
SUCCESS
# Check that we properly handle ALTER VIEW statements.
@@ -1206,7 +1206,7 @@ drop trigger v2_bi;
set @message=null;
set @var=9;
execute stmt using @var;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
SUCCESS
select @message;
@@ -2578,7 +2578,7 @@ SELECT * FROM t1;
a
2048
1025
-1024
+2048
DROP TABLE t1;
#
# End of 10.1 tests
diff --git a/mysql-test/main/ps_ddl.test b/mysql-test/main/ps_ddl.test
index 90226d379bf..4524a749527 100644
--- a/mysql-test/main/ps_ddl.test
+++ b/mysql-test/main/ps_ddl.test
@@ -250,7 +250,8 @@ drop trigger t1_bd;
set @val=11;
execute stmt using @val;
-call p_verify_reprepare_count(1);
+# No trigger in opened table => nothing to check => no reprepare
+call p_verify_reprepare_count(0);
select @message;
--echo Test 6-e: removing a relevant trigger
@@ -259,7 +260,8 @@ drop trigger t1_bi;
set @val=12;
execute stmt using @val;
-call p_verify_reprepare_count(1);
+# No trigger in opened table => nothing to check => no reprepare
+call p_verify_reprepare_count(0);
select @message;
set @val=13;
execute stmt using @val;
@@ -374,7 +376,8 @@ select * from t3;
flush table t1;
set @var=9;
execute stmt using @var;
-call p_verify_reprepare_count(1);
+# flush tables now do not mean reprepare
+call p_verify_reprepare_count(0);
select * from t2;
select * from t3;
drop view v1;
@@ -743,7 +746,7 @@ call p_verify_reprepare_count(1);
flush table t2;
execute stmt;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
--echo # Check that we properly handle ALTER VIEW statements.
execute stmt;
@@ -967,7 +970,8 @@ drop trigger v2_bi;
set @message=null;
set @var=9;
execute stmt using @var;
-call p_verify_reprepare_count(1);
+# No trigger in opened table => nothing to check => no reprepare
+call p_verify_reprepare_count(0);
select @message;
create trigger v2_bi after insert on v2 for each row set @message="v2_ai";
set @var= 10;
diff --git a/mysql-test/main/view.result b/mysql-test/main/view.result
index b14f2af56de..fd84cff9fa1 100644
--- a/mysql-test/main/view.result
+++ b/mysql-test/main/view.result
@@ -6816,6 +6816,34 @@ r
drop view v1;
drop table t1;
#
+# MDEV-17124: mariadb 10.1.34, views and prepared statements:
+# ERROR 1615 (HY000): Prepared statement needs to be re-prepared
+#
+set @tdc= @@table_definition_cache, @tc= @@table_open_cache;
+set global table_definition_cache= 400, table_open_cache= 400;
+create table tt (a int, primary key(a)) engine=MyISAM;
+create view v as select * from tt;
+insert into tt values(1),(2),(3),(4);
+prepare stmt from 'select * from tt';
+#fill table definition cache
+execute stmt;
+a
+1
+2
+3
+4
+prepare stmt from 'select * from v';
+execute stmt;
+a
+1
+2
+3
+4
+drop database db;
+drop view v;
+drop table tt;
+set global table_definition_cache= @tdc, table_open_cache= @tc;
+#
# End of 10.2 tests
#
#
diff --git a/mysql-test/main/view.test b/mysql-test/main/view.test
index 770be8dbb5f..038c7107d07 100644
--- a/mysql-test/main/view.test
+++ b/mysql-test/main/view.test
@@ -6540,6 +6540,46 @@ drop view v1;
drop table t1;
--echo #
+--echo # MDEV-17124: mariadb 10.1.34, views and prepared statements:
+--echo # ERROR 1615 (HY000): Prepared statement needs to be re-prepared
+--echo #
+
+set @tdc= @@table_definition_cache, @tc= @@table_open_cache;
+set global table_definition_cache= 400, table_open_cache= 400;
+
+create table tt (a int, primary key(a)) engine=MyISAM;
+create view v as select * from tt;
+insert into tt values(1),(2),(3),(4);
+
+prepare stmt from 'select * from tt';
+--echo #fill table definition cache
+--disable_query_log
+--disable_result_log
+create database db;
+use db;
+--let $tables=401
+while ($tables)
+{
+ --eval create table t$tables (i int) engine=MyISAM
+ --eval select * from t$tables
+ --dec $tables
+}
+
+use test;
+
+--enable_query_log
+--enable_result_log
+execute stmt;
+prepare stmt from 'select * from v';
+execute stmt;
+
+# Cleanup
+drop database db;
+drop view v;
+drop table tt;
+set global table_definition_cache= @tdc, table_open_cache= @tc;
+
+--echo #
--echo # End of 10.2 tests
--echo #
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index 59b4027a352..18fffb2445d 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -173,11 +173,12 @@ write_parameter(IO_CACHE *file, const uchar* base, File_option *parameter)
{
/* string have to be allocated already */
LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
- time_t tm= my_time(0);
-
- get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
- tm);
- val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
+ // number of microseconds since Epoch, timezone-independent
+ my_hrtime_t tm= my_hrtime();
+ // Paded to 19 characters for compatibility
+ val_s->length= snprintf(val_s->str, MICROSECOND_TIMESTAMP_BUFFER_SIZE,
+ "%019lld", tm.val);
+ DBUG_ASSERT(val_s->length == MICROSECOND_TIMESTAMP_BUFFER_SIZE-1);
if (my_b_write(file, (const uchar *)val_s->str,
PARSE_FILE_TIMESTAMPLENGTH))
DBUG_RETURN(TRUE);
@@ -833,16 +834,16 @@ File_parser::parse(uchar* base, MEM_ROOT *mem_root,
{
/* string have to be allocated already */
LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
- /* yyyy-mm-dd HH:MM:SS = 19(PARSE_FILE_TIMESTAMPLENGTH) characters */
- if (ptr[PARSE_FILE_TIMESTAMPLENGTH] != '\n')
+ /* 19 characters of timestamp */
+ if (ptr[MICROSECOND_TIMESTAMP_BUFFER_SIZE-1] != '\n')
{
my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
parameter->name.str, line);
DBUG_RETURN(TRUE);
}
- memcpy(val->str, ptr, PARSE_FILE_TIMESTAMPLENGTH);
- val->str[val->length= PARSE_FILE_TIMESTAMPLENGTH]= '\0';
- ptr+= (PARSE_FILE_TIMESTAMPLENGTH+1);
+ memcpy(val->str, ptr, MICROSECOND_TIMESTAMP_BUFFER_SIZE-1);
+ val->str[val->length= MICROSECOND_TIMESTAMP_BUFFER_SIZE-1]= '\0';
+ ptr+= MICROSECOND_TIMESTAMP_BUFFER_SIZE;
break;
}
case FILE_OPTIONS_STRLIST:
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 614d6d58c0b..385040b1e03 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1906,6 +1906,11 @@ retry_share:
table_list->alias.str);
goto err_lock;
}
+
+ /* Open view */
+ if (mysql_make_view(thd, share, table_list, false))
+ goto err_lock;
+
/*
This table is a view. Validate its metadata version: in particular,
that it was a view when the statement was prepared.
@@ -1913,10 +1918,6 @@ retry_share:
if (check_and_update_table_version(thd, table_list, share))
goto err_lock;
- /* Open view */
- if (mysql_make_view(thd, share, table_list, false))
- goto err_lock;
-
/* TODO: Don't free this */
tdc_release_share(share);
@@ -2721,7 +2722,7 @@ static bool inject_reprepare(THD *thd)
@sa Execute_observer
@sa check_prepared_statement() to see cases when an observer is installed
- @sa TABLE_LIST::is_table_ref_id_equal()
+ @sa TABLE_LIST::is_the_same_definition()
@sa TABLE_SHARE::get_table_ref_id()
@param[in] thd used to report errors
@@ -2738,7 +2739,7 @@ static bool
check_and_update_table_version(THD *thd,
TABLE_LIST *tables, TABLE_SHARE *table_share)
{
- if (! tables->is_table_ref_id_equal(table_share))
+ if (! tables->is_the_same_definition(thd, table_share))
{
if (thd->m_reprepare_observer &&
thd->m_reprepare_observer->report_error(thd))
@@ -2844,7 +2845,9 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags)
DBUG_ASSERT(share->is_view);
- if (flags & CHECK_METADATA_VERSION)
+ err= mysql_make_view(thd, share, table_list, (flags & OPEN_VIEW_NO_PARSE));
+
+ if (!err && (flags & CHECK_METADATA_VERSION))
{
/*
Check TABLE_SHARE-version of view only if we have been instructed to do
@@ -2859,7 +2862,6 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, uint flags)
goto ret;
}
- err= mysql_make_view(thd, share, table_list, (flags & OPEN_VIEW_NO_PARSE));
ret:
tdc_release_share(share);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 733f399a32c..043fb6f24d8 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3879,6 +3879,7 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
id(id_arg),
column_usage(MARK_COLUMNS_READ),
lex(lex_arg),
+ hr_prepare_time({0}),
db(null_clex_str)
{
name= null_clex_str;
@@ -3897,6 +3898,7 @@ void Statement::set_statement(Statement *stmt)
column_usage= stmt->column_usage;
lex= stmt->lex;
query_string= stmt->query_string;
+ hr_prepare_time= stmt->hr_prepare_time;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index d3e2b72b12d..7ef5e19a45d 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1122,6 +1122,7 @@ public:
LEX_CSTRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor
+ my_hrtime_t hr_prepare_time; // time of preparation in microseconds
/*
Points to the query associated with this statement. It's const, but
we need to declare it char * because all table handlers are written
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 71dc85c3100..47024e7c219 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -4311,6 +4311,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (thd->spcont == NULL)
general_log_write(thd, COM_STMT_PREPARE, query(), query_length());
}
+ // The same format as for triggers to compare
+ hr_prepare_time= my_hrtime();
DBUG_RETURN(error);
}
@@ -4404,8 +4406,8 @@ Prepared_statement::execute_loop(String *expanded_query,
uchar *packet_end)
{
Reprepare_observer reprepare_observer;
- bool error;
int reprepare_attempt= 0;
+ bool error;
iterations= FALSE;
/*
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index eaed36a3506..bae712a407d 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -7117,13 +7117,15 @@ static bool store_trigger(THD *thd, Trigger *trigger,
table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
- if (trigger->create_time)
+ if (trigger->hr_create_time.val)
{
+ /* timestamp is in microseconds */
table->field[16]->set_notnull();
- thd->variables.time_zone->gmt_sec_to_TIME(&timestamp,
- (my_time_t)(trigger->create_time/100));
- /* timestamp is with 6 digits */
- timestamp.second_part= (trigger->create_time % 100) * 10000;
+ thd->variables.time_zone->
+ gmt_sec_to_TIME(&timestamp,
+ (my_time_t)
+ hrtime_to_time(trigger->hr_create_time));
+ timestamp.second_part= hrtime_sec_part(trigger->hr_create_time);
((Field_temporal_with_date*) table->field[16])->store_time_dec(&timestamp,
2);
}
@@ -10294,12 +10296,14 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
trigger->db_cl_name.length,
system_charset_info);
- if (trigger->create_time)
+ if (trigger->hr_create_time.val)
{
MYSQL_TIME timestamp;
- thd->variables.time_zone->gmt_sec_to_TIME(&timestamp,
- (my_time_t)(trigger->create_time/100));
- timestamp.second_part= (trigger->create_time % 100) * 10000;
+ thd->variables.time_zone->
+ gmt_sec_to_TIME(&timestamp,
+ (my_time_t)
+ hrtime_to_time(trigger->hr_create_time));
+ timestamp.second_part= hrtime_sec_part(trigger->hr_create_time);
p->store(&timestamp, 2);
}
else
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 247055d397c..375b2e12d65 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -37,6 +37,8 @@
#ifdef WITH_WSREP
#include "debug_sync.h"
#endif /* WITH_WSREP */
+#include <my_time.h>
+#include <mysql_time.h>
LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str,
const char* str, size_t length,
@@ -212,7 +214,7 @@ static File_option triggers_file_parameters[]=
},
{
{ STRING_WITH_LEN("created") },
- my_offsetof(class Table_triggers_list, create_times),
+ my_offsetof(class Table_triggers_list, hr_create_times),
FILE_OPTIONS_ULLLIST
},
{ { 0, 0 }, 0, FILE_OPTIONS_STRING }
@@ -896,6 +898,10 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
if (!(trigger= new (&table->mem_root) Trigger(this, 0)))
goto err_without_cleanup;
+ /* Time with in microseconds */
+ trigger->hr_create_time= make_hr_time(thd->query_start(),
+ thd->query_start_sec_part());
+
/* Create trigger_name.TRN file to ensure trigger name is unique */
if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type,
(uchar*)&trigname, trigname_file_parameters))
@@ -904,8 +910,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
/* Populate the trigger object */
trigger->sql_mode= thd->variables.sql_mode;
- /* Time with 2 decimals, like in MySQL 5.7 */
- trigger->create_time= ((ulonglong) thd->query_start())*100 + thd->query_start_sec_part()/10000;
build_trig_stmt_query(thd, tables, stmt_query, &trigger_definition,
&trigger->definer, trg_definer_holder);
@@ -973,7 +977,7 @@ void Table_triggers_list::empty_lists()
client_cs_names.empty();
connection_cl_names.empty();
db_cl_names.empty();
- create_times.empty();
+ hr_create_times.empty();
}
@@ -1009,7 +1013,7 @@ bool Trigger::add_to_file_list(void* param_arg)
base->client_cs_names.push_back(&client_cs_name, mem_root) ||
base->connection_cl_names.push_back(&connection_cl_name, mem_root) ||
base->db_cl_names.push_back(&db_cl_name, mem_root) ||
- base->create_times.push_back(&create_time, mem_root))
+ base->hr_create_times.push_back(&hr_create_time.val, mem_root))
return 1;
return 0;
}
@@ -1391,7 +1395,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
List_iterator_fast<LEX_CSTRING> it_client_cs_name(trigger_list->client_cs_names);
List_iterator_fast<LEX_CSTRING> it_connection_cl_name(trigger_list->connection_cl_names);
List_iterator_fast<LEX_CSTRING> it_db_cl_name(trigger_list->db_cl_names);
- List_iterator_fast<ulonglong> it_create_times(trigger_list->create_times);
+ List_iterator_fast<ulonglong>
+ it_create_times(trigger_list->hr_create_times);
LEX *old_lex= thd->lex;
LEX lex;
sp_rcontext *save_spcont= thd->spcont;
@@ -1477,7 +1482,14 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
trigger->sql_mode= sql_mode;
trigger->definition= *trg_create_str;
- trigger->create_time= trg_create_time ? *trg_create_time : 0;
+ trigger->hr_create_time=
+ my_hrtime_t({trg_create_time ? *trg_create_time : 0});
+ /*
+ Fix time if in 100th of second (comparison with max uint * 100
+ (max possible timestamp in the old format))
+ */
+ if (trigger->hr_create_time.val < 429496729400ULL)
+ trigger->hr_create_time.val*= 10000;
trigger->name= sp ? sp->m_name : empty_clex_str;
trigger->on_table_name.str= (char*) lex.raw_trg_on_table_name_begin;
trigger->on_table_name.length= (lex.raw_trg_on_table_name_end -
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index ae3d1738b16..f906fa0d1a8 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -115,7 +115,7 @@ public:
GRANT_INFO subject_table_grants;
sql_mode_t sql_mode;
/* Store create time. Can't be mysql_time_t as this holds also sub seconds */
- ulonglong create_time;
+ my_hrtime_t hr_create_time; // Create time timestamp in microseconds
trg_event_type event;
trg_action_time_type action_time;
uint action_order;
@@ -198,7 +198,7 @@ public:
*/
List<ulonglong> definition_modes_list;
/** Create times for triggers */
- List<ulonglong> create_times;
+ List<ulonglong> hr_create_times;
List<LEX_CSTRING> definers_list;
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index b0b4b075046..68a8410f559 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -786,7 +786,7 @@ static File_option view_parameters[]=
my_offsetof(TABLE_LIST, with_check),
FILE_OPTIONS_ULONGLONG},
{{ STRING_WITH_LEN("timestamp")},
- my_offsetof(TABLE_LIST, timestamp),
+ my_offsetof(TABLE_LIST, hr_timestamp),
FILE_OPTIONS_TIMESTAMP},
{{ STRING_WITH_LEN("create-version")},
my_offsetof(TABLE_LIST, file_version),
@@ -810,6 +810,15 @@ static File_option view_parameters[]=
FILE_OPTIONS_STRING}
};
+
+static File_option view_timestamp_parameters[]=
+{
+
+ {{ C_STRING_WITH_LEN("timestamp")}, 0, FILE_OPTIONS_TIMESTAMP},
+ {{NullS, 0}, 0, FILE_OPTIONS_STRING}
+};
+
+
static LEX_CSTRING view_file_type[]= {{STRING_WITH_LEN("VIEW") }};
@@ -827,8 +836,8 @@ int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum,
&path, path_buff, sizeof(path_buff),
&file, view);
/* init timestamp */
- if (!view->timestamp.str)
- view->timestamp.str= view->timestamp_buffer;
+ if (!view->hr_timestamp.str)
+ view->hr_timestamp.str= view->timestamp_buffer;
if (swap_alg && view->algorithm != VIEW_ALGORITHM_UNDEFINED)
{
@@ -1029,8 +1038,8 @@ loop_out:
&path, path_buff, sizeof(path_buff),
&file, view);
/* init timestamp */
- if (!view->timestamp.str)
- view->timestamp.str= view->timestamp_buffer;
+ if (!view->hr_timestamp.str)
+ view->hr_timestamp.str= view->timestamp_buffer;
/* check old .frm */
{
@@ -1155,7 +1164,32 @@ err:
DBUG_RETURN(error);
}
+/**
+ Reads view definition "version"
+ @param[in] share Share object of view
+
+ @return true on error, otherwise false
+*/
+
+bool mariadb_view_version_get(TABLE_SHARE *share)
+{
+ DBUG_ASSERT(share->is_view);
+
+ if (!(share->tabledef_version.str=
+ (uchar*) alloc_root(&share->mem_root,
+ MICROSECOND_TIMESTAMP_BUFFER_SIZE)))
+ return TRUE;
+ share->tabledef_version.length= 0; // safety if the drfinition file is brocken
+
+ DBUG_ASSERT(share->view_def != NULL);
+ if (share->view_def->parse((uchar *) &share->tabledef_version, NULL,
+ view_timestamp_parameters, 1,
+ &file_parser_dummy_hook))
+ return TRUE;
+ DBUG_ASSERT(share->tabledef_version.length == MICROSECOND_TIMESTAMP_BUFFER_SIZE-1);
+ return FALSE;
+}
/**
read VIEW .frm and create structures
@@ -1217,6 +1251,10 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
mysql_derived_reinit(thd, NULL, table);
DEBUG_SYNC(thd, "after_cached_view_opened");
+ if (!share->tabledef_version.length)
+ {
+ mariadb_view_version_get(share);
+ }
DBUG_RETURN(0);
}
@@ -1253,8 +1291,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
arena= thd->activate_stmt_arena_if_needed(&backup);
/* init timestamp */
- if (!table->timestamp.str)
- table->timestamp.str= table->timestamp_buffer;
+ if (!table->hr_timestamp.str)
+ table->hr_timestamp.str= table->timestamp_buffer;
/* prepare default values for old format */
table->view_suid= TRUE;
table->definer.user.str= table->definer.host.str= 0;
@@ -1270,6 +1308,19 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
required_view_parameters,
&file_parser_dummy_hook)))
goto end;
+ if (!share->tabledef_version.length)
+ {
+ share->tabledef_version.str= (const uchar *)
+ memdup_root(&share->mem_root,
+ (const void *)
+ table->hr_timestamp.str,
+ (share->tabledef_version.length=
+ table->hr_timestamp.length));
+ }
+ if (!table->tabledef_version.length)
+ {
+ table->set_view_def_version(&table->hr_timestamp);
+ }
/*
check old format view .frm
@@ -2200,7 +2251,7 @@ mysql_rename_view(THD *thd,
object for it.
*/
view_def.reset();
- view_def.timestamp.str= view_def.timestamp_buffer;
+ view_def.hr_timestamp.str= view_def.timestamp_buffer;
view_def.view_suid= TRUE;
/* get view definition and source */
diff --git a/sql/table.cc b/sql/table.cc
index 506195127b2..5d91026963a 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -8861,6 +8861,73 @@ bool TABLE_LIST::is_with_table()
return derived && derived->with_element;
}
+
+/**
+ Check if the definition are the same.
+
+ If versions do not match it check definitions (with checking and setting
+ trigger definition versions (times)
+
+ @param[in] view TABLE_LIST of the view
+ @param[in] share Share object of view
+
+ @return false on error or different definitions.
+
+ @sa check_and_update_table_version()
+*/
+
+bool TABLE_LIST::is_the_same_definition(THD* thd, TABLE_SHARE *s)
+{
+ enum enum_table_ref_type tp= s->get_table_ref_type();
+ if (m_table_ref_type == tp)
+ {
+ /*
+ Cache have not changed which means that definition was not changed
+ including triggers
+ */
+ if (m_table_ref_version == s->get_table_ref_version())
+ return TRUE;
+
+ /*
+ If cache changed then check content version
+ */
+ if ((tabledef_version.length &&
+ tabledef_version.length == s->tabledef_version.length &&
+ memcmp(tabledef_version.str, s->tabledef_version.str,
+ tabledef_version.length) == 0))
+ {
+ // Definition have not changed, let's check if triggers changed.
+ if (table && table->triggers)
+ {
+
+ my_hrtime_t hr_stmt_prepare= thd->hr_prepare_time;
+ if (hr_stmt_prepare.val)
+ for(uint i= 0; i < TRG_EVENT_MAX; i++)
+ for (uint j= 0; j < TRG_ACTION_MAX; j++)
+ {
+ Trigger *tr=
+ table->triggers->get_trigger((trg_event_type)i,
+ (trg_action_time_type)j);
+ if (tr)
+ if (hr_stmt_prepare.val <= tr->hr_create_time.val)
+ {
+ set_tabledef_version(s);
+ return FALSE;
+ }
+ }
+ }
+ set_table_id(s);
+ return TRUE;
+ }
+ else
+ tabledef_version.length= 0;
+ }
+ else
+ set_tabledef_version(s);
+ return FALSE;
+}
+
+
uint TABLE_SHARE::actual_n_key_parts(THD *thd)
{
return use_ext_keys &&
diff --git a/sql/table.h b/sql/table.h
index 6036afd3750..1eb09f119ed 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -32,8 +32,18 @@
#include "filesort_utils.h"
#include "parse_file.h"
-/* buffer for timestamp (19+1) */
-#define VIEW_TIME_STAMP_BUFFER_SIZE (PARSE_FILE_TIMESTAMPLENGTH + 1)
+/*
+ Buffer for unix timestamp in microseconds:
+ 9,223,372,036,854,775,807 (signed int64 maximal value)
+ 1 234 567 890 123 456 789
+
+ Note: we can use unsigned for calculation, but practically they
+ are the same by probability to overflow them (signed int64 in
+ microseconds is enough for almost 3e5 years) and signed allow to
+ avoid increasing the buffer (the old buffer for human readable
+ date was 19+1).
+*/
+#define MICROSECOND_TIMESTAMP_BUFFER_SIZE (19 + 1)
/* Structs that defines the TABLE */
@@ -1028,7 +1038,7 @@ struct TABLE_SHARE
with a base table, a base table is replaced with a temporary
table and so on.
- @sa TABLE_LIST::is_table_ref_id_equal()
+ @sa TABLE_LIST::is_the_same_definition()
*/
ulong get_table_ref_version() const
{
@@ -2369,6 +2379,12 @@ struct TABLE_LIST
to view with SQL SECURITY DEFINER)
*/
Security_context *security_ctx;
+ uchar tabledef_version_buf[MY_UUID_SIZE >
+ MICROSECOND_TIMESTAMP_BUFFER_SIZE-1 ?
+ MY_UUID_SIZE + 1 :
+ MICROSECOND_TIMESTAMP_BUFFER_SIZE];
+ LEX_CUSTRING tabledef_version;
+
/*
This view security context (non-zero only for views with
SQL SECURITY DEFINER)
@@ -2382,7 +2398,7 @@ struct TABLE_LIST
LEX_CSTRING source; /* source of CREATE VIEW */
LEX_CSTRING view_db; /* saved view database */
LEX_CSTRING view_name; /* saved view name */
- LEX_STRING timestamp; /* GMT time stamp of last operation */
+ LEX_STRING hr_timestamp; /* time stamp of last operation */
LEX_USER definer; /* definer of view */
ulonglong file_version; /* version of file's field set */
ulonglong mariadb_version; /* version of server on creation */
@@ -2465,7 +2481,7 @@ struct TABLE_LIST
/* TABLE_TYPE_UNKNOWN if any type is acceptable */
Table_type required_type;
handlerton *db_type; /* table_type for handler */
- char timestamp_buffer[VIEW_TIME_STAMP_BUFFER_SIZE];
+ char timestamp_buffer[MICROSECOND_TIMESTAMP_BUFFER_SIZE];
/*
This TABLE_LIST object is just placeholder for prelocking, it will be
used for implicit LOCK TABLES only and won't be used in real statement.
@@ -2657,19 +2673,7 @@ struct TABLE_LIST
*/
bool process_index_hints(TABLE *table);
- /**
- Compare the version of metadata from the previous execution
- (if any) with values obtained from the current table
- definition cache element.
-
- @sa check_and_update_table_version()
- */
- inline bool is_table_ref_id_equal(TABLE_SHARE *s) const
- {
- return (m_table_ref_type == s->get_table_ref_type() &&
- m_table_ref_version == s->get_table_ref_version());
- }
-
+ bool is_the_same_definition(THD *thd, TABLE_SHARE *s);
/**
Record the value of metadata version of the corresponding
table definition cache element in this parse tree node.
@@ -2686,6 +2690,26 @@ struct TABLE_LIST
m_table_ref_version= table_ref_version_arg;
}
+ void set_table_id(TABLE_SHARE *s)
+ {
+ set_table_ref_id(s);
+ set_tabledef_version(s);
+ }
+
+ void set_tabledef_version(TABLE_SHARE *s)
+ {
+ if (!tabledef_version.length && s->tabledef_version.length)
+ {
+ DBUG_ASSERT(s->tabledef_version.length <
+ sizeof(tabledef_version_buf));
+ tabledef_version.str= tabledef_version_buf;
+ memcpy(tabledef_version_buf, s->tabledef_version.str,
+ (tabledef_version.length= s->tabledef_version.length));
+ // safety
+ tabledef_version_buf[tabledef_version.length]= 0;
+ }
+ }
+
/* Set of functions returning/setting state of a derived table/view. */
inline bool is_non_derived()
{
@@ -2814,6 +2838,12 @@ struct TABLE_LIST
}
}
+ inline void set_view_def_version(LEX_STRING *version)
+ {
+ m_table_ref_type= TABLE_REF_VIEW;
+ tabledef_version.str= (const uchar *) version->str;
+ tabledef_version.length= version->length;
+ }
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
bool prep_where(THD *thd, Item **conds, bool no_where_clause);