developers
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 10 participants
- 6870 discussions

Re: [Maria-developers] [Commits] 607e22e: MDEV-8633: information_schema.index_statistics doesn't delete
by Sergei Golubchik 15 Jun '16
by Sergei Golubchik 15 Jun '16
15 Jun '16
Hi, Jan!
On Apr 26, Jan Lindström wrote:
> revision-id: 607e22e1426ef3d7e11e3a2e9d7f884d36cf3573 (mariadb-10.0.24-43-g607e22e)
> parent(s): ce38adddfa91949b30956abd0e4cab201effcdef
> committer: Jan Lindström
> timestamp: 2016-04-26 13:47:30 +0300
> message:
>
> MDEV-8633: information_schema.index_statistics doesn't delete
> item when drop table indexes or drop table;
>
> Problem was that table and index statistics is removed from
> persistent tables but not from memory cache. Added functions
> to remove table and index statistics from memory cache.
That's of course, ok. Just minor comments, see below:
> diff --git a/sql/sql_show.cc b/sql/sql_show.cc
> index b8926b9..ac4503d 100644
> --- a/sql/sql_show.cc
> +++ b/sql/sql_show.cc
> @@ -3442,6 +3442,100 @@ int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond)
> DBUG_RETURN(0);
> }
>
> +/* Remove all indexes from global index statistics */
"all indexes for a given table"
> +static
> +int del_global_index_stats(THD *thd, uchar* cache_key, uint cache_key_length)
> +{
> + int res = 0;
> + DBUG_ENTER("del_global_index_stats");
> +
> + mysql_mutex_lock(&LOCK_global_index_stats);
> +
> + for (uint i= 0; i < global_index_stats.records && !res;)
> + {
> + INDEX_STATS *index_stats =
> + (INDEX_STATS*) my_hash_element(&global_index_stats, i);
> +
> + /* We search correct db\0table_name\0 string */
> + if (index_stats &&
> + index_stats->index_name_length >= cache_key_length &&
> + !memcmp(index_stats->index, cache_key, cache_key_length))
> + {
> + res= my_hash_delete(&global_index_stats, (uchar*)index_stats);
> + /*
> + In our HASH implementation on deletion one elements
> + is moved into a place where a deleted element was,
> + and the last element is moved into the empty space.
> + Thus we need to re-examine the current element, but
> + we don't have to restart the search from the beginning.
> + */
> + }
> + else
> + i++;
> + }
> +
> + mysql_mutex_unlock(&LOCK_global_index_stats);
> + DBUG_RETURN(res);
> +}
> +
> +/* Remove a table from global table statistics */
> +
> +int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table)
> +{
> + TABLE_STATS *table_stats;
> + int res = 0;
> + uchar *cache_key;
> + uint cache_key_length;
> + DBUG_ENTER("del_global_TABLE_stat");
correct letter case: "del_global_table_stat".
> +
> + cache_key_length= db->length + 1 + table->length + 1;
> +
> + if(!(cache_key= (uchar *)my_malloc(cache_key_length,
> + MYF(MY_WME | MY_ZEROFILL))))
> + {
> + /* Out of memory error already given */
> + res = 1;
> + goto end;
> + }
> +
> + memcpy(cache_key, db->str, db->length);
> + memcpy(cache_key + db->length + 1, table->str, table->length);
> +
> + res= del_global_index_stats(thd, cache_key, cache_key_length);
> +
> + mysql_mutex_lock(&LOCK_global_table_stats);
> +
> + if(!res && (table_stats= (TABLE_STATS*) my_hash_search(&global_table_stats,
here and below you, probably, don't want if (!res) - if the table is
dropped, you should delete as many its entries as possible, even if
something fails for an unknown reason.
> + cache_key,
> + cache_key_length)))
> + res= my_hash_delete(&global_table_stats, (uchar*)table_stats);
> +
> + my_free(cache_key);
> + mysql_mutex_unlock(&LOCK_global_table_stats);
> +
> +end:
> + DBUG_RETURN(res);
> +}
> +
> +/* Remove a index from global index statistics */
> +
> +int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info)
The difference is del_global_index_stat and del_global_index_stats is
rather subtle. Could you rename them to make it clearer?
For example, you can rename the static one to
del_global_index_stats_for_table.
> +{
> + INDEX_STATS *index_stats;
> + uint key_length= table->s->table_cache_key.length + key_info->name_length + 1;
> + int res = 0;
> + DBUG_ENTER("del_global_index_stat");
> + mysql_mutex_lock(&LOCK_global_index_stats);
> +
> + if((index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats,
> + key_info->cache_name,
> + key_length)))
> + res= my_hash_delete(&global_index_stats, (uchar*)index_stats);
> +
> + mysql_mutex_unlock(&LOCK_global_index_stats);
> + DBUG_RETURN(res);
> +}
>
> /* Fill information schema table with index statistics */
>
> diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
> index e86c840..644ecf2 100644
> --- a/sql/sql_statistics.cc
> +++ b/sql/sql_statistics.cc
> @@ -29,6 +29,7 @@
> #include "sql_statistics.h"
> #include "opt_range.h"
> #include "my_atomic.h"
> +#include "sql_show.h"
>
> /*
> The system variable 'use_stat_tables' can take one of the
> @@ -3193,6 +3194,9 @@ int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab)
> rc= 1;
> }
>
> + if (!rc)
again here and below, better to do it without if (!rc)
> + rc= del_global_table_stat(thd, db, tab);
> +
> thd->restore_stmt_binlog_format(save_binlog_format);
>
> close_system_tables(thd, &open_tables_backup);
> @@ -3339,6 +3343,9 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
> }
> }
>
> + if (!rc)
> + rc= del_global_index_stat(thd, tab, key_info);
> +
> thd->restore_stmt_binlog_format(save_binlog_format);
>
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
Hi, Sachin!
Sounds good. Please continue with tests. It's difficult to see what is
working and what isn't without them...
What do you plan to do this week? Row comparison? Hidden columns?
On Jun 14, Sachin Setia wrote:
> Hello Sergei,
> Weekly Report for third week of gsoc
>
> 1. Improved code as suggested by you.
> 2.Update worked.
> 3.Tried with with tests but ./mtr failed , so tried some bug fixes now it
> is working.
> 4.Tried unique index for text , and trying for varchar length > file->
> max_key_part_length()
>
>
> Regards
> sachin
>
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0

14 Jun '16
GSoC (week 2)
Hello everyone,
Finally, I solved the issue with a mismatch of a key number
between sql layer and MyISAM file.I also found a simpler approach for this
with the help of my mentor.
One of the other issues that I had to deal with was compatibility issue of
key definitions between .frm file and information stored in MyISAM storage
engine. The issue was due to check_defination which compares .frm file
and MyISAM definitions of a table.The previous prototype of
check_definition didn't contain mi_uniquedef. I have modified this function
to compare mi_uniquedef.The main problem was due to the difference in
a number of keys which we pass as an argument in ha_myisam::open.
I have also started working on InnoDB.I discussed some of the
implementation approaches with my mentor. I made changes to some of the
functions in ha_innobase.cc.
Regards,
Shubham
On 31 May 2016 at 03:32, Sergei Golubchik <serg(a)mariadb.org> wrote:
> Hi, Shubham!
>
> Good, thanks!
>
> On May 31, Shubham Barai wrote:
> > GSoC (week 1)
> >
> > Hello everyone,
> >
> > After developing a prototype for MyISAM ,there were some issues to be
> > resolved. One of the issues is related to error regarding a duplicate
> > entry. So if there is a duplicate entry for a particular key in a row, it
> > generates an error for another key.
> >
> > After debugging, I found out that it was due to mismatch of a key number
> > between sql layer and MyISAM file. Keys are stored in the order of
> user's
> > input in sql layer. In table2myisam function, this order is changed as
> each
> > key is either stored as mi_keydef or mi_uniquedef. In mi_create, for each
> > unique definition, an extra key is created in the form of mi_keydef and
> > stored in MYI file which creates the issue of mismatch.
> >
> > I tried to solve the issue by adding extra member sql_key_no to mi_keydef
> > and mi_uniquedef.I also modified mi_keydef_write and mi_keydef_read to
> > support a new member but after compiling, I got some error related to a
> key
> > file. Also with the help of my mentor, I found out that it could result
> in
> > compatibility issues related to old MyISAM file.
> >
> > So currently, I am trying to solve this using another approach.
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
2
2

[Maria-developers] Please review MDEV-10181 Illegal mix of collation for a field and an ASCII string as a view field
by Alexander Barkov 10 Jun '16
by Alexander Barkov 10 Jun '16
10 Jun '16
Hi Sergei,
Please review a patch for MDEV-10181.
Thanks!
2
1

10 Jun '16
Hi
Im currently doing a reload process to an empty myisam table with 4 indexes
on Maria db 10 1 13 on win64 and noted that on the last step that the
status reports that its using repair to rebuild the table.
Myisam-repair-threads remain the default but noted that many threads were
running .im not sure if all of them were for repair. Is there any
recommendation which i should take using the referred setting with a
greater value?would setting it to zero make maridb decide which is there
best value? Would the indexes get too much fragmented ? Thanks!
2
1

Re: [Maria-developers] [Commits] 9a03e78: MDEV-5535: Cannot reopen temporary table
by Sergei Golubchik 08 Jun '16
by Sergei Golubchik 08 Jun '16
08 Jun '16
Hi, Nirbhay!
On Jun 08, Nirbhay Choubey wrote:
> revision-id: 9a03e780f179b575dc0d7be8de0c1731adf8726e (mariadb-10.2.0-82-g9a03e78)
> parent(s): 3826f59972f19791754c7ef542e9e4a9beb67004
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-06-08 10:09:42 -0400
> message:
>
> MDEV-5535: Cannot reopen temporary table
>
> Fix for some test failures and clean up.
>
> diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
> index acb9507..e62cc13 100644
> --- a/sql/temporary_tables.cc
> +++ b/sql/temporary_tables.cc
> @@ -30,6 +30,9 @@
> #define IS_USER_TABLE(A) ((A->tmp_table == TRANSACTIONAL_TMP_TABLE) || \
> (A->tmp_table == NON_TRANSACTIONAL_TMP_TABLE))
>
> +#define HAS_TEMPORARY_TABLES ((!rgi_slave && has_temporary_tables()) || \
> + (rgi_slave && \
> + unlikely(has_slave_temporary_tables())))
Ok, now I'm confused.
This begs for a function, like
if (rgi_slave)
return has_temporary_tables();
else
return has_temporary_tables();
but we had it and have just split it because you always called it with
a constant true or false, never with rgi_slave.
So, a function is still needed? Or was it not needed before but is
needed now?
Oh, I see... It was
bool Temporary_tables::is_empty(bool check_slave)
{
DBUG_ENTER("Temporary_tables::is_empty");
bool result;
if (!m_thd)
DBUG_RETURN(true);
rpl_group_info *rgi_slave= m_thd->rgi_slave;
if (check_slave && rgi_slave)
result= (rgi_slave->rli->save_temp_table_shares == NULL) ? true : false;
else
result= (m_table_shares == NULL) ? true : false;
DBUG_RETURN(result);
}
so you should've split it into:
bool has_temporary_tables()
{ return m_table_shares; }
bool has_slave_temporary_tables()
{ return rgi_slave ? rgi_slave->rli->save_temp_table_shares : has_temporary_tables(); }
because your 'check_slave' was always true or always false, so you
should've split this function in two. one for check_slave=true, and one
for check_slave=false;
> /**
> Check whether temporary tables exist. The decision is made based on the
> @@ -117,7 +117,7 @@ TABLE *THD::find_temporary_table(const char *db,
> uint key_length;
> bool locked;
>
> - if (!(has_temporary_tables() || (rgi_slave && has_slave_temporary_tables())))
> + if (!HAS_TEMPORARY_TABLES)
> {
> DBUG_RETURN(NULL);
> }
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
1
0

Re: [Maria-developers] 56a2835: MDEV-5535: Cannot reopen temporary table
by Sergei Golubchik 08 Jun '16
by Sergei Golubchik 08 Jun '16
08 Jun '16
Hi, Nirbhay!
Just a couple of questions/comments, see below.
And please pay attention to Kristian's email about locking and parallel
replication. I don't think I can confidently review it in a patch, so
you may want to spend more time testing or analyzing that code.
Ok to push, when you're ready! Thanks!
On May 26, Nirbhay Choubey wrote:
> revision-id: 56a2835872c4ac7296ec0ae2ff618822855b0fc0 (mariadb-10.1.8-82-g56a2835)
> parent(s): 28c289626f318631d707f85b057a90af99018b06
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-05-26 20:42:47 -0400
> message:
>
> MDEV-5535: Cannot reopen temporary table
>
> mysqld maintains a list of TABLE objects for all temporary
> tables in THD, where each table is represented by a TABLE
> object. A query referencing a temporary table for more than
> once, however, failed with ER_CANT_REOPEN_TABLE error.
"because a TABLE_SHARE was allocate together with the TABLE, so
temporary tables always had one TABLE per TABLE_SHARE"
> This patch lift this restriction by preserving the TABLE_SHARE
> object of each created temporary table, so that multiple instances
> of TABLE objects can be created.
better "by separating TABLE and TABLE_SHARE objects and storing
TABLE_SHARE's in the list in a THD, and TABLE's in the list in
the corresponding TABLE_SHARE"
> diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
> index 7bdd9c1..77f458b 100644
> --- a/sql/rpl_rli.cc
> +++ b/sql/rpl_rli.cc
> @@ -1060,24 +1061,43 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
>
> void Relay_log_info::close_temporary_tables()
> {
> - TABLE *table,*next;
> DBUG_ENTER("Relay_log_info::close_temporary_tables");
>
> - for (table=save_temporary_tables ; table ; table=next)
> + TMP_TABLE_SHARE *share;
> + TABLE *table;
> +
> + while ((share= save_temporary_tables.pop_front()))
> {
> - next=table->next;
> + /*
> + Iterate over the list of tables for this TABLE_SHARE and close them.
> + */
> + while ((table= share->all_tmp_tables.pop_front()))
> + {
> + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
> + table->s->db.str, table->s->table_name.str));
> +
> + /* Reset in_use as the table may have been created by another thd */
> + table->in_use= 0;
> + free_io_cache(table);
> + /*
> + Lets not free TABLE_SHARE here as there could be multiple TABLEs opened
> + for the same table (TABLE_SHARE).
> + */
> + closefrm(table, false);
> + my_free(table);
> + }
>
> - /* Reset in_use as the table may have been created by another thd */
> - table->in_use=0;
> /*
> Don't ask for disk deletion. For now, anyway they will be deleted when
> slave restarts, but it is a better intention to not delete them.
> */
> - DBUG_PRINT("info", ("table: 0x%lx", (long) table));
> - close_temporary(table, 1, 0);
> +
> + free_table_share(share);
> + my_free(share);
> }
> - save_temporary_tables= 0;
> - slave_open_temp_tables= 0;
> +
> + save_temporary_tables.empty();
Is it needed?
you've popped all shares from the list, it's empty.
I'd rather add an assert instead
> +
> DBUG_VOID_RETURN;
> }
>
> diff --git a/sql/sql_class.h b/sql/sql_class.h
> index ae240ae..a70c4a9 100644
> --- a/sql/sql_class.h
> +++ b/sql/sql_class.h
> @@ -1247,6 +1247,61 @@ enum enum_locked_tables_mode
> LTM_PRELOCKED_UNDER_LOCK_TABLES
> };
>
> +/**
> + The following structure is an extension to TABLE_SHARE and is
> + exclusively for temporary tables.
> +
> + @note:
> + Although, TDC_element has data members (like next, prev &
> + all_tables) to store the list of TABLE_SHARE & TABLE objects
> + related to a particular TABLE_SHARE, they cannot be moved to
> + TABLE_SHARE in order to be reused for temporary tables. This
> + is because, as concurrent threads iterating through hash of
> + TDC_element's may need access to all_tables, but if all_tables
> + is made part of TABLE_SHARE, then TDC_element->share->all_tables
> + is not always guaranteed to be valid, as TDC_element can live
> + longer than TABLE_SHARE.
> +*/
> +struct TMP_TABLE_SHARE : public TABLE_SHARE
> +{
> +private:
> + /*
> + Link to all temporary table shares. Declared as private to
> + avoid direct manipulation with those objects. One should
> + use methods of I_P_List template instead.
> + */
> + TMP_TABLE_SHARE *tmp_next;
> + TMP_TABLE_SHARE **tmp_prev;
> +
> + friend struct All_tmp_table_shares;
> +
> +public:
> + /*
> + Doubly-linked (back-linked) lists of used and unused TABLE objects
> + for this share.
and unused? you don't free them but keep in the list?
> + */
> + All_share_tables_list all_tmp_tables;
> +};
> +
> +/**
> + Helper class which specifies which members of TMP_TABLE_SHARE are
> + used for participation in the list of temporary tables.
> +*/
> +
> +struct All_tmp_table_shares
> +{
> + static inline TMP_TABLE_SHARE **next_ptr(TMP_TABLE_SHARE *l)
> + {
> + return &l->tmp_next;
> + }
> + static inline TMP_TABLE_SHARE ***prev_ptr(TMP_TABLE_SHARE *l)
> + {
> + return &l->tmp_prev;
> + }
> +};
> +
> +/* Also used in rpl_rli.h. */
> +typedef I_P_List <TMP_TABLE_SHARE, All_tmp_table_shares> All_tmp_tables_list;
>
> /**
> Class that holds information about tables which were opened and locked
> diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
> new file mode 100644
> index 0000000..1514b72
> --- /dev/null
> +++ b/sql/temporary_tables.cc
...
> +TABLE *THD::find_temporary_table(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("THD::find_temporary_table");
> +
> + TABLE *table;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> + bool locked;
> +
> + if (!(has_temporary_tables() || (rgi_slave && has_slave_temporary_tables())))
hmm, really? you had here is_empty(true) before
and your is_empty() was working like this:
if (flag_is_true)
{ do rgi_slave }
else
{ do thd }
your old is_empty was not checking thd if the argument was true,
your new code does, as if your is_empty() was
do thd;
if (flag_is_true)
{ do rgi_slave}
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + key_length= create_tmp_table_def_key(key, db, table_name);
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1

Re: [Maria-developers] [Commits] c285dbe: MDEV-5535: Cannot reopen temporary table
by Sergei Golubchik 08 Jun '16
by Sergei Golubchik 08 Jun '16
08 Jun '16
Hi, Nirbhay!
I like the approach, there were only few comments, but still there were
some :)
See below.
On May 16, Nirbhay Choubey wrote:
> revision-id: c285dbece2881e1d864bb758d77edc899d568c04 (mariadb-10.1.8-82-gc285dbe)
> parent(s): 222ca736f737e888115c732c8ecad84faf0a2529
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-05-16 23:59:10 -0400
> message:
>
> MDEV-5535: Cannot reopen temporary table
Would be nice to have a somewhat more verbose commit comment here :)
> diff --git a/mysql-test/t/reopen_temp_table-master.test b/mysql-test/t/reopen_temp_table-master.test
> new file mode 100644
> index 0000000..5ac2ca8
> --- /dev/null
> +++ b/mysql-test/t/reopen_temp_table-master.test
> @@ -0,0 +1 @@
> +--tmpdir=$MYSQLTEST_VARDIR//tmp
Eh... I suppose you mean reopen_temp_table-master.opt
And, in fact, you can simply use reopen_temp_table.opt
Btw, why two slashes? And why do you need to set tmpdir at all?
I suspect you don't - because your tests apparently succeed without it :)
> diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
> index aa2cae5..f729733 100644
> --- a/sql/sql_insert.cc
> +++ b/sql/sql_insert.cc
> @@ -4051,7 +4051,12 @@ static TABLE *create_table_from_items(THD *thd,
> }
> else
> {
> - if (open_temporary_table(thd, create_table))
> + /*
> + The pointer to the newly created temporary table has been stored in
> + table->create_info.
> + */
> + create_table->table= create_info->table;
Where was this happening before you've added an explicit assignment?
(note the assert below - it worked, so somewhere this must've been assigned)
> + if (!create_table->table)
> {
> /*
> This shouldn't happen as creation of temporary table should make
> @@ -4060,7 +4065,6 @@ static TABLE *create_table_from_items(THD *thd,
> */
> DBUG_ASSERT(0);
> }
> - DBUG_ASSERT(create_table->table == create_info->table);
why?
> }
> }
> else
> @@ -4308,6 +4326,27 @@ bool select_create::send_eof()
> DBUG_RETURN(true);
> }
>
> + if (table->s->tmp_table)
> + {
> + /*
> + Now is good time to add the new table to THD temporary tables list.
> + But, before that we need to check if same table got created by the sub-
> + statement.
> + */
> + if (thd->temporary_tables.find_table_share(table->s->table_cache_key.str,
> + table->s->table_cache_key.length))
> + {
> + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table->alias.c_ptr());
ER_TABLE_EXISTS_ERROR, eh?
I'm not sure it's the best way to solve this. It's an error
that neither CREATE ... IF NOT EXISTS or CREATE OR REPLACE can fix.
But I don't have a good solution for this. If a concurrent thread
would've tried to create a table meanwhile (assuming, non-temporary),
it would wait on the metadata lock, that protects table creation.
so, logically, trying to create a table from inside create table
or trying to drop a used table from stored function (your ER_CANT_REOPEN_TABLE)
should fail with ER_LOCK_DEADLOCK or ER_LOCK_ABORTED - because it's, basically,
trying to get a conflicting metadata lock within the same thread, clearly a
case of the deadlock.
but somehow I believe that returning ER_LOCK_DEADLOCK on a purely myisam
test case with temporary tables - that will be confusing as hell :(
so, ok, let's keep your ER_TABLE_EXISTS_ERROR. But, if possible, please
commit this code (save_tmp_table_share and ER_CANT_REOPEN_TABLE) in a separate
commit, after the main MDEV-5535 commit. To have it clearly distinct
in the history, in case we'll want to change this behaviour later.
> + abort_result_set();
> + DBUG_RETURN(true);
> + }
> + else
> + {
> + DBUG_ASSERT(save_tmp_table_share);
> + thd->temporary_tables.relink_table_share(save_tmp_table_share);
> + }
> + }
> +
> /*
> Do an implicit commit at end of statement for non-temporary
> tables. This can fail, but we should unlock the table
> diff --git a/sql/sql_table.cc b/sql/sql_table.cc
> index 40032c3..93fba14 100644
> --- a/sql/sql_table.cc
> +++ b/sql/sql_table.cc
> @@ -2281,23 +2281,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
> */
> DBUG_ASSERT(!(thd->locked_tables_mode &&
> table->open_type != OT_BASE_ONLY &&
> - find_temporary_table(thd, table) &&
> + thd->temporary_tables.find_table(table) &&
> table->mdl_request.ticket != NULL));
>
> - /*
> - drop_temporary_table may return one of the following error codes:
> - . 0 - a temporary table was successfully dropped.
> - . 1 - a temporary table was not found.
> - . -1 - a temporary table is used by an outer statement.
> - */
> if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table))
> error= 1;
> else
> {
> table_creation_was_logged= table->table->s->table_creation_was_logged;
> - if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1)
> + if (thd->temporary_tables.drop_table(table->table, &is_trans, true))
> {
> - DBUG_ASSERT(thd->in_sub_stmt);
> + error= 1;
> goto err;
Why are changes in this hunk? (comment, assert, -1, etc)
> }
> table->table= 0;
> diff --git a/sql/table.h b/sql/table.h
> index ab39603..8b7c665 100644
> --- a/sql/table.h
> +++ b/sql/table.h
> @@ -600,6 +601,7 @@ struct TABLE_STATISTICS_CB
> struct TABLE_SHARE
> {
> TABLE_SHARE() {} /* Remove gcc warning */
> + TABLE_SHARE *next, *prev;
Eh? Did you forget to remove this?
>
> /** Category of this table. */
> TABLE_CATEGORY table_category;
> diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
> new file mode 100644
> index 0000000..ed5b6b8
> --- /dev/null
> +++ b/sql/temporary_tables.cc
> @@ -0,0 +1,1457 @@
> +/*
> + Copyright (c) 2016 MariaDB Corporation
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> +*/
> +
> +#include "sql_acl.h" /* TMP_TABLE_ACLS */
> +#include "sql_base.h" /* free_io_cache,
> + tdc_create_key */
> +#include "lock.h" /* mysql_lock_remove */
> +#include "log_event.h" /* Query_log_event */
> +#include "sql_show.h" /* append_identifier */
> +#include "sql_handler.h" /* mysql_ha_rm_temporary_tables */
> +#include "temporary_tables.h" /* Temporary_tables */
> +#include "rpl_rli.h" /* rpl_group_info */
> +
> +#define IS_USER_TABLE(A) ((A->tmp_table == TRANSACTIONAL_TMP_TABLE) || \
> + (A->tmp_table == NON_TRANSACTIONAL_TMP_TABLE))
> +
> +
> +/*
> + Initialize the Temporary_tables object. Currently it always returns
> + false (success).
> +
> + @param thd [IN] Thread handle
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::init(THD *thd)
> +{
> + DBUG_ENTER("Temporary_tables::init");
> + this->m_thd= thd;
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Check whether temporary tables exist. The decision is made based on the
> + existence of TMP_TABLE_SHAREs.
> +
> + @param check_slave [IN] Also check the slave temporary tables.
> +
> + @return false Temporary tables exist
> + true No temporary table exist
> +*/
> +bool Temporary_tables::is_empty(bool check_slave)
not a very good idea. You always use it as is_empty(true) or is_empty(false).
that is, the caller always knows whether to check slave or not.
but then you use a common function and it needs to do a completely
unnecessary if() inside. Better to have two functions, like
is_empty() and is_empty_slave(). Or at least make this function inline, then
the compiler will have a chance to optimize it.
> +{
> + DBUG_ENTER("Temporary_tables::is_empty");
> +
> + bool result;
> +
> + if (!m_thd)
> + {
> + DBUG_RETURN(true);
> + }
> +
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> +
> + if (check_slave && rgi_slave)
> + {
> + result= (rgi_slave->rli->save_temp_table_shares == NULL) ? true : false;
> + }
> + else
> + {
> + result= (m_table_shares == NULL) ? true : false;
> + }
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> + /*
> + Reset the Temporary_tables object. Currently, it always returns
> + false (success).
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::reset()
> +{
> + DBUG_ENTER("Temporary_tables::reset");
> + m_table_shares= 0;
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Create a temporary table, open it and return the TABLE handle.
> +
> + @param hton [IN] Handlerton
> + @param frm [IN] Binary frm image
> + @param path [IN] File path (without extension)
> + @param db [IN] Schema name
> + @param table_name [IN] Table name
> + @param open_in_engine [IN] Whether open table in SE
> + @param created [OUT] Whether table was created?
can it ever happen for *create==true but a return value is NULL?
or the other way around? how?
> +
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::create_and_open_table(handlerton *hton,
> + LEX_CUSTRING *frm,
> + const char *path,
> + const char *db,
> + const char *table_name,
> + bool open_in_engine,
> + bool *created)
> +{
> + DBUG_ENTER("Temporary_tables::create_and_open_table");
> +
> + TMP_TABLE_SHARE *share;
> + TABLE *table= NULL;
> + bool locked;
> +
> + *created= false;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + locked= lock_tables();
old code didn't seem to have it here. how comes?
> +
> + if ((share= create_table(hton, frm, path, db, table_name)))
> + {
> + *created= true;
> + table= open_table(share, table_name, open_in_engine);
> + }
> +
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_RETURN(table);
> +}
> +
> +/*
> + Check whether an open table with db/table name is in use.
> +
> + @param db [IN] Database name
> + @param table_name [IN] Table name
> +
> + @return Success Pointer to first used table instance.
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_table(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> +
> + TABLE *table;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> + bool locked;
> +
> + if (is_empty(true))
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + key_length= create_table_def_key(key, db, table_name);
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + locked= lock_tables();
> + table = find_table(key, key_length, TABLE_IN_USE);
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_RETURN(table);
> +}
> +
> +
> +/*
> + Check whether an open table specified in TABLE_LIST is in use.
> +
> + @return tl [IN] TABLE_LIST
> +
> + @return Success Pointer to first used table instance.
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_table(const TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> + TABLE *table= find_table(tl->get_db_name(), tl->get_table_name());
> + DBUG_RETURN(table);
> +}
> +
> +
> +/*
> + Check whether an open table with the specified key is in use.
> + The key, in this case, is not the usual key used for temporary tables.
> + It does not contain server_id & pseudo_thread_id. This function is
> + essentially used use to check whether there is any temporary table
> + which _shadows_ a base table.
> + (see: Query_cache::send_result_to_client())
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_table_reduced_key_length(const char *key,
> + uint key_length)
> +{
> + DBUG_ENTER("Temporary_tables::find_table_reduced_key_length");
> +
> + TABLE *result= NULL;
> + bool locked;
> +
> + locked= lock_tables();
> +
> + for (TMP_TABLE_SHARE *share= m_table_shares; share; share= share->next)
> + {
> + if ((share->table_cache_key.length - TMP_TABLE_KEY_EXTRA) == key_length
> + && !memcmp(share->table_cache_key.str, key, key_length))
> + {
> + /*
> + A matching TMP_TABLE_SHARE is found. We now need to find a TABLE
> + instance in use.
> + */
> + for (TABLE *table= share->table; table; table= table->next)
> + {
> + if (table->query_id != 0)
> + {
> + result= table;
> + break;
> + }
> + }
> + }
> + }
> +
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Lookup the TMP_TABLE_SHARE using the given db/table_name.The server_id and
> + pseudo_thread_id used to generate table definition key is taken from m_thd
> + (see create_table_def_key()). Return NULL is none found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TMP_TABLE_SHARE *Temporary_tables::find_table_share(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::find_table_share");
> +
> + TMP_TABLE_SHARE *share;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> +
> + key_length= create_table_def_key(key, db, table_name);
> + share= find_table_share(key, key_length);
> +
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Lookup TMP_TABLE_SHARE using the specified TABLE_LIST element.
> + Return NULL is none found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TMP_TABLE_SHARE *Temporary_tables::find_table_share(const TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::find_table_share");
> + TMP_TABLE_SHARE *share= find_table_share(tl->get_db_name(),
> + tl->get_table_name());
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Lookup TMP_TABLE_SHARE using the specified table definition key.
> + Return NULL is none found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TMP_TABLE_SHARE *Temporary_tables::find_table_share(const char *key,
> + uint key_length)
> +{
> + DBUG_ENTER("Temporary_tables::find_table_share");
> +
> + TMP_TABLE_SHARE *share;
> + TMP_TABLE_SHARE *result= NULL;
> + bool locked;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + locked= lock_tables();
> +
> + for (share= m_table_shares; share; share= share->next)
> + {
> + if (share->table_cache_key.length == key_length &&
> + !(memcmp(share->table_cache_key.str, key, key_length)))
> + {
> + result= share;
> + break;
> + }
> + }
> +
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Find a temporary table specified by TABLE_LIST instance in the open table
> + list and prepare its TABLE instance for use. If
> +
> + This function tries to resolve this table in the list of temporary tables
> + of this thread. Temporary tables are thread-local and "shadow" base
> + tables with the same name.
> +
> + @note In most cases one should use Temporary_tables::open_tables() instead
> + of this call.
> +
> + @note One should finalize process of opening temporary table for table
> + list element by calling open_and_process_table(). This function
> + is responsible for table version checking and handling of merge
> + tables.
> +
> + @note We used to check global_read_lock before opening temporary tables.
> + However, that limitation was artificial and is removed now.
> +
> + @param tl [IN] TABLE_LIST
> +
> + @return Error status.
> + @retval false On success. If a temporary table exists for the given
> + key, tl->table is set.
> + @retval TRUE On error. my_error() has been called.
letter case is a bit weird, 'false', but 'TRUE' :)
> +*/
> +bool Temporary_tables::open_table(TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::open_table");
> +
> + TMP_TABLE_SHARE *share;
> + TABLE *table= NULL;
> + bool locked;
> +
> + /*
> + Code in open_table() assumes that TABLE_LIST::table can be non-zero only
> + for pre-opened temporary tables.
> + */
> + DBUG_ASSERT(tl->table == NULL);
> +
> + /*
> + This function should not be called for cases when derived or I_S
> + tables can be met since table list elements for such tables can
> + have invalid db or table name.
> + Instead Temporary_tables::open_tables() should be used.
> + */
> + DBUG_ASSERT(!tl->derived && !tl->schema_table);
> +
> + if (tl->open_type == OT_BASE_ONLY || is_empty(true))
> + {
> + DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
> + DBUG_RETURN(false);
> + }
> +
> + if (wait_for_prior_commit())
this wasn't in the original code
> + {
> + DBUG_RETURN(true);
> + }
> +
> + locked= lock_tables();
neither was this
> +
> + /*
> + First check if there is a reusable open table available in the
> + open table list.
> + */
> + if (find_and_use_table(tl, &table))
> + {
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> + DBUG_RETURN(true); /* Error */
> + }
> +
> + /*
> + No reusable table was found. We will have to open a new instance.
> + */
> + if (!table && (share= find_table_share(tl)))
> + {
> + table= open_table(share, tl->get_table_name(), true);
> + }
> +
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + if (!table)
> + {
> + if (tl->open_type == OT_TEMPORARY_ONLY &&
> + tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
> + {
> + my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name);
> + DBUG_RETURN(true);
> + }
> + DBUG_RETURN(false);
> + }
> +
> +#ifdef WITH_PARTITION_STORAGE_ENGINE
> + if (tl->partition_names)
> + {
> + /* Partitioned temporary tables is not supported. */
> + DBUG_ASSERT(!table->part_info);
> + my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
> + DBUG_RETURN(true);
> + }
> +#endif
> +
> + table->query_id= m_thd->query_id;
> + m_thd->thread_specific_used= true;
> +
> + /* It is neither a derived table nor non-updatable view. */
> + tl->updatable= true;
> + tl->table= table;
> +
> + table->init(m_thd, tl);
> +
> + DBUG_PRINT("info", ("Using temporary table"));
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Pre-open temporary tables corresponding to table list elements.
> +
> + @note One should finalize process of opening temporary tables
> + by calling open_tables(). This function is responsible
> + for table version checking and handling of merge tables.
what kind of version checking can there be for temporary tables?
> +
> + @param tl [IN] TABLE_LIST
> +
> + @return false On success. If a temporary table exists
> + for the given element, tl->table is set.
> + true On error. my_error() has been called.
> +*/
> +bool Temporary_tables::open_tables(TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::open_tables");
> +
> + TABLE_LIST *first_not_own= m_thd->lex->first_not_own_table();
> +
> + for (TABLE_LIST *table= tl; table && table != first_not_own;
> + table= table->next_global)
> + {
> + if (table->derived || table->schema_table)
> + {
> + /*
> + Derived and I_S tables will be handled by a later call to open_tables().
> + */
> + continue;
> + }
> +
> + if ((m_thd->temporary_tables.open_table(table)))
eh? couldn't you simply do
if (open_table(table))
? why do you need this "m_thd->temporary_tables." ?
> + {
> + DBUG_RETURN(true);
> + }
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
> + creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread.
> +
> + Temporary tables created in a sql slave is closed by
> + Relay_log_info::close_temporary_tables().
> +
> + @return false Success
> + true Failure
> +*/
> +bool Temporary_tables::close_tables()
> +{
> + DBUG_ENTER("Temporary_tables::close_tables");
> +
> + TMP_TABLE_SHARE *share;
> + TMP_TABLE_SHARE *share_next;
> + TABLE *table, *table_next;
> + bool error= false;
> +
> + if (!m_table_shares)
> + {
> + DBUG_RETURN(false);
> + }
> + DBUG_ASSERT(!m_thd->rgi_slave);
> +
> + /*
> + Ensure we don't have open HANDLERs for tables we are about to close.
> + This is necessary when Temporary_tables::close_tables() is called as
> + part of execution of BINLOG statement (e.g. for format description event).
> + */
> + mysql_ha_rm_temporary_tables(m_thd);
> +
> + /* Close all open temporary tables. */
> + for (TMP_TABLE_SHARE *share= m_table_shares; share; share= share->next)
> + {
> + /* Traverse the table list. */
> + table= share->table;
> + while (table)
> + {
> + table_next= table->next;
> + free_table(table);
> + table= table_next;
> + }
> + }
> +
> + // Write DROP TEMPORARY TABLE query log events to binary log.
> + if (mysql_bin_log.is_open())
> + {
> + error= log_events_and_free_shares();
> + }
> + else
> + {
> + share= m_table_shares;
> + while (share)
> + {
> + share_next= share->next;
> + free_table_share(share, true);
> + share= share_next;
> + }
> + }
> + reset();
> +
> + DBUG_RETURN(error);
> +}
> +
> +
> +/*
> + Rename a temporary table.
> +
> + @param table [IN] Table handle
> + @param db [IN] New schema name
> + @param table_name [IN] New table name
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::rename_table(TABLE *table,
> + const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::rename_table");
> +
> + char *key;
> + uint key_length;
> +
> + TMP_TABLE_SHARE *share= static_cast<TMP_TABLE_SHARE *>(table->s);
> +
> + if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
> + {
> + DBUG_RETURN(true);
> + }
> +
> + /*
> + Temporary tables are renamed by simply changing their table definition key.
> + */
> + key_length= create_table_def_key(key, db, table_name);
> + share->set_table_cache_key(key, key_length);
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Drop a temporary table.
> +
> + Try to locate the table in the list of open temporary tables.
> + If the table is found:
> + - If the table is locked with LOCK TABLES or by prelocking,
> + unlock it and remove it from the list of locked tables
> + (THD::lock). Currently only transactional temporary tables
> + are locked.
> + - Close the temporary table, remove its .FRM.
> + - Remove the table share from the list of temporary table shares.
> +
> + This function is used to drop user temporary tables, as well as
> + internal tables created in CREATE TEMPORARY TABLE ... SELECT
> + or ALTER TABLE.
> +
> + @param table [IN] Temporary table to be deleted
> + @param is_trans [OUT] Is set to the type of the table:
> + transactional (e.g. innodb) as true or
> + non-transactional (e.g. myisam) as false.
> + @paral delete_table [IN] Whether to delete the table files.
> +
> + @return false Table was dropped
> + true Error
> +*/
> +bool Temporary_tables::drop_table(TABLE *table,
> + bool *is_trans,
> + bool delete_table)
> +{
> + DBUG_ENTER("Temporary_tables::drop_table");
> +
> + TMP_TABLE_SHARE *share;
> + TABLE *tab;
> + TABLE *tab_next;
> + bool result, locked;
> +
> + DBUG_ASSERT(table);
> + DBUG_ASSERT(table->query_id);
> + DBUG_PRINT("tmptable", ("Dropping table: '%s'.'%s'",
> + table->s->db.str, table->s->table_name.str));
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(true);
> + }
old code didn't seem to have that. Why do you?
> +
> + locked= lock_tables();
> +
> + share= static_cast<TMP_TABLE_SHARE *>(table->s);
suggestion: have a function:
static inline TMP_TABLE_SHARE *tmp_table_share(TABLE *t) {
DBUG_ASSERT(t->s->tmp_table);
return static_cast<TMP_TABLE_SHARE *>(table->s);
}
> +
> + /* Table might be in use by some outer statement. */
> + for (tab= share->table; tab; tab= tab->next)
> + {
> + if (tab != table && tab->query_id != 0)
> + {
> + /* Found a table instance in use. This table cannot be be dropped. */
> + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
> + result= true;
> + goto end;
> + }
> + }
> +
> + if (is_trans)
> + *is_trans= table->file->has_transactions();
> +
> + /*
> + Iterate over the list of open tables and close all tables referencing the
> + same table share.
the comment sounds a bit odd now, when all open tables are in the
list inside the share.
> + */
> + tab= share->table;
> + while (tab)
> + {
> + tab_next= tab->next;
> + if ((result= free_table(tab))) goto end;
> + tab= tab_next;
> + }
> +
> + result= free_table_share(share, delete_table);
> +
> +end:
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/**
> + Delete the temporary table files.
> +
> + @param base [IN] Handlerton for table to be deleted.
> + @param path [IN] Path to the table to be deleted (i.e. path
> + to its .frm without an extension).
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::remove_table(handlerton *base, const char *path)
> +{
> + DBUG_ENTER("Temporary_tables::remove_table");
> +
> + bool error= false;
> + handler *file;
> + char frm_path[FN_REFLEN + 1];
> +
> + strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
> + if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
> + {
> + error= true;
> + }
> + file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
> + if (file && file->ha_delete_table(path))
> + {
> + error= true;
> + sql_print_warning("Could not remove temporary table: '%s', error: %d",
> + path, my_errno);
> + }
> +
> + delete file;
> + DBUG_RETURN(error);
> +}
> +
> +
> +/*
> + Mark all temporary tables which were used by the current statement or
> + sub-statement as free for reuse, but only if the query_id can be cleared.
> +
> + @remark For temp tables associated with a open SQL HANDLER the query_id
> + is not reset until the HANDLER is closed.
> +*/
> +void Temporary_tables::mark_tables_as_free_for_reuse()
> +{
> + DBUG_ENTER("Temporary_tables::mark_tables_as_free_for_reuse");
> +
> + bool locked;
> +
> + if (m_thd->query_id == 0)
> + {
> + /*
> + Thread has not executed any statement and has not used any
> + temporary tables.
> + */
> + DBUG_VOID_RETURN;
> + }
> +
> + locked= lock_tables();
> +
> + for (TMP_TABLE_SHARE *share= m_table_shares; share; share= share->next)
> + {
> + for (TABLE *table= share->table; table; table= table->next)
> + {
> + if ((table->query_id == m_thd->query_id) && !table->open_by_handler)
> + {
> + mark_table_as_free_for_reuse(table);
> + }
> + }
> + }
> +
> + if (locked)
> + {
> + DBUG_ASSERT(m_locked);
> + unlock_tables();
> + }
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/*
> + Reset a single temporary table. Effectively this "closes" one temporary
> + table in a session.
> +
> + @param table Temporary table
> +*/
> +void Temporary_tables::mark_table_as_free_for_reuse(TABLE *table)
> +{
> + DBUG_ENTER("Temporary_tables::mark_table_as_free_for_reuse");
> +
> + DBUG_ASSERT(table->s->tmp_table);
> +
> + table->query_id= 0;
> + table->file->ha_reset();
> +
> + /* Detach temporary MERGE children from temporary parent. */
> + DBUG_ASSERT(table->file);
> + table->file->extra(HA_EXTRA_DETACH_CHILDREN);
> +
> + /*
> + Reset temporary table lock type to it's default value (TL_WRITE).
> +
> + Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
> + .. SELECT FROM tmp and UPDATE may under some circumstances modify
> + the lock type of the tables participating in the statement. This
> + isn't a problem for non-temporary tables since their lock type is
> + reset at every open, but the same does not occur for temporary
> + tables for historical reasons.
> +
> + Furthermore, the lock type of temporary tables is not really that
> + important because they can only be used by one query at a time.
> + Nonetheless, it's safer from a maintenance point of view to reset
> + the lock type of this singleton TABLE object as to not cause problems
> + when the table is reused.
> +
> + Even under LOCK TABLES mode its okay to reset the lock type as
> + LOCK TABLES is allowed (but ignored) for a temporary table.
> + */
> + table->reginfo.lock_type= TL_WRITE;
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +TMP_TABLE_SHARE *Temporary_tables::unlink_table_share(TABLE_SHARE *share)
> +{
> + DBUG_ENTER("Temporary_tables::unlink_table");
> + TMP_TABLE_SHARE *tmp_table_share;
> +
> + lock_tables();
> + tmp_table_share= static_cast<TMP_TABLE_SHARE *>(share);
> + unlink<TMP_TABLE_SHARE>(&m_table_shares, tmp_table_share);
> + unlock_tables();
> +
> + DBUG_RETURN(tmp_table_share);
> +}
> +
> +
> +void Temporary_tables::relink_table_share(TMP_TABLE_SHARE *share)
> +{
> + DBUG_ENTER("Temporary_tables::relink_table");
> +
> + lock_tables();
> + link<TMP_TABLE_SHARE>(&m_table_shares, share);
> + unlock_tables();
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/*
> + Create a table definition key.
> +
> + @param key [OUT] Buffer for the key to be created (must
> + be of size MAX_DBKRY_LENGTH)
> + @param db [IN] Database name
> + @param table_name [IN] Table name
> +
> + @return Key length.
> +
> + @note
> + The table key is create from:
> + db + \0
> + table_name + \0
> +
> + Additionally, we add the following to make each temporary table unique on
> + the slave.
> +
> + 4 bytes of master thread id
> + 4 bytes of pseudo thread id
> +*/
> +
> +uint Temporary_tables::create_table_def_key(char *key, const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::create_table_def_key");
> +
> + uint key_length;
> +
> + key_length= tdc_create_key(key, db, table_name);
> + int4store(key + key_length, m_thd->variables.server_id);
> + int4store(key + key_length + 4, m_thd->variables.pseudo_thread_id);
> + key_length += TMP_TABLE_KEY_EXTRA;
> +
> + DBUG_RETURN(key_length);
> +}
> +
> +
> +/*
> + Create a temporary table.
> +
> + @param hton [IN] Handlerton
> + @param frm [IN] Binary frm image
> + @param path [IN] File path (without extension)
> + @param db [IN] Schema name
> + @param table_name [IN] Table name
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TMP_TABLE_SHARE *Temporary_tables::create_table(handlerton *hton,
> + LEX_CUSTRING *frm,
> + const char *path,
> + const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::create_table");
> +
> + TMP_TABLE_SHARE *share;
> + char key_cache[MAX_DBKEY_LENGTH];
> + char *saved_key_cache;
> + char *tmp_path;
> + uint key_length;
> + int res;
> +
> + if (wait_for_prior_commit())
you're doing it in almost every method. And when one method calls another,
you're doing it twice. Or thrice. Isn't is too much? (I don't know)
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + /* Create the table definition key for the temporary table. */
> + key_length= create_table_def_key(key_cache, db, table_name);
> +
> + if (!(share= (TMP_TABLE_SHARE *) my_malloc(sizeof(TMP_TABLE_SHARE) +
> + strlen(path) +
> + 1 + key_length, MYF(MY_WME))))
> + {
> + DBUG_RETURN(NULL); /* Out of memory */
> + }
> +
> + tmp_path= (char *)(share + 1);
> + saved_key_cache= strmov(tmp_path, path) + 1;
> + memcpy(saved_key_cache, key_cache, key_length);
> +
> + init_tmp_table_share(m_thd, share, saved_key_cache, key_length,
> + strend(saved_key_cache) + 1, tmp_path);
> +
> + share->table= 0;
> + share->db_plugin= ha_lock_engine(m_thd, hton);
> +
> + /*
> + Prefer using frm image over file. The image might not be available in
> + ALTER TABLE, when the discovering engine took over the ownership (see
> + TABLE::read_frm_image).
> + */
> + res= (frm->str)
> + ? share->init_from_binary_frm_image(m_thd, false, frm->str, frm->length)
> + : open_table_def(m_thd, share, GTS_TABLE | GTS_USE_DISCOVERY);
> +
> + if (res)
> + {
> + /*
> + No need to lock share->mutex as this is not needed for temporary tables.
> + */
> + ::free_table_share(share);
> + my_free(share);
> + DBUG_RETURN(NULL);
> + }
> +
> + share->m_psi= PSI_CALL_get_table_share(true, share);
> +
> + /* Add share to the head of the temporary table share list. */
> + link<TMP_TABLE_SHARE>(&m_table_shares, share);
> +
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Find a table with the specified key.
> +
> + @param key [IN] Key
> + @param key_length [IN] Key length
> + @param state [IN] Open table state to look for
> +
> + @return Success Pointer to the table instance.
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_table(const char *key, uint key_length,
> + Table_state state)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> +
> + for (TMP_TABLE_SHARE *share= m_table_shares; share; share= share->next)
> + {
> + if (share->table_cache_key.length == key_length &&
> + !(memcmp(share->table_cache_key.str, key, key_length)))
> + {
> + /* A matching TMP_TABLE_SHARE is found. */
> + for (TABLE *table= share->table; table; table= table->next)
> + {
> + switch (state)
> + {
> + case TABLE_IN_USE:
> + if (table->query_id > 0) DBUG_RETURN(table);
> + break;
> + case TABLE_NOT_IN_USE:
> + if (table->query_id == 0) DBUG_RETURN(table);
> + break;
> + case TABLE_ANY:
> + DBUG_RETURN(table);
> + default: /* Invalid */
> + DBUG_ASSERT(0);
> + DBUG_RETURN(NULL);
> + }
> + }
> + }
> + }
> +
> + DBUG_RETURN(NULL);
> +}
> +
> +
> +/*
> + Find a reusable table in the open table list using the specified TABLE_LIST.
> +
> + @param tl [IN] Table list
> + @param out_table [OUT] Pointer to the requested TABLE object
> +
> + @return Success false
> + Failure true
> +*/
> +bool Temporary_tables::find_and_use_table(const TABLE_LIST *tl,
> + TABLE **out_table)
> +{
> + DBUG_ENTER("Temporary_tables::find_and_use_table");
> +
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> + bool result;
> +
> + key_length= create_table_def_key(key, tl->get_db_name(),
> + tl->get_table_name());
> + result= use_table(find_table(key, key_length, TABLE_NOT_IN_USE), out_table);
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Open a table from the specified TABLE_SHARE with the given alias.
> +
> + @param share [IN] Table share
> + @param alias [IN] Table alias
> + @param open_in_engine [IN] Whether open table in SE
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::open_table(TMP_TABLE_SHARE *share,
> + const char *alias,
> + bool open_in_engine)
> +{
> + DBUG_ENTER("Temporary_tables::open_table");
> +
> + TABLE *table;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
> + {
> + DBUG_RETURN(NULL); /* Out of memory */
> + }
> +
> + if (open_table_from_share(m_thd, share, alias,
> + (open_in_engine) ?
> + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
> + HA_GET_INDEX) : 0,
> + (uint) (READ_KEYINFO | COMPUTE_TYPES |
> + EXTRA_RECORD),
> + ha_open_options,
> + table,
> + open_in_engine ? false : true))
> + {
> + my_free(table);
> + DBUG_RETURN(NULL);
> + }
> +
> + table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
> + table->grant.privilege= TMP_TABLE_ACLS;
> + share->tmp_table= (table->file->has_transactions() ?
> + TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
> +
> + table->pos_in_table_list= 0;
> + table->query_id= m_thd->query_id;
> +
> + /* Add table to the head of table list. */
> + link<TABLE>(&share->table, table);
I don't know if that's correct. You use TABLE::next and TABLE::prev pointers
to link all tables of a given TMP_TABLE_SHARE into a list. But these
pointers are used to link all tables into a THD::open_tables list
(and may be for other purposes too?).
In fact, TABLE has dedicated pointers for this list that you want:
/**
Links for the list of all TABLE objects for this share.
Declared as private to avoid direct manipulation with those objects.
One should use methods of I_P_List template instead.
*/
TABLE *share_all_next, **share_all_prev;
> +
> + /* Increment Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_increment32(&slave_open_temp_tables);
> + }
> +
> + DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s' 0x%lx", table->s->db.str,
> + table->s->table_name.str, (long) table));
> + DBUG_RETURN(table);
> +}
> +
> +
function comment?
> +bool Temporary_tables::use_table(TABLE *table, TABLE **out_table)
> +{
> + DBUG_ENTER("Temporary_tables::use_table");
> +
> + *out_table= table;
> + if (!table)
> + DBUG_RETURN(false);
> +
old code had a block here about "Temporary tables are not safe for parallel
replication". it's in wait_for_prior_commit now, but you aren't calling it
here. why?
> + /*
> + We need to set the THD as it may be different in case of
> + parallel replication
> + */
> + if (table->in_use != m_thd)
> + {
> + table->in_use= m_thd;
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Close a temporary table.
> +
> + @param table [IN] Table handle
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::close_table(TABLE *table)
> +{
> + DBUG_ENTER("Temporary_tables::close_table");
> +
> + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx alias: '%s'",
> + table->s->db.str, table->s->table_name.str,
> + (long) table, table->alias.c_ptr()));
> +
> + free_io_cache(table);
don't forget to remove free_io_cache() calls when rebasing your
work on top of the latest 10.2
(they were removed from close_temporary() in 260dd476b05 commit)
> + closefrm(table, false);
> + my_free(table);
> +
> + /* Decrement Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_decrement32(&slave_open_temp_tables);
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +bool Temporary_tables::wait_for_prior_commit()
> +{
> + DBUG_ENTER("Temporary_tables::wait_for_prior_commit");
> +
> + /*
> + Temporary tables are not safe for parallel replication. They were
> + designed to be visible to one thread only, so have no table locking.
> + Thus there is no protection against two conflicting transactions
> + committing in parallel and things like that.
> +
> + So for now, anything that uses temporary tables will be serialised
> + with anything before it, when using parallel replication.
> +
> + TODO: We might be able to introduce a reference count or something
> + on temp tables, and have slave worker threads wait for it to reach
> + zero before being allowed to use the temp table. Might not be worth
> + it though, as statement-based replication using temporary tables is
> + in any case rather fragile.
> + */
> + if (m_thd->rgi_slave &&
> + m_thd->rgi_slave->rli->save_temp_table_shares &&
> + m_thd->rgi_slave->is_parallel_exec &&
> + m_thd->wait_for_prior_commit())
> + DBUG_RETURN(true);
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Write query log events with "DROP TEMPORARY TABLES .." for each pseudo
> + thread to the binary log.
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::log_events_and_free_shares()
> +{
> + DBUG_ENTER("Temporary_tables::log_events_and_free_shares");
> +
> + DBUG_ASSERT(!m_thd->rgi_slave);
> +
> + TMP_TABLE_SHARE *share;
> + TMP_TABLE_SHARE *next;
> + TMP_TABLE_SHARE *prev_share;
> + // Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE.
> + bool was_quote_show= true;
> + bool error= false;
> + bool found_user_tables= false;
> + // Better add "IF EXISTS" in case a RESET MASTER has been done.
> + const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
> + char buf[FN_REFLEN];
> +
> + String s_query(buf, sizeof(buf), system_charset_info);
> + s_query.copy(stub, sizeof(stub) - 1, system_charset_info);
> +
> + /*
> + Insertion sort of temporary tables by pseudo_thread_id to build ordered
> + list of sublists of equal pseudo_thread_id.
> + */
> +
> + for (prev_share= m_table_shares, share= prev_share->next;
> + share;
> + prev_share= share, share= share->next)
> + {
> + TMP_TABLE_SHARE *prev_sorted; /* Same as for prev_share */
> + TMP_TABLE_SHARE *sorted;
> +
> + if (IS_USER_TABLE(share))
> + {
> + if (!found_user_tables)
> + found_user_tables= true;
> +
> + for (prev_sorted= NULL, sorted= m_table_shares;
> + sorted != share;
> + prev_sorted= sorted, sorted= sorted->next)
> + {
> + if (!IS_USER_TABLE(sorted) ||
> + tmpkeyval(sorted) > tmpkeyval(share))
> + {
> + /*
> + Move into the sorted part of the list from the unsorted.
> + */
> + prev_share->next= share->next;
> + share->next= sorted;
> + if (prev_sorted)
> + {
> + prev_sorted->next= share;
> + }
> + else
> + {
> + m_table_shares= share;
> + }
> + share= prev_share;
> + break;
> + }
> + }
> + }
> + }
> +
> + /*
> + We always quote db & table names.
> + */
> + if (found_user_tables &&
> + !(was_quote_show= MY_TEST(m_thd->variables.option_bits &
> + OPTION_QUOTE_SHOW_CREATE)))
> + {
> + m_thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
> + }
> +
> + /*
> + Scan sorted temporary tables to generate sequence of DROP.
> + */
> + for (share= m_table_shares; share; share= next)
> + {
> + if (IS_USER_TABLE(share))
> + {
> + bool save_thread_specific_used= m_thd->thread_specific_used;
> + my_thread_id save_pseudo_thread_id= m_thd->variables.pseudo_thread_id;
> + char db_buf[FN_REFLEN];
> + String db(db_buf, sizeof(db_buf), system_charset_info);
> +
> + /*
> + Set pseudo_thread_id to be that of the processed table.
> + */
> + m_thd->variables.pseudo_thread_id= tmpkeyval(share);
> +
> + db.copy(share->db.str, share->db.length, system_charset_info);
> + /*
> + Reset s_query() if changed by previous loop.
> + */
> + s_query.length(sizeof(stub) - 1);
> +
> + /*
> + Loop forward through all tables that belong to a common database
> + within the sublist of common pseudo_thread_id to create single
> + DROP query.
> + */
> + for (;
> + share &&
> + IS_USER_TABLE(share) &&
> + tmpkeyval(share) == m_thd->variables.pseudo_thread_id &&
> + share->db.length == db.length() &&
> + memcmp(share->db.str, db.ptr(), db.length()) == 0;
> + share= next)
> + {
> + /*
> + We are going to add ` around the table names and possible more
> + due to special characters.
> + */
> + append_identifier(m_thd, &s_query, share->table_name.str,
> + share->table_name.length);
> + s_query.append(',');
> + next= share->next;
> + remove_table(share->db_type(), share->path.str);
> + ::free_table_share(share);
> + my_free(share);
> + }
> +
> + m_thd->clear_error();
> + CHARSET_INFO *cs_save= m_thd->variables.character_set_client;
> + m_thd->variables.character_set_client= system_charset_info;
> + m_thd->thread_specific_used= true;
> +
> + Query_log_event qinfo(m_thd, s_query.ptr(),
> + s_query.length() - 1 /* to remove trailing ',' */,
> + false, true, false, 0);
> + qinfo.db= db.ptr();
> + qinfo.db_len= db.length();
> + m_thd->variables.character_set_client= cs_save;
> +
> + m_thd->get_stmt_da()->set_overwrite_status(true);
> + if ((error= (mysql_bin_log.write(&qinfo) || error)))
> + {
> + /*
> + If we're here following THD::cleanup, thence the connection
> + has been closed already. So lets print a message to the
> + error log instead of pushing yet another error into the
> + stmt_da.
> +
> + Also, we keep the error flag so that we propagate the error
> + up in the stack. This way, if we're the SQL thread we notice
> + that Temporary_tables::close_tables failed. (Actually, the SQL
> + thread only calls Temporary_tables::close_tables while applying
> + old Start_log_event_v3 events.)
> + */
> + sql_print_error("Failed to write the DROP statement for "
> + "temporary tables to binary log");
> + }
> +
> + m_thd->get_stmt_da()->set_overwrite_status(false);
> + m_thd->variables.pseudo_thread_id= save_pseudo_thread_id;
> + m_thd->thread_specific_used= save_thread_specific_used;
> + }
> + else
> + {
> + next= share->next;
> + free_table_share(share, true);
> + }
> + }
> +
> + if (!was_quote_show)
> + {
> + /*
> + Restore option.
> + */
> + m_thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
> + }
> +
> + DBUG_RETURN(error);
> +}
> +
> +/*
> + Delete the files and free the specified table share.
> +*/
> +bool Temporary_tables::free_table_share(TMP_TABLE_SHARE *share,
> + bool delete_table)
> +{
> + DBUG_ENTER("Temporary_tables::free_table_share");
> +
> + bool result= false;
> +
> + /* Delete the share from table share list */
> + unlink<TMP_TABLE_SHARE>(&m_table_shares, share);
> +
> + if (delete_table)
> + {
> + result= remove_table(share->db_type(), share->path.str);
> + }
> +
> + ::free_table_share(share);
> + my_free(share);
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Free the specified table object.
> +*/
> +bool Temporary_tables::free_table(TABLE *table)
> +{
> + DBUG_ENTER("Temporary_tables::free_table");
> +
> + bool result= false;
> +
> + /* Delete the table from table list */
> + unlink<TABLE>(&static_cast<TMP_TABLE_SHARE *>(table->s)->table, table);
> +
> + /*
> + If LOCK TABLES list is not empty and contains this table, unlock the table
> + and remove the table from this list.
> + */
> + mysql_lock_remove(m_thd, m_thd->lock, table);
> +
> + result= close_table(table);
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +bool Temporary_tables::lock_tables()
> +{
> + /* Do not proceed if a lock has already been taken. */
> + if (m_locked)
> + {
> + return false;
> + }
> +
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> + if (rgi_slave)
> + {
> + mysql_mutex_lock(&rgi_slave->rli->data_lock);
> + m_table_shares= rgi_slave->rli->save_temp_table_shares;
> + m_locked= true;
> + }
> + return m_locked;
> +}
> +
> +
> +void Temporary_tables::unlock_tables()
> +{
> + if (!m_locked)
> + {
> + return;
> + }
> +
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> + if (rgi_slave)
> + {
> + rgi_slave->rli->save_temp_table_shares= m_table_shares;
> + mysql_mutex_unlock(&rgi_slave->rli->data_lock);
> + m_locked= false;
> + /*
> + Temporary tables are shared with other by sql execution threads.
> + As a safety measure, clear the pointer to the common area.
> + */
> + reset();
> + }
> + return;
> +}
> +
> diff --git a/sql/temporary_tables.h b/sql/temporary_tables.h
> new file mode 100644
> index 0000000..5a5ed0d
> --- /dev/null
> +++ b/sql/temporary_tables.h
> @@ -0,0 +1,150 @@
> +#ifndef TEMPORARY_TABLES_H
> +#define TEMPORARY_TABLES_H
> +/*
> + Copyright (c) 2016 MariaDB Corporation
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> +*/
> +
> +#define TMP_TABLE_KEY_EXTRA 8
> +
> +struct TMP_TABLE_SHARE: public TABLE_SHARE
> +{
> + /* List of TABLE instances created out of this TABLE_SHARE. */
> + TABLE *table;
Hmm, if you will use TABLE::share_all_prev and TABLE::share_all_next, as
I've written above, you should also use TDC_element that keeps the list
of TABLE's per TABLE_SHARE. And also it has next/prev pointers
to link all TABLE_SHARE's in a list... Perhaps you won't need
TMP_TABLE_SHARE after all? Could you talk with Svoj about it, please?
perhaps you can simply reuse existing TABLE_SHARE code
for linking TABLE_SHARE's into a list and linking TABLE's into
a per-TABLE_SHARE list? TDC_element is a bit of an overkill for
temporary tables, we can either live with that or extract those
bits that you need into a "mini-TDC_element" ? Dunno, best to discuss it
with Svoj.
> +
> + /* Pointers to access TMP_TABLE_SHARE instances. */
> + TMP_TABLE_SHARE *next;
> + TMP_TABLE_SHARE *prev;
> +};
> +
> +
> +class Temporary_tables
> +{
> +public:
> + Temporary_tables() : m_thd(0), m_table_shares(0), m_locked(false)
> + {}
> + bool init(THD *thd);
> + bool is_empty(bool check_slave);
> + bool reset();
> +
> + TABLE *create_and_open_table(handlerton *hton, LEX_CUSTRING *frm,
> + const char *path, const char *db,
> + const char *table_name, bool open_in_engine,
> + bool *created);
> +
> + TABLE *find_table(const char *db, const char *table_name);
> + TABLE *find_table(const TABLE_LIST *tl);
> + TABLE *find_table_reduced_key_length(const char *key, uint key_length);
> +
> + TMP_TABLE_SHARE *find_table_share(const char *db, const char *table_name);
> + TMP_TABLE_SHARE *find_table_share(const TABLE_LIST *tl);
> + TMP_TABLE_SHARE *find_table_share(const char *key, uint key_length);
> +
> + bool open_table(TABLE_LIST *tl);
> + bool open_tables(TABLE_LIST *tl);
> +
> + bool close_tables();
> + bool rename_table(TABLE *table, const char *db, const char *table_name);
> + bool drop_table(TABLE *table, bool *is_trans, bool delete_table);
> + bool remove_table(handlerton *hton, const char *path);
> + void mark_tables_as_free_for_reuse();
> + void mark_table_as_free_for_reuse(TABLE *table);
> +
> + TMP_TABLE_SHARE *unlink_table_share(TABLE_SHARE *share);
> + void relink_table_share(TMP_TABLE_SHARE *share);
> +
> +private:
> + /* THD handler */
> + THD *m_thd;
already discussed
> +
> + /* List of temporary table shares */
> + TMP_TABLE_SHARE *m_table_shares;
> +
> + /* Whether a lock has been acquired. */
> + bool m_locked;
> +
> + /* Opened table states. */
> + enum Table_state {
> + TABLE_IN_USE,
> + TABLE_NOT_IN_USE,
> + TABLE_ANY
> + };
> +
> + uint create_table_def_key(char *key,
> + const char *db,
> + const char *table_name);
> +
> + TMP_TABLE_SHARE *create_table(handlerton *hton,
> + LEX_CUSTRING *frm,
> + const char *path,
> + const char *db,
> + const char *table_name);
> +
> + TABLE *find_table(const char *key, uint key_length, Table_state state);
> +
> + bool find_and_use_table(const TABLE_LIST *tl, TABLE **out_table);
> +
> + TABLE *open_table(TMP_TABLE_SHARE *share, const char *alias,
> + bool open_in_engine);
> +
> + bool use_table(TABLE *table, TABLE **out_table);
> + bool close_table(TABLE *table);
> + bool wait_for_prior_commit();
> + bool log_events_and_free_shares();
> +
> + bool free_table_share(TMP_TABLE_SHARE *share, bool delete_table);
> + bool free_table(TABLE *table);
> +
> + bool lock_tables();
> + void unlock_tables();
> +
> + /* List operations */
> + template <class T>
> + void link(T **list, T *element)
> + {
> + element->next= *list;
> + if (element->next)
> + element->next->prev= element;
> + *list= element;
> + (*list)->prev= 0;
> + }
> +
> + template <class T>
> + void unlink(T **list, T *element)
> + {
> + if (element->prev)
> + {
> + element->prev->next= element->next;
> + if (element->prev->next)
> + element->next->prev= element->prev;
> + }
> + else
> + {
> + DBUG_ASSERT(element == *list);
> +
> + *list= element->next;
> + if (*list)
> + element->next->prev= 0;
> + }
> + }
No need to reinvent the wheel, you can use I_P_List for that.
> +
> + uint tmpkeyval(TMP_TABLE_SHARE *share)
> + {
> + return uint4korr(share->table_cache_key.str +
> + share->table_cache_key.length - 4);
> + }
> +};
> +
> +#endif /* TEMPORARY_TABLES_H */
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
3
5
Hello Sergei,
This script:
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (a VARCHAR(30), b DOUBLE);
INSERT INTO t1 values('aaaa bbbb cccc dddd', MATCH (a) AGAINST('bbbb' IN
BOOLEAN MODE));
SELECT * FROM t1;
returns:
+---------------------+------+
| a | b |
+---------------------+------+
| aaaa bbbb cccc dddd | -1 |
+---------------------+------+
What does this -1 mean?
Is this a bug?
Thanks!
3
5
Hi,
Here is the week 2 report
WEEK 2
dire = mariadb/server
task - adding 'aggregate' field to the mysql.proc table.
sub tasks
1) aggregate field ---> mysql.proc table
2)create function -----> aggregate and non-aggregate
3)show create function ----> aggregate and non-aggregate
4)drop function -----> aggregate and non-aggregate
5)alter function -----> aggregate and non-aggregate
For the user he has two options for AGGREGATE (TRUE,FALSE);
details
1) Added the field 'aggregate' to the proc table .
Order should be maintained for the fields added.
2) Also included the column in the enumberation of db storage.
3) Added a new is_aggregate characteristic to the lex structure, which
would help in identifying if a function is aggregate or not.
4) So after parsing the CREATE FUNCTION query ,adding to the table YES if
function is aggregate else adding NO to the proc table.
5) SHOW FUNCTION query to load the aggregate field. We have the
is_aggregate field to load the value in it.
6) DROP FUNCTION, required no changes to be made as the primary key does
not have the aggregate field
7) ALTER FUNCTION , required adding the syntax for the AGGREGATE
characteristic , so rules were added to add this additional alter
characteristic. Then we had to update the routine row in the proc
table for all the field that are going to be altered . So we store the new
altered fields to the proc table for the stated fields.
2
1

06 Jun '16
Hello Everyone,
Actually I was thinking why there is so small limit on virtual column
string.Does it have any design implication or some other reason
Regards
sachin
2
1
Hi,
I have added the test where I have put both ALTER,CREATE and SHOW FUNCTION
queries. Should I make separate test for the queries or clubbing them in
one is fine .
On Sat, Jun 4, 2016 at 7:25 PM, Varun Gupta <varungupta1803(a)gmail.com>
wrote:
> Hi,
> I have also written a test for the alter, show and create functions. I
> have pushed it, also I am about to go through some tests already written
> and try to make the tests similar to them.
>
> On Sat, Jun 4, 2016 at 5:34 PM, Varun Gupta <varungupta1803(a)gmail.com>
> wrote:
>
>> Hi,
>> I am done with ALTER FUNCTION, i have done this by adding new field. I
>> have committed the code. Please review.
>> Also I wanted to discuss more about the new filed which i added to the
>> sp_chistics struct.
>> Also I am writing tests for the alter queries. I would also need some
>> more suggestions about what I should do next .
>>
>>
>> On Sat, Jun 4, 2016 at 10:49 AM, Varun Gupta <varungupta1803(a)gmail.com>
>> wrote:
>>
>>> I do but to update the function, the function prototype of
>>> sp_update_routine() is
>>> sp_update_routine(THD *thd, stored_procedure_type
>>> type, sp_name *name,st_sp_chistics *chistics), so to pass if the field to
>>> be updated is AGGREGATE I have to have some way to send the updated value
>>> to the above function.
>>>
>>> On Sat, Jun 4, 2016 at 2:24 AM, Sanja <sanja.byelkin(a)gmail.com> wrote:
>>>
>>>> Don't you have it in sp_head?
>>>> Am 03.06.2016 22:33 schrieb "Varun Gupta" <varungupta1803(a)gmail.com>:
>>>>
>>>>> Hi,
>>>>> I need a bit of suggestion on how to pass that we are changing the
>>>>> aggregate field, from the parser I need to send the field so in the
>>>>> mysql_execute_function I can call the sp_update_routine.
>>>>>
>>>>> My ideas is
>>>>> 1) addition of one field to lex structure for aggregate.
>>>>> 2) instead of having is_aggregate in sp_head, we could put that field
>>>>> in in sp_name.
>>>>>
>>>>> On Fri, Jun 3, 2016 at 11:41 PM, Varun Gupta <varungupta1803(a)gmail.com
>>>>> > wrote:
>>>>>
>>>>>> Hi,
>>>>>> I have added the syntax , patch committed, please review it :)
>>>>>>
>>>>>> On Fri, Jun 3, 2016 at 10:35 PM, Varun Gupta <
>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> After going through the code I come to the conclusion that a rule
>>>>>>> for AGGREGATE field needs to be added to the ALTER FUNCTION
>>>>>>> characteristics. So I am going forward with adding this syntax.
>>>>>>>
>>>>>>> On Fri, Jun 3, 2016 at 6:14 PM, Sanja <sanja.byelkin(a)gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi!
>>>>>>>>
>>>>>>>> ALTER TABLE usually has the same syntax as CREATE (at least I do
>>>>>>>> not remember adding option like here) so I doubts that it is correct.
>>>>>>>>
>>>>>>>> On Fri, Jun 3, 2016 at 2:40 PM, Varun Gupta <
>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>
>>>>>>>>> Hi,
>>>>>>>>> For the alter function func_name field value,
>>>>>>>>> the syntax we have is
>>>>>>>>> ALTER FUNCTION_SYM sp_name sp_a_chistics
>>>>>>>>>
>>>>>>>>> sp_a_chistics:
>>>>>>>>> /* Empty */ {}
>>>>>>>>> | sp_a_chistics sp_chistic {}
>>>>>>>>>
>>>>>>>>> sp_a_chistics:
>>>>>>>>> | AGGREGSTE_SYM option
>>>>>>>>> option:
>>>>>>>>> | YES
>>>>>>>>> | NO
>>>>>>>>>
>>>>>>>>> or option could be any string.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Fri, Jun 3, 2016 at 1:22 PM, Varun Gupta <
>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> Hi,
>>>>>>>>>> The error is fixed , now show works for aggregate functions too :)
>>>>>>>>>>
>>>>>>>>>> On Fri, Jun 3, 2016 at 1:01 PM, Varun Gupta <
>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>>> For non aggregate function , show output is correct and when I
>>>>>>>>>>> run the query SELECT * from mysql.proc table, aggregate field shows NO.
>>>>>>>>>>> For aggregate function, when I run the query SELECT * from
>>>>>>>>>>> mysql.proc table, aggregate field shows YES.
>>>>>>>>>>>
>>>>>>>>>>> On Fri, Jun 3, 2016 at 12:56 PM, Varun Gupta <
>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Yes it does, but I am not adding anything to the buffer in that
>>>>>>>>>>>> case.
>>>>>>>>>>>>
>>>>>>>>>>>> On Fri, Jun 3, 2016 at 12:53 PM, Sanja <sanja.byelkin(a)gmail.com
>>>>>>>>>>>> > wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Could you be more specific? what test?
>>>>>>>>>>>>>
>>>>>>>>>>>>> Does your code change SHOW for non-aggregate function?
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Fri, Jun 3, 2016 at 9:20 AM, Varun Gupta <
>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>> To solve the problem with the buffer ,can you tell me is
>>>>>>>>>>>>>> there any tests that are run before I run my own tests .
>>>>>>>>>>>>>> I meant if I change the value of
>>>>>>>>>>>>>> the buf->append(STRING_WITH_LEN("FUNCTION "));
>>>>>>>>>>>>>> to buf->append(STRING_WITH_LEN("pUNCTION ")); , even then i get the same
>>>>>>>>>>>>>> error.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Fri, Jun 3, 2016 at 1:32 AM, Varun Gupta <
>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Have you gone through the patch. Do you like that I have
>>>>>>>>>>>>>>> added a field agg_res to the functions ? Or should I find some other to
>>>>>>>>>>>>>>> figure it out .
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 10:59 PM, Varun Gupta <
>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> patch commited .
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 10:53 PM, Vicențiu Ciorbaru <
>>>>>>>>>>>>>>>> cvicentiu(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Strange. Need to try this out myself. If you don't figure
>>>>>>>>>>>>>>>>> it out I'll come back later tonight with some info.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Make sure the lengths are indeed correct. Maybe you are
>>>>>>>>>>>>>>>>> getting a segfault there. Or maybe some code expects that string to have a
>>>>>>>>>>>>>>>>> different length for some reason.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Vicentiu
>>>>>>>>>>>>>>>>> On Thu, 2 Jun 2016 at 20:21, Varun Gupta <
>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> If I just remove the buf->append statement it works .
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 10:48 PM, Vicențiu Ciorbaru <
>>>>>>>>>>>>>>>>>> cvicentiu(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Hi Varun,
>>>>>>>>>>>>>>>>>>> Your problem is before this code I think. Does it work
>>>>>>>>>>>>>>>>>>> without your changes in show_create_sp?
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> Vicentiu
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> On Thu, 2 Jun 2016 at 20:16, Varun Gupta <
>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> In the file sp.cc, function show_create_sp, we have the
>>>>>>>>>>>>>>>>>>>> buffer that holds the output .
>>>>>>>>>>>>>>>>>>>> 1) First i have the buf->alloc , here I change the
>>>>>>>>>>>>>>>>>>>> size to also include AGGREGATE.
>>>>>>>>>>>>>>>>>>>> 2) Then i do buf->append(STRING_WITH_LEN("AGGREGATE
>>>>>>>>>>>>>>>>>>>> ")), but this gives an error,
>>>>>>>>>>>>>>>>>>>> ERROR : At line 78: query 'call mtr.check_testcase()'
>>>>>>>>>>>>>>>>>>>> failed: 1457: Failed to load routine mtr.check_testcase. The table
>>>>>>>>>>>>>>>>>>>> mysql.proc is missing, corrupt, or contains bad data (internal code -6)
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 10:33 PM, Vicențiu Ciorbaru <
>>>>>>>>>>>>>>>>>>>> cvicentiu(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> Hi varun,
>>>>>>>>>>>>>>>>>>>>> Can you be more specific? Lots of things can go wrong.
>>>>>>>>>>>>>>>>>>>>> Vicentiu
>>>>>>>>>>>>>>>>>>>>> On Thu, 2 Jun 2016 at 20:02, Varun Gupta <
>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>> for the show_create_routine , i am trying to add the
>>>>>>>>>>>>>>>>>>>>>> AGGREGATE string to the buffer , but I am getting an error. For the query
>>>>>>>>>>>>>>>>>>>>>> SHOW CREATE FUNCTION func_name , is there a script where I need to make
>>>>>>>>>>>>>>>>>>>>>> changes ?
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 4:02 PM, Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>>> I have fixed the errors and now the aggregate field
>>>>>>>>>>>>>>>>>>>>>>> is getting stored in the database. I have made a commit.
>>>>>>>>>>>>>>>>>>>>>>> Now I am working on the show_create_sp to include
>>>>>>>>>>>>>>>>>>>>>>> aggregate in the output.
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 12:48 PM, Sanja <
>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> /* DB storage of Stored PROCEDUREs and FUNCTIONs */
>>>>>>>>>>>>>>>>>>>>>>>> enum
>>>>>>>>>>>>>>>>>>>>>>>> {
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 9:16 AM, Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>>>>> In the second point which place are you talking
>>>>>>>>>>>>>>>>>>>>>>>>> about
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 12:33 PM, Sanja <
>>>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>> Above assert means that Aria engine is not
>>>>>>>>>>>>>>>>>>>>>>>>>> initiated (it would be better to have whole stack trace).
>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>> 1) Primary key is not a field but index :)
>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>> 2) I do not see that you changed enum where field
>>>>>>>>>>>>>>>>>>>>>>>>>> of the database numbered.
>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 6:59 AM, Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>>>>>>> I have put the aggregate field just before the
>>>>>>>>>>>>>>>>>>>>>>>>>>> primary key field . I hope this is fine . I have pushed the change on
>>>>>>>>>>>>>>>>>>>>>>>>>>> github.
>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 12:59 AM, Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>> This is the error that leads to sigabrt error
>>>>>>>>>>>>>>>>>>>>>>>>>>>> mysqld:
>>>>>>>>>>>>>>>>>>>>>>>>>>>> /home/batman/gsoc/MARIADB/server/storage/maria/ma_create.c:83:
>>>>>>>>>>>>>>>>>>>>>>>>>>>> maria_create: Assertion `maria_inited' failed.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Thu, Jun 2, 2016 at 12:06 AM, Sanja <
>>>>>>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mtr has --boot-ddd option or just find other
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> way to say us an error which prevents bootstrap SQL running
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Am 01.06.2016 20:33 schrieb "Varun Gupta" <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com>:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> I have put the field at the end of the table
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> but still I am not able to install system databases.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Could not install system database from
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /home/batman/gsoc/MARIADB/server/mysql-test/var/tmp/bootstrap.sql . I have
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> seen this file looks fine to me .
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 11:04 PM, Vicențiu
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Ciorbaru <vicentiu(a)mariadb.org> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi Varun,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Adding extra fields to the proc table should
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> be done at the end of the table. We have code that assumes the table has
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> columns in that particular order. This is most likely why you are getting
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> the failures.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Regards,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Vicentiu
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, 1 Jun 2016 at 20:05 Sanja <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1) Why you decided to put it in the middle?
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> for more incompatibility?
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2) Do you know that space should be put
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> after come and not before? (it is about SQL you changed).
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 3) check that there is no really
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mtr.check_testcase if it is absent then check how your changes broke
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> creation of procedures (SQL of creation the procedure you can find
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> in mysql-test/include/mtr_check.sql
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> If it is there then check why code of
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> finding procedure become broken.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 6:13 PM, Varun Gupta
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hey,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> I have made changes to the code and have
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> pushed the code on github, I am stuck with an error which I can't figure
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> out . The error is
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> At line 78: query 'call
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mtr.check_testcase()' failed: 1305: PROCEDURE mtr.check_testcase does not
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> exist
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> not ok
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 3:40 PM, Sanja <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Yes, called from mysql_install_db
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 11:59 AM, Varun
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Gupta <varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Sanja , in mysql_system_tables.sql is
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> where I found tables are created? Are u talking about this script.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 3:17 PM, Sanja <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> sanja.byelkin(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Probably you have to change code where
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> the table is created (if I remember correctly it is bootstrap script), then
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> recreate database (mysql-test-run is doing it for you)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> then upgrade script should be fixed but
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> it can be done later.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 11:44 AM, Varun
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Gupta <varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Well I am getting an error which says
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> that column expected 21 but are 20. Do I need to drop the already created
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mysql.proc table? In my opinion I should.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 1:23 PM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Vicențiu Ciorbaru <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> vicentiu(a)mariadb.org> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi Varun,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Looks good to me.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Vicentiu
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, 1 Jun 2016 at 10:49 Varun
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Gupta <varungupta1803(a)gmail.com>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> As I was going through with adding a
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> field to the proc table.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> This is what I think should be
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> done, please correct me if I am wrong :)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> First add a new field to the enum {
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MYSQL_PROC_FIELD_IS_AGGREGATE }
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Then store the value in
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> table->field[MYSQL_PROC_FIELD_IS_AGGREGATE]->store(sp->is_aggregate
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ?1:2),TRUE);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Wed, Jun 1, 2016 at 9:32 AM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi Vicentiu,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> As Sanja was telling that the
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> mysql.proc table should have an additional field to store if a function is
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AGGREGATE or not. Well I completely agree with it. So I can add that or by
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> the FETCH GROUP NEXT ROW , we can tell a function is aggregate or not , in
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> such case we would not need any new field in the mysql.proc table
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Tue, May 31, 2016 at 9:46 PM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Sanja <sanja.byelkin(a)gmail.com>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ok
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Tue, May 31, 2016 at 6:12 PM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> For the aggregate functions like
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> for sum, we go through these functions as in these function we have the
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> aggregator which calls the add() function
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Tue, May 31, 2016 at 8:52 PM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Sanja <sanja.byelkin(a)gmail.com>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hi!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> On Tue, May 31, 2016 at 5:10 PM,
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Varun Gupta <
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> varungupta1803(a)gmail.com> wrote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> For the aggregate results as we
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> have functions which we need to execute from sp_head::execute
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> a) For the first record , we
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> use init_sum_functions
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> b) For the other records we
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> would have update_sum_functions
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> We would have the entire
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> aggregation in these 2 functions.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Why? I do not understand why we
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> need this and what they will do.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Maybe, you do not understand how
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> it works now.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Item_field has reference to the
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> table and as soon as table set to the correct record it will read correct
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> values
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> But it should not bother you!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> You get expressions from the parameters and get values from them, the
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> question is only that it should be done in correct moment when tables set
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> on the record you need (and actually you should not even know what is under
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> the hood because it can be even different table if aggregation made via
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> temporary table.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> it is completely legal to ask
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SUM(a-b) where a and b could be fields of different tables.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>
>>
>
2
1
yes sir
https://github.com/SachinSetiya/server
regards
sachin
On Mon, Jun 6, 2016 at 12:39 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
wrote:
> yes sir
> https://github.com/SachinSetiya/server
> regards
> sachin
>
> On Mon, Jun 6, 2016 at 12:31 PM, Oleksandr Byelkin <sanja(a)montyprogram.com
> > wrote:
>
>> Are there there in github?
>> Am 06.06.2016 07:57 schrieb Sachin Setia <sachinsetia1001(a)gmail.com>:
>>
>> Hello Sir
>> Weekly Report for second week of gsoc
>> 1. Implemented unique in the case of null
>> 2. Implemented unique(A,b,c....)
>>
>> Currently working on
>> tests , field hiding
>>
>> Regards
>> sachin
>>
>> On Tue, May 31, 2016 at 10:49 PM, Sergei Golubchik <serg(a)mariadb.org>
>> wrote:
>>
>> Hi, Sachin!
>>
>> On May 31, Sachin Setia wrote:
>> > Hello Sir
>> > Weekly Report For Gsoc
>> > 1. Implemented hash function in parser for one column
>> > 2. Implemenetd hash function for unlimited columns in parser
>> > 3. Implemented unique checking in case of hash collusion(it retrieves
>> the
>> > row and compare data) but this only woks for unique blob column
>> >
>> > Currently Working on
>> > unique checking in case of unique(a,b,c)
>>
>> Thanks. This is ok.
>>
>> Next time, please, send it to maria-developers list, not to me only :)
>>
>> Do you have questions? Any code you want me to look at?
>>
>> Regards,
>> Sergei
>> Chief Architect MariaDB
>> and security(a)mariadb.org
>>
>>
>>
>
1
0
Hi, Shubham!
Here's a first review.
Summary: looks good so far, main comments:
1. Coding style. Use the same coding style as everywhere else in the
file you're editing. It's the same in sql/ and in myisam/ directories.
But InnoDB uses different coding style.
2. Tests, tests, tests! Please start adding tests now, and commit them
into your branch. They should be somewhere under mysql-test/ directory.
The full documentation is here: https://mariadb.com/kb/en/mariadb/mysqltest/
But even without it you can create your tests by starting of from
existing test files.
More comments below:
> diff --git a/sql/sql_table.cc b/sql/sql_table.cc
> index dfce503..31f282b 100644
> --- a/sql/sql_table.cc
> +++ b/sql/sql_table.cc
> @@ -3876,8 +3876,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
> column->length= MAX_LEN_GEOM_POINT_FIELD;
> if (!column->length)
> {
> - my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
> - DBUG_RETURN(TRUE);
> + key_info->algorithm=HA_KEY_ALG_HASH;
There's more to it. The task title is, indeed, "unique index for
blobs", but the actual goal is to have unique indexes for anything
that is too long for normal btree indexes. That's what MI_UNIQUE does.
so, you should support unique indexes also for blobs where column->length
is specified, but is too large. Or unique indexes for a combination of
ten long varchar columns, for example.
So, you also need to patch the code where it issues ER_TOO_LONG_KEY.
Also we'd want to use MI_UNIQUE for keys where a user has
explicitly specified USING HASH, but let's look at it later
> + // my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
> + // DBUG_RETURN(TRUE);
> }
> }
> #ifdef HAVE_SPATIAL
> diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
> index 72bc4c0..9aba216 100644
> --- a/storage/myisam/ha_myisam.cc
> +++ b/storage/myisam/ha_myisam.cc
> @@ -216,44 +216,59 @@ static void mi_check_print_msg(HA_CHECK *param, const char* msg_type,
> 0 OK
> !0 error code
> */
> -
> int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
> - MI_COLUMNDEF **recinfo_out, uint *records_out)
> + MI_COLUMNDEF **recinfo_out,MI_UNIQUEDEF **uniquedef_out ,uint *records_out)
> {
> - uint i, j, recpos, minpos, fieldpos, temp_length, length;
> + uint i, j, recpos, minpos, fieldpos, temp_length, length, k, l, m;
> enum ha_base_keytype type= HA_KEYTYPE_BINARY;
> uchar *record;
> KEY *pos;
> MI_KEYDEF *keydef;
> + MI_UNIQUEDEF *uniquedef;
> MI_COLUMNDEF *recinfo, *recinfo_pos;
> HA_KEYSEG *keyseg;
> TABLE_SHARE *share= table_arg->s;
> uint options= share->db_options_in_use;
> DBUG_ENTER("table2myisam");
> + pos= table_arg->key_info;
> + share->uniques=0;
> + for (i= 0; i < share->keys; i++,pos++)
> + {
> + if(pos->algorithm==HA_KEY_ALG_HASH)
> + {
> + share->uniques++ ;
> + }
> + }
> if (!(my_multi_malloc(MYF(MY_WME),
> recinfo_out, (share->fields * 2 + 2) * sizeof(MI_COLUMNDEF),
> - keydef_out, share->keys * sizeof(MI_KEYDEF),
> + keydef_out, (share->keys - share->uniques) * sizeof(MI_KEYDEF),
> + uniquedef_out, share->uniques * sizeof(MI_UNIQUEDEF),
> &keyseg,
> (share->key_parts + share->keys) * sizeof(HA_KEYSEG),
> NullS)))
> DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
> keydef= *keydef_out;
> recinfo= *recinfo_out;
> + uniquedef= *uniquedef_out;
> pos= table_arg->key_info;
> + k=0;
> + m=0;
better call your indexes 'k' and 'u', for keydefs and uniques.
> for (i= 0; i < share->keys; i++, pos++)
> {
> - keydef[i].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
> - keydef[i].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
> + if(pos->algorithm!=HA_KEY_ALG_HASH)
> + {
> + keydef[k].flag= ((uint16) pos->flags & (HA_NOSAME | HA_FULLTEXT | HA_SPATIAL));
> + keydef[k].key_alg= pos->algorithm == HA_KEY_ALG_UNDEF ?
> (pos->flags & HA_SPATIAL ? HA_KEY_ALG_RTREE : HA_KEY_ALG_BTREE) :
> pos->algorithm;
> - keydef[i].block_length= pos->block_size;
> - keydef[i].seg= keyseg;
> - keydef[i].keysegs= pos->user_defined_key_parts;
> + keydef[k].block_length= pos->block_size;
> + keydef[k].seg= keyseg;
> + keydef[k].keysegs= pos->user_defined_key_parts;
> for (j= 0; j < pos->user_defined_key_parts; j++)
> {
> Field *field= pos->key_part[j].field;
> type= field->key_type();
> - keydef[i].seg[j].flag= pos->key_part[j].key_part_flag;
> + keydef[k].seg[j].flag= pos->key_part[j].key_part_flag;
>
> if (options & HA_OPTION_PACK_KEYS ||
> (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
> @@ -266,52 +281,104 @@ int table2myisam(TABLE *table_arg, MI_KEYDEF **keydef_out,
> {
> /* No blobs here */
> if (j == 0)
> - keydef[i].flag|= HA_PACK_KEY;
> + keydef[k].flag|= HA_PACK_KEY;
> if (!(field->flags & ZEROFILL_FLAG) &&
> (field->type() == MYSQL_TYPE_STRING ||
> field->type() == MYSQL_TYPE_VAR_STRING ||
> ((int) (pos->key_part[j].length - field->decimals())) >= 4))
> - keydef[i].seg[j].flag|= HA_SPACE_PACK;
> + keydef[k].seg[j].flag|= HA_SPACE_PACK;
> }
> else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
> - keydef[i].flag|= HA_BINARY_PACK_KEY;
> + keydef[k].flag|= HA_BINARY_PACK_KEY;
> }
> - keydef[i].seg[j].type= (int) type;
> - keydef[i].seg[j].start= pos->key_part[j].offset;
> - keydef[i].seg[j].length= pos->key_part[j].length;
> - keydef[i].seg[j].bit_start= keydef[i].seg[j].bit_end=
> - keydef[i].seg[j].bit_length= 0;
> - keydef[i].seg[j].bit_pos= 0;
> - keydef[i].seg[j].language= field->charset_for_protocol()->number;
> + keydef[k].seg[j].type= (int) type;
> + keydef[k].seg[j].start= pos->key_part[j].offset;
> + keydef[k].seg[j].length= pos->key_part[j].length;
> + keydef[k].seg[j].bit_start= keydef[k].seg[j].bit_end=
> + keydef[k].seg[j].bit_length= 0;
> + keydef[k].seg[j].bit_pos= 0;
> + keydef[k].seg[j].language= field->charset_for_protocol()->number;
>
> if (field->null_ptr)
> {
> - keydef[i].seg[j].null_bit= field->null_bit;
> - keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
> + keydef[k].seg[j].null_bit= field->null_bit;
> + keydef[k].seg[j].null_pos= (uint) (field->null_ptr-
> (uchar*) table_arg->record[0]);
> }
> else
> {
> - keydef[i].seg[j].null_bit= 0;
> - keydef[i].seg[j].null_pos= 0;
> + keydef[k].seg[j].null_bit= 0;
> + keydef[k].seg[j].null_pos= 0;
> }
> if (field->type() == MYSQL_TYPE_BLOB ||
> field->type() == MYSQL_TYPE_GEOMETRY)
> {
> - keydef[i].seg[j].flag|= HA_BLOB_PART;
> + keydef[k].seg[j].flag|= HA_BLOB_PART;
> /* save number of bytes used to pack length */
> - keydef[i].seg[j].bit_start= (uint) (field->pack_length() -
> + keydef[k].seg[j].bit_start= (uint) (field->pack_length() -
> portable_sizeof_char_ptr);
> }
> else if (field->type() == MYSQL_TYPE_BIT)
> {
> - keydef[i].seg[j].bit_length= ((Field_bit *) field)->bit_len;
> - keydef[i].seg[j].bit_start= ((Field_bit *) field)->bit_ofs;
> - keydef[i].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
> + keydef[k].seg[j].bit_length= ((Field_bit *) field)->bit_len;
> + keydef[k].seg[j].bit_start= ((Field_bit *) field)->bit_ofs;
> + keydef[k].seg[j].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
> (uchar*) table_arg->record[0]);
> }
> }
> keyseg+= pos->user_defined_key_parts;
> + k++;
> + }
> + else
> + {
> + uniquedef[m].sql_key_no=i;
> + uniquedef[m].null_are_equal=1;
> + uniquedef[m].seg=keyseg;
please follow code formatting rules that you can see elsewhere in this file
> + uniquedef[m].keysegs= pos->user_defined_key_parts;
> + for (l= 0; l < pos->user_defined_key_parts; l++)
> + {
> + Field *field= pos->key_part[l].field;
> + type= field->key_type();
> + uniquedef[m].seg[l].flag= pos->key_part[l].key_part_flag;
> + uniquedef[m].seg[l].type= (int) type;
> + uniquedef[m].seg[l].start= pos->key_part[l].offset;
> + uniquedef[m].seg[l].length= pos->key_part[l].length;
> + uniquedef[m].seg[l].bit_start= uniquedef[m].seg[l].bit_end=
> + uniquedef[m].seg[l].bit_length= 0;
> + uniquedef[m].seg[l].bit_pos= 0;
> + uniquedef[m].seg[l].language= field->charset_for_protocol()->number;
> +
> + if (field->null_ptr)
> + {
> + uniquedef[m].seg[l].null_bit= field->null_bit;
> + uniquedef[m].seg[l].null_pos= (uint) (field->null_ptr-
> + (uchar*) table_arg->record[0]);
> + }
> + else
> + {
> + uniquedef[m].seg[l].null_bit= 0;
> + uniquedef[m].seg[l].null_pos= 0;
> + }
> + if (field->type() == MYSQL_TYPE_BLOB ||
> + field->type() == MYSQL_TYPE_GEOMETRY)
> + {
> + uniquedef[m].seg[l].flag|= HA_BLOB_PART;
> + /* save number of bytes used to pack length */
> + uniquedef[m].seg[l].bit_start= (uint) (field->pack_length() -
> + portable_sizeof_char_ptr);
> + }
> + else if (field->type() == MYSQL_TYPE_BIT)
> + {
> + uniquedef[m].seg[l].bit_length= ((Field_bit *) field)->bit_len;
> + uniquedef[m].seg[l].bit_start= ((Field_bit *) field)->bit_ofs;
> + uniquedef[m].seg[l].bit_pos= (uint) (((Field_bit *) field)->bit_ptr -
> + (uchar*) table_arg->record[0]);
> + }
this seems to be the same code as for keydefs, isn't it?
better to extract it into a separate function. Like:
setup_keyparts(pos, keydef[k].seg);
setup_keyparts(pos, uniquedef[m].seg);
> +
> + }
> + keyseg+= pos->user_defined_key_parts;
> + m++;
> + }
> }
> if (table_arg->found_next_number_field)
> keydef[share->next_number_index].flag|= HA_AUTO_KEY;
> @@ -453,6 +528,7 @@ int check_definition(MI_KEYDEF *t1_keyinfo, MI_COLUMNDEF *t1_recinfo,
> MI_KEYDEF *t2_keyinfo, MI_COLUMNDEF *t2_recinfo,
> uint t2_keys, uint t2_recs, bool strict, TABLE *table_arg)
> {
> + return 0;
to be fixed, I suppose?
> uint i, j;
> DBUG_ENTER("check_definition");
> my_bool mysql_40_compat= table_arg && table_arg->s->frm_version < FRM_VER_TRUE_VARCHAR;
> diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c
> index ff96ee8..4d2b62a 100644
> --- a/storage/myisam/mi_write.c
> +++ b/storage/myisam/mi_write.c
> @@ -188,6 +189,28 @@ int mi_write(MI_INFO *info, uchar *record)
> mi_flush_bulk_insert(info, j);
> }
> info->errkey= (int) i;
> + prev=-1;
> + keydef_no=0;
> + for (k=0 ; k < share->state.header.uniques ; k++)
> + {
> + MI_UNIQUEDEF *def= share->uniqueinfo + k;
> +
> + if(keydef_no < (int) i+1)
> + {
> + diff= ((int)def->sql_key_no) - prev -1;
> + keydef_no += diff;
> + }
> + prev= (int) def->sql_key_no;
> + if(keydef_no <= (int) i+1)
> + {
> + info->errkey += 1;
> + }
> + else
> + break;
this can be done a bit simpler, without diff, prev, or keydef_no:
for (k=0; k < share->state.header.uniques; k++, i++)
if (i < share->uniqueinfo[k].sql_key_no)
break;
info->errkey= (int) i;
> +
> + }
> +
> +
> while ( i-- > 0)
> {
> if (mi_is_key_active(share->state.key_map, i))
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1
Hi.
So i spent quite some time trying to get in use an existing JSON-parsing
libraries.
But surprisingly enough none seemed to be good enough.
Problems i met were like this:
1. Parsers are just slow.
- because they copy data - particularly key names and values.
- because they use memory allocation/deallocations
- because they convert numbers from strings to some numeric formats.
Not that i criticise them - it's all mostly because authors of
these libraries
were concentrating on the ease of use for the inexperienced user.
2. They have to implement the support for different character sets, and
cannot use our code for charsets directly. That leads to further
slowdown
of execution and some inconsistencies with the existing behaviour.
3. Their interface is inconvenient. In order to implement the required
JSON functions
we'd like to start/stop parsing on various stages, skip parts of
JSON, insert
something to the found place etc. With the usual interface of the
libraries
doing all this looks really ugly and inefficient. So that MySQL
doesn't even
try to implement this - it only uses the library to convert the
JSON to internal
format and then only operates with that internal.
4. Libraries tend to have their own ways of reporting errors while we'd
like use our
error messages. Now MySQL just prints the library-generated
messages, they cannot
translate or somehow modyfy them.
5. We have to parse not only JSON, but also json-paths, as parts of
various JSON functions.
Libraries don't usually parse the json-path. Partly because these
paths can be very different in
various applications.
Still parts of the path (strings to be specific) should be parsed
just as JSON, and it'd be good
to use the JSON parser for it, which can require ugly tricks. Like
MySQL constructs JSON
from that piece of json-path to be able to feed that to the JSON
parser.
Above all that, i must note that the JSON is pretty simple and compact
as a data format. And
i seriously belive handling it can be done with great efficiency.
So i decided to invest in local JSON parser, taking JSON_CHECKER as a
source of inspiration.
That JSON_CHECKER was produced by JSON.org itself, is really fast and
nice. But it's not
a parser - it just checks if the text makes the correct JSON.
Well it took some time, but now ready. Now opening it for review.
Best regards.
HF
2
2

Re: [Maria-developers] [Commits] 9b88b52: MDEV-8931: (server part of) session state tracking
by Sergei Golubchik 02 Jun '16
by Sergei Golubchik 02 Jun '16
02 Jun '16
Hi, Sanja!
Here's a combined review of all three patches:
On May 30, Oleksandr Byelkin wrote:
> diff --git a/include/mysql_com.h b/include/mysql_com.h
> index c13999a..954c173 100644
> --- a/include/mysql_com.h
> +++ b/include/mysql_com.h
> @@ -520,6 +544,30 @@ enum enum_mysql_set_option
> MYSQL_OPTION_MULTI_STATEMENTS_OFF
> };
>
> +/*
> + Type of state change information that the server can include in the Ok
> + packet.
> + Note : 1) session_state_type shouldn't go past 255 (i.e. 1-byte boundary).
> + 2) Modify the definition of SESSION_TRACK_END when a new member is
> + added.
these "notes" are a fragile way of adding restrictions.
1. add a compile_time_assert for SESSION_TRACK_END < 256
2. in fact, you have compile_time_assert's for SESSION_TRACK_SCHEMA and
SESSION_TRACK_STATE_CHANGE, but they say < 251. And it looks like
SESSION_TRACK_END should be < 251 (not 256) too.
3. perhaps it's better to move SESSION_TRACK_END into the enum,
and give it a visually distinct name, like SESSION_TRACK_always_at_the_end
then it'll always be the last, and you can compile_time_assert that it's
less than 251.
> +*/
> +enum enum_session_state_type
> +{
> + SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */
> + SESSION_TRACK_SCHEMA, /* Current schema */
> + SESSION_TRACK_STATE_CHANGE, /* track session state changes */
> + SESSION_TRACK_GTIDS,
> + SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */
> + SESSION_TRACK_TRANSACTION_STATE /* Transaction state */
> +};
> +
> +#define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES
> +
> +#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_STATE
> +
> +#define IS_SESSION_STATE_TYPE(T) \
> + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) <= SESSION_TRACK_END))
> +
> #define net_new_transaction(net) ((net)->pkt_nr=0)
>
> #ifdef __cplusplus
> diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
> index bcb45ae..c7ebb79 100644
> --- a/libmysqld/lib_sql.cc
> +++ b/libmysqld/lib_sql.cc
> @@ -1171,7 +1171,8 @@ bool
> net_send_ok(THD *thd,
> uint server_status, uint statement_warn_count,
> ulonglong affected_rows, ulonglong id, const char *message,
> - bool unused __attribute__((unused)))
> + bool unused1 __attribute__((unused)),
> + bool unused2 __attribute__((unused)))
as I wrote in the previous review:
1. you don't need __attribute__((unused)) in C++, you can omit the parameter name
2. we compile with -Wno-unused-parameter anyway
> {
> DBUG_ENTER("emb_net_send_ok");
> MYSQL_DATA *data;
> diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
> index cc43265..be6893c 100644
> --- a/mysql-test/r/mysqld--help.result
> +++ b/mysql-test/r/mysqld--help.result
> @@ -903,6 +903,22 @@ The following options may be given as the first argument:
> files within specified directory
> --server-id=# Uniquely identifies the server instance in the community
> of replication partners
> + --session-track-schema
> + Track changes to the 'default schema'.
why in quotes?
> + (Defaults to on; use --skip-session-track-schema to disable.)
> + --session-track-state-change
> + Track changes to the 'session state'.
ditto
> + --session-track-system-variables=name
> + Track changes in registered system variables.
> + --session-track-transaction-info=name
> + Track changes to the transaction attributes. OFF to
> + disable; STATE to track just transaction state (Is there
> + an active transaction? Does it have any data? etc.);
> + CHARACTERISTICS to track transaction state and report all
> + statements needed to start a transaction with the same
> + characteristics (isolation level, read only/read write,
> + snapshot - but not any work done / data modified within
> + the transaction).
> --show-slave-auth-info
> Show user and password in SHOW SLAVE HOSTS on this
> master.
> @@ -1387,6 +1403,10 @@ safe-user-create FALSE
> secure-auth TRUE
> secure-file-priv (No default value)
> server-id 0
> +session-track-schema TRUE
> +session-track-state-change FALSE
> +session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone
this is what, a default value?
why any session tracking is enabled by default?
> +session-track-transaction-info OFF
> show-slave-auth-info FALSE
> silent-startup FALSE
> skip-grant-tables TRUE
> diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
> index 6dca520..b739c67 100644
> --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
> +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
> @@ -3313,6 +3313,34 @@ NUMERIC_BLOCK_SIZE 1
> ENUM_VALUE_LIST NULL
> READ_ONLY NO
> COMMAND_LINE_ARGUMENT REQUIRED
> +VARIABLE_NAME SESSION_TRACK_SCHEMA
these variables don't do anything in embedded. do we want
to have them here at all?
> +SESSION_VALUE ON
> +GLOBAL_VALUE ON
> +GLOBAL_VALUE_ORIGIN COMPILE-TIME
> +DEFAULT_VALUE ON
> +VARIABLE_SCOPE SESSION
> +VARIABLE_TYPE BOOLEAN
> +VARIABLE_COMMENT Track changes to the 'default schema'.
> +NUMERIC_MIN_VALUE NULL
> +NUMERIC_MAX_VALUE NULL
> +NUMERIC_BLOCK_SIZE NULL
> +ENUM_VALUE_LIST OFF,ON
> +READ_ONLY NO
> +COMMAND_LINE_ARGUMENT OPTIONAL
> +VARIABLE_NAME SESSION_TRACK_STATE_CHANGE
> +SESSION_VALUE OFF
> +GLOBAL_VALUE OFF
> +GLOBAL_VALUE_ORIGIN COMPILE-TIME
> +DEFAULT_VALUE OFF
> +VARIABLE_SCOPE SESSION
> +VARIABLE_TYPE BOOLEAN
> +VARIABLE_COMMENT Track changes to the 'session state'.
> +NUMERIC_MIN_VALUE NULL
> +NUMERIC_MAX_VALUE NULL
> +NUMERIC_BLOCK_SIZE NULL
> +ENUM_VALUE_LIST OFF,ON
> +READ_ONLY NO
> +COMMAND_LINE_ARGUMENT OPTIONAL
> VARIABLE_NAME SKIP_EXTERNAL_LOCKING
> SESSION_VALUE NULL
> GLOBAL_VALUE ON
> diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
> index 1620579..89999a3 100644
> --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
> +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
> @@ -3775,6 +3775,48 @@ NUMERIC_BLOCK_SIZE 1
> ENUM_VALUE_LIST NULL
> READ_ONLY NO
> COMMAND_LINE_ARGUMENT REQUIRED
> +VARIABLE_NAME SESSION_TRACK_SCHEMA
> +SESSION_VALUE ON
> +GLOBAL_VALUE ON
> +GLOBAL_VALUE_ORIGIN COMPILE-TIME
> +DEFAULT_VALUE ON
> +VARIABLE_SCOPE SESSION
> +VARIABLE_TYPE BOOLEAN
> +VARIABLE_COMMENT Track changes to the 'default schema'.
> +NUMERIC_MIN_VALUE NULL
> +NUMERIC_MAX_VALUE NULL
> +NUMERIC_BLOCK_SIZE NULL
> +ENUM_VALUE_LIST OFF,ON
> +READ_ONLY NO
> +COMMAND_LINE_ARGUMENT OPTIONAL
> +VARIABLE_NAME SESSION_TRACK_STATE_CHANGE
> +SESSION_VALUE OFF
> +GLOBAL_VALUE OFF
> +GLOBAL_VALUE_ORIGIN COMPILE-TIME
> +DEFAULT_VALUE OFF
> +VARIABLE_SCOPE SESSION
> +VARIABLE_TYPE BOOLEAN
> +VARIABLE_COMMENT Track changes to the 'session state'.
> +NUMERIC_MIN_VALUE NULL
> +NUMERIC_MAX_VALUE NULL
> +NUMERIC_BLOCK_SIZE NULL
> +ENUM_VALUE_LIST OFF,ON
> +READ_ONLY NO
> +COMMAND_LINE_ARGUMENT OPTIONAL
> +VARIABLE_NAME SESSION_TRACK_SYSTEM_VARIABLES
> +SESSION_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone
> +GLOBAL_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone
> +GLOBAL_VALUE_ORIGIN COMPILE-TIME
> +DEFAULT_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone
> +VARIABLE_SCOPE SESSION
> +VARIABLE_TYPE VARCHAR
> +VARIABLE_COMMENT Track changes in registered system variables.
> +NUMERIC_MIN_VALUE NULL
> +NUMERIC_MAX_VALUE NULL
> +NUMERIC_BLOCK_SIZE NULL
> +ENUM_VALUE_LIST NULL
> +READ_ONLY NO
> +COMMAND_LINE_ARGUMENT REQUIRED
where's SESSION_TRACK_TRANSACTION_INFO?
> VARIABLE_NAME SKIP_EXTERNAL_LOCKING
> SESSION_VALUE NULL
> GLOBAL_VALUE ON
> diff --git a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test
> new file mode 100644
> index 0000000..bbb32bb
> --- /dev/null
> +++ b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test
> @@ -0,0 +1,133 @@
> +--source include/not_embedded.inc
> +
> +--echo #
> +--echo # Variable name : session_track_system_variables
> +--echo # Scope : Global & Session
> +--echo #
> +
> +--echo # Global - default
> +SELECT @@global.session_track_system_variables;
> +--echo # Session - default
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES
> +--disable_warnings
> +SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
> +--enable_warnings
why do you disable warnings here?
> +
> +--echo # via INFORMATION_SCHEMA.SESSION_VARIABLES
> +--disable_warnings
> +SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
> +--enable_warnings
> +
> +# Save the global value to be used to restore the original value.
> +SET @global_saved_tmp = @@global.session_track_system_variables;
> +--echo
> +
> +--echo # Altering global variable's value
> +SET @@global.session_track_system_variables='autocommit';
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Altering session variable's value
> +SET @@session.session_track_system_variables='autocommit';
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Variables' values in a new session.
> +connect (con1,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +--echo # Global - expect "autocommit"
> +SELECT @@global.session_track_system_variables;
> +--echo
> +--echo # Session - expect "autocommit"
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Switching to the default connection.
> +connection default;
> +
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Test if DEFAULT is working as expected.
> +SET @@global.session_track_system_variables = DEFAULT;
> +SET @@session.session_track_system_variables = DEFAULT;
> +--echo
> +
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Variables' values in a new session (con2).
> +connect (con2,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Altering session should not affect global.
> +SET @@session.session_track_system_variables = 'sql_mode';
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Variables' values in a new session (con3).
> +connect (con3,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +--echo # Altering global should not affect session.
> +SET @@global.session_track_system_variables = 'sql_mode';
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # Switching to the default connection.
> +connection default;
> +
> +--echo # Testing NULL
> +SET @@global.session_track_system_variables = NULL;
> +SET @@session.session_track_system_variables = NULL;
> +
> +--echo # Global - expect "" instead of NULL
> +SELECT @@global.session_track_system_variables;
> +--echo # Session - expect "" instead of NULL
> +SELECT @@session.session_track_system_variables;
> +
> +--echo # testing with duplicate entries.
> +# Lets first set it to some valid value.
> +SET @@global.session_track_system_variables= "time_zone";
> +SET @@session.session_track_system_variables= "time_zone";
> +# Now set with duplicate entries (must pass)
> +SET @@global.session_track_system_variables= "sql_mode,sql_mode";
> +SET @@session.session_track_system_variables= "sql_mode,sql_mode";
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # testing ordering
> +SET @@global.session_track_system_variables= "time_zone,sql_mode";
> +SET @@session.session_track_system_variables= "time_zone,sql_mode";
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +--echo # special values
> +SET @@global.session_track_system_variables= "*";
> +SET @@session.session_track_system_variables= "*";
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +SET @@global.session_track_system_variables= "";
> +SET @@session.session_track_system_variables= "";
> +SELECT @@global.session_track_system_variables;
> +SELECT @@session.session_track_system_variables;
> +--echo
> +
> +
> +--echo # Restoring the original values.
> +SET @@global.session_track_system_variables = @global_saved_tmp;
> +
> +--echo # End of tests.
> diff --git a/sql/sql_string.h b/sql/sql_string.h
> index 51a11c7..feab807 100644
> --- a/sql/sql_string.h
> +++ b/sql/sql_string.h
> @@ -559,6 +566,17 @@ class String
> return Ptr+ old_length; /* Area to use */
> }
>
> + inline bool prep_alloc(uint32 arg_length, uint32 step_alloc)
why couldn't you use String::reserve(arg_length, step_alloc) ?
> + {
> + uint32 new_length= arg_length + str_length;
> + if (new_length > Alloced_length)
> + {
> + if (realloc(new_length + step_alloc))
> + return true;
> + }
> + return false;
> + }
> +
> inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
> {
> uint32 new_length= arg_length + str_length;
> @@ -623,6 +641,8 @@ class String
> {
> return !sortcmp(this, other, cs);
> }
> + void q_net_store_length(ulonglong length);
> + void q_net_store_data(const uchar *from, size_t length);
why didn't you define these two inline?
> };
>
>
> diff --git a/sql/sql_string.cc b/sql/sql_string.cc
> index 40339d5..2690310 100644
> --- a/sql/sql_string.cc
> +++ b/sql/sql_string.cc
> @@ -1157,3 +1158,16 @@ uint convert_to_printable(char *to, size_t to_len,
> *t= '\0';
> return t - to;
> }
> +
> +void String::q_net_store_length(ulonglong length)
> +{
May be, add DBUG_ASSERT that the buffer is big enough?
> + char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length);
> + str_length= pos - Ptr;
> +}
> +
> +void String::q_net_store_data(const uchar *from, size_t length)
> +{
and here too
> + q_net_store_length(length);
> + bool res= append((const char *)from, length);
should be q_append here
> + DBUG_ASSERT(!res);
> +}
> diff --git a/sql/mysqld.h b/sql/mysqld.h
> index e538cbd..4173752 100644
> --- a/sql/mysqld.h
> +++ b/sql/mysqld.h
> @@ -135,6 +135,7 @@ extern my_bool lower_case_file_system;
> extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
> extern my_bool opt_secure_auth;
> extern const char *current_dbug_option;
> +extern const char *current_session_track_system_variables;
this 'current_session_track_system_variables' string is not mentioned
anywhere else in your patch
> extern char* opt_secure_file_priv;
> extern char* opt_secure_backup_file_priv;
> extern size_t opt_secure_backup_file_priv_len;
> diff --git a/sql/mysqld.cc b/sql/mysqld.cc
> index 845d114..e3aba86 100644
> --- a/sql/mysqld.cc
> +++ b/sql/mysqld.cc
> @@ -691,6 +691,14 @@ THD *next_global_thread(THD *thd)
> }
>
> struct system_variables global_system_variables;
> +/**
> + Following is just for options parsing, used with a difference against
> + global_system_variables.
> +
> + TODO: something should be done to get rid of following variables
> +*/
> +const char *current_dbug_option="";
> +
why did you move it?
> struct system_variables max_system_variables;
> struct system_status_var global_status_var;
>
> @@ -5313,6 +5320,17 @@ static int init_server_components()
> }
> plugins_are_initialized= TRUE; /* Don't separate from init function */
>
> + {
> + Session_tracker session_track_system_variables_check;
> + if (session_track_system_variables_check.
> + server_boot_verify(system_charset_info))
make server_boot_verify a class method and invoke it here
without creating a useless instance of Session_tracker
> + {
> + sql_print_error("The variable session_track_system_variables has "
> + "invalid values.");
> + unireg_abort(1);
> + }
> + }
> +
> /* we do want to exit if there are any other unknown options */
> if (remaining_argc > 1)
> {
> diff --git a/sql/protocol.cc b/sql/protocol.cc
> index 6469581..73a704a 100644
> --- a/sql/protocol.cc
> +++ b/sql/protocol.cc
> @@ -197,7 +198,8 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
> @param affected_rows Number of rows changed by statement
> @param id Auto_increment id for first row (if used)
> @param message Message to send to the client (Used by mysql_status)
> -
> + @param is_eof this called inted of old EOF packet
"instead"
> +
> @return
> @retval FALSE The message was successfully sent
> @retval TRUE An error occurred and the messages wasn't sent properly
> @@ -209,10 +211,18 @@ bool
> net_send_ok(THD *thd,
> uint server_status, uint statement_warn_count,
> ulonglong affected_rows, ulonglong id, const char *message,
> + bool is_eof,
> bool skip_flush)
> {
> NET *net= &thd->net;
> - uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
> + StringBuffer<MYSQL_ERRMSG_SIZE + 10> store;
> +
> + /*
> + To be used to manage the data storage in case session state change
> + information is present.
> + */
confusing comment, better remove it completely
> + bool state_changed= false;
> +
> bool error= FALSE;
> DBUG_ENTER("net_send_ok");
>
> @@ -222,38 +232,81 @@ net_send_ok(THD *thd,
> DBUG_RETURN(FALSE);
> }
>
> - buff[0]=0; // No fields
> - pos=net_store_length(buff+1,affected_rows);
> - pos=net_store_length(pos, id);
> + /*
> + OK send instead of EOF still require 0xFE header, but OK packet content.
> + */
> + if (is_eof)
> + {
> + DBUG_ASSERT(thd->client_capabilities & CLIENT_DEPRECATE_EOF);
> + store.q_append((char)254);
> + }
> + else
> + store.q_append('\0');
> +
> + /* affected rows */
> + store.q_net_store_length(affected_rows);
> +
> + /* last insert id */
> + store.q_net_store_length(id);
> +
> if (thd->client_capabilities & CLIENT_PROTOCOL_41)
> {
> DBUG_PRINT("info",
> ("affected_rows: %lu id: %lu status: %u warning_count: %u",
> (ulong) affected_rows,
> (ulong) id,
> (uint) (server_status & 0xffff),
> (uint) statement_warn_count));
> - int2store(pos, server_status);
> - pos+=2;
> + store.q_append2b(server_status);
>
> /* We can only return up to 65535 warnings in two bytes */
> uint tmp= MY_MIN(statement_warn_count, 65535);
> - int2store(pos, tmp);
> - pos+= 2;
> + store.q_append2b(tmp);
> }
> else if (net->return_status) // For 4.0 protocol
> {
> - int2store(pos, server_status);
> - pos+=2;
> + store.q_append2b(server_status);
> }
> thd->get_stmt_da()->set_overwrite_status(true);
>
> - if (message && message[0])
> - pos= net_store_data(pos, (uchar*) message, strlen(message));
> - error= my_net_write(net, buff, (size_t) (pos-buff));
> - if (!error && !skip_flush)
> + if ((thd->client_capabilities & CLIENT_SESSION_TRACK))
> + {
> + if (server_status & SERVER_SESSION_STATE_CHANGED)
> + state_changed= true;
> + /* the info field */
> + if (state_changed || (message && message[0]))
> + {
> + store.q_net_store_data((uchar*) message, message ? strlen(message) : 0);
use safe_strlen(message) instead
> + }
> +
> + /* session state change information */
> + if (unlikely(state_changed))
> + {
> + store.set_charset(thd->variables.collation_database);
> +
> + thd->session_tracker.store(thd, &store);
> + }
> + }
> + else if (message && message[0])
> + {
> + /* the info field, if there is a message to store */
> + DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE);
> + store.q_net_store_data((uchar*) message, strlen(message));
> + }
> +
I think, better to write it as
state_changed= thd->client_capabilities & CLIENT_SESSION_TRACK &&
server_status & SERVER_SESSION_STATE_CHANGED;
if (state_changed || (message && message[0]))
{
DBUG_ASSERT(safe_strlen(message) <= MYSQL_ERRMSG_SIZE);
store.q_net_store_data((uchar*) safe_str(message), safe_strlen(message));
}
if (unlikely(state_changed))
{
store.set_charset(thd->variables.collation_database);
thd->session_tracker.store(thd, &store);
}
that is without nested if() and without duplicating message writing lines
> + if (store.length() > MAX_PACKET_LENGTH)
> + {
> + net->error= 1;
> + net->last_errno= ER_NET_OK_PACKET_TOO_LARGE;
> + my_error(ER_NET_OK_PACKET_TOO_LARGE, MYF(0));
> + DBUG_PRINT("info", ("OK packet too large"));
> + DBUG_RETURN(1);
1. That's kinda unexpected. ER_NET_OK_PACKET_TOO_LARGE when
sending an OK packet?
2. does it work at all? I mean, my_error() from inside net_send_ok() -
please verify that it's supported.
> + }
> + error= my_net_write(net, (const unsigned char*)store.ptr(), store.length());
> + if (!error && (!skip_flush || is_eof))
Hmm, so you're still flushing for the EOF packet? Why?
> error= net_flush(net);
>
> + thd->server_status&= ~SERVER_SESSION_STATE_CHANGED;
>
> thd->get_stmt_da()->set_overwrite_status(false);
> DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
> @@ -858,14 +928,19 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
>
> if (flags & SEND_EOF)
> {
> - /*
> - Mark the end of meta-data result set, and store thd->server_status,
> - to show that there is no cursor.
> - Send no warning information, as it will be sent at statement end.
> - */
> - if (write_eof_packet(thd, &thd->net, thd->server_status,
> - thd->get_stmt_da()->current_statement_warn_count()))
> - DBUG_RETURN(1);
> +
> + /* if it is new client do not send EOF packet */
> + if (!(thd->client_capabilities & CLIENT_DEPRECATE_EOF))
> + {
> + /*
> + Mark the end of meta-data result set, and store thd->server_status,
> + to show that there is no cursor.
what about "to show that there is no cursor"?
new clients won't have this information?
> + Send no warning information, as it will be sent at statement end.
> + */
> + if (write_eof_packet(thd, &thd->net, thd->server_status,
> + thd->get_stmt_da()->current_statement_warn_count()))
> + DBUG_RETURN(1);
> + }
> }
> DBUG_RETURN(prepare_for_send(list->elements));
>
> diff --git a/sql/session_tracker.h b/sql/session_tracker.h
> new file mode 100644
> index 0000000..5d529b8
> --- /dev/null
> +++ b/sql/session_tracker.h
> @@ -0,0 +1,299 @@
> +#ifndef SESSION_TRACKER_INCLUDED
> +#define SESSION_TRACKER_INCLUDED
> +
> +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
> + Copyright (c) 2016, 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
> +
> +#include "m_string.h"
> +#include "thr_lock.h"
> +
> +/* forward declarations */
> +class THD;
> +class set_var;
> +class String;
> +
> +
> +enum enum_session_tracker
> +{
> + SESSION_SYSVARS_TRACKER, /* Session system variables */
> + CURRENT_SCHEMA_TRACKER, /* Current schema */
> + SESSION_STATE_CHANGE_TRACKER,
> + SESSION_GTIDS_TRACKER, /* Tracks GTIDs */
> + TRANSACTION_INFO_TRACKER /* Transaction state */
> +};
> +
> +#define SESSION_TRACKER_END TRANSACTION_INFO_TRACKER
> +
> +
> +/**
> + State_tracker
> +
> + An abstract class that defines the interface for any of the server's
> + 'session state change tracker'. A tracker, however, is a sub- class of
> + this class which takes care of tracking the change in value of a part-
> + icular session state type and thus defines various methods listed in this
> + interface. The change information is later serialized and transmitted to
> + the client through protocol's OK packet.
> +
> + Tracker system variables :-
> + A tracker is normally mapped to a system variable. So in order to enable,
> + disable or modify the sub-entities of a tracker, the user needs to modify
> + the respective system variable either through SET command or via command
> + line option. As required in system variable handling, this interface also
> + includes two functions to help in the verification of the supplied value
> + (ON_CHECK) and the updation (ON_UPDATE) of the tracker system variable,
> + namely - check() and update().
Why do you still have check() method here?
It isn't used anywhere.
(I've checked out your branch and removed all check() methods and it
still compiled fine)
> +*/
> +
> +class State_tracker
> +{
> +protected:
> + /**
> + Is tracking enabled for a particular session state type ?
> +
> + @note: It is cache to avoid virtual functions and checking thd
"it is a cache of the corresponding thd->variables.session_track_xxx variable"
> + when we want mark tracker as changed.
> + */
> + bool m_enabled;
> +
> + /** Has the session state type changed ? */
> + bool m_changed;
> +
> +public:
> + /** Constructor */
> + State_tracker() : m_enabled(false), m_changed(false)
> + {}
> +
> + /** Destructor */
> + virtual ~State_tracker()
> + {}
> +
> + /** Getters */
> + bool is_enabled() const
> + { return m_enabled; }
> +
> + bool is_changed() const
> + { return m_changed; }
> +
> + /** Called in the constructor of THD*/
> + virtual bool enable(THD *thd)= 0;
> +
> + /** To be invoked when the tracker's system variable is checked (ON_CHECK). */
> + virtual bool check(THD *thd, set_var *var)= 0;
> +
> + /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/
> + virtual bool update(THD *thd)= 0;
> +
> + /** Store changed data into the given buffer. */
> + virtual bool store(THD *thd, String *buf)= 0;
> +
> + /** Mark the entity as changed. */
> + virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0;
> +};
> +
> +bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
> +bool sysvartrack_reprint_value(THD *thd, char *str, size_t len);
> +bool sysvartrack_update(THD *thd);
> +size_t sysvartrack_value_len(THD *thd);
> +bool sysvartrack_value_construct(THD *thd, char *val, size_t len);
> +
> +
> +/**
> + Session_tracker
> +
> + This class holds an object each for all tracker classes and provides
> + methods necessary for systematic detection and generation of session
> + state change information.
> +*/
> +
> +class Session_tracker
> +{
> +private:
> + State_tracker *m_trackers[SESSION_TRACKER_END + 1];
> +
> + /* The following two functions are private to disable copying. */
> + Session_tracker(Session_tracker const &other)
> + {
> + DBUG_ASSERT(FALSE);
> + }
> + Session_tracker& operator= (Session_tracker const &rhs)
> + {
> + DBUG_ASSERT(FALSE);
> + return *this;
> + }
> +
> +public:
> +
> + Session_tracker();
> + ~Session_tracker()
> + {
> + deinit();
> + }
> +
> + /* trick to make happy memory accounting system */
> + void deinit()
> + {
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + {
> + if (m_trackers[i])
> + delete m_trackers[i];
> + m_trackers[i]= NULL;
> + }
> + }
> +
> + void enable(THD *thd);
> + bool server_boot_verify(const CHARSET_INFO *char_set);
no "const CHARSET_INFO", it's MySQL-ism. We have put "const" into
the CHARSET_INFO type definiton.
> +
> + /** Returns the pointer to the tracker object for the specified tracker. */
> + inline State_tracker *get_tracker(enum_session_tracker tracker) const
> + {
> + return m_trackers[tracker];
> + }
> +
> + inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
> + LEX_CSTRING *data)
> + {
> + if (m_trackers[tracker]->is_enabled())
> + m_trackers[tracker]->mark_as_changed(thd, data);
> + }
> +
> +
> + void store(THD *thd, String *main_buf);
> +};
> +
> +
> +/*
> + Transaction_state_tracker
> +*/
> +
> +/**
> + Transaction state (no transaction, transaction active, work attached, etc.)
> +*/
> +enum enum_tx_state {
> + TX_EMPTY = 0, ///< "none of the below"
> + TX_EXPLICIT = 1, ///< an explicit transaction is active
> + TX_IMPLICIT = 2, ///< an implicit transaction is active
> + TX_READ_TRX = 4, ///< transactional reads were done
> + TX_READ_UNSAFE = 8, ///< non-transaction reads were done
> + TX_WRITE_TRX = 16, ///< transactional writes were done
> + TX_WRITE_UNSAFE = 32, ///< non-transactional writes were done
> + TX_STMT_UNSAFE = 64, ///< "unsafe" (non-deterministic like UUID()) stmts
> + TX_RESULT_SET = 128, ///< result-set was sent
s/result-set/result set/
> + TX_WITH_SNAPSHOT= 256, ///< WITH CONSISTENT SNAPSHOT was used
> + TX_LOCKED_TABLES= 512 ///< LOCK TABLES is active
> +};
> +
> +
> +/**
> + Transaction access mode
> +*/
> +enum enum_tx_read_flags {
> + TX_READ_INHERIT = 0, ///< not explicitly set, inherit session.tx_read_only
> + TX_READ_ONLY = 1, ///< START TRANSACTION READ ONLY, or tx_read_only=1
> + TX_READ_WRITE = 2, ///< START TRANSACTION READ WRITE, or tx_read_only=0
> +};
> +
> +
> +/**
> + Transaction isolation level
> +*/
> +enum enum_tx_isol_level {
> + TX_ISOL_INHERIT = 0, ///< not explicitly set, inherit session.tx_isolation
> + TX_ISOL_UNCOMMITTED = 1,
> + TX_ISOL_COMMITTED = 2,
> + TX_ISOL_REPEATABLE = 3,
> + TX_ISOL_SERIALIZABLE= 4
> +};
> +
> +
> +/**
> + Transaction tracking level
> +*/
> +enum enum_session_track_transaction_info {
> + TX_TRACK_NONE = 0, ///< do not send tracker items on transaction info
> + TX_TRACK_STATE = 1, ///< track transaction status
> + TX_TRACK_CHISTICS = 2 ///< track status and characteristics
> +};
> +
> +
> +/**
> + This is a tracker class that enables & manages the tracking of
> + current transaction info for a particular connection.
> +*/
> +
> +class Transaction_state_tracker : public State_tracker
> +{
> +public:
> + /** Constructor */
> + Transaction_state_tracker();
> + bool enable(THD *thd)
> + { return update(thd); }
> + bool check(THD *thd, set_var *var)
> + { return false; }
> + bool update(THD *thd);
> + bool store(THD *thd, String *buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> +
> + /** Change transaction characteristics */
> + void set_read_flags(THD *thd, enum enum_tx_read_flags flags);
> + void set_isol_level(THD *thd, enum enum_tx_isol_level level);
> +
> + /** Change transaction state */
> + void clear_trx_state(THD *thd, uint clear);
> + void add_trx_state(THD *thd, uint add);
> + void add_trx_state_from_thd(THD *thd);
> + void end_trx(THD *thd);
> +
> + /** Helper function: turn table info into table access flag */
> + enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx);
> +
> +private:
> + enum enum_tx_changed {
> + TX_CHG_NONE = 0, ///< no changes from previous stmt
> + TX_CHG_STATE = 1, ///< state has changed from previous stmt
> + TX_CHG_CHISTICS = 2 ///< characteristics have changed from previous stmt
> + };
Huh? I've just seen an identical enum just above this class definition?
> +
> + /** any trackable changes caused by this statement? */
> + uint tx_changed;
> +
> + /** transaction state */
> + uint tx_curr_state, tx_reported_state;
> +
> + /** r/w or r/o set? session default? */
> + enum enum_tx_read_flags tx_read_flags;
> +
> + /** isolation level */
> + enum enum_tx_isol_level tx_isol_level;
> +
> + void reset();
> +
> + inline void update_change_flags(THD *thd)
> + {
> + tx_changed &= ~TX_CHG_STATE;
> + tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0;
> + if (tx_changed != TX_CHG_NONE)
> + mark_as_changed(thd, NULL);
> + }
> +};
> +
> +#define TRANSACT_TRACKER(X) \
> + do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \
> + {((Transaction_state_tracker *) \
> + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)) \
> + ->X; } } while(0)
> +
> +#endif /* SESSION_TRACKER_INCLUDED */
> diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
> new file mode 100644
> index 0000000..ae7be5f
> --- /dev/null
> +++ b/sql/session_tracker.cc
> @@ -0,0 +1,1631 @@
> +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
> + Copyright (c) 2016, 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
> +
> +
> +#include "sql_plugin.h"
> +#include "session_tracker.h"
> +
> +#include "hash.h"
> +#include "table.h"
> +#include "rpl_gtid.h"
> +#include "sql_class.h"
> +#include "sql_show.h"
> +#include "sql_plugin.h"
> +#include "set_var.h"
> +
> +class Not_implemented_tracker : public State_tracker
> +{
> +public:
> + bool enable(THD *thd)
> + { return false; }
> + bool check(THD *, set_var *)
> + { return false; }
> + bool update(THD *)
> + { return false; }
> + bool store(THD *, String *)
> + { return false; }
> + void mark_as_changed(THD *, LEX_CSTRING *tracked_item_name)
> + {}
> +
> +};
> +
> +static my_bool name_array_filler(void *ptr, void *data_ptr);
> +/**
> + Session_sysvars_tracker
> +
> + This is a tracker class that enables & manages the tracking of session
> + system variables. It internally maintains a hash of user supplied variable
> + references and a boolean field to store if the variable was changed by the
> + last statement.
> +*/
> +
> +class Session_sysvars_tracker : public State_tracker
> +{
> +private:
> +
> + struct sysvar_node_st {
> + sys_var *m_svar;
> + bool *test_load;
> + bool m_changed;
> + };
because you use sysvar_node_st, you need to allocate nodes all the time -
many small mallocs.
I think this can be avoided, like this:
1. you don't need test_load pointer, if a variable is not loaded,
it cannot be changed either, so it's okay to use only m_changed and
not look at *test_load.
It means that for plugin sysvars you cannot store sys_var* in the hash,
you need a new method "void *sys_var::safe_ptr()". Or, may be,
"intptr sys_var::tracker_key()". It'll return this for server sysvars
and a bookmark for plugin sysvars.
2. as for m_changed, it's just one bit, you can use the lowest bit
of the key for this. sys_var::safe_ptr() (or sys_var::tracker_key())
is always aligned, so few lowest bits are free. You can use the second
bit to mark all plugin variables (to know whether a pointer is
to sysvar or to a bookmark).
alternatively, you don't store m_changed in the hash, but keep a list
of all modified sysvars. this will also be useful in the store method
(and solves the problem or growing hash when '*' is specified)
Then you won't need to allocate anything and you can store the key directly
in the hash.
> +
> + class vars_list
> + {
> + private:
> + /**
> + Registered system variables. (@@session_track_system_variables)
> + A hash to store the name of all the system variables specified by the
> + user.
> + */
> + HASH m_registered_sysvars;
> + /** Size of buffer for string representation */
> + size_t buffer_length;
> + myf m_mem_flag;
> + /**
> + If TRUE then we want to check all session variable.
> + */
> + bool track_all;
> + void init()
> + {
> + my_hash_init(&m_registered_sysvars,
> + &my_charset_bin,
> + 4, 0, 0, (my_hash_get_key) sysvars_get_key,
> + my_free, MYF(HASH_UNIQUE |
> + ((m_mem_flag & MY_THREAD_SPECIFIC) ?
> + HASH_THREAD_SPECIFIC : 0)));
> + }
> + void free_hash()
> + {
> + if (my_hash_inited(&m_registered_sysvars))
> + {
> + my_hash_free(&m_registered_sysvars);
> + }
> + }
> +
> + uchar* search(const sys_var *svar)
> + {
> + return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar,
> + sizeof(sys_var *)));
> + }
> +
> + public:
> + vars_list() :
> + buffer_length(0)
> + {
> + m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0;
> + init();
> + }
> +
> + size_t get_buffer_length()
> + {
> + DBUG_ASSERT(buffer_length != 0); // asked earlier then should
> + return buffer_length;
> + }
> + ~vars_list()
> + {
> + /* free the allocated hash. */
> + if (my_hash_inited(&m_registered_sysvars))
> + {
> + my_hash_free(&m_registered_sysvars);
> + }
> + }
> +
> + uchar* search(sysvar_node_st *node, const sys_var *svar)
may be, rename to insert_or_search ?
> + {
> + uchar *res;
> + res= search(svar);
> + if (!res)
> + {
> + if (track_all)
will the hash grow all the time when track_all is true?
> + {
> + insert(node, svar, m_mem_flag);
> + return search(svar);
> + }
> + }
> + return res;
> + }
> +
> + uchar* operator[](ulong idx)
> + {
> + return my_hash_element(&m_registered_sysvars, idx);
> + }
> + bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag);
> + void reset();
> + void copy(vars_list* from, THD *thd);
> + bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
> + const CHARSET_INFO *char_set, bool session_created);
> + bool construct_var_list(char *buf, size_t buf_len);
> + };
> + /**
> + Two objects of vars_list type are maintained to manage
> + various operations.
> + */
> + vars_list *orig_list, *tool_list;
> +
> +public:
> + Session_sysvars_tracker()
> + {
> + orig_list= new (std::nothrow) vars_list();
> + tool_list= new (std::nothrow) vars_list();
> + }
> +
> + ~Session_sysvars_tracker()
> + {
> + if (orig_list)
> + delete orig_list;
> + if (tool_list)
> + delete tool_list;
> + }
> +
> + size_t get_buffer_length()
> + {
> + return orig_list->get_buffer_length();
> + }
> + bool construct_var_list(char *buf, size_t buf_len)
> + {
> + return orig_list->construct_var_list(buf, buf_len);
> + }
> +
> + /**
> + Method used to check the validity of string provided
> + for session_track_system_variables during the server
> + startup.
> + */
> + static bool server_init_check(THD *thd, const CHARSET_INFO *char_set,
> + LEX_STRING var_list)
> + {
> + vars_list dummy;
> + bool result;
> + result= dummy.parse_var_list(thd, var_list, false, char_set, false);
> + return result;
> + }
> + static bool server_init_process(THD *thd, const CHARSET_INFO *char_set,
> + LEX_STRING var_list)
> + {
> + vars_list dummy;
> + bool result;
> + result= dummy.parse_var_list(thd, var_list, false, char_set, false);
> + if (!result)
> + dummy.construct_var_list(var_list.str, var_list.length + 1);
> + return result;
> + }
> +
> + void reset();
> + bool enable(THD *thd);
> + bool check(THD *thd, set_var *var);
> + bool check_str(THD *thd, LEX_STRING val);
> + bool update(THD *thd);
> + bool store(THD *thd, String *buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> + /* callback */
> + static uchar *sysvars_get_key(const char *entry, size_t *length,
> + my_bool not_used __attribute__((unused)));
> +
> + friend my_bool name_array_filler(void *ptr, void *data_ptr);
why wouldn't you make this one a class method too?
> +};
> +
> +
> +
> +/**
> + Current_schema_tracker,
> +
> + This is a tracker class that enables & manages the tracking of current
> + schema for a particular connection.
> +*/
> +
> +class Current_schema_tracker : public State_tracker
> +{
> +private:
> + bool schema_track_inited;
> + void reset();
> +
> +public:
> +
> + Current_schema_tracker()
> + {
> + schema_track_inited= false;
> + }
> +
> + bool enable(THD *thd)
> + { return update(thd); }
> + bool check(THD *thd, set_var *var)
> + { return false; }
> + bool update(THD *thd);
> + bool store(THD *thd, String *buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> +};
> +
> +/*
> + Session_state_change_tracker
> +
> + This is a boolean tracker class that will monitor any change that contributes
> + to a session state change.
> + Attributes that contribute to session state change include:
> + - Successful change to System variables
> + - User defined variables assignments
> + - temporary tables created, altered or deleted
> + - prepared statements added or removed
> + - change in current database
> + - change of current role
> +*/
> +
> +class Session_state_change_tracker : public State_tracker
> +{
> +private:
> +
> + void reset();
> +
> +public:
> + Session_state_change_tracker();
> + bool enable(THD *thd)
> + { return update(thd); };
> + bool check(THD *thd, set_var *var)
> + { return false; }
> + bool update(THD *thd);
> + bool store(THD *thd, String *buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> + bool is_state_changed(THD*);
> + void ensure_enabled(THD *thd)
> + {}
this method is never used
> +};
> +
> +
> +/* To be used in expanding the buffer. */
> +static const unsigned int EXTRA_ALLOC= 1024;
> +
> +
> +void Session_sysvars_tracker::vars_list::reset()
> +{
> + buffer_length= 0;
> + track_all= 0;
> + if (m_registered_sysvars.records)
> + my_hash_reset(&m_registered_sysvars);
> +}
> +
> +/**
> + Copy the given list.
> +
> + @param from Source vars_list object.
> + @param thd THD handle to retrive the charset in use.
> +
> + @retval true there is something to track
> + @retval false nothing to track
> +*/
> +
> +void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
> +{
> + reset();
> + track_all= from->track_all;
> + free_hash();
> + buffer_length= from->buffer_length;
> + m_registered_sysvars= from->m_registered_sysvars;
> + from->init();
> +}
> +
> +/**
> + Inserts the variable to be tracked into m_registered_sysvars hash.
> +
> + @param node Node to be inserted.
> + @param svar address of the system variable
> +
> + @retval false success
> + @retval true error
> +*/
> +
> +bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
> + const sys_var *svar,
> + myf mem_flag)
> +{
> + if (!node)
> + {
> + if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
> + MYF(MY_WME | mem_flag))))
> + {
> + reset();
> + return true;
> + }
> + }
> +
> + node->m_svar= (sys_var *)svar;
> + node->test_load= node->m_svar->test_load;
> + node->m_changed= false;
> + if (my_hash_insert(&m_registered_sysvars, (uchar *) node))
> + {
> + my_free(node);
> + if (!search((sys_var *)svar))
> + {
> + //EOF (error is already reported)
> + reset();
> + return true;
> + }
> + }
> + return false;
> +}
> +
> +/**
> + Parse the specified system variables list.
> +
> + @Note In case of invalid entry a warning is raised per invalid entry.
> + This is done in order to handle 'potentially' valid system
> + variables from uninstalled plugins which might get installed in
> + future.
> +
> +
> + @param thd [IN] The thd handle.
> + @param var_list [IN] System variable list.
> + @param throw_error [IN] bool when set to true, returns an error
> + in case of invalid/duplicate values.
> + @param char_set [IN] charecter set information used for string
> + manipulations.
> + @param session_created [IN] bool variable which says if the parse is
> + already executed once. The mutex on variables
> + is not acquired if this variable is false.
why?
> +
> + @return
> + true Error
> + false Success
> +*/
> +bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
> + LEX_STRING var_list,
> + bool throw_error,
> + const CHARSET_INFO *char_set,
> + bool session_created)
> +{
> + const char separator= ',';
> + char *token, *lasts= NULL;
> + size_t rest= var_list.length;
> +
> + if (!var_list.str || var_list.length == 0)
> + {
> + buffer_length= 1;
> + return false;
> + }
> +
> + if(!strcmp(var_list.str,(const char *)"*"))
> + {
> + track_all= true;
> + buffer_length= 2;
> + return false;
> + }
> +
> + buffer_length= var_list.length + 1;
> + token= var_list.str;
> +
> + track_all= false;
> + /*
> + If Lock to the plugin mutex is not acquired here itself, it results
> + in having to acquire it multiple times in find_sys_var_ex for each
> + token value. Hence the mutex is handled here to avoid a performance
> + overhead.
> + */
> + if (!thd || session_created)
> + mysql_mutex_lock(&LOCK_plugin);
> + for (;;)
> + {
> + sys_var *svar;
> + LEX_STRING var;
> +
> + lasts= (char *) memchr(token, separator, rest);
> +
> + var.str= token;
> + if (lasts)
> + {
> + var.length= (lasts - token);
> + rest-= var.length + 1;
> + }
> + else
> + var.length= rest;
> +
> + /* Remove leading/trailing whitespace. */
> + trim_whitespace(char_set, &var);
> +
> + if ((svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true)))
> + {
> + if (insert(NULL, svar, m_mem_flag) == TRUE)
> + goto error;
> + }
> + else if (throw_error && session_created && thd)
> + {
> + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
> + ER_WRONG_VALUE_FOR_VAR,
> + "%.*s is not a valid system variable and will"
> + "be ignored.", (int)var.length, token);
> + }
> + else
> + goto error;
> +
> + if (lasts)
> + token= lasts + 1;
> + else
> + break;
> + }
> + if (!thd || session_created)
> + mysql_mutex_unlock(&LOCK_plugin);
> +
> + return false;
> +
> +error:
> + if (!thd || session_created)
> + mysql_mutex_unlock(&LOCK_plugin);
> + return true;
> +}
> +
> +struct name_array_filler_data
> +{
> + LEX_CSTRING **names;
> + uint idx;
> +
> +};
> +
> +/** Collects variable references into array */
> +static my_bool name_array_filler(void *ptr, void *data_ptr)
> +{
> + Session_sysvars_tracker::sysvar_node_st *node=
> + (Session_sysvars_tracker::sysvar_node_st *)ptr;
> + name_array_filler_data *data= (struct name_array_filler_data *)data_ptr;
> + if (*node->test_load)
> + data->names[data->idx++]= &node->m_svar->name;
> + return FALSE;
> +}
> +
> +/* Sorts variable references array */
> +static int name_array_sorter(const void *a, const void *b)
> +{
> + LEX_CSTRING **an= (LEX_CSTRING **)a, **bn=(LEX_CSTRING **)b;
> + size_t min= MY_MIN((*an)->length, (*bn)->length);
> + int res= strncmp((*an)->str, (*bn)->str, min);
> + if (res == 0)
> + res= ((int)(*bn)->length)- ((int)(*an)->length);
> + return res;
> +}
> +
> +/**
> + Construct variable list by internal hash with references
> +*/
> +
> +bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
> + size_t buf_len)
> +{
> + struct name_array_filler_data data;
> + size_t left= buf_len;
> + size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *);
> + const char separator= ',';
> +
> + if (unlikely(buf_len < 1))
> + return true;
> +
> + if (unlikely(track_all))
> + {
> + if (buf_len < 2)
> + return true;
> + buf[0]= '*';
> + buf[1]= '\0';
> + return false;
> + }
> +
> + if (m_registered_sysvars.records == 0)
> + {
> + buf[0]= '\0';
> + return false;
> + }
> +
> + data.names= (LEX_CSTRING**)my_safe_alloca(names_size);
> +
> + if (unlikely(!data.names))
> + return true;
> +
> + data.idx= 0;
> +
> + mysql_mutex_lock(&LOCK_plugin);
> + my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data);
> + DBUG_ASSERT(data.idx <= m_registered_sysvars.records);
> +
> +
> + if (m_registered_sysvars.records == 0)
you could've done that before my_hash_iterate :)
> + {
> + mysql_mutex_unlock(&LOCK_plugin);
> + buf[0]= '\0';
> + return false;
> + }
> +
> + my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *),
> + &name_array_sorter);
> +
> + for(uint i= 0; i < data.idx; i++)
> + {
> + LEX_CSTRING *nm= data.names[i];
> + size_t ln= nm->length + 1;
> + if (ln > left)
> + {
> + mysql_mutex_unlock(&LOCK_plugin);
> + my_safe_afree(data.names, names_size);
> + return true;
> + }
> + memcpy(buf, nm->str, nm->length);
> + buf[nm->length]= separator;
> + buf+= ln;
> + left-= ln;
> + }
> + mysql_mutex_unlock(&LOCK_plugin);
> +
> + buf--; buf[0]= '\0';
> + my_safe_afree(data.names, names_size);
> +
> + return false;
> +}
> +
> +/**
> + Enable session tracker by parsing global value of tracked variables.
> +
> + @param thd [IN] The thd handle.
> +
> + @retval true Error
> + @retval false Success
> +*/
> +
> +bool Session_sysvars_tracker::enable(THD *thd)
> +{
> + sys_var *svar;
> +
> + mysql_mutex_lock(&LOCK_plugin);
> + svar= find_sys_var_ex(thd, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
> + SESSION_TRACK_SYSTEM_VARIABLES_NAME.length,
> + false, true);
> + DBUG_ASSERT(svar);
> +
> + set_var tmp(thd, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL);
> + svar->session_save_default(thd, &tmp);
> +
> + if (tool_list->parse_var_list(thd, tmp.save_result.string_value,
> + true, thd->charset(), false) == true)
> + {
> + mysql_mutex_unlock(&LOCK_plugin);
> + return true;
> + }
> + mysql_mutex_unlock(&LOCK_plugin);
> + orig_list->copy(tool_list, thd);
> + m_enabled= true;
This is the only tracker where enable() is different from update().
And the reason for that is that in update() you reuse results of the
check, so you only need to parse the string in enable().
But this is wrong. You cannot reuse results of the check(). The example
is kind of artificial, but still:
SET @@a=1, @@b=2, @@a=3;
if the update() method of @@b will fail, @@a final value should be 1.
but in your case (@@a is @@session_track_system_variables) it will
be 3, because you remember the value of the last check only.
So, the correct behavior is (for all sysvars):
in check it should only do the check and store the value in the
provided set_var::save_result union.
in update it should use this value.
and if you change session_track_system_variables to behave as other
sysvars do, then your enable() will be the same as update(),
and you can remove enable() method completely from all trackers.
> +
> + return false;
> +}
> +
> +
> +/**
> + Check system variable name(s).
> +
> + @note This function is called from the ON_CHECK() function of the
> + session_track_system_variables' sys_var class.
> +
> + @param thd [IN] The thd handle.
> + @param var [IN] A pointer to set_var holding the specified list of
> + system variable names.
> +
> + @retval true Error
> + @retval false Success
> +*/
> +
> +inline bool Session_sysvars_tracker::check(THD *thd, set_var *var)
> +{
> + return check_str(thd, var->save_result.string_value);
> +}
> +
> +inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING val)
> +{
> + tool_list->reset();
> + return tool_list->parse_var_list(thd, val, true,
> + thd->charset(), true);
> +}
> +
> +
> +/**
> + Once the value of the @@session_track_system_variables has been
> + successfully updated, this function calls
> + Session_sysvars_tracker::vars_list::copy updating the hash in orig_list
> + which represents the system variables to be tracked.
> +
> + @note This function is called from the ON_UPDATE() function of the
> + session_track_system_variables' sys_var class.
> +
> + @param thd [IN] The thd handle.
> +
> + @retval true Error
> + @retval false Success
> +*/
> +
> +bool Session_sysvars_tracker::update(THD *thd)
> +{
> + orig_list->copy(tool_list, thd);
> + return false;
> +}
> +
> +
> +/**
> + Store the data for changed system variables in the specified buffer.
> + Once the data is stored, we reset the flags related to state-change
> + (see reset()).
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @retval true Error
> + @retval false Success
> +*/
> +
> +bool Session_sysvars_tracker::store(THD *thd, String *buf)
> +{
> + char val_buf[SHOW_VAR_FUNC_BUFF_SIZE];
> + SHOW_VAR show;
> + const char *value;
> + sysvar_node_st *node;
> + const CHARSET_INFO *charset;
> + size_t val_length, length;
> + int idx= 0;
> +
> + /* As its always system variable. */
> + show.type= SHOW_SYS;
> +
> + while ((node= (sysvar_node_st *) (*orig_list)[idx]))
Let's be consistent. Either you use my_hash_iterate in construct_var_list()
and here. Or you use [] in both functions. Pick the one that you like
more and use it.
> + {
> + if (node->m_changed)
> + {
> + mysql_mutex_lock(&LOCK_plugin);
> + if (!*node->test_load)
> + {
> + mysql_mutex_unlock(&LOCK_plugin);
> + continue;
> + }
> + sys_var *svar= node->m_svar;
> + show.name= svar->name.str;
> + show.value= (char *) svar;
> +
> + value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL,
> + &charset, val_buf, &val_length);
if you have a sysvar already, you can simply do svar->val_str()
> + mysql_mutex_unlock(&LOCK_plugin);
> +
> + length= net_length_size(svar->name.length) +
> + svar->name.length +
> + net_length_size(val_length) +
> + val_length;
> +
> + compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251);
> + buf->prep_alloc(1 + net_length_size(length) + length, EXTRA_ALLOC);
> +
> + /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */
> + buf->q_net_store_length((ulonglong)SESSION_TRACK_SYSTEM_VARIABLES);
> +
> + /* Length of the overall entity. */
> + buf->q_net_store_length((ulonglong)length);
> +
> + /* System variable's name (length-encoded string). */
> + buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length);
> +
> + /* System variable's value (length-encoded string). */
> + buf->q_net_store_data((const uchar*)value, val_length);
> + }
> + ++ idx;
> + }
> +
> + reset();
> +
> + return false;
> +}
> +
> +
> +/**
> + Mark the system variable as changed.
> +
> + @param [IN] pointer on a variable
> +*/
> +
> +void Session_sysvars_tracker::mark_as_changed(THD *thd,
> + LEX_CSTRING *var)
> +{
> + sysvar_node_st *node= NULL;
> + sys_var *svar= (sys_var *)var;
> + /*
> + Check if the specified system variable is being tracked, if so
> + mark it as changed and also set the class's m_changed flag.
> + */
> + if ((node= (sysvar_node_st *) (orig_list->search(node, svar))))
> + {
> + node->m_changed= true;
> + m_changed= true;
> + /* do not cache the statement when there is change in session state */
> + thd->lex->safe_to_cache_query= 0;
> + thd->server_status|= SERVER_SESSION_STATE_CHANGED;
> + }
> +}
> +
> +
> +/**
> + Supply key to a hash.
> +
> + @param entry [IN] A single entry.
> + @param length [OUT] Length of the key.
> + @param not_used Unused.
> +
> + @return Pointer to the key buffer.
> +*/
> +
> +uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry,
> + size_t *length,
> + my_bool not_used __attribute__((unused)))
> +{
> + *length= sizeof(sys_var *);
> + return (uchar *) &(((sysvar_node_st *) entry)->m_svar);
> +}
> +
> +
> +/**
> + Prepare/reset the m_registered_sysvars hash for next statement.
> +*/
> +
> +void Session_sysvars_tracker::reset()
> +{
> + sysvar_node_st *node;
> + int idx= 0;
> +
> + while ((node= (sysvar_node_st *) (*orig_list)[idx]))
> + {
> + node->m_changed= false;
> + ++ idx;
> + }
> + m_changed= false;
> +}
> +
> +static Session_sysvars_tracker* sysvar_tracker(THD *thd)
> +{
> + return (Session_sysvars_tracker*)
> + thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER);
> +}
> +
> +bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
> +{
> + LEX_STRING tmp= {(char *)str, len};
> + if (thd && sysvar_tracker(thd)->is_enabled())
> + return sysvar_tracker(thd)->check_str(thd, tmp);
> + return Session_sysvars_tracker::server_init_check(thd, system_charset_info,
> + tmp);
this if() won't be needed, if Session_sysvars_tracker::check_str will not
store the parsed hash, as I commented earier
> +}
> +bool sysvartrack_reprint_value(THD *thd, char *str, size_t len)
> +{
> + LEX_STRING tmp= {str, len};
> + return Session_sysvars_tracker::server_init_process(thd,
> + system_charset_info,
> + tmp);
> +}
> +bool sysvartrack_update(THD *thd)
> +{
> + return sysvar_tracker(thd)->update(thd);
> +}
> +size_t sysvartrack_value_len(THD *thd)
> +{
> + return sysvar_tracker(thd)->get_buffer_length();
> +}
> +bool sysvartrack_value_construct(THD *thd, char *val, size_t len)
> +{
> + return sysvar_tracker(thd)->construct_var_list(val, len);
> +}
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +/**
> + Enable/disable the tracker based on @@session_track_schema's value.
> +
> + @param thd [IN] The thd handle.
> +
> + @return
> + false (always)
> +*/
> +
> +bool Current_schema_tracker::update(THD *thd)
> +{
> + m_enabled= thd->variables.session_track_schema;
> + return false;
> +}
> +
> +
> +/**
> + Store the schema name as length-encoded string in the specified buffer.
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @reval false Success
> + @retval true Error
> +*/
> +
> +bool Current_schema_tracker::store(THD *thd, String *buf)
> +{
> + ulonglong db_length, length;
> +
> + /*
> + Protocol made (by unknown reasons) redundant:
> + It saves length of database name and name of database name +
> + length of saved length of database length.
> + */
> + length= db_length= thd->db_length;
> + length += net_length_size(length);
> +
> + compile_time_assert(SESSION_TRACK_SCHEMA < 251);
> + compile_time_assert(NAME_LEN < 251);
> + DBUG_ASSERT(net_length_size(length) < 251);
eh? net_length_size < 251? may be length < 251?
> + if (buf->prep_alloc(1 + 1 + length, EXTRA_ALLOC))
> + return true;
> +
> + /* Session state type (SESSION_TRACK_SCHEMA) */
> + buf->q_net_store_length((ulonglong)SESSION_TRACK_SCHEMA);
you can use buf->q_append((char)SESSION_TRACK_SCHEMA) instead.
It's faster and you already know that SESSION_TRACK_SCHEMA will be encoded
in one byte. This applies to other similar places too.
> +
> + /* Length of the overall entity. */
> + buf->q_net_store_length(length);
> +
> + /* Length and current schema name */
> + buf->q_net_store_data((const uchar *)thd->db, thd->db_length);
> +
> + reset();
> +
> + return false;
> +}
> +
> +
> +/**
> + Mark the tracker as changed.
> +*/
> +
> +void Current_schema_tracker::mark_as_changed(THD *thd, LEX_CSTRING *)
> +{
> + m_changed= true;
> + thd->lex->safe_to_cache_query= 0;
> + thd->server_status|= SERVER_SESSION_STATE_CHANGED;
put this code into the parent State_tracker class
then all other trackers won't need to implement it.
Only Session_sysvars_tracker will, but still it'll call
State_tracker::mark_as_changed(...).
> +}
> +
> +
> +/**
> + Reset the m_changed flag for next statement.
> +
> + @return void
> +*/
> +
> +void Current_schema_tracker::reset()
> +{
> + m_changed= false;
> +}
> +
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +
> +Transaction_state_tracker::Transaction_state_tracker()
> +{
> + m_enabled = false;
> + tx_changed = TX_CHG_NONE;
> + tx_curr_state =
> + tx_reported_state= TX_EMPTY;
> + tx_read_flags = TX_READ_INHERIT;
> + tx_isol_level = TX_ISOL_INHERIT;
> +}
> +
> +/**
> + Enable/disable the tracker based on @@session_track_transaction_info.
> +
> + @param thd [IN] The thd handle.
> +
> + @retval true if updating the tracking level failed
> + @retval false otherwise
> +*/
> +
> +bool Transaction_state_tracker::update(THD *thd)
> +{
> +#ifdef EMBEDDED_LIBRARY
why you don't do that for other trackers?
or, in other words, why do you do it for this tracker?
> + return true;
> +
> +#else
> + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
> + {
> + /*
> + If we only just turned reporting on (rather than changing between
> + state and characteristics reporting), start from a defined state.
> + */
> + if (!m_enabled)
> + {
> + tx_curr_state =
> + tx_reported_state = TX_EMPTY;
> + tx_changed |= TX_CHG_STATE;
> + m_enabled= true;
> + }
> + if (thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS)
> + tx_changed |= TX_CHG_CHISTICS;
> + mark_as_changed(thd, NULL);
> + }
> + else
> + m_enabled= false;
> +
> + return false;
> +#endif
> +}
> +
> +
> +/**
> + Store the transaction state (and, optionally, characteristics)
> + as length-encoded string in the specified buffer. Once the data
> + is stored, we reset the flags related to state-change (see reset()).
> +
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @retval false Success
> + @retval true Error
> +*/
> +
> +static LEX_CSTRING isol[]= {
> + { STRING_WITH_LEN("READ UNCOMMITTED") },
> + { STRING_WITH_LEN("READ COMMITTED") },
> + { STRING_WITH_LEN("REPEATABLE READ") },
> + { STRING_WITH_LEN("SERIALIZABLE") }
> +};
> +
> +bool Transaction_state_tracker::store(THD *thd, String *buf)
> +{
> + /* STATE */
> + if (tx_changed & TX_CHG_STATE)
> + {
> + uchar *to= (uchar *) buf->prep_append(11, EXTRA_ALLOC);
> +
> + to= net_store_length((uchar *) to,
> + (ulonglong) SESSION_TRACK_TRANSACTION_STATE);
> +
> + to= net_store_length((uchar *) to, (ulonglong) 9);
> + to= net_store_length((uchar *) to, (ulonglong) 8);
> +
> + *(to++)= (tx_curr_state & TX_EXPLICIT) ? 'T' :
> + ((tx_curr_state & TX_IMPLICIT) ? 'I' : '_');
> + *(to++)= (tx_curr_state & TX_READ_UNSAFE) ? 'r' : '_';
> + *(to++)= ((tx_curr_state & TX_READ_TRX) ||
> + (tx_curr_state & TX_WITH_SNAPSHOT)) ? 'R' : '_';
> + *(to++)= (tx_curr_state & TX_WRITE_UNSAFE) ? 'w' : '_';
> + *(to++)= (tx_curr_state & TX_WRITE_TRX) ? 'W' : '_';
> + *(to++)= (tx_curr_state & TX_STMT_UNSAFE) ? 's' : '_';
> + *(to++)= (tx_curr_state & TX_RESULT_SET) ? 'S' : '_';
> + *(to++)= (tx_curr_state & TX_LOCKED_TABLES) ? 'L' : '_';
This doesn't make much sense, but I suppose that's what MySQL does :(
btw, why don't you use String methods here? q_net_store_length
and q_append? That'd be consistent with the rest of the code.
> + }
> +
> + /* CHARACTERISTICS -- How to restart the transaction */
> +
> + if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) &&
> + (tx_changed & TX_CHG_CHISTICS))
> + {
> + bool is_xa= (thd->transaction.xid_state.xa_state != XA_NOTR);
> + size_t start;
> +
> + /* 2 length by 1 byte and code */
> + buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC);
> +
> + /* Session state type (SESSION_TRACK_TRANSACTION_CHARACTERISTICS) */
> + buf->q_net_store_length((ulonglong)
> + SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
> + compile_time_assert(SESSION_TRACK_TRANSACTION_CHARACTERISTICS < 251);
> +
> + /* Whole length: Track result will fit in 251 byte (in worst case 110) */
> + buf->append('\0');
> +
> + /* String length: Track result will fit in 251 byte (in worst case 110) */
> + buf->append('\0');
remove these two comments, use q_append, add a comment, like
/* placeholders for lengths. will be filled in at the end */
> +
> + start= buf->length();
> +
> + {
> + /*
> + We have four basic replay scenarios:
> +
> + a) SET TRANSACTION was used, but before an actual transaction
> + was started, the load balancer moves the connection elsewhere.
> + In that case, the same one-shots should be set up in the
> + target session. (read-only/read-write; isolation-level)
> +
> + b) The initial transaction has begun; the relevant characteristics
> + are the session defaults, possibly overridden by previous
> + SET TRANSACTION statements, possibly overridden or extended
> + by options passed to the START TRANSACTION statement.
> + If the load balancer wishes to move this transaction,
> + it needs to be replayed with the correct characteristics.
> + (read-only/read-write from SET or START;
> + isolation-level from SET only, snapshot from START only)
> +
> + c) A subsequent transaction started with START TRANSACTION
> + (which is legal syntax in lieu of COMMIT AND CHAIN in MySQL)
> + may add/modify the current one-shots:
> +
> + - It may set up a read-only/read-write one-shot.
> + This one-shot will override the value used in the previous
> + transaction (whether that came from the default or a one-shot),
> + and, like all one-shots currently do, it will carry over into
> + any subsequent transactions that don't explicitly override them
> + in turn. This behavior is not guaranteed in the docs and may
> + change in the future, but the tracker item should correctly
> + reflect whatever behavior a given version of mysqld implements.
> +
> + - It may also set up a WITH CONSISTENT SNAPSHOT one-shot.
> + This one-shot does not currently carry over into subsequent
> + transactions (meaning that with "traditional syntax", WITH
> + CONSISTENT SNAPSHOT can only be requested for the first part
> + of a transaction chain). Again, the tracker item should reflect
> + mysqld behavior.
> +
> + d) A subsequent transaction started using COMMIT AND CHAIN
> + (or, for that matter, BEGIN WORK, which is currently
> + legal and equivalent syntax in MySQL, or START TRANSACTION
> + sans options) will re-use any one-shots set up so far
> + (with SET before the first transaction started, and with
> + all subsequent STARTs), except for WITH CONSISTANT SNAPSHOT,
> + which will never be chained and only applies when explicitly
> + given.
> +
> + It bears noting that if we switch sessions in a follow-up
> + transaction, SET TRANSACTION would be illegal in the old
> + session (as a transaction is active), whereas in the target
> + session which is being prepared, it should be legal, as no
> + transaction (chain) should have started yet.
> +
> + Therefore, we are free to generate SET TRANSACTION as a replay
> + statement even for a transaction that isn't the first in an
> + ongoing chain. Consider
> +
> + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
> + START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;
> + # work
> + COMMIT AND CHAIN;
> +
> + If we switch away at this point, the replay in the new session
> + needs to be
> +
> + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
> + START TRANSACTION READ ONLY;
> +
> + When a transaction ends (COMMIT/ROLLBACK sans CHAIN), all
> + per-transaction characteristics are reset to the session's
> + defaults.
> +
> + This also holds for a transaction ended implicitly! (transaction.cc)
> + Once again, the aim is to have the tracker item reflect on a
> + given mysqld's actual behavior.
> + */
> +
> + /*
> + "ISOLATION LEVEL"
> + Only legal in SET TRANSACTION, so will always be replayed as such.
> + */
> + if (tx_isol_level != TX_ISOL_INHERIT)
> + {
> + /*
> + Unfortunately, we can't re-use tx_isolation_names /
> + tx_isolation_typelib as it hyphenates its items.
> + */
> + buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL "));
> + buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length);
this could be simply
buf->append(&isol[tx_isol_level - 1]);
or even
buf->append(isol + tx_isol_level);
if you add a dummy element to isol.
but whatever... long version works too.
> + buf->append(STRING_WITH_LEN("; "));
> + }
> +
> + /*
> + Start transaction will usually result in TX_EXPLICIT (transaction
> + started, but no data attached yet), except when WITH CONSISTENT
> + SNAPSHOT, in which case we may have data pending.
> + If it's an XA transaction, we don't go through here so we can
> + first print the trx access mode ("SET TRANSACTION READ ...")
> + separately before adding XA START (whereas with START TRANSACTION,
> + we can merge the access mode into the same statement).
> + */
> + if ((tx_curr_state & TX_EXPLICIT) && !is_xa)
> + {
> + buf->append(STRING_WITH_LEN("START TRANSACTION"));
> +
> + /*
> + "WITH CONSISTENT SNAPSHOT"
> + Defaults to no, can only be enabled.
> + Only appears in START TRANSACTION.
> + */
> + if (tx_curr_state & TX_WITH_SNAPSHOT)
> + {
> + buf->append(STRING_WITH_LEN(" WITH CONSISTENT SNAPSHOT"));
> + if (tx_read_flags != TX_READ_INHERIT)
> + buf->append(STRING_WITH_LEN(","));
> + }
> +
> + /*
> + "READ WRITE / READ ONLY" can be set globally, per-session,
> + or just for one transaction.
> +
> + The latter case can take the form of
> + START TRANSACTION READ (WRITE|ONLY), or of
> + SET TRANSACTION READ (ONLY|WRITE).
> + (Both set thd->read_only for the upcoming transaction;
> + it will ultimately be re-set to the session default.)
> +
> + As the regular session-variable tracker does not monitor the one-shot,
> + we'll have to do it here.
> +
> + If READ is flagged as set explicitly (rather than just inherited
> + from the session's default), we'll get the actual bool from the THD.
> + */
> + if (tx_read_flags != TX_READ_INHERIT)
> + {
> + if (tx_read_flags == TX_READ_ONLY)
> + buf->append(STRING_WITH_LEN(" READ ONLY"));
> + else
> + buf->append(STRING_WITH_LEN(" READ WRITE"));
> + }
> + buf->append(STRING_WITH_LEN("; "));
> + }
> + else if (tx_read_flags != TX_READ_INHERIT)
> + {
> + /*
> + "READ ONLY" / "READ WRITE"
> + We could transform this to SET TRANSACTION even when it occurs
> + in START TRANSACTION, but for now, we'll resysynthesize the original
> + command as closely as possible.
> + */
> + buf->append(STRING_WITH_LEN("SET TRANSACTION "));
> + if (tx_read_flags == TX_READ_ONLY)
> + buf->append(STRING_WITH_LEN("READ ONLY; "));
> + else
> + buf->append(STRING_WITH_LEN("READ WRITE; "));
> + }
> +
> + if ((tx_curr_state & TX_EXPLICIT) && is_xa)
> + {
> + XID *xid= &thd->transaction.xid_state.xid;
> + long glen, blen;
> +
> + buf->append(STRING_WITH_LEN("XA START"));
> +
> + if ((glen= xid->gtrid_length) > 0)
> + {
> + buf->append(STRING_WITH_LEN(" '"));
> + buf->append(xid->data, glen);
> +
> + if ((blen= xid->bqual_length) > 0)
> + {
> + buf->append(STRING_WITH_LEN("','"));
> + buf->append(xid->data + glen, blen);
> + }
> + buf->append(STRING_WITH_LEN("'"));
> +
> + if (xid->formatID != 1)
> + {
> + buf->append(STRING_WITH_LEN(","));
> + buf->append_ulonglong(xid->formatID);
> + }
> + }
> +
> + buf->append(STRING_WITH_LEN("; "));
> + }
> +
> + // discard trailing space
> + if (buf->length() > start)
> + buf->length(buf->length() - 1);
> + }
> +
> + {
> + ulonglong length= buf->length() - start;
> + uchar *place= (uchar *)(buf->ptr() + (start - 2));
> + DBUG_ASSERT(length < 249); // in fact < 110
> + DBUG_ASSERT(start >= 3);
> +
> + DBUG_ASSERT((place - 1)[0] == SESSION_TRACK_TRANSACTION_CHARACTERISTICS);
> + /* Length of the overall entity. */
> + place[0]= length + 1;
> + /* Transaction characteristics (length-encoded string). */
> + place[1]= length;
> + }
> + }
> +
> + reset();
> +
> + return false;
> +}
> +
> +
> +/**
> + Mark the tracker as changed.
> +*/
> +
> +void Transaction_state_tracker::mark_as_changed(THD *thd,
> + LEX_CSTRING *tracked_item_nam)
> +{
> + m_changed= true;
> + thd->lex->safe_to_cache_query= 0;
> + thd->server_status|= SERVER_SESSION_STATE_CHANGED;
> +}
> +
> +
> +/**
> + Reset the m_changed flag for next statement.
> +*/
> +
> +void Transaction_state_tracker::reset()
> +{
> + m_changed= false;
> + tx_reported_state= tx_curr_state;
> + tx_changed= TX_CHG_NONE;
> +}
> +
> +
> +/**
> + Helper function: turn table info into table access flag.
> + Accepts table lock type and engine type flag (transactional/
> + non-transactional), and returns the corresponding access flag
> + out of TX_READ_TRX, TX_READ_UNSAFE, TX_WRITE_TRX, TX_WRITE_UNSAFE.
> +
> + @param thd [IN] The thd handle
> + @param set [IN] The table's access/lock type
> + @param set [IN] Whether the table's engine is transactional
> +
> + @return The table access flag
> +*/
> +
> +enum_tx_state Transaction_state_tracker::calc_trx_state(THD *thd,
> + thr_lock_type l,
> + bool has_trx)
> +{
> + enum_tx_state s;
> + bool read= (l <= TL_READ_NO_INSERT);
> +
> + if (read)
> + s= has_trx ? TX_READ_TRX : TX_READ_UNSAFE;
> + else
> + s= has_trx ? TX_WRITE_TRX : TX_WRITE_UNSAFE;
I'd do an array lookup, like
return arr[l <= TL_READ_NO_INSERT][has_trx];
but whatever...
> +
> + return s;
> +}
> +
> +
> +/**
> + Register the end of an (implicit or explicit) transaction.
> +
> + @param thd [IN] The thd handle
> +*/
> +void Transaction_state_tracker::end_trx(THD *thd)
> +{
> + DBUG_ASSERT(thd->variables.session_track_transaction_info > TX_TRACK_NONE);
> +
> + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
> + return;
> +
> + if (tx_curr_state != TX_EMPTY)
> + {
> + if (tx_curr_state & TX_EXPLICIT)
> + tx_changed |= TX_CHG_CHISTICS;
> + tx_curr_state &= TX_LOCKED_TABLES;
> + }
> + update_change_flags(thd);
> +}
> +
> +
> +/**
> + Clear flags pertaining to the current statement or transaction.
> + May be called repeatedly within the same execution cycle.
> +
> + @param thd [IN] The thd handle.
> + @param set [IN] The flags to clear
> +*/
> +
> +void Transaction_state_tracker::clear_trx_state(THD *thd, uint clear)
> +{
> + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
> + return;
> +
> + tx_curr_state &= ~clear;
> + update_change_flags(thd);
> +}
> +
> +
> +/**
> + Add flags pertaining to the current statement or transaction.
> + May be called repeatedly within the same execution cycle,
> + e.g. to add access info for more tables.
> +
> + @param thd [IN] The thd handle.
> + @param set [IN] The flags to add
> +*/
> +
> +void Transaction_state_tracker::add_trx_state(THD *thd, uint add)
> +{
> + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
> + return;
> +
> + if (add == TX_EXPLICIT)
> + {
> + /* Always send characteristic item (if tracked), always replace state. */
> + tx_changed |= TX_CHG_CHISTICS;
> + tx_curr_state = TX_EXPLICIT;
> + }
> +
> + /*
> + If we're not in an implicit or explicit transaction, but
> + autocommit==0 and tables are accessed, we flag "implicit transaction."
> + */
> + else if (!(tx_curr_state & (TX_EXPLICIT|TX_IMPLICIT)) &&
> + (thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) &&
> + (add &
> + (TX_READ_TRX | TX_READ_UNSAFE | TX_WRITE_TRX | TX_WRITE_UNSAFE)))
> + tx_curr_state |= TX_IMPLICIT;
> +
> + /*
> + Only flag state when in transaction or LOCK TABLES is added.
> + */
> + if ((tx_curr_state & (TX_EXPLICIT | TX_IMPLICIT)) ||
> + (add & TX_LOCKED_TABLES))
> + tx_curr_state |= add;
> +
> + update_change_flags(thd);
> +}
> +
> +
> +/**
> + Add "unsafe statement" flag if applicable.
> +
> + @param thd [IN] The thd handle.
> + @param set [IN] The flags to add
> +*/
> +
> +void Transaction_state_tracker::add_trx_state_from_thd(THD *thd)
> +{
> + if (m_enabled)
> + {
> + if (thd->lex->is_stmt_unsafe())
> + add_trx_state(thd, TX_STMT_UNSAFE);
> + }
> +}
> +
> +
> +/**
> + Set read flags (read only/read write) pertaining to the next
> + transaction.
> +
> + @param thd [IN] The thd handle.
> + @param set [IN] The flags to set
> +*/
> +
> +void Transaction_state_tracker::set_read_flags(THD *thd,
> + enum enum_tx_read_flags flags)
> +{
> + if (m_enabled && (tx_read_flags != flags))
> + {
> + tx_read_flags = flags;
> + tx_changed |= TX_CHG_CHISTICS;
> + mark_as_changed(thd, NULL);
> + }
> +}
> +
> +
> +/**
> + Set isolation level pertaining to the next transaction.
> +
> + @param thd [IN] The thd handle.
> + @param set [IN] The isolation level to set
> +*/
> +
> +void Transaction_state_tracker::set_isol_level(THD *thd,
> + enum enum_tx_isol_level level)
> +{
> + if (m_enabled && (tx_isol_level != level))
> + {
> + tx_isol_level = level;
> + tx_changed |= TX_CHG_CHISTICS;
> + mark_as_changed(thd, NULL);
> + }
> +}
> +
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +Session_state_change_tracker::Session_state_change_tracker()
> +{
> + m_changed= false;
> +}
> +
> +/**
> + @Enable/disable the tracker based on @@session_track_state_change value.
> +
> + @param thd [IN] The thd handle.
> + @return false (always)
> +
> +**/
> +
> +bool Session_state_change_tracker::update(THD *thd)
> +{
> + m_enabled= thd->variables.session_track_state_change;
> + return false;
> +}
> +
> +/**
> + Store the '1' in the specified buffer when state is changed.
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @reval false Success
> + @retval true Error
> +**/
> +
> +bool Session_state_change_tracker::store(THD *thd, String *buf)
> +{
> + if (buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC))
> + return true;
> +
> + compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251);
> + /* Session state type (SESSION_TRACK_STATE_CHANGE) */
> + buf->q_net_store_length((ulonglong)SESSION_TRACK_STATE_CHANGE);
> +
> + /* Length of the overall entity (1 byte) */
> + buf->q_append('\1');
> +
> + DBUG_ASSERT(is_state_changed(thd));
> + buf->q_append('1');
> +
> + reset();
> +
> + return false;
> +}
> +
> +/**
> + Mark the tracker as changed and associated session
> + attributes accordingly.
> +*/
> +
> +void Session_state_change_tracker::mark_as_changed(THD *thd, LEX_CSTRING *)
> +{
> + m_changed= true;
> + thd->lex->safe_to_cache_query= 0;
> + thd->server_status|= SERVER_SESSION_STATE_CHANGED;
> +}
> +
> +/**
> + Reset the m_changed flag for next statement.
> +*/
> +
> +void Session_state_change_tracker::reset()
> +{
> + m_changed= false;
> +}
> +
> +/**
> + Find if there is a session state change.
> +*/
> +
> +bool Session_state_change_tracker::is_state_changed(THD *)
> +{
> + return m_changed;
> +}
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +/**
> + @brief Initialize session tracker objects.
> +*/
> +
> +Session_tracker::Session_tracker()
> +{
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + m_trackers[i]= NULL;
> +}
> +
> +
> +/**
> + @brief Enables the tracker objects.
> +
> + @param thd [IN] The thread handle.
> +
> + @return void
> +*/
> +
> +void Session_tracker::enable(THD *thd)
> +{
> + /*
> + Originally and correctly this allocation was in the constructor and
> + deallocation in the destructor, but in this case memory counting
> + system works incorrectly (for example in INSERT DELAYED thread)
> + */
> + deinit();
> + m_trackers[SESSION_SYSVARS_TRACKER]=
> + new (std::nothrow) Session_sysvars_tracker();
> + m_trackers[CURRENT_SCHEMA_TRACKER]=
> + new (std::nothrow) Current_schema_tracker;
> + m_trackers[SESSION_STATE_CHANGE_TRACKER]=
> + new (std::nothrow) Session_state_change_tracker;
> + m_trackers[SESSION_GTIDS_TRACKER]=
> + new (std::nothrow) Not_implemented_tracker;
> + m_trackers[TRANSACTION_INFO_TRACKER]=
> + new (std::nothrow) Transaction_state_tracker;
why do you need to allocate them dynamically at all?
Just put them inside Session_tracker object.
> +
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + m_trackers[i]->enable(thd);
> +}
> +
> +
> +/**
> + Method called during the server startup to verify the contents
> + of @@session_track_system_variables.
> +
> + @retval false Success
> + @retval true Failure
> +*/
> +
> +bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set)
> +{
> + Session_sysvars_tracker *server_tracker;
> + bool result;
> + sys_var *svar= find_sys_var_ex(NULL, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
> + SESSION_TRACK_SYSTEM_VARIABLES_NAME.length,
> + false, true);
> + DBUG_ASSERT(svar);
> + set_var tmp(NULL, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL);
> + svar->session_save_default(NULL, &tmp);
> + server_tracker= new (std::nothrow) Session_sysvars_tracker();
> + result= server_tracker->server_init_check(NULL, char_set,
> + tmp.save_result.string_value);
> + delete server_tracker;
grrrr. really? svar->session_save_default() and new/delete a tracker?
just to verify the value?
> + return result;
> +}
> +
> +
> +/**
> + @brief Store all change information in the specified buffer.
> +
> + @param thd [IN] The thd handle.
> + @param buf [OUT] Reference to the string buffer to which the state
> + change data needs to be written.
> +*/
> +
> +void Session_tracker::store(THD *thd, String *buf)
> +{
> + size_t start;
> +
> + /*
> + Probably most track result will fit in 251 byte so lets made it at
> + least efficient. We allocate 1 byte for length and then will move
> + string if there is more.
> + */
> + buf->append('\0');
> + start= buf->length();
> +
> + /* Get total length. */
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + {
> + if (m_trackers[i]->is_changed() &&
> + m_trackers[i]->store(thd, buf))
> + {
> + buf->length(start); // it is safer to have 0-length block in case of error
> + return;
> + }
> + }
> +
> + size_t length= buf->length() - start;
> + uchar *data= (uchar *)(buf->ptr() + start);
> + uint size;
> +
> + if ((size= net_length_size(length)) != 1)
> + {
> + if (buf->prep_alloc(size - 1, EXTRA_ALLOC))
> + {
> + buf->length(start); // it is safer to have 0-length block in case of error
> + return;
> + }
> + memmove(data + (size - 1), data, length);
> + }
> +
> + net_store_length(data - 1, length);
> +}
> diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
> index 7dfc622..c9e83ae 100644
> --- a/sql/share/errmsg-utf8.txt
> +++ b/sql/share/errmsg-utf8.txt
> @@ -7168,6 +7167,10 @@ skip-to-error-number 3000
> ER_MYSQL_57_TEST
> eng "5.7 test"
>
> +ER_NET_OK_PACKET_TOO_LARGE 08S01
> + eng "OK packet too large"
> + ukr "Пакет OK надто великий"
as I've said above, this is a rather strange error.
> +
> # MariaDB extra error numbers starts from 4000
> skip-to-error-number 4000
>
> diff --git a/sql/lock.cc b/sql/lock.cc
> index 2e44786..e2d2da0 100644
> --- a/sql/lock.cc
> +++ b/sql/lock.cc
> @@ -89,6 +89,7 @@ extern HASH open_cache;
>
> static int lock_external(THD *thd, TABLE **table,uint count);
> static int unlock_external(THD *thd, TABLE **table,uint count);
> +static void track_table_access(THD *thd, TABLE **tables, size_t count);
this forward declaration is not needed
>
> /* Map the return value of thr_lock to an error from errmsg.txt */
> static int thr_lock_errno_to_mysql[]=
> @@ -244,6 +245,39 @@ void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
>
>
> /**
> + Scan array of tables for access types; update transaction tracker
> + accordingly.
> +
> + @param thd The current thread.
> + @param tables An array of pointers to the tables to lock.
> + @param count The number of tables to lock.
> +*/
> +
> +static void track_table_access(THD *thd, TABLE **tables, size_t count)
> +{
> + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
> + {
> + Transaction_state_tracker *tst= (Transaction_state_tracker *)
> + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
> + enum enum_tx_state s;
> +
> + while (count--)
> + {
> + TABLE *t= tables[count];
> +
> + if (t)
> + {
> + s= tst->calc_trx_state(thd, t->reginfo.lock_type,
> + t->file->has_transactions());
don't do calc_trx_state, turn it into a calc_and_add_trx_state.
or, may be,
tst->add_trx_state(thd, t->reginfo.lock_type, t->file->has_transactions())
because in all cases where calc_trx_state is used, the caller
calls add_trx_state immediately after that.
> + tst->add_trx_state(thd, s);
> + }
> + }
> + }
> +}
> +
> +
> +
> +/**
> Lock tables.
>
> @param thd The current thread.
> diff --git a/sql/set_var.cc b/sql/set_var.cc
> index 5392a00..5f2bc93 100644
> --- a/sql/set_var.cc
> +++ b/sql/set_var.cc
> @@ -204,8 +209,29 @@ bool sys_var::update(THD *thd, set_var *var)
> (on_update && on_update(this, thd, OPT_GLOBAL));
> }
> else
> - return session_update(thd, var) ||
> + {
> + bool ret= session_update(thd, var) ||
> (on_update && on_update(this, thd, OPT_SESSION));
> +
> + /*
> + Make sure we don't session-track variables that are not actually
> + part of the session. tx_isolation and and tx_read_only for example
> + exist as GLOBAL, SESSION, and one-shot ("for next transaction only").
> + */
> + if ((var->type == OPT_SESSION) && (!ret))
first, s/var->type/type/
second, when type is not OPT_SESSION? only for tx_isolation and tx_read_only?
btw, why that many parentheses?
> + {
> + thd->session_tracker.mark_as_changed(thd, SESSION_SYSVARS_TRACKER,
> + (LEX_CSTRING*)var->var);
> + /*
> + Here MySQL sends variable name to avoid reporting change of
> + the tracker itself, but we decided that it is not needed
> + */
> + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
> + NULL);
> + }
> +
> + return ret;
> + }
> }
>
> uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base)
> diff --git a/sql/sp_head.cc b/sql/sp_head.cc
> index d58b51a..396f308 100644
> --- a/sql/sp_head.cc
> +++ b/sql/sp_head.cc
> @@ -2975,6 +2977,16 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
>
> reinit_stmt_before_use(thd, m_lex);
>
> +#ifndef EMBEDDED_LIBRARY
> + /*
> + if there was instruction which changed tracking state before, result
> + can go with this command OK packet, so better do not cache the result.
sorry, I cannot parse that :(
> + */
> + if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
> + (thd->server_status & SERVER_SESSION_STATE_CHANGED))
> + thd->lex->safe_to_cache_query= 0;
> +#endif
> +
> if (open_tables)
> res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
>
> diff --git a/sql/sql_base.cc b/sql/sql_base.cc
> index 08a9647..924e9d4 100644
> --- a/sql/sql_base.cc
> +++ b/sql/sql_base.cc
> @@ -5013,6 +5018,20 @@ static bool check_lock_and_start_stmt(THD *thd,
> table_list->table->file->print_error(error, MYF(0));
> DBUG_RETURN(1);
> }
> +
> + /*
> + Record in transaction state tracking
> + */
> + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE)
> + {
> + Transaction_state_tracker *tst= (Transaction_state_tracker *)
> + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER);
> + enum enum_tx_state s=
> + tst->calc_trx_state(thd, lock_type,
> + table_list->table->file->has_transactions());
> + tst->add_trx_state(thd, s);
after you get rid of a separatre calc_trx_state call, you'll be able to
use TRANSACT_TRACKER here too
> + }
> +
> DBUG_RETURN(0);
> }
>
> diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
> index 42b80d9..b0dacca 100644
> --- a/sql/sql_cache.cc
> +++ b/sql/sql_cache.cc
> @@ -1381,6 +1381,19 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
> DBUG_VOID_RETURN;
> }
>
> + /*
> + Do not store queries while tracking transaction state.
> + The tracker already flags queries that actually have
> + transaction tracker items, but this will make behavior
> + more straight forward.
why?
> + */
> + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
> + {
> + DBUG_PRINT("qcache", ("Do not work with transaction tracking"));
> + DBUG_VOID_RETURN;
> + }
> +
> +
> /* The following assert fails if we haven't called send_result_to_client */
> DBUG_ASSERT(thd->base_query.is_alloced() ||
> thd->base_query.ptr() == thd->query());
> @@ -1719,6 +1732,18 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
> goto err;
> }
>
> + /*
> + Don't allow serving from Query_cache while tracking transaction
> + state. This is a safeguard in case an otherwise matching query
> + was added to the cache before tracking was turned on.
how could that be possible?
> + */
> + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE)
> + {
> + DBUG_PRINT("qcache", ("Do not work with transaction tracking"));
> + goto err;
> + }
> +
> +
> thd->query_cache_is_applicable= 1;
> sql= org_sql; sql_end= sql + query_length;
>
> diff --git a/sql/sql_db.cc b/sql/sql_db.cc
> index 2ba67cb..1780a81 100644
> --- a/sql/sql_db.cc
> +++ b/sql/sql_db.cc
> @@ -1034,7 +1034,10 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
> it to 0.
> */
> if (thd->db && cmp_db_names(thd->db, db) && !error)
> + {
> mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
> + thd->session_tracker.mark_as_changed(thd, CURRENT_SCHEMA_TRACKER, NULL);
another point for simply including all trackers into Session_tracker.
this method will be inlined, the check for is_enabled will be inlined too,
so in the normal case this will become much cheaper.
> + }
> my_dirend(dirp);
> DBUG_RETURN(error);
> }
> diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
> index f540c26..0342a88 100644
> --- a/sql/sql_plugin.cc
> +++ b/sql/sql_plugin.cc
> @@ -269,6 +269,7 @@ struct st_bookmark
> uint name_len;
> int offset;
> uint version;
> + bool loaded;
I'm not commenting on changes in this file, because I hope they will
go away completely, see my earlier comment.
> char key[1];
> };
>
> @@ -322,6 +323,8 @@ static void unlock_variables(THD *thd, struct system_variables *vars);
> static void cleanup_variables(struct system_variables *vars);
> static void plugin_vars_free_values(sys_var *vars);
> static void restore_ptr_backup(uint n, st_ptr_backup *backup);
> +#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
> +#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
nope. remove those macros, as I've already wrote in a previous review.
I introduced them to track memory allocations (all _ci macros pass
"caller info" down the stack, __FILE__ and __LINE__, so that safemalloc
would show not the line where malloc was called, but where, say,
init_dynamic_array was called). in MariaDB this is not needed, because
I've changed safemalloc to remember stack traces. So all _ci macros are
long gone now, don't add them back from MySQL code.
> static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
> static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
> static void reap_plugins(void);
> diff --git a/sql/sql_show.cc b/sql/sql_show.cc
> index f41fb39..b6ed6e6 100644
> --- a/sql/sql_show.cc
> +++ b/sql/sql_show.cc
> @@ -3272,109 +3398,21 @@ static bool show_status_array(THD *thd, const char *wild,
> name_buffer, wild))) &&
> (!cond || cond->val_int()))
> {
> - void *value=var->value;
> - const char *pos, *end; // We assign a lot of const's
> + const char *pos; // We assign a lot of const's
> + size_t length;
>
> if (show_type == SHOW_SYS)
> - {
> - sys_var *var= (sys_var *) value;
> - show_type= var->show_type();
> mysql_mutex_lock(&LOCK_global_system_variables);
> - value= var->value_ptr(thd, scope, &null_lex_str);
> - charset= var->charset(thd);
> - }
> + pos= get_one_variable(thd, var, scope, show_type, status_var,
> + &charset, buff, &length);
this wasn't needed, if you won't use get_one_variable() anywhere else
>
> - pos= end= buff;
> - /*
> - note that value may be == buff. All SHOW_xxx code below
> - should still work in this case
> - */
> - switch (show_type) {
> - case SHOW_DOUBLE_STATUS:
> - value= ((char *) status_var + (intptr) value);
> - /* fall through */
> - case SHOW_DOUBLE:
> - /* 6 is the default precision for '%f' in sprintf() */
> - end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
> - break;
> - case SHOW_LONG_STATUS:
> - value= ((char *) status_var + (intptr) value);
> - /* fall through */
> - case SHOW_ULONG:
> - case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
> - end= int10_to_str(*(long*) value, buff, 10);
> - break;
> - case SHOW_LONGLONG_STATUS:
> - value= ((char *) status_var + (intptr) value);
> - /* fall through */
> - case SHOW_ULONGLONG:
> - end= longlong10_to_str(*(longlong*) value, buff, 10);
> - break;
> - case SHOW_HA_ROWS:
> - end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
> - break;
> - case SHOW_BOOL:
> - end= strmov(buff, *(bool*) value ? "ON" : "OFF");
> - break;
> - case SHOW_MY_BOOL:
> - end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
> - break;
> - case SHOW_UINT:
> - end= int10_to_str((long) *(uint*) value, buff, 10);
> - break;
> - case SHOW_SINT:
> - end= int10_to_str((long) *(int*) value, buff, -10);
> - break;
> - case SHOW_SLONG:
> - end= int10_to_str(*(long*) value, buff, -10);
> - break;
> - case SHOW_SLONGLONG:
> - end= longlong10_to_str(*(longlong*) value, buff, -10);
> - break;
> - case SHOW_HAVE:
> - {
> - SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
> - pos= show_comp_option_name[(int) tmp];
> - end= strend(pos);
> - break;
> - }
> - case SHOW_CHAR:
> - {
> - if (!(pos= (char*)value))
> - pos= "";
> - end= strend(pos);
> - break;
> - }
> - case SHOW_CHAR_PTR:
> - {
> - if (!(pos= *(char**) value))
> - pos= "";
> -
> - end= strend(pos);
> - break;
> - }
> - case SHOW_LEX_STRING:
> - {
> - LEX_STRING *ls=(LEX_STRING*)value;
> - if (!(pos= ls->str))
> - end= pos= "";
> - else
> - end= pos + ls->length;
> - break;
> - }
> - case SHOW_UNDEF:
> - break; // Return empty string
> - case SHOW_SYS: // Cannot happen
> - default:
> - DBUG_ASSERT(0);
> - break;
> - }
> - table->field[1]->store(pos, (uint32) (end - pos), charset);
> + table->field[1]->store(pos, (uint32) length, charset);
> + thd->count_cuted_fields= CHECK_FIELD_IGNORE;
> table->field[1]->set_notnull();
> -
> - if (var->type == SHOW_SYS)
> + if (show_type == SHOW_SYS)
> mysql_mutex_unlock(&LOCK_global_system_variables);
>
> +
> if (schema_table_store_record(thd, table))
> {
> res= TRUE;
> diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
> index 4bf2028..3be2e55 100644
> --- a/sql/sys_vars.cc
> +++ b/sql/sys_vars.cc
> @@ -5365,3 +5377,72 @@ static Sys_var_ulong Sys_log_tc_size(
> DEFAULT(my_getpagesize() * 6),
> BLOCK_SIZE(my_getpagesize()));
> #endif
> +
> +const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME=
> + {STRING_WITH_LEN("session_track_system_variables")};
I think you don't need that^^^. You shouldn't search for Sys_var_sesvartrack
by name anyway.
> +
> +static Sys_var_sesvartrack Sys_track_session_sys_vars(
> + SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
> + "Track changes in registered system variables.",
> + CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
> + DEFAULT("autocommit,character_set_client,character_set_connection,"
> + "character_set_results,time_zone"),
> + NO_MUTEX_GUARD);
> +
> +static bool update_session_track_schema(sys_var *self, THD *thd,
> + enum_var_type type)
> +{
> + DBUG_ENTER("update_session_track_schema");
> + DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->update(thd));
> +}
> +
> +static Sys_var_mybool Sys_session_track_schema(
> + "session_track_schema",
> + "Track changes to the 'default schema'.",
> + SESSION_VAR(session_track_schema),
> + CMD_LINE(OPT_ARG), DEFAULT(TRUE),
> + NO_MUTEX_GUARD, NOT_IN_BINLOG,
> + ON_CHECK(0),
> + ON_UPDATE(update_session_track_schema));
> +
> +
> +static bool update_session_track_tx_info(sys_var *self, THD *thd,
> + enum_var_type type)
> +{
> + DBUG_ENTER("update_session_track_tx_info");
> + DBUG_RETURN(thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)->update(thd));
> +}
> +
> +static const char *session_track_transaction_info_names[]=
> + { "OFF", "STATE", "CHARACTERISTICS", NullS };
> +
> +static Sys_var_enum Sys_session_track_transaction_info(
> + "session_track_transaction_info",
> + "Track changes to the transaction attributes. OFF to disable; "
> + "STATE to track just transaction state (Is there an active transaction? "
> + "Does it have any data? etc.); CHARACTERISTICS to track transaction "
> + "state "
> + "and report all statements needed to start a transaction with the same "
> + "characteristics (isolation level, read only/read write, snapshot - "
> + "but not any work done / data modified within the transaction).",
> + SESSION_VAR(session_track_transaction_info),
> + CMD_LINE(REQUIRED_ARG), session_track_transaction_info_names,
> + DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
> + ON_UPDATE(update_session_track_tx_info));
> +
> +
> +static bool update_session_track_state_change(sys_var *self, THD *thd,
> + enum_var_type type)
> +{
> + DBUG_ENTER("update_session_track_state_change");
> + DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->update(thd));
> +}
> +
> +static Sys_var_mybool Sys_session_track_state_change(
> + "session_track_state_change",
> + "Track changes to the 'session state'.",
> + SESSION_VAR(session_track_state_change),
> + CMD_LINE(OPT_ARG), DEFAULT(FALSE),
> + NO_MUTEX_GUARD, NOT_IN_BINLOG,
> + ON_CHECK(0),
> + ON_UPDATE(update_session_track_state_change));
> diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
> index 2488e80..cf01835 100644
> --- a/sql/sys_vars.ic
> +++ b/sql/sys_vars.ic
> @@ -535,6 +537,105 @@ class Sys_var_charptr: public sys_var
> }
> };
>
> +class Sys_var_charptr: public Sys_var_charptr_base
> +{
> +public:
> + Sys_var_charptr(const char *name_arg,
> + const char *comment, int flag_args, ptrdiff_t off, size_t size,
> + CMD_LINE getopt,
> + enum charset_enum is_os_charset_arg,
> + const char *def_val, PolyLock *lock=0,
> + enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
> + on_check_function on_check_func=0,
> + on_update_function on_update_func=0,
> + const char *substitute=0) :
> + Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt,
> + is_os_charset_arg, def_val, lock, binlog_status_arg,
> + on_check_func, on_update_func, substitute)
> + {
> + SYSVAR_ASSERT(scope() == GLOBAL);
> + SYSVAR_ASSERT(size == sizeof(char *));
> + }
> +
> + bool session_update(THD *thd, set_var *var)
> + {
> + DBUG_ASSERT(FALSE);
> + return true;
> + }
> + void session_save_default(THD *thd, set_var *var)
> + { DBUG_ASSERT(FALSE); }
> +};
> +
> +class Sys_var_sesvartrack: public Sys_var_charptr_base
> +{
> +public:
> + Sys_var_sesvartrack(const char *name_arg,
> + const char *comment,
> + CMD_LINE getopt,
> + enum charset_enum is_os_charset_arg,
> + const char *def_val, PolyLock *lock) :
> + Sys_var_charptr_base(name_arg, comment,
> + SESSION_VAR(session_track_system_variables),
> + getopt, is_os_charset_arg, def_val, lock,
> + VARIABLE_NOT_IN_BINLOG, 0, 0, 0)
> + {}
> + bool do_check(THD *thd, set_var *var)
> + {
> + if (Sys_var_charptr_base::do_check(thd, var) ||
> + sysvartrack_validate_value(thd, var->save_result.string_value.str,
> + var->save_result.string_value.length))
> + return TRUE;
> + return FALSE;
> + }
> + bool global_update(THD *thd, set_var *var)
> + {
> + char *new_val= global_update_prepare(thd, var);
> + if (new_val)
> + {
> + if (sysvartrack_reprint_value(thd, new_val,
> + var->save_result.string_value.length))
> + new_val= 0;
> + }
> + global_update_finish(new_val);
> + return (new_val == 0 && var->save_result.string_value.str != 0);
> + }
> + bool session_update(THD *thd, set_var *var)
> + {
> + return sysvartrack_update(thd);
> + }
> + void session_save_default(THD *thd, set_var *var)
> + {
> + var->save_result.string_value.str= global_var(char*);
> + var->save_result.string_value.length=
> + strlen(var->save_result.string_value.str);
> + /* parse and feel list with default values */
> + if (thd)
> + {
> + bool res=
> + sysvartrack_validate_value(thd,
> + var->save_result.string_value.str,
> + var->save_result.string_value.length);
> + DBUG_ASSERT(res == 0);
> + }
> + }
> + uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
> + {
> + DBUG_ASSERT(thd != NULL);
> + size_t len= sysvartrack_value_len(thd);
> + char *res= 0;
> + char *buf= (char *)my_safe_alloca(len);
> + if (buf && !sysvartrack_value_construct(thd, buf, len))
> + {
> + size_t len= strlen(buf) + 1;
> + res= (char*) thd->alloc(len + sizeof(char *));
why don't you use thd->alloc up front? why alloca and then memcpy?
> + if (res)
> + memcpy((*((char**) res)= res + sizeof(char *)), buf, len);
> + my_safe_afree(buf, len);
> + }
> + return (uchar *)res;
> + }
> +};
> +
>
> class Sys_var_proxy_user: public sys_var
> {
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0

[Maria-developers] Binary is built, but does it have Cassandra-v2 (Was: Re: June, 30th meeting: outcome)
by Sergey Petrunia 02 Jun '16
by Sergey Petrunia 02 Jun '16
02 Jun '16
On Wed, Jun 01, 2016 at 08:46:45PM +0100, Charles Muurmu wrote:
> Hi Sergei,
>
> make now runs to completion and make install as well
I'm a bit surprised. I'm looking at
https://github.com/charlesmuurmu/server/blob/MDEV-8947/storage/cassandra-v2…
And I still see two "IF(...)" lines but three "ENDIF" lines.
Are you sure cassandra-v2 gets built and linked?
As I said earlier:
>> Do you know how to check if mysqld binary has storage engine linked?
>>
>> I think this would work on OS X, too:
>>
>> "nm mysqld | grep cassandra"
>>
>> this command must produce non-empty output (for an example, try "nm
>> mysqld | grep myisam").
If you've made any changes to storage/cassandra-v2, please commit them and push.
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
1
0

[Maria-developers] MDEV-10101 Wrong error message of SELECT 1 UNION (SELECT 1 FROM t1 GROUP BY 1 WITH ROLLUP)
by Alexander Barkov 01 Jun '16
by Alexander Barkov 01 Jun '16
01 Jun '16
Hello Sergei,
Please review a patch for MDEV-10101.
Thanks.
2
1

[Maria-developers] Please review MDEV-10124 Incorrect usage of CUBE/ROLLUP and ORDER BY with GROUP_CONCAT(a ORDER BY a)
by Alexander Barkov 01 Jun '16
by Alexander Barkov 01 Jun '16
01 Jun '16
Hi Sergei,
Please review MDEV-10124
Thanks.
Here's the story of the related code:
1. The original patch from Wax
commit: 0b505fb437eedd1b31c99888247c2259539c095b
date: Tue Mar 18 03:07:40 2003
opt_gorder_clause reused the regular order_clause,
which already had some protection against ROLLUP queries:
order_clause:
ORDER_SYM BY
{
LEX *lex=Lex;
if (lex->current_select->linkage != GLOBAL_OPTIONS_TYPE &&
lex->current_select->select_lex()->olap !=
UNSPECIFIED_OLAP_TYPE)
{
net_printf(lex->thd, ER_WRONG_USAGE,
"CUBE/ROLLUP",
"ORDER BY");
YYABORT;
}
} order_list;
The assumption that ORDER BY in group_concat() had to have
the same ROLLUP restriction (with order_clause) was wrong.
Moreover, GROUP_CONCAT() in select_item_list was not affected
by this restriction, because WITH ROLLUP goes after
select_item_list and therefore sel->olap is always equal
to UNSPECIFIED_OLAP_TYPE during select_item_list.
GROUP BY was not affected:
- it goes before WITH ROLLUP and sel->olap is still
UNSPECIFIED_OLAP_TYPE
- Aggregate functions like AVG(), GROUP_CONCAT() in GROUP BY
are not allowed
So only GROUP_CONCAT() in HAVING and ORDER BY clauses
were erroneously affected by this restriction.
2. Bug#27848 rollup in union part causes error with order of union
commit: 3f6073ae63f9cb738cb6f0a6a8136e1d21ba0e1b
Author: unknown <igor(a)olga.mysql.com> 2007-12-15 01:42:46
The condition in the ROLLUP protection code became more complex.
Note, opt_gconcat_order still reuses the regular order_clause.
3. Bug#16347426 ASSERTION FAILED: (SELECT_INSERT &&
!TABLES->NEXT_NAME_RESOLUTION_TABLE) || !TAB
commit: 2d83663380f5c0ea720e31f51898912b0006cd9f
author: Chaithra Gopalareddy <chaithra.gopalareddy(a)oracle.com>
date: 2013-04-14 06:00:49
opt_gorder_clause was refactored not to use order_clause and
collect information directly to select->gorder_list.
The ROLLUP protection code was duplicated from order_clause
to the new version of opt_gorder_clause.
2
1

[Maria-developers] Please review MDEV-9497 oqgraph fails to build with boost 1.60
by Vicențiu Ciorbaru 31 May '16
by Vicențiu Ciorbaru 31 May '16
31 May '16
Hi Sergei!
Can you please review the following patch for MariaDB 10.0. It fixes a
compilation failure in Debian/Ubuntu when boost 1.60 or later is installed.
I've looked into the code and checked if the #ifdef'ed code is used
anywhere. There is absolutely no reference to it. I doubt it affect
anything.
Thanks!
Vicentiu
+--- mariadb-10.0-10.0.25.orig/storage/oqgraph/oqgraph_shim.h
++++ mariadb-10.0-10.0.25/storage/oqgraph/oqgraph_shim.h
+@@ -254,7 +254,7 @@ namespace boost
- #if BOOST_VERSION >= 104601
+ #if BOOST_VERSION >= 104601 && BOOST_VERSION < 106000
template <>
struct graph_bundle_type<oqgraph3::graph>
{
2
1

30 May '16
GSoC (week 1)
Hello everyone,
After developing a prototype for MyISAM ,there were some issues to be
resolved. One of the issues is related to error regarding a duplicate
entry. So if there is a duplicate entry for a particular key in a row, it
generates an error for another key.
After debugging, I found out that it was due to mismatch of a key number
between sql layer and MyISAM file. Keys are stored in the order of user's
input in sql layer. In table2myisam function, this order is changed as each
key is either stored as mi_keydef or mi_uniquedef. In mi_create, for each
unique definition, an extra key is created in the form of mi_keydef and
stored in MYI file which creates the issue of mismatch.
I tried to solve the issue by adding extra member sql_key_no to mi_keydef
and mi_uniquedef.I also modified mi_keydef_write and mi_keydef_read to
support a new member but after compiling, I got some error related to a key
file. Also with the help of my mentor, I found out that it could result in
compatibility issues related to old MyISAM file.
So currently, I am trying to solve this using another approach.
Regards,
Shubham
1
0

Re: [Maria-developers] [Commits] 53696a6: MDEV-10004: Galera's pc.recovery process fails in 10.1 with systemd
by Sergey Vojtovich 27 May '16
by Sergey Vojtovich 27 May '16
27 May '16
Hi Nirbhay,
Looks good to push.
On Thu, May 26, 2016 at 11:52:04PM -0400, Nirbhay Choubey wrote:
> revision-id: 53696a63a2a517e04bf27382184162da50994ecb (mariadb-10.1.14-4-g53696a6)
> parent(s): 9a1c4e900b98fdb9940aab57c895753f175c2bd8
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-05-26 23:52:04 -0400
> message:
>
> MDEV-10004: Galera's pc.recovery process fails in 10.1 with systemd
...skip...
> +# Safety checks
> +if [ -n "$log_file" -a -f "$log_file" ]; then
> + [ "$euid" = "0" ] && chown $user $log_file
> + chmod 600 $log_file
> +else
> + echo "WSREP: mktemp failed"
Should this be "log" instead of "echo"?
Regards,
Sergey
2
1
Hi,
I have added the AGGREGATE keyword to the parser . Here is the link to the
repository https://github.com/varunraiko/aggregate-functions .
On Wed, May 18, 2016 at 9:50 PM, Sanja <sanja.byelkin(a)gmail.com> wrote:
> Hi!
>
> If we get it automatically then of course it should be done, but I
> doubts... We will see.
>
> On Wed, May 18, 2016 at 6:18 PM, Sergei Golubchik <serg(a)mariadb.org>
> wrote:
>
>> Hi, Sanja!
>>
>> On May 18, Sanja wrote:
>> > >
>> > > No, sorry, this doesn't work. It works for procedures, but not for
>> > > functions. See:
>> > >
>> > > CREATE FUNCTION f1 (a INT) RETURNS INT
>> > > BEGIN
>> > > RETURN SELECT f2(val) FROM t1 WHERE id=a;
>> > > END;
>> > >
>> > > CREATE FUNCTION f2 (b INT) RETURNS INT
>> > > BEGIN
>> > > ...
>> > > FETCH GROUP NEXT ROW;
>> > > ...
>> > > RETURN something;
>> > > END;
>> > >
>> > > Here, depending on what function is declared aggregate you will have
>> > > different results.
>> >
>> > I think in the first implementation we can have only one level
>> > functions. if we will have time we can then expand it for calls of
>> > other functions. But first the mechanism of temporary leaving then
>> > entering functions should be created (then it can be reused for
>> > recursive calls.
>>
>> First implementation - may be (althought I don't understand why - this
>> requires no extra coding, nested function calls will just work
>> automatically). But the first implementation should not choose to use
>> the syntax that makes this extension impossible.
>>
>> For example, Varun's project does not include window function support.
>> At all. But we will be able to add it later without redoing everything,
>> exising syntax can accomodate this new feature.
>>
>> Regards,
>> Sergei
>> Chief Architect MariaDB
>> and security(a)mariadb.org
>>
>
>
3
10

Re: [Maria-developers] [Commits] 454a9dc: MDEV-10004: Galera's pc.recovery process fails in 10.1 with systemd
by Sergey Vojtovich 27 May '16
by Sergey Vojtovich 27 May '16
27 May '16
Hi Nirbhay,
I believe this patch is acceptable. Still I didn't completely understand the
need for all this complexity and your answer to Daniel's question.
Some minor comments inline.
On Fri, May 20, 2016 at 11:04:24PM -0400, Nirbhay Choubey wrote:
> revision-id: 454a9dc53a5b02af316dd713c577082eb8cf79c4 (mariadb-10.1.14-4-g454a9dc)
> parent(s): 9a1c4e900b98fdb9940aab57c895753f175c2bd8
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-05-20 23:04:20 -0400
> message:
>
> MDEV-10004: Galera's pc.recovery process fails in 10.1 with systemd
>
> Galera recovery process works in two phases. In the first
> phase, mysqld is started as non-daemon with --wsrep-recover
> to recover and fetch the last logged global transaction ID.
> This ID is then used in second phase as the start position
> (--wsrep-start-position=XX) to start mysqld as daemon.
>
> As this process was implemented in mysqld_safe script, the
> recovery did not work when server was started using systemd.
>
> Fixed by introducing a shell script (wsrep_recovery.sh) that
> mimics the first phase of the recovery process.
>
> ---
> cmake/systemd.cmake | 3 +-
> scripts/galera_recovery.sh | 94 ++++++++++++++++++++++++++++++++++++++++
> support-files/mariadb.service.in | 11 ++++-
> 3 files changed, 106 insertions(+), 2 deletions(-)
>
> diff --git a/cmake/systemd.cmake b/cmake/systemd.cmake
> index b0161cf..17f44f7 100644
> --- a/cmake/systemd.cmake
> +++ b/cmake/systemd.cmake
> @@ -55,9 +55,10 @@ MACRO(CHECK_SYSTEMD)
> IF(HAVE_SYSTEMD AND HAVE_SYSTEMD_SD_DAEMON_H AND HAVE_SYSTEMD_SD_LISTEN_FDS
> AND HAVE_SYSTEMD_SD_NOTIFY AND HAVE_SYSTEMD_SD_NOTIFYF)
> ADD_DEFINITIONS(-DHAVE_SYSTEMD)
> - SET(SYSTEMD_SCRIPTS mariadb-service-convert galera_new_cluster)
> + SET(SYSTEMD_SCRIPTS mariadb-service-convert galera_new_cluster galera_recovery)
> SET(SYSTEMD_DEB_FILES "usr/bin/mariadb-service-convert
> usr/bin/galera_new_cluster
> + usr/bin/galera_recovery
> ${INSTALL_SYSTEMD_UNITDIR}/mariadb.service
> ${INSTALL_SYSTEMD_UNITDIR}/mariadb@.service
> ${INSTALL_SYSTEMD_UNITDIR}/mariadb(a)bootstrap.service.d/use_galera_new_cluster.conf")
> diff --git a/scripts/galera_recovery.sh b/scripts/galera_recovery.sh
> new file mode 100755
> index 0000000..6d4f1d5
> --- /dev/null
> +++ b/scripts/galera_recovery.sh
> @@ -0,0 +1,94 @@
> +#!/bin/sh
> +
> +# Copyright (c) 2016 MariaDB Corporation
> +#
> +# 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
> +# the Free Software Foundation; version 2 of the License.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
> +
> +
> +# This script is intended to be executed by systemd. It starts mysqld with
> +# --wsrep-recover to recover from a non-graceful shutdown, determines the
> +# last stored global transaction ID and echoes it in --wsrep-start-position=XX
> +# format. The output is then captured and used by systemd to start mysqld.
> +# If the server was configured to start without wsrep, nothing is echoed.
> +
> +user='@MYSQLD_USER@'
> +log_file=$(mktemp /tmp/wsrep_recovery.XXXXXX)
> +euid=$(id -u)
> +recovered_pos=""
> +skipped=""
> +start_pos=""
> +start_pos_opt=""
> +ret=0
> +
> +log ()
> +{
> + local msg="$1"
> + # Print all messages to stderr as we reserve stdout for printing
> + # --wsrep-start-position=XXXX.
> + echo "$msg" >&2
> +}
> +
> +finish()
> +{
> + rm -f "$log_file"
> +}
> +
> +trap finish EXIT
> +
> +# Safety checks
> +if [ -z "$log_file" ]; then
> + log "WSREP: mktemp failed"
> + exit 1
> +fi
> +
> +if [ -f "$log_file" ]; then
> + [ "$euid" = "0" ] && chown $user $log_file
> + chmod 600 $log_file
> +else
> + log "WSREP: Failed to change the permissions of $log_file."
I'd say it's rather mktemp failed than change permissions failed.
> + exit 1
> +fi
> +
> +# Redirect server's error log to the log file.
> +eval /usr/sbin/mysqld --user=$user --wsrep_recover 2> "$log_file"
> +ret=$?
> +if [ $ret -ne 0 ]; then
> + # Something went wrong, let us also print the error log so that it
> + # shows up in systemctl status output as a hint to the user.
> + log "WSREP: Failed to start mysqld for wsrep recovery: '`cat $log_file`'"
> + exit 1
> +fi
Since this script is executed unconditionally, what shall happen if wsrep is
not enabled at compile time (--wsrep_recover is not known)? Fail the whole
service startup?
> +
> +# Parse server's error log for recovered position. The server prints
> +# "..skipping position recovery.." if started without wsrep.
> +
> +recovered_pos="$(grep 'WSREP: Recovered position:' $log_file)"
> +
> +if [ -z "$recovered_pos" ]; then
> + skipped="$(grep WSREP $log_file | grep 'skipping position recovery')"
> + if [ -z "$skipped" ]; then
> + log "WSREP: Failed to recover position: '`cat $log_file`'"
> + exit 1
Can we come here with wsrep disabled?
> + else
> + log "WSREP: Position recovery skipped."
> + fi
> +else
> + start_pos="$(echo $recovered_pos | sed 's/.*WSREP\:\ Recovered\ position://' \
> + | sed 's/^[ \t]*//')"
> + log "WSREP: Recovered position $start_pos"
> + start_pos_opt="--wsrep_start_position=$start_pos"
> +fi
> +
> +echo "$start_pos_opt"
> +
> diff --git a/support-files/mariadb.service.in b/support-files/mariadb.service.in
> index b18674b..00162d4 100644
> --- a/support-files/mariadb.service.in
> +++ b/support-files/mariadb.service.in
> @@ -48,6 +48,12 @@ CapabilityBoundingSet=CAP_IPC_LOCK
> # Execute pre and post scripts as root, otherwise it does it as User=
> PermissionsStartOnly=true
>
> +# Perform automatic wsrep recovery. When server is started without wsrep,
> +# galera_recovery simply returns an empty string. In any case, however,
> +# the script is not expected to return with a non-zero status.
> +ExecStartPre=/bin/sh -c "VAR=`/usr/bin/galera_recovery`; [ $? -eq 0 ] && \
> + systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1"
I believe _WSREP_START_POSITION won't work with multi instance version. Not
sure if it works but we may need to use something like _WSREP_START_POSITION%I
here.
> +
> # Needed to create system tables etc.
> # ExecStartPre=/usr/bin/mysql_install_db -u mysql
>
> @@ -57,9 +63,12 @@ PermissionsStartOnly=true
> # This isn't a replacement for my.cnf.
> # _WSREP_NEW_CLUSTER is for the exclusive use of the script galera_new_cluster
> @SYSTEMD_EXECSTARTPRE@
> -ExecStart=/usr/sbin/mysqld $MYSQLD_OPTS $_WSREP_NEW_CLUSTER
> +ExecStart=/usr/sbin/mysqld $MYSQLD_OPTS $_WSREP_NEW_CLUSTER $_WSREP_START_POSITION
> @SYSTEMD_EXECSTARTPOST@
>
> +# Unset _WSREP_START_POSITION environment variable.
> +ExecStartPost=/bin/sh -c "systemctl unset-environment _WSREP_START_POSITION"
> +
> KillMode=process
> KillSignal=SIGTERM
>
> _______________________________________________
> commits mailing list
> commits(a)mariadb.org
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
Regards,
Sergey
2
1

Re: [Maria-developers] [ME-585] Integrate existing Community Patch “Flashback” into MariaDB Server.
by Kristian Nielsen 26 May '16
by Kristian Nielsen 26 May '16
26 May '16
Lixun Peng <lixun.peng(a)mariadb.com> writes:
> Hi Kristian,
>
> I ported Flashback to MariaDB 10.1, could you please review this patch?
Thanks for the patch. Some comments and questions below, inline in the
patch.
> [2. text/plain; ME-585.diff]
>
> commit 11421434433fb6a5160dae721f0ecb0549e4a5fe
> Author: plinux <lixun.penglx(a)alibaba-inc.com>
> Date: Wed May 25 19:46:43 2016 +0800
>
> [ME-585] Integrate existing Community Patch “Flashback” into MariaDB Server.
>
> ==== Description ====
>
> Flashback can roll-back the instances/databases/tables to a snapshot.
> It's implement on Server-Level by full image format binary logs, so it supports all engines.
> Currently, it’s a feature inside mysqlbinlog tool (with --flashback arguments).
>
> ==== New Arguments ====
>
> --flashback (-B)
> It will let mysqlbinlog to work on FLASHBACK mode.
>
> --table (-T)
> It's similar with -d, but this argument is for specific tables.
>
> --review
> It will let mysqlbinlog print the SQL for reviewing.
> Reviewing feature will create a new "review" table to record the data that will be modified by FLASHBACK feature
> If you don't set the --review-dbname/tablename, "review" table will created on current DB, and the table name will be "__original_table_name". And for getting the original table struct, mysqlbinlog need to connect mysql, so --user/--host/--password is necessary.
>
> --review-dbname
> The DB name that you want to store the review table.
>
> --review-tablename
> The TABLE name that you want to store the original data before modified.
> Only if you set -T, this argument is useful.
Nice documentation! Some suggestions for improvement:
Maybe you could mention explicitly to what point the database is rolled
back. I assume it is to the point at which mysqlbinlog would normally start
dumping? I think it would be useful for users to have an explicit list of
how the starting point may be specified (the most important probably being
--start-datetime that you use in the examples).
Also, what happens if one needs to flashback across multiple binlog files?
What happens in the case of DDL (or other query_log_event)? I assume these
cannot be flashback'ed. Will the tool stop with an error before doing
anything? Will it stop with an error half-way, leaving a partial flashback?
Or will it leave a corrupted state (row events flashed back, query event
modifications kept)?
It is not 100% clear to me what the --review feature does. Given table t1,
does it create a table `__original_t1`, which contains the subset of rows of
t1 that would be modified by flashback - basically the after image of all
row events?
There does not seem to be any test case for the --review option.
Did you test this feature with binlog checksums enabled?
What is the format for table name in --table and --review-tablename options?
Is it DB.TABLE, where the quialifying DB is mandatory? This does not seem to
be tested in the test case.
Is it possible to flashback across multiple binlog files?
What happens if the user attempts to use flashback with the option to fetch
binlog files from the server rather than from local files?
>
> ==== Example ====
>
> I have a table "t" in database "test", we can compare the output with --flashback and without.
> #client/mysqlbinlog /data/mysqldata_10.0/binlog/mysql-bin.000001 -vv -d test -T t --start-datetime="2013-03-27 14:54:00" > /tmp/1.sql
> #client/mysqlbinlog /data/mysqldata_10.0/binlog/mysql-bin.000001 -vv -uroot -d test -T t --start-datetime="2013-03-27 14:54:00" -B --review > /tmp/2.sql
>
> Then, importing the output flashback file, it can flashback your database/table to the special time.
>
> ==== Implement ====
>
> 1. As we know, if binlog_format is ROW (binlog-row-image=FULL in 5.6 and later), all columns’ values are store in the row event, so we can get the data before mis-operation.
>
> 2. Just do following things:
> 2.1 Change Event Type, INSERT->DELETE, DELETE->INSERT
> 2.2 For Update_Event, swapping the SET part and WHERE part
> 2.3 Applying those events from the last one to the first one which mis-operation happened.
> 2.4 All the data will be recovered by inverse operations of mis-oprerations.
>
> diff --git a/client/client_priv.h b/client/client_priv.h
> index c0c4954..31ad7df 100644
> --- a/client/client_priv.h
> +++ b/client/client_priv.h
> @@ -65,6 +65,8 @@ enum options_client
> OPT_MYSQLDUMP_SLAVE_APPLY,
> OPT_MYSQLDUMP_SLAVE_DATA,
> OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT,
> + OPT_REVIEW,
> + OPT_REVIEW_DBNAME, OPT_REVIEW_TABLENAME,
> OPT_SLAP_CSV, OPT_SLAP_CREATE_STRING,
> OPT_SLAP_AUTO_GENERATE_SQL_LOAD_TYPE, OPT_SLAP_AUTO_GENERATE_WRITE_NUM,
> OPT_SLAP_AUTO_GENERATE_ADD_AUTO,
> diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
> index da41268..969540e 100644
> --- a/client/mysqlbinlog.cc
> +++ b/client/mysqlbinlog.cc
> @@ -44,7 +44,6 @@
> #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
>
>
> -#include "sql_string.h" // needed for Rpl_filter
Hm, why remove this include? I assume this is related to starting to use the
`String' class in mysqlbinlog?
I know there is quite a mess with mysqlbinlog.cc sharing parts of the server
source code, so I could imagine something "interesting" going on here - can
you explain, and probably put in a comment describing the issue?
> #include "sql_list.h" // needed for Rpl_filter
> #include "rpl_filter.h"
>
> @@ -63,6 +62,10 @@ Rpl_filter *binlog_filter= 0;
> /* Needed for Rpl_filter */
> CHARSET_INFO* system_charset_info= &my_charset_utf8_general_ci;
>
> +/* Needed for Flashback */
> +DYNAMIC_ARRAY binlog_events; // Storing the events output string
> +String stop_event_string; // Storing the STOP_EVENT output string
> +
> char server_version[SERVER_VERSION_LENGTH];
> ulong server_id = 0;
>
> @@ -86,7 +89,7 @@ static const char *load_groups[]=
> static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
> static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
>
> -static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0;
> +static bool one_database=0, one_table=0, to_last_remote_log= 0, disable_log_bin= 0;
> static bool opt_hexdump= 0, opt_version= 0;
> const char *base64_output_mode_names[]=
> {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS};
> @@ -96,6 +99,7 @@ TYPELIB base64_output_mode_typelib=
> static enum_base64_output_mode opt_base64_output_mode= BASE64_OUTPUT_UNSPEC;
> static char *opt_base64_output_mode_str= NullS;
> static char* database= 0;
> +static char* table= 0;
> static my_bool force_opt= 0, short_form= 0, remote_opt= 0;
> static my_bool debug_info_flag, debug_check_flag;
> static my_bool force_if_open_opt= 1;
> @@ -129,6 +133,10 @@ static MYSQL* mysql = NULL;
> static const char* dirname_for_local_load= 0;
> static bool opt_skip_annotate_row_events= 0;
>
> +static my_bool opt_flashback;
> +static my_bool opt_review;
> +static char *review_dbname, *review_tablename;
> +
> /**
> Pointer to the Format_description_log_event of the currently active binlog.
>
> @@ -788,6 +796,23 @@ print_skip_replication_statement(PRINT_EVENT_INFO *pinfo, const Log_event *ev)
> }
>
> /**
> + Indicates whether the given table should be filtered out,
> + according to the --table=X option.
> +
> + @param log_tblname Name of table.
> +
> + @return nonzero if the table with the given name should be
> + filtered out, 0 otherwise.
> +*/
> +static bool shall_skip_table(const char *log_tblname)
> +{
> + return one_table &&
> + (log_tblname != NULL) &&
> + strcmp(log_tblname, table);
> +}
> +
> +
> +/**
> Prints the given event in base64 format.
>
> The header is printed to the head cache and the body is printed to
> @@ -949,6 +974,10 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
> Exit_status retval= OK_CONTINUE;
> IO_CACHE *const head= &print_event_info->head_cache;
>
> + /* Bypass flashback settings to event */
> + ev->is_flashback= opt_flashback;
> + ev->need_review= opt_review;
> +
> /*
> Format events are not concerned by --offset and such, we always need to
> read them to be able to process the wanted events.
> @@ -985,7 +1014,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
> retval= OK_STOP;
> goto end;
> }
> - if (!short_form)
> + if (!short_form && !opt_flashback)
> fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
>
> if (!opt_hexdump)
> @@ -1210,12 +1239,123 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
> case TABLE_MAP_EVENT:
> {
> Table_map_log_event *map= ((Table_map_log_event *)ev);
> - if (shall_skip_database(map->get_db_name()))
> + if (shall_skip_database(map->get_db_name()) ||
> + shall_skip_table(map->get_table_name()))
> {
> print_event_info->m_table_map_ignored.set_table(map->get_table_id(), map);
> destroy_evt= FALSE;
> goto end;
> }
> + /* Create review table for Flashback */
> + if (opt_review)
> + {
> + // Check table has created or not
^^^ "Check if the table was already created" ?
> + Table_map_log_event *exist_table;
> + exist_table= print_event_info->m_table_map.get_table(map->get_table_id());
> +
> + if (!exist_table)
> + {
> +
> + MYSQL *conn;
> + MYSQL_RES *res;
> + MYSQL_ROW row;
> + char tmp_sql[8096];
> + int tmp_sql_offset;
> +
> + conn = mysql_init(NULL);
> + if (!mysql_real_connect(conn, host, user, pass,
> + map->get_db_name(), port, sock, 0))
> + {
> + fprintf(stderr, "%s\n", mysql_error(conn));
> + exit(1);
> + }
> +
> + if (mysql_query(conn, "SET group_concat_max_len=10000;"))
> + {
> + fprintf(stderr, "%s\n", mysql_error(conn));
> + exit(1);
> + }
> +
> + memset(tmp_sql, 0, sizeof(tmp_sql));
> + sprintf(tmp_sql, " "
> + "SELECT Group_concat(cols) "
What happens if Group_concat() truncates the result?
Maybe you need to first determine the maximum size, and SET SESSION
group_concat_max_len accordingly. Or alternatively, you could just select
each column as a row, and do the concatenation in the C code.
> + "FROM (SELECT 'op_type char(1)' cols "
> + " UNION ALL "
> + " SELECT Concat(column_name, '_old ', column_type, ' ', "
> + " IF(character_set_name IS NOT NULL, "
> + " Concat('character set ', character_set_name, ' '), ' '), "
> + " IF(collation_name IS NOT NULL, "
> + " Concat('collate ', collation_name, ' '), ' ')) cols "
> + " FROM information_schema.columns "
> + " WHERE table_schema = '%s' "
> + " AND table_name = '%s' "
This is vulnerable to sql injection. You need to use proper quoting
functions.
Also, the generated statement does not seem to work correctly if column
names contain special characters.
You should add test cases that tests this - table names with ' ` " \n
characters in them, as well as column names.
> + " UNION ALL "
> + " SELECT Concat(column_name, '_new ', column_type, ' ', "
> + " IF(character_set_name IS NOT NULL, "
> + " Concat('character set ', character_set_name, ' '), ' '), "
> + " IF(collation_name IS NOT NULL, "
> + " Concat('collate ', collation_name, ' '), ' ')) cols "
> + " FROM information_schema.columns "
> + " WHERE table_schema = '%s' "
> + " AND table_name = '%s') tmp;",
> + map->get_db_name(), map->get_table_name(),
> + map->get_db_name(), map->get_table_name());
> +
> + if (mysql_query(conn, tmp_sql))
> + {
> + fprintf(stderr, "%s\n", mysql_error(conn));
> + exit(1);
> + }
> + res = mysql_use_result(conn);
> + if ((row = mysql_fetch_row(res)) != NULL) // only one row
> + {
> + if (review_dbname)
> + {
> + ev->set_review_dbname(review_dbname);
> + }
> + else
> + {
> + ev->set_review_dbname(map->get_db_name());
> + }
> + if (review_tablename)
> + {
> + ev->set_review_tablename(review_tablename);
> + }
> + else
> + {
> + memset(tmp_sql, 0, sizeof(tmp_sql));
> + sprintf(tmp_sql, "__%s", map->get_table_name());
> + ev->set_review_tablename(tmp_sql);
> + }
> + memset(tmp_sql, 0, sizeof(tmp_sql));
> + tmp_sql_offset= sprintf(tmp_sql, "CREATE TABLE IF NOT EXISTS");
> + tmp_sql_offset+= sprintf(tmp_sql + tmp_sql_offset, " `%s`.`%s` (%s) Engine = InnoDB%s",
> + ev->get_review_dbname(), ev->get_review_tablename(), row[0], print_event_info->delimiter);
> + }
> + fprintf(result_file, "%s\n", tmp_sql);
> + mysql_free_result(res);
> + mysql_close(conn);
> + }
> + else
> + {
> + char tmp_str[128];
> +
> + if (review_dbname)
> + ev->set_review_dbname(review_dbname);
> + else
> + ev->set_review_dbname(map->get_db_name());
> +
> + if (review_tablename)
> + ev->set_review_tablename(review_tablename);
> + else
> + {
> + memset(tmp_str, 0, sizeof(tmp_str));
> + sprintf(tmp_str, "__%s", map->get_table_name());
> + ev->set_review_tablename(tmp_str);
> + }
> + }
> + }
> +
> /*
> The Table map is to be printed, so it's just the time when we may
> print the kept Annotate event (if there is any).
> @@ -1281,6 +1421,41 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
> */
> if (ev)
> {
> + /* Holding event output if needed */
> + if (!ev->output_buf.is_empty())
> + {
> + String *tmp_str = new String[1];
> +
> + //if (!short_form && opt_flashback)
> + //{
> + // tmp_str->append("# at ");
> + // tmp_str->append(llstr(pos,ll_buff));
> + // tmp_str->append("\n");
> + //}
What is this? If it is not needed, remove rather than leaving stray
commented-out code.
> + tmp_str->append(ev->output_buf);
> +
> + if (opt_flashback)
> + {
> + if (ev_type == STOP_EVENT)
> + stop_event_string.copy(*tmp_str);
What makes STOP_EVENT special? I don't understand the logic in the code
here.
> + else
> + (void) push_dynamic(&binlog_events, (uchar *) tmp_str);
> + }
> + else
> + {
> + fprintf(result_file, "%s", tmp_str->ptr());
> + delete tmp_str;
> + }
> + ev->free_output_buffer();
> + }
> + //else
> + //{
> + // if (!short_form && opt_flashback)
> + // fprintf(result_file, "# at %s\n",llstr(pos,ll_buff));
> + //}
Same about commented-out code.
> +
> + if (remote_opt)
> + ev->temp_buf= 0;
> if (destroy_evt) /* destroy it later if not set (ignored table map) */
> delete ev;
> }
> @@ -1339,6 +1514,11 @@ static struct my_option my_options[] =
> "already have. NOTE: you will need a SUPER privilege to use this option.",
> &disable_log_bin, &disable_log_bin, 0, GET_BOOL,
> NO_ARG, 0, 0, 0, 0, 0, 0},
> + {"flashback", 'B', "Flashback feature can rollback you committed data to a special time point,"
> + "before Flashback feature writing a row, original row can insert to review-dbname.review-tablename,"
> + "and mysqlbinlog will login mysql by user(-u) and password(-p) and host(-h).",
> + &opt_flashback, &opt_flashback, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
> + 0, 0},
> {"force-if-open", 'F', "Force if binlog was not closed properly.",
> &force_if_open_opt, &force_if_open_opt, 0, GET_BOOL, NO_ARG,
> 1, 0, 0, 0, 0, 0},
> @@ -1382,6 +1562,17 @@ static struct my_option my_options[] =
> "prefix for the file names.",
> &result_file_name, &result_file_name, 0, GET_STR, REQUIRED_ARG,
> 0, 0, 0, 0, 0, 0},
> + {"review", OPT_REVIEW, "Print review sql in output file.",
> + &opt_review, &opt_review, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
> + 0, 0},
> + {"review-dbname", OPT_REVIEW_DBNAME,
> + "Writing flashback original row data into this db",
> + &review_dbname, &review_dbname,
> + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
> + {"review-tablename", OPT_REVIEW_TABLENAME,
> + "Writing flashback original row data into this table",
> + &review_tablename, &review_tablename,
> + 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
> {"server-id", 0,
> "Extract only binlog entries created by the server having the given id.",
> &server_id, &server_id, 0, GET_ULONG,
> @@ -1445,6 +1636,9 @@ static struct my_option my_options[] =
> &stop_position, &stop_position, 0, GET_ULL,
> REQUIRED_ARG, (longlong)(~(my_off_t)0), BIN_LOG_HEADER_SIZE,
> (ulonglong)(~(my_off_t)0), 0, 0, 0},
> + {"table", 'T', "List entries for just this table (local log only).",
> + &table, &table, 0, GET_STR_ALLOC, REQUIRED_ARG,
> + 0, 0, 0, 0, 0, 0},
> {"to-last-log", 't', "Requires -R. Will not stop at the end of the \
> requested binlog but rather continue printing until the end of the last \
> binlog of the MySQL server. If you send the output to the same MySQL server, \
> @@ -1554,6 +1748,7 @@ static void cleanup()
> {
> my_free(pass);
> my_free(database);
> + my_free(table);
> my_free(host);
> my_free(user);
> my_free(const_cast<char*>(dirname_for_local_load));
> @@ -1624,6 +1819,9 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
> break;
> #endif
> #include <sslopt-case.h>
> + case 'B':
> + opt_flashback= 1;
> + break;
> case 'd':
> one_database = 1;
> break;
> @@ -1645,10 +1843,16 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
> case 'R':
> remote_opt= 1;
> break;
> + case 'T':
> + one_table= 1;
> + break;
> case OPT_MYSQL_PROTOCOL:
> opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
> opt->name);
> break;
> + case OPT_REVIEW:
> + opt_review= 1;
> + break;
> case OPT_START_DATETIME:
> start_datetime= convert_str_to_timestamp(start_datetime_str);
> break;
> @@ -1848,7 +2052,7 @@ static Exit_status dump_log_entries(const char* logname)
> dump_local_log_entries(&print_event_info, logname));
>
> /* Set delimiter back to semicolon */
> - if (!opt_raw_mode)
> + if (!opt_raw_mode && !opt_flashback)
> fprintf(result_file, "DELIMITER ;\n");
> strmov(print_event_info.delimiter, ";");
> return rc;
> @@ -2648,6 +2852,8 @@ int main(int argc, char** argv)
> DBUG_ENTER("main");
> DBUG_PROCESS(argv[0]);
>
> + (void) my_init_dynamic_array(&binlog_events, sizeof(String), 1024, 1024, MYF(0));
> +
> my_init_time(); // for time functions
> tzset(); // set tzname
>
> @@ -2784,6 +2990,35 @@ int main(int argc, char** argv)
> start_position= BIN_LOG_HEADER_SIZE;
> }
>
> + /* If enable flashback, need to print the events from the end to the beginning */
> + if(opt_flashback)
> + {
> + ///if (binlog_events.elements > 0)
> + ///{
> + /// String *event_str= dynamic_element(&binlog_events, 0, String*);
> + /// fprintf(result_file, "%s", event_str->ptr());
> + /// delete event_str;
> + /// delete_dynamic_element(&binlog_events, 0);
> + ///}
Same about commented-out code.
> + uint i= 0;
> + for (i= binlog_events.elements; i > 0; --i)
> + {
> + String *event_str= dynamic_element(&binlog_events, i - 1, String*);
> + fprintf(result_file, "%s", event_str->ptr());
> + delete event_str;
> + delete_dynamic_element(&binlog_events, i - 1);
> + }
> + fprintf(result_file, "COMMIT\n/*!*/;\n");
> + }
Hm, so the entire output from flashback needs to be stored in RAM.
I guess it is a reasonable limitation. But I think it should be documented,
so users will be aware of this, and can eg. cut a large flashback into
smaller pieces, if needed.
But you don't seem to check the return value from push_dynamic() ?
That seems a requirement to do, and give an appropriate error message for
out-of-memory. (Though probably most often the OOM killer will get to it
first...).
> +
> + /* Set delimiter back to semicolon */
> + if (!stop_event_string.is_empty())
> + fprintf(result_file, "%s", stop_event_string.ptr());
> + if (opt_flashback)
> + fprintf(result_file, "DELIMITER ;\n");
> +
> + delete_dynamic(&binlog_events);
> +
> if (!opt_raw_mode)
> {
> /*
> diff --git a/include/my_sys.h b/include/my_sys.h
> index 36530eb..4989f95 100644
> --- a/include/my_sys.h
> +++ b/include/my_sys.h
> @@ -609,6 +609,7 @@ static inline size_t my_b_bytes_in_cache(const IO_CACHE *info)
> }
>
> int my_b_copy_to_file(IO_CACHE *cache, FILE *file);
> +char* my_b_copy_to_string(IO_CACHE *cache, size_t *bytes_in_cache); // Used for Flashback
Why this addition? It does not seem to be used anywhere else in the patch?
> my_off_t my_b_append_tell(IO_CACHE* info);
> my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
> int my_b_pread(IO_CACHE *info, uchar *Buffer, size_t Count, my_off_t pos);
> diff --git a/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result b/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result
> new file mode 100644
> index 0000000..c7c3493
> --- /dev/null
> +++ b/mysql-test/suite/binlog/r/mysqlbinlog_row_flashback.result
> @@ -0,0 +1,459 @@
> +SET binlog_format= ROW;
> +#
> +# Preparatory cleanup.
> +#
> +DROP TABLE IF EXISTS t1;
> +#
> +# We need a fixed timestamp to avoid varying results.
> +#
> +SET timestamp=1000000000;
> +#
> +# Delete all existing binary logs.
> +#
> +RESET MASTER;
> +CREATE TABLE t1 (
> +c01 tinyint,
> +c02 smallint,
> +c03 mediumint,
> +c04 int,
> +c05 bigint,
> +c06 char(10),
> +c07 varchar(20),
> +c08 TEXT
> +);
> +#
> +# Insert data to t1
> +#
> +INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
> +INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
> +INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 65535));
> +#
> +# Update t1
> +#
> +UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3;
> +#
> +# Clear t1
> +#
> +DELETE FROM t1;
> +FLUSH LOGS;
> +#
> +# Without -B
> +#
> +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
> +/*!40019 SET @@session.max_insert_delayed_threads=0*/;
> +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
> +DELIMITER /*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Start: binlog v 4, server v #.##.## created 010909 9:46:40 at startup
> +ROLLBACK/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Gtid list []
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Binlog checkpoint master-bin.000001
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-1 ddl
> +/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
> +/*!100001 SET @@session.gtid_domain_id=0*//*!*/;
> +/*!100001 SET @@session.server_id=1*//*!*/;
> +/*!100001 SET @@session.gtid_seq_no=1*//*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +use `test`/*!*/;
> +SET TIMESTAMP=1000000000/*!*/;
> +SET @@session.pseudo_thread_id=#/*!*/;
> +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
> +SET @@session.sql_mode=1342177280/*!*/;
> +SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
> +/*!\C latin1 *//*!*/;
> +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;
> +SET @@session.lc_time_names=0/*!*/;
> +SET @@session.collation_database=DEFAULT/*!*/;
> +CREATE TABLE t1 (
> +c01 tinyint,
> +c02 smallint,
> +c03 mediumint,
> +c04 int,
> +c05 bigint,
> +c06 char(10),
> +c07 varchar(20),
> +c08 TEXT
> +)
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-2
> +/*!100001 SET @@session.gtid_seq_no=2*//*!*/;
> +BEGIN
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +COMMIT
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-3
> +/*!100001 SET @@session.gtid_seq_no=3*//*!*/;
> +BEGIN
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +COMMIT
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-4
> +/*!100001 SET @@session.gtid_seq_no=4*//*!*/;
> +BEGIN
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +COMMIT
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-5
> +/*!100001 SET @@session.gtid_seq_no=5*//*!*/;
> +BEGIN
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Update_rows: table id # flags: STMT_END_F
> +### UPDATE `test`.`t1`
> +### WHERE
> +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### SET
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### UPDATE `test`.`t1`
> +### WHERE
> +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### SET
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +COMMIT
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-6
> +/*!100001 SET @@session.gtid_seq_no=6*//*!*/;
> +BEGIN
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +# at #
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Delete_rows: table id #
> +#010909 9:46:40 server id 1 end_log_pos # Delete_rows: table id # flags: STMT_END_F
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +COMMIT
> +/*!*/;
> +# at #
> +#010909 9:46:40 server id 1 end_log_pos # Rotate to master-bin.000002 pos: 4
> +DELIMITER ;
> +# End of log file
> +ROLLBACK /* added by mysqlbinlog */;
> +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
> +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
> +#
> +# With -B
> +#
> +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
> +/*!40019 SET @@session.max_insert_delayed_threads=0*/;
> +/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
> +DELIMITER /*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Start: binlog v 4, server v #.##.## created 010909 9:46:40 at startup
> +ROLLBACK/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Gtid list []
> +#010909 9:46:40 server id 1 end_log_pos # Binlog checkpoint master-bin.000001
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-1 ddl
> +/*!100101 SET @@session.skip_parallel_replication=0*//*!*/;
> +/*!100001 SET @@session.gtid_domain_id=0*//*!*/;
> +/*!100001 SET @@session.server_id=1*//*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-2
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-3
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-4
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-5
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
> +#010909 9:46:40 server id 1 end_log_pos # GTID 0-1-6
> +#010909 9:46:40 server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number #
Hm, what are these? Are they left-overs from reading forward over the
binlog prior to emitting flashback operations?
If these are just comments, I suppose they are not harmful, but they might
be confusing? It seems the rows_log_event and xid_events are filtered out,
maybe the table_map and gtid events should also not be printed in -B mode?
> +#010909 9:46:40 server id 1 end_log_pos # Rotate to master-bin.000002 pos: 4
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +BEGIN
> +/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Delete_rows: table id #
> +#010909 9:46:40 server id 1 end_log_pos # Delete_rows: table id # flags: STMT_END_F
Again, why these Delete_rows comments? Do I understand correctly, that in
the "normal" output, this would be BINLOG statements, where the events are
changed INSERT<->DELETE and so on? Maybe these comments should be changed as
well, to avoid confusion.
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### INSERT INTO `test`.`t1`
> +### SET
> +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
So it looks like the flashback events are output in the same order that they
were originally executed? Doesn't it need to be in the reverse order?
I am thinking in particular of transactions that UPDATE the same row
multiple times. For example:
BEGIN;
UPDATE t1 SET a=3 WHERE a=2 AND pk=1;
UPDATE t1 SET a=4 WHERE a=3 AND pk=1;
COMMIT;
This needs to appear in the flashback output in the reverse order:
BEGIN;
UPDATE t1 SET a=3 WHERE a=4 AND pk=1;
UPDATE t1 SET a=2 WHERE a=3 AND pk=1;
COMMIT;
If flashback'ed in the same order as originally executed, it would leave the
wrong value "3" in a (or maybe fail with a "cannot find row" error).
In any case, I think a test case for this should be added.
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +BEGIN
> +/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Update_rows: table id # flags: STMT_END_F
> +### UPDATE `test`.`t1`
> +### WHERE
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### SET
> +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### UPDATE `test`.`t1`
> +### WHERE
> +### @1=100 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +### SET
> +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
So this Query event was originally the COMMIT event, right?
Shouldn't there be a COMMIT in the flashback output also?
The test case seems to use MyISAM. Probably there should be also tests with
InnoDB (maybe a good idea to have both a t1 MyISAM and a t2 InnoDB, to test
mixing them, also testing mixed MyISAM/InnoDB transactions).
> +SET TIMESTAMP=1000000000/*!*/;
> +BEGIN
> +/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=127 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=32767 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=8388607 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=2147483647 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=9223372036854775807 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='aaaaaaaaaa' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='aaaaaaaaaaaaaaaaaaaa' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +BEGIN
> +/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=2 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=3 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=4 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=5 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='abc' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='abcdefg' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='abcedfghijklmnopqrstuvwxyz' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +SET TIMESTAMP=1000000000/*!*/;
> +BEGIN
> +/*!*/;
> +#010909 9:46:40 server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F
> +### DELETE FROM `test`.`t1`
> +### WHERE
> +### @1=0 /* TINYINT meta=0 nullable=1 is_null=0 */
> +### @2=0 /* SHORTINT meta=0 nullable=1 is_null=0 */
> +### @3=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
> +### @4=0 /* INT meta=0 nullable=1 is_null=0 */
> +### @5=0 /* LONGINT meta=0 nullable=1 is_null=0 */
> +### @6='' /* STRING(10) meta=65034 nullable=1 is_null=0 */
> +### @7='' /* VARSTRING(20) meta=20 nullable=1 is_null=0 */
> +### @8='' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */
> +#010909 9:46:40 server id 1 end_log_pos # Query thread_id=# exec_time=# error_code=0
> +use `test`/*!*/;
> +SET TIMESTAMP=1000000000/*!*/;
> +SET @@session.pseudo_thread_id=#/*!*/;
> +SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
> +SET @@session.sql_mode=1342177280/*!*/;
> +SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
> +/*!\C latin1 *//*!*/;
> +SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;
> +SET @@session.lc_time_names=0/*!*/;
> +SET @@session.collation_database=DEFAULT/*!*/;
> +COMMIT
> +/*!*/;
> +DELIMITER ;
> +# End of log file
> +ROLLBACK /* added by mysqlbinlog */;
> +/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
> +/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
> +#
> +# Insert data to t1
> +#
> +TRUNCATE TABLE t1;
> +INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
> +INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
> +INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60));
> +#
> +# Delete all existing binary logs.
> +#
> +RESET MASTER;
> +SELECT * FROM t1;
> +c01 c02 c03 c04 c05 c06 c07 c08
> +0 0 0 0 0
> +1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz
> +127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
> +#
> +# Operate some data
> +#
> +UPDATE t1 SET c01=20;
> +UPDATE t1 SET c02=200;
> +UPDATE t1 SET c03=2000;
> +DELETE FROM t1;
> +FLUSH LOGS;
> +#
> +# With -B
> +#
> +SELECT * FROM t1;
> +c01 c02 c03 c04 c05 c06 c07 c08
> +127 32767 8388607 2147483647 9223372036854775807 aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
> +0 0 0 0 0
> +1 2 3 4 5 abc abcdefg abcedfghijklmnopqrstuvwxyz
> +DROP TABLE t1;
> diff --git a/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test
> new file mode 100644
> index 0000000..eb0f14f
> --- /dev/null
> +++ b/mysql-test/suite/binlog/t/mysqlbinlog_row_flashback.test
> @@ -0,0 +1,110 @@
> +SET binlog_format= ROW;
This should not be needed, since you have the proper
have_binlog_format_row.inc. Or is there some specific reason it is needed,
and if so, what?
> +
> +--source include/have_log_bin.inc
> +--source include/have_binlog_format_row.inc
> +
> +--echo #
> +--echo # Preparatory cleanup.
> +--echo #
> +--disable_warnings
> +DROP TABLE IF EXISTS t1;
> +--enable_warnings
> +
> +--echo #
> +--echo # We need a fixed timestamp to avoid varying results.
> +--echo #
> +SET timestamp=1000000000;
> +
> +--echo #
> +--echo # Delete all existing binary logs.
> +--echo #
> +RESET MASTER;
> +
> +
> +CREATE TABLE t1 (
> + c01 tinyint,
> + c02 smallint,
> + c03 mediumint,
> + c04 int,
> + c05 bigint,
> + c06 char(10),
> + c07 varchar(20),
> + c08 TEXT
> +);
> +
> +--echo #
> +--echo # Insert data to t1
> +--echo #
> +INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
> +INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
> +INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 65535));
> +
> +
> +--echo #
> +--echo # Update t1
> +--echo #
> +UPDATE t1 SET c01=100 WHERE c02=0 OR c03=3;
> +
> +--echo #
> +--echo # Clear t1
> +--echo #
> +DELETE FROM t1;
> +
> +FLUSH LOGS;
> +
> +--echo #
> +--echo # Without -B
> +--echo #
> +
> +let $MYSQLD_DATADIR= `select @@datadir`;
> +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
> +--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /((a)[0-9]*=[0-9]*[.][0-9]{1,3})[0-9e+-]*[^ ]*(.*(FLOAT|DOUBLE).*[*].)/\1...\2/
> +--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001
> +
> +--echo #
> +--echo # With -B
> +--echo #
> +
> +let $MYSQLD_DATADIR= `select @@datadir`;
> +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
> +--replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /((a)[0-9]*=[0-9]*[.][0-9]{1,3})[0-9e+-]*[^ ]*(.*(FLOAT|DOUBLE).*[*].)/\1...\2/
> +--exec $MYSQL_BINLOG -B --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001
> +
> +--echo #
> +--echo # Insert data to t1
> +--echo #
> +TRUNCATE TABLE t1;
> +INSERT INTO t1 VALUES(0,0,0,0,0,'','','');
> +INSERT INTO t1 VALUES(1,2,3,4,5, "abc", "abcdefg", "abcedfghijklmnopqrstuvwxyz");
> +INSERT INTO t1 VALUES(127, 32767, 8388607, 2147483647, 9223372036854775807, repeat('a', 10), repeat('a', 20), repeat('a', 60));
> +
> +--echo #
> +--echo # Delete all existing binary logs.
> +--echo #
> +RESET MASTER;
> +SELECT * FROM t1;
> +
> +--echo #
> +--echo # Operate some data
> +--echo #
> +
> +UPDATE t1 SET c01=20;
> +UPDATE t1 SET c02=200;
> +UPDATE t1 SET c03=2000;
> +
> +DELETE FROM t1;
> +
> +FLUSH LOGS;
> +
> +--echo #
> +--echo # With -B
> +--echo #
> +
> +let $MYSQLD_DATADIR= `select @@datadir`;
> +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
> +--exec $MYSQL_BINLOG -B -vv $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback.sql
> +--exec $MYSQL -e "SET binlog_format= ROW; source $MYSQLTEST_VARDIR/tmp/mysqlbinlog_row_flashback.sql;"
> +
> +SELECT * FROM t1;
> +
> +DROP TABLE t1;
> diff --git a/sql/log_event.cc b/sql/log_event.cc
> index ea0afdd..4cf54d3 100644
> --- a/sql/log_event.cc
> +++ b/sql/log_event.cc
> @@ -299,17 +299,28 @@ class Write_on_release_cache
> constructor, but it would be possible to create a subclass
> holding the IO_CACHE itself.
> */
> - Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0)
> - : m_cache(cache), m_file(file), m_flags(flags)
> + Write_on_release_cache(IO_CACHE *cache, FILE *file, flag_set flags = 0, Log_event *ev = NULL)
> + : m_cache(cache), m_file(file), m_flags(flags), m_ev(ev)
> {
> reinit_io_cache(m_cache, WRITE_CACHE, 0L, FALSE, TRUE);
> }
>
> ~Write_on_release_cache()
> {
> - copy_event_cache_to_file_and_reinit(m_cache, m_file);
> - if (m_flags & FLUSH_F)
> - fflush(m_file);
> + if(m_ev == NULL)
> + {
> + copy_event_cache_to_file_and_reinit(m_cache, m_file);
> + if (m_flags | FLUSH_F)
> + fflush(m_file);
> + }
> + else // if m_ev<>NULL, then storing the output in output_buf
> + {
> + size_t bytes_in_cache= 0;
> + char *buff= 0;
> + buff= copy_event_cache_to_string_and_reinit(m_cache, &bytes_in_cache);
> + m_ev->output_buf.append(buff, bytes_in_cache);
> + my_free(buff);
> + }
> }
>
> /*
> @@ -339,6 +350,7 @@ class Write_on_release_cache
> IO_CACHE *m_cache;
> FILE *m_file;
> flag_set m_flags;
> + Log_event *m_ev; // Used for Flashback
> };
>
> /*
> @@ -2356,7 +2368,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
> d= (ulong) (i64 / 1000000);
> t= (ulong) (i64 % 1000000);
>
> - my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d",
> + my_b_printf(file, "'%04d-%02d-%02d %02d:%02d:%02d'",
> (int) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100),
> (int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100);
Why is this change needed?
Maybe it is so that SQL output for --review can work?
There does not seem to be any test case for this, should probably be added.
> return 8;
> @@ -2590,22 +2602,29 @@ size_t
> Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
> PRINT_EVENT_INFO *print_event_info,
> MY_BITMAP *cols_bitmap,
> - const uchar *value, const uchar *prefix)
> + const uchar *value, const uchar *prefix,
> + const my_bool only_parse)
Can you come up with a better name for "only_parse"? It is not clear to me
what it means, I think it is an option if the function needs to instead
generate the SQL for filling-in the flashback review table?
> {
> const uchar *value0= value;
> const uchar *null_bits= value;
> uint null_bit_index= 0;
> char typestr[64]= "";
> -
> +
> + /* Storing the review SQL */
"Storing the SQL for filling in the flashback review table" would be easier
to understand.
> + IO_CACHE *review_sql= &print_event_info->review_sql_cache;
> + uchar *begin_pos;
> + size_t bytes_in_cache;
> +
> /*
> Skip metadata bytes which gives the information about nullabity of master
> columns. Master writes one bit for each affected column.
> */
>
> value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
> -
> - my_b_printf(file, "%s", prefix);
> -
> +
> + if (!only_parse)
> + my_b_printf(file, "%s", prefix);
> +
> for (size_t i= 0; i < td->size(); i ++)
> {
> size_t size;
> @@ -2614,8 +2633,10 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
>
> if (bitmap_is_set(cols_bitmap, i) == 0)
> continue;
> -
> - my_b_printf(file, "### @%d=", static_cast<int>(i + 1));
> +
> + if (!only_parse)
> + my_b_printf(file, "### @%d=", static_cast<int>(i + 1));
> +
> if (!is_null)
> {
> size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
> @@ -2627,9 +2648,67 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
> return 0;
> }
> }
> - if (!(size= log_event_print_value(file,is_null? NULL: value,
> - td->type(i), td->field_metadata(i),
> - typestr, sizeof(typestr))))
> +
> + if (!only_parse)
> + {
> + begin_pos= file->write_pos;
> + size= log_event_print_value(file,is_null? NULL: value,
> + td->type(i), td->field_metadata(i),
> + typestr, sizeof(typestr));
> +
> + if (need_review)
> + {
> + String tmp_str, hex_str;
> + IO_CACHE tmp_cache;
> + char *buff;
> +
> + // Using a tmp IO_CACHE to get the value output
> + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
> + size= log_event_print_value(&tmp_cache, is_null? NULL: value,
> + td->type(i), td->field_metadata(i),
> + typestr, sizeof(typestr));
> + buff= copy_event_cache_to_string_and_reinit(&tmp_cache, &bytes_in_cache);
> + close_cached_file(&tmp_cache);
> +
> + switch (td->type(i)) // Covert string to Hex
> + {
> + case MYSQL_TYPE_VARCHAR:
> + case MYSQL_TYPE_VAR_STRING:
> + case MYSQL_TYPE_STRING:
> + case MYSQL_TYPE_BLOB:
> + // Avoid write_pos changed to a new area
> + tmp_str.free();
I do not understand this comment or code ... the tmp_str is newly created at
this point. Is it because tmp_str.free() is needed to initialise it to
empty? Maybe you can clarify.
> + tmp_str.append(buff + 1, bytes_in_cache - 2); // Removing quotation marks
> + if (hex_str.alloc(tmp_str.length()*2+1)) // if no memory
> + {
> + tmp_str.free();
> + tmp_str.append(buff, bytes_in_cache);
> + my_b_printf(review_sql, ", %s", tmp_str.ptr());
But what happens if no memory? Does it just silently output wrong SQL (SQL
injection...)? Why doesn't it produce a proper error instead?
> + break;
> + }
> + octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length());
> + my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr());
> + break;
> + default:
> + tmp_str.free();
> + tmp_str.append(buff, bytes_in_cache);
> + my_b_printf(review_sql, ", %s", tmp_str.ptr());
> + break;
> + }
> + my_free(buff);
> + }
> + }
> + else
> + {
> + IO_CACHE tmp_cache;
> + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
> + size= log_event_print_value(&tmp_cache,is_null? NULL: value,
> + td->type(i), td->field_metadata(i),
> + typestr, sizeof(typestr));
> + close_cached_file(&tmp_cache);
> + }
> +
> + if (!size)
> return 0;
>
> if (!is_null)
> @@ -2637,23 +2716,91 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
>
> if (print_event_info->verbose > 1)
> {
> - my_b_write(file, (uchar*)" /* ", 4);
> + if (!only_parse)
> + {
> + my_b_write(file, (uchar*)" /* ", 4);
>
> - my_b_printf(file, "%s ", typestr);
> -
> - my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
> - td->field_metadata(i),
> - td->maybe_null(i), is_null);
> - my_b_write(file, (uchar*)"*/", 2);
> + my_b_printf(file, "%s ", typestr);
> +
> + my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
> + td->field_metadata(i),
> + td->maybe_null(i), is_null);
> + my_b_write(file, (uchar*)"*/", 2);
> + }
> }
> -
> - my_b_write_byte(file, '\n');
> -
> +
> + if (!only_parse)
> + my_b_write_byte(file, '\n');
> +
> null_bit_index++;
> }
> return value - value0;
> }
>
> +void Rows_log_event::exchange_update_rows(PRINT_EVENT_INFO *print_event_info,
> + uchar *rows_buff)
Ok, so I think this function handles the problem that I mentioned earlier -
that the individual rows for UPDATE need to be flashbacked in the reverse
order for things to work.
Please explain this in a comment describing the function - and please add
test cases that tests this.
I'm also wondering - isn't the same swapping needed for INSERT and DELETE,
in case of self-referencing foreign keys?
CREATE TABLE t1 (a INT PRIMARY KEY, b INT,
FOREIGN KEY my_fk(b) REFERENCES t1(a))
ENGINE=InnoDB;
BEGIN;
INSERT INTO t1 VALUES (1, NULL);
INSERT INTO t1 VALUES (2, 1), (3, 2), (4, 3);
COMMIT;
DELETE FROM t1 ORDER BY a DESC;
You should add test cases for these kinds of issues as well.
> +{
> + Table_map_log_event *map;
> + table_def *td;
> + DYNAMIC_ARRAY rows_arr;
> + uchar *data_buff= rows_buff + m_rows_before_size;
> +
> + if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
> + !(td= map->create_table_def()))
> + return;
> +
> + (void) my_init_dynamic_array(&rows_arr, sizeof(String), 8, 8, MYF(0));
> +
> + for (uchar *value= m_rows_buf; value < m_rows_end; )
> + {
> + uchar *start_pos= value;
> + size_t length1;
> + if (!(length1= print_verbose_one_row(NULL, td, print_event_info,
> + &m_cols, value,
> + (const uchar*) "", TRUE)))
> + return;
> + value+= length1;
> +
> + size_t length2;
> + if (!(length2= print_verbose_one_row(NULL, td, print_event_info,
> + &m_cols, value,
> + (const uchar*) "", TRUE)))
> + return;
> + value+= length2;
> +
> + /* Swap SET and WHERE part */
> + uchar *swap_buff1= (uchar *) my_malloc(length1, MYF(0));
> + uchar *swap_buff2= (uchar *) my_malloc(length2, MYF(0));
> +
> + memcpy(swap_buff1, start_pos, length1); // SET part
> + memcpy(swap_buff2, start_pos + length1, length2); // WHERE part
> +
> + memcpy(start_pos, swap_buff2, length2);
> + memcpy(start_pos + length2, swap_buff1, length1);
> +
> + /* Copying one row into a buff, and pushing into the array */
> + String *one_row= new String[1];
> + one_row->append((const char *)start_pos, (uint32)(length1 + length2), &my_charset_bin);
> + (void) push_dynamic(&rows_arr, (uchar *) one_row);
> +
> + /* Free tmp buffers */
> + my_free(swap_buff1);
> + my_free(swap_buff2);
> + }
> +
> + /* Copying rows from the end to the begining into event */
> + uchar *pos= data_buff;
> + for (uint i= rows_arr.elements; i > 0; --i)
> + {
> + String *one_row= dynamic_element(&rows_arr, i - 1, String*);
> +
> + memcpy(pos, (uchar *)one_row->ptr(), one_row->length());
> + pos+= one_row->length();
> + delete one_row;
> + }
> + delete_dynamic(&rows_arr);
> +}
> +
>
> /**
> Print a row event into IO cache in human readable form (in SQL format)
> @@ -2667,8 +2814,10 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
> Table_map_log_event *map;
> table_def *td;
> const char *sql_command, *sql_clause1, *sql_clause2;
> + const char *sql_command_short;
> Log_event_type general_type_code= get_general_type_code();
> -
> + IO_CACHE *review_sql= &print_event_info->review_sql_cache;
> +
> if (m_extra_row_data)
> {
> uint8 extra_data_len= m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET];
> @@ -2698,19 +2847,23 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
> sql_command= "INSERT INTO";
> sql_clause1= "### SET\n";
> sql_clause2= NULL;
> + sql_command_short= "I";
> break;
> case DELETE_ROWS_EVENT:
> sql_command= "DELETE FROM";
> sql_clause1= "### WHERE\n";
> sql_clause2= NULL;
> + sql_command_short= "D";
> break;
> case UPDATE_ROWS_EVENT:
> sql_command= "UPDATE";
> sql_clause1= "### WHERE\n";
> sql_clause2= "### SET\n";
> + sql_command_short= "U";
> break;
> default:
> sql_command= sql_clause1= sql_clause2= NULL;
> + sql_command_short= "";
> DBUG_ASSERT(0); /* Not possible */
> }
>
> @@ -2736,6 +2889,11 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
> my_b_printf(file, "### %s %`s.%`s\n",
> sql_command,
> map->get_db_name(), map->get_table_name());
> +
> + if (need_review)
> + my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'",
This is vulnerable to sql injection on the database and table names. Check
the existing code, there are functions that handle quoting the names
correctly.
By the way, there is also the issue that depending on the SQL mode, `` or ""
are the correct quoting characters to use. I seem to recall that there is an
SQL mode where `` cannot be used to quote names? But I cannot seem to find
it just now, so maybe I'm wrong, and `` can always be used?
> + map->get_review_dbname(), map->get_review_tablename(), sql_command_short);
> +
> /* Print the first image */
> if (!(length= print_verbose_one_row(file, td, print_event_info,
> &m_cols, value,
> @@ -2752,6 +2910,15 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
> goto end;
> value+= length;
> }
> + else
> + {
> + if (need_review)
> + for (size_t i= 0; i < td->size(); i ++)
> + my_b_printf(review_sql, ", NULL");
Does this generate an insert of a row with all NULLs? What is the purpose of
this?
If this is to handle the case where the server logged an event in not full
rows mode, shouldn't this generate a sensible error to the user instead?
Maybe with an option to override the error (if DBA is desparate and wants to
just recover whatever can be recovered), rather than silently do a partial
flashback leaving the data in unknown and inconsistent state?
Same about query_log_event, what happens if those are found during
flashback? Shouldn't it generate an error?
> + }
> +
> + if (need_review)
> + my_b_printf(review_sql, ")%s\n", print_event_info->delimiter);
> }
>
> end:
> @@ -2767,7 +2934,7 @@ void Log_event::print_base64(IO_CACHE* file,
> PRINT_EVENT_INFO* print_event_info,
> bool more)
> {
> - const uchar *ptr= (const uchar *)temp_buf;
> + uchar *ptr= (uchar *)temp_buf;
> uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
> DBUG_ENTER("Log_event::print_base64");
>
> @@ -2779,6 +2946,32 @@ void Log_event::print_base64(IO_CACHE* file,
> DBUG_VOID_RETURN;
> }
>
> + if (is_flashback)
> + {
> + switch (ptr[EVENT_TYPE_OFFSET]) {
> + case WRITE_ROWS_EVENT:
> + ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT;
> + break;
> + case WRITE_ROWS_EVENT_V1:
> + ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT_V1;
> + break;
> + case DELETE_ROWS_EVENT:
> + ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT;
> + break;
> + case DELETE_ROWS_EVENT_V1:
> + ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT_V1;
> + break;
> + case UPDATE_ROWS_EVENT:
> + case UPDATE_ROWS_EVENT_V1:
> + Rows_log_event *ev= NULL;
> + ev= new Update_rows_log_event((const char*) ptr, size,
> + glob_description_event);
> + ev->exchange_update_rows(print_event_info, ptr);
> + delete ev;
> + break;
> + }
> + }
> +
> if (base64_encode(ptr, (size_t) size, tmp_str))
> {
> DBUG_ASSERT(0);
> @@ -2795,7 +2988,7 @@ void Log_event::print_base64(IO_CACHE* file,
> my_b_printf(file, "'%s\n", print_event_info->delimiter);
> }
>
> - if (print_event_info->verbose)
> + if (print_event_info->verbose || need_review)
> {
> Rows_log_event *ev= NULL;
> Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET];
> @@ -2811,6 +3004,11 @@ void Log_event::print_base64(IO_CACHE* file,
> Table_map_log_event *map;
> map= new Table_map_log_event((const char*) ptr, size,
> glob_description_event);
> + if (need_review)
> + {
> + map->set_review_dbname(m_review_dbname.ptr());
> + map->set_review_tablename(m_review_tablename.ptr());
> + }
> print_event_info->m_table_map.set_table(map->get_table_id(), map);
> break;
> }
> @@ -2838,14 +3036,23 @@ void Log_event::print_base64(IO_CACHE* file,
> default:
> break;
> }
> -
> +
> if (ev)
> {
> - ev->print_verbose(file, print_event_info);
> + ev->need_review= need_review;
> + if (print_event_info->verbose)
> + ev->print_verbose(file, print_event_info);
> + else
> + {
> + IO_CACHE tmp_cache;
> + open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
> + ev->print_verbose(&tmp_cache, print_event_info);
> + close_cached_file(&tmp_cache);
> + }
> delete ev;
> }
> }
> -
> +
> my_free(tmp_str);
> DBUG_VOID_RETURN;
> }
> @@ -4127,7 +4334,7 @@ void Query_log_event::print_query_header(IO_CACHE* file,
>
> void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
> {
> - Write_on_release_cache cache(&print_event_info->head_cache, file);
> + Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this);
>
> /**
> reduce the size of io cache so that the write function is called
> @@ -4136,8 +4343,24 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
> DBUG_EXECUTE_IF ("simulate_file_write_error",
> {(&cache)->write_pos= (&cache)->write_end- 500;});
> print_query_header(&cache, print_event_info);
> - my_b_write(&cache, (uchar*) query, q_len);
> - my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
> + if (!is_flashback)
> + {
> + my_b_write(&cache, (uchar*) query, q_len);
> + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
> + }
> + else // is_flashback == 1
> + {
> + if (strcmp("BEGIN", query) == 0)
> + {
> + my_b_write(&cache, (uchar*) "COMMIT", 6);
> + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
> + }
Hm, in MariaDB, transactions in the binlog do not start with a
Query_log_event "BEGIN" event, they start with a Gtid_log_event. You
probably need to handle this to output a COMMIT.
> + else if (strcmp("COMMIT", query) == 0)
> + {
> + my_b_write(&cache, (uchar*) "BEGIN", 5);
> + my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
> + }
> + }
> }
> #endif /* MYSQL_CLIENT */
>
> @@ -6885,10 +7108,11 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
> print_event_info->server_id_printed= true;
> }
>
> - my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
> - buf, print_event_info->delimiter);
> + if (!is_flashback)
> + my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
> + buf, print_event_info->delimiter);
> }
> - if (!(flags2 & FL_STANDALONE))
> + if (!(flags2 & FL_STANDALONE) && !is_flashback)
> my_b_printf(&cache, "BEGIN\n%s\n", print_event_info->delimiter);
> }
>
> @@ -7531,7 +7755,7 @@ bool Xid_log_event::write()
> void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
> {
> Write_on_release_cache cache(&print_event_info->head_cache, file,
> - Write_on_release_cache::FLUSH_F);
> + Write_on_release_cache::FLUSH_F, this);
>
> if (!print_event_info->short_form)
> {
> @@ -7541,7 +7765,10 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
> print_header(&cache, print_event_info, FALSE);
> my_b_printf(&cache, "\tXid = %s\n", buf);
> }
> - my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
> + if (!is_flashback)
> + my_b_printf(&cache, "COMMIT%s\n", print_event_info->delimiter);
> + else
> + my_b_printf(&cache, "BEGIN%s\n", print_event_info->delimiter);
> }
> #endif /* MYSQL_CLIENT */
>
> @@ -8185,7 +8412,7 @@ void Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info
> void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
> {
> Write_on_release_cache cache(&print_event_info->head_cache, file,
> - Write_on_release_cache::FLUSH_F);
> + Write_on_release_cache::FLUSH_F, this);
>
> if (print_event_info->short_form)
> return;
> @@ -9517,6 +9744,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
> m_rows_end= m_rows_buf + data_size;
> m_rows_cur= m_rows_end;
> memcpy(m_rows_buf, ptr_rows_data, data_size);
> + m_rows_before_size= ptr_rows_data - (const uchar *) buf; // Get the size that before SET part
> }
> else
> m_cols.bitmap= 0; // to not free it
> @@ -10346,6 +10574,7 @@ void Rows_log_event::print_helper(FILE *file,
> {
> IO_CACHE *const head= &print_event_info->head_cache;
> IO_CACHE *const body= &print_event_info->body_cache;
> + IO_CACHE *const sql= &print_event_info->review_sql_cache;
> if (!print_event_info->short_form)
> {
> bool const last_stmt_event= get_flags(STMT_END_F);
> @@ -10358,8 +10587,17 @@ void Rows_log_event::print_helper(FILE *file,
>
> if (get_flags(STMT_END_F))
> {
> - copy_event_cache_to_file_and_reinit(head, file);
> - copy_event_cache_to_file_and_reinit(body, file);
> + reinit_io_cache(head, READ_CACHE, 0L, FALSE, FALSE);
> + output_buf.append(head, head->end_of_file);
> + reinit_io_cache(head, WRITE_CACHE, 0, FALSE, TRUE);
> +
> + reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE);
> + output_buf.append(body, body->end_of_file);
> + reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE);
> +
> + reinit_io_cache(sql, READ_CACHE, 0L, FALSE, FALSE);
> + output_buf.append(sql, sql->end_of_file);
> + reinit_io_cache(sql, WRITE_CACHE, 0, FALSE, TRUE);
> }
> }
> #endif
> @@ -12856,6 +13094,7 @@ st_print_event_info::st_print_event_info()
> myf const flags = MYF(MY_WME | MY_NABP);
> open_cached_file(&head_cache, NULL, NULL, 0, flags);
> open_cached_file(&body_cache, NULL, NULL, 0, flags);
> + open_cached_file(&review_sql_cache, NULL, NULL, 0, flags);
> }
> #endif
>
> diff --git a/sql/log_event.h b/sql/log_event.h
> index bc850c2..c274368 100644
> --- a/sql/log_event.h
> +++ b/sql/log_event.h
> @@ -41,6 +41,7 @@
> #include "rpl_utility.h"
> #include "hash.h"
> #include "rpl_tblmap.h"
> +#include "sql_string.h"
> #endif
>
> #ifdef MYSQL_SERVER
> @@ -52,7 +53,9 @@
> #include "rpl_gtid.h"
>
> /* Forward declarations */
> +#ifndef MYSQL_CLIENT
> class String;
> +#endif
Ok, so the #include of sql_string.h was moved into log_event.h from
mysqlbinlog.cc. Why was this necessary? Could it be avoided somehow? If not,
I think at least a comment is needed here explaining why this #ifndef
MYSQL_CLIENT is needed, the whole sharing of code between mysqlbinlog and
server is already very confusing.
>
> #define PREFIX_SQL_LOAD "SQL_LOAD-"
> #define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
> @@ -781,9 +784,10 @@ typedef struct st_print_event_info
> ~st_print_event_info() {
> close_cached_file(&head_cache);
> close_cached_file(&body_cache);
> + close_cached_file(&review_sql_cache);
> }
> bool init_ok() /* tells if construction was successful */
> - { return my_b_inited(&head_cache) && my_b_inited(&body_cache); }
> + { return my_b_inited(&head_cache) && my_b_inited(&body_cache) && my_b_inited(&review_sql_cache); }
>
>
> /* Settings on how to print the events */
> @@ -811,6 +815,9 @@ typedef struct st_print_event_info
> */
> IO_CACHE head_cache;
> IO_CACHE body_cache;
> +
> + /* Storing the SQL for reviewing */
> + IO_CACHE review_sql_cache;
> } PRINT_EVENT_INFO;
> #endif
>
> @@ -1159,6 +1166,41 @@ class Log_event
> void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
> bool is_more);
> #endif
> +
> + /* The following code used for Flashback */
> + my_bool is_flashback;
> + my_bool need_review;
I'm wondering if a better name would be "need_flashback_review". "review" is
such a generic term...
> + String output_buf; // Storing the event output
> + String m_review_dbname;
> + String m_review_tablename;
Did you consider the performance impact of adding these extra fields to the
Log_event class? I am thinking of the overhead for the server (master and
slave), which creates quite a lot of events, but does not have anything to
do with flashback?
Did you consider alternatives, such as putting the new fields somewhere else
(maybe struct st_print_event_info), or using #ifdef to add these only for
mysqlbinlog, not the server?
> +
> + void set_review_dbname(const char *name)
> + {
> + if (name)
> + {
> + m_review_dbname.free();
> + m_review_dbname.append(name);
> + }
> + }
> + void set_review_tablename(const char *name)
> + {
> + if (name)
> + {
> + m_review_tablename.free();
> + m_review_tablename.append(name);
> + }
> + }
> + const char *get_review_dbname() const { return m_review_dbname.ptr(); }
> + const char *get_review_tablename() const { return m_review_tablename.ptr(); }
> + void free_output_buffer()
> + {
> + if (!output_buf.is_empty())
> + {
> + output_buf.free();
> + }
> + }
> + /* End */
> +
> /*
> read_log_event() functions read an event from a binlog or relay
> log; used by SHOW BINLOG EVENTS, the binlog_dump thread on the
> @@ -1293,7 +1335,13 @@ class Log_event
> }
> Log_event(const char* buf, const Format_description_log_event
> *description_event);
> - virtual ~Log_event() { free_temp_buf();}
> + virtual ~Log_event()
> + {
> + free_temp_buf();
> +#ifdef MYSQL_CLIENT
> + free_output_buffer();
> +#endif
> + }
> void register_temp_buf(char* buf, bool must_free)
> {
> temp_buf= buf;
> @@ -4265,12 +4313,14 @@ class Rows_log_event : public Log_event
> #ifdef MYSQL_CLIENT
> /* not for direct call, each derived has its own ::print() */
> virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info)= 0;
> + void exchange_update_rows(PRINT_EVENT_INFO *print_event_info, uchar *rows_buff); // Exchanging the SET/WHERE part in UPDATE event
> void print_verbose(IO_CACHE *file,
> PRINT_EVENT_INFO *print_event_info);
> size_t print_verbose_one_row(IO_CACHE *file, table_def *td,
> PRINT_EVENT_INFO *print_event_info,
> MY_BITMAP *cols_bitmap,
> - const uchar *ptr, const uchar *prefix);
> + const uchar *ptr, const uchar *prefix,
> + const my_bool only_parse= 0); // if only_parse=1, then print result is unnecessary
> #endif
>
> #ifdef MYSQL_SERVER
> @@ -4407,6 +4457,8 @@ class Rows_log_event : public Log_event
> uchar *m_rows_cur; /* One-after the end of the data */
> uchar *m_rows_end; /* One-after the end of the allocated space */
>
> + size_t m_rows_before_size; /* The length before m_rows_buf BY P.Linux */
> +
Probably don't need the "BY P.Linux" here.
> flag_set m_flags; /* Flags for row-level events */
>
> Log_event_type m_type; /* Actual event type */
> @@ -4889,6 +4941,23 @@ class Ignorable_log_event : public Log_event {
> };
>
>
> +static inline char *copy_event_cache_to_string_and_reinit(IO_CACHE *cache, size_t *bytes_in_cache)
> +{
> + char *buff;
> + String tmp;
> +
> + tmp.length(0);
> + reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE);
> + tmp.append(cache, cache->end_of_file);
> + reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
> +
> + buff= (char *) my_malloc(tmp.length() + 1, MYF(0));
> + memcpy(buff, tmp.ptr(), tmp.length());
> + *bytes_in_cache= tmp.length();
> +
> + return buff;
> +}
> +
> static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
> FILE *file)
> {
Hope this helps,
- Kristian.
1
1

[Maria-developers] GSoC 2016: Can anybody help me with a minor technical problem?
by Галина Шалыгина 22 May '16
by Галина Шалыгина 22 May '16
22 May '16
Hi everyone,
I've just posted it in my blog here
<http://gsocmariadbshagalla.blogspot.ru/> :
Finially I finished my project on recursive CTE, so I can resume working on
my project of GSOC 2016.
As I said in my previous post, I decided to start with building items
clones (items are used to build different kinds of expressions in MySQL).
Items are typical tree structures. If I just copy a node in the item
structure, pointers to the subtrees won't be right. Fortunately this
pointers can be fixed in the method Item_func_or_sum::build_clone. To copy
nodes we use copy constructor. As we don't know the type of the node in
general the copy constructor should be virtual. I called it get_copy. It
should be implemented for each terminal class of items. There are dozens of
such classes.
How they could be caught?
I used the following trick.
My get_copy for the base class Item is 'pseudo-abstract':
Item *tem::get_copy(...) { dbug_print_item(this); DBUG_ASSERT(0); return 0;
}
Here dbug_print_item(this) helps me to understand for which class get_copy
is missed.
If I call now method build_clone, for example in JOIN::optimize_inner(), to
build a clone for the WHERE condition, and launch tests, it'll be easy for
me to understand for which classes lack implementations of get_copy.
Here I've faced some minor problem: how to skip the queries that are
excuted by mtr before it starts running the tests.
Anybody knows?
(Now I have a workaround: call build_clone() conditinally and manually
trigger the condition. Of course it's not nice.)
Best wishes,
Galina Shalygina.
2
1

[Maria-developers] GSoC 2016 Community Bonding : Building Maxscale from Source
by Lisa Brinson 20 May '16
by Lisa Brinson 20 May '16
20 May '16
Hi everyone,
I have created a blog on which I will bee making posts on progress in
developing a filter for MS server, My first post explains how I installed
MariaDB and build Maxscale from source, a challenging task. Please find the
blog post HERE
<https://maxscalefiltergsocproject.wordpress.com/2016/05/06/maxscale-filter-…>
and feel free to provide comments.
Lisa.
1
0
Hi Sergei!
As i told you i was prototyping for hash table
It is done around 90% apart from one thing when hash is same
how to get record from .myd file when i have offset of record
so currently i am skipping it
But it is very fast i do not know why this is so fast here are
results of employee database
salary table definition
CREATE TABLE salaries (
emp_no INT NOT NULL,
salary blob NOT NULL,
from_date DATE NOT NULL,
to_date DATE NOT NULL,
FOREIGN KEY (emp_no) REFERENCES employees (emp_no) ON DELETE CASCADE,
PRIMARY KEY (emp_no, from_date)
)
;
And query is
MariaDB [employees]> select distinct salary from salaries;
Result with out using hash table
+--------+
85814 rows in set (2 min 24.76 sec)
Result with using hash table
| 39420 |
+--------+
85809 rows in set (6.24 sec)
( number of rows are not equal but this can be solved if i get record by
offset)
I am sure there is something wrong.The whole hash table is in memory like
wise the
b tree of hash is in memory but why there is so much improvement. Please
sir check the
prototype and tell if i am wrong .thanks
Regards
sachin
On Mon, May 2, 2016 at 11:43 AM, Sergei Golubchik <serg(a)mariadb.org> wrote:
> Hi, Sachin!
>
> On May 02, Sachin Setia wrote:
> > I am sorry sir Currently my exam are going on
> > But i am working on prototype of second project. Will be done by tommorow
> > Regards
> > sachin
>
> Sure thing, that's totally fine!
>
> Still, in the future, if you plan to go silent for a while (exam or you
> just want to relax for a few days or something else :) - please drop me
> a short email and then I will know that you didn't disappear from the
> project. Thanks!
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
2
4

Re: [Maria-developers] 1e883e9: MDEV-5535: Cannot reopen temporary table
by Sergei Golubchik 19 May '16
by Sergei Golubchik 19 May '16
19 May '16
Hi, Nirbhay!
Here's a review of MDEV-5535.
Sorry, it took a while - this was a big patch.
The approach is fine, my comments are mostly about details.
On Mar 07, Nirbhay Choubey wrote:
> diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result
> index ee0b3ab..94b02a6 100644
> --- a/mysql-test/r/temp_table.result
> +++ b/mysql-test/r/temp_table.result
> @@ -308,3 +308,185 @@ show status like 'com_drop%table';
> Variable_name Value
> Com_drop_table 2
> Com_drop_temporary_table 1
> +#
> +# MDEV-5535: Cannot reopen temporary table
> +#
> +DROP DATABASE IF EXISTS temp_db;
> +CREATE DATABASE temp_db;
> +USE temp_db;
> +#
> +# SHOW TABLES do not list temporary tables.
> +#
> +CREATE TEMPORARY TABLE temp_t1(i INT) ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES(1);
> +SELECT * FROM temp_t1;
> +i
> +1
> +SHOW TABLES;
> +Tables_in_temp_db
> +DROP TABLE temp_t1;
> +#
> +# Create and drop a temporary table.
> +#
> +CREATE TEMPORARY TABLE temp_t1(i INT) ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES(1);
> +SELECT * FROM temp_t1;
> +i
> +1
> +DROP TABLE temp_t1;
> +SELECT * FROM temp_t1;
> +ERROR 42S02: Table 'temp_db.temp_t1' doesn't exist
> +#
> +# Create a tempporary table and base table with same name and DROP TABLE.
> +#
> +CREATE TABLE t1(c1 VARCHAR(20)) ENGINE=INNODB;
> +INSERT INTO t1 VALUES("BASE TABLE");
> +CREATE TEMPORARY TABLE t1(c1 VARCHAR(20)) ENGINE=INNODB;
> +INSERT INTO t1 VALUES("TEMPORARY TABLE");
> +SELECT * FROM t1;
> +c1
> +TEMPORARY TABLE
> +DROP TABLE t1;
> +SELECT * FROM t1;
> +c1
> +BASE TABLE
> +DROP TABLE t1;
> +SELECT * FROM t1;
> +ERROR 42S02: Table 'temp_db.t1' doesn't exist
> +#
> +# Create a tempporary table and base table with same name and DROP TEMPORARY
> +# TABLE.
> +#
> +CREATE TABLE t1(c1 VARCHAR(20)) ENGINE=INNODB;
> +INSERT INTO t1 VALUES("BASE TABLE");
> +CREATE TEMPORARY TABLE t1(c1 VARCHAR(20)) ENGINE=INNODB;
> +INSERT INTO t1 VALUES("TEMPORARY TABLE");
> +SELECT * FROM t1;
> +c1
> +TEMPORARY TABLE
> +DROP TEMPORARY TABLE t1;
> +SELECT * FROM t1;
> +c1
> +BASE TABLE
> +DROP TEMPORARY TABLE t1;
> +ERROR 42S02: Unknown table 'temp_db.t1'
> +SELECT * FROM t1;
> +c1
> +BASE TABLE
> +DROP TABLE t1;
> +#
> +# Create a temporary table and drop its parent database.
> +#
> +USE temp_db;
> +CREATE TEMPORARY TABLE temp_t1(i INT) ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES (1);
> +DROP DATABASE temp_db;
> +CREATE DATABASE temp_db;
> +USE temp_db;
> +DROP TEMPORARY TABLE temp_t1;
> +#
> +# Similar to above, but this time with a base table with same name.
> +#
> +USE temp_db;
> +CREATE TABLE t1(i INT)ENGINE=INNODB;
> +CREATE TEMPORARY TABLE t1(i INT) ENGINE=INNODB;
> +INSERT INTO t1 VALUES (1);
> +DROP DATABASE temp_db;
> +CREATE DATABASE temp_db;
> +USE temp_db;
> +DROP TEMPORARY TABLE t1;
> +DROP TABLE t1;
> +ERROR 42S02: Unknown table 'temp_db.t1'
> +#
> +# Create a temporary table within a function.
> +#
> +USE temp_db;
> +CREATE FUNCTION f1() RETURNS INT
> +BEGIN
> +DROP TEMPORARY TABLE IF EXISTS temp_t1;
> +CREATE TEMPORARY TABLE temp_t1(i INT) ENGINE=INNODB;
> +INSERT INTO `temp_t1` VALUES(1);
> +RETURN (SELECT COUNT(*) FROM temp_t1);
> +END|
> +SELECT f1();
> +f1()
> +1
> +SELECT * FROM temp_t1;
> +i
> +1
> +DROP TABLE temp_t1;
> +CREATE TEMPORARY TABLE `temp_t1`(i INT) ENGINE=INNODB;
> +SELECT f1();
> +f1()
> +1
> +SELECT * FROM temp_t1;
> +i
> +1
> +DROP FUNCTION f1;
> +#
> +# Create and drop a temporary table within a function.
> +#
> +CREATE FUNCTION f2() RETURNS INT
> +BEGIN
> +DROP TEMPORARY TABLE IF EXISTS temp_t1;
> +CREATE TEMPORARY TABLE temp_t1(i INT) ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES(1);
> +DROP TABLE temp_t1;
> +RETURN 0;
> +END|
> +ERROR HY000: Explicit or implicit commit is not allowed in stored function or trigger.
> +#
> +# Create a temporary table within a function and select it from another
> +# function.
> +#
> +CREATE FUNCTION f2() RETURNS INT
> +BEGIN
> +DROP TEMPORARY TABLE IF EXISTS temp_t1;
> +CREATE TEMPORARY TABLE temp_t1 (i INT) ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES (1);
> +RETURN f2_1();
> +END|
> +CREATE FUNCTION f2_1() RETURNS INT
> +RETURN (SELECT COUNT(*) FROM temp_t1)|
> +DROP TEMPORARY TABLE temp_t1;
> +SELECT f2();
> +f2()
> +1
> +DROP TEMPORARY TABLE temp_t1;
> +DROP FUNCTION f2;
> +#
> +# Create temporary table like base table.
> +#
> +CREATE TABLE t1(i INT) ENGINE=INNODB;
> +INSERT INTO t1 VALUES(1);
> +CREATE TEMPORARY TABLE temp_t1 LIKE t1;
> +SELECT * FROM temp_t1;
> +i
> +CREATE TEMPORARY TABLE t1 LIKE t1;
> +ERROR 42000: Not unique table/alias: 't1'
> +DROP TABLE temp_t1, t1;
> +#
> +# Create temporary table as base table.
> +#
> +CREATE TABLE t1(i INT) ENGINE=INNODB;
> +INSERT INTO t1 VALUES(1);
> +CREATE TEMPORARY TABLE temp_t1 AS SELECT * FROM t1;
> +SELECT * FROM temp_t1;
> +i
> +1
> +DROP TABLE temp_t1, t1;
> +#
> +# Reopen temporary table
> +#
> +CREATE TEMPORARY TABLE temp_t1(i int)ENGINE=INNODB;
> +INSERT INTO temp_t1 VALUES(1), (2);
> +SELECT * FROM temp_t1 a, temp_t1 b;
> +i i
> +1 1
> +1 2
> +2 1
> +2 2
> +DROP TABLE temp_t1;
> +# Cleanup
> +DROP DATABASE temp_db;
> +# MDEV-5535: End of tests
What's that? You have lots of tests for temporary tables - not that I mind
the number, but they are mostly unrelated to MDEV-5535. Only the one last
test is about the new feature. I'd expect it to be the other way around -
many tests for the new functionality and few, if at all - for the unmodified
behavior.
> diff --git a/mysql-test/r/temp_table_frm.result b/mysql-test/r/temp_table_frm.result
> index 19c6638..58e9a0a 100644
> --- a/mysql-test/r/temp_table_frm.result
> +++ b/mysql-test/r/temp_table_frm.result
> @@ -16,6 +16,6 @@ variable_name session_status.variable_value - t1.variable_value
> OPENED_FILES 0
> OPENED_PLUGIN_LIBRARIES 0
> OPENED_TABLE_DEFINITIONS 2
> -OPENED_TABLES 1
> +OPENED_TABLES 2
why did it change?
UPDATE: now, I see - you close and open temporary tables all the time.
See my comment about it below.
> OPENED_VIEWS 0
> drop table t1;
> diff --git a/mysql-test/suite/handler/handler.inc b/mysql-test/suite/handler/handler.inc
> index c71dc53..ca67b62 100644
> --- a/mysql-test/suite/handler/handler.inc
> +++ b/mysql-test/suite/handler/handler.inc
> @@ -489,7 +489,7 @@ handler t1 open as a1;
> handler a1 read a=(1);
> handler a1 read a next;
> handler a1 read a next;
> ---error ER_CANT_REOPEN_TABLE
> +#--error ER_CANT_REOPEN_TABLE
remove it, don't comment. And, please, fix the comment before this test.
> select a,b from t1;
> handler a1 read a prev;
> handler a1 read a prev;
> diff --git a/mysql-test/suite/multi_source/status_vars.result b/mysql-test/suite/multi_source/status_vars.result
> index 12917f9..50d500e 100644
> --- a/mysql-test/suite/multi_source/status_vars.result
> +++ b/mysql-test/suite/multi_source/status_vars.result
> @@ -76,22 +76,34 @@ start slave;
> include/wait_for_slave_to_start.inc
> set binlog_format = statement;
> create temporary table tmp1 (i int) engine=MyISAM;
> +show status like 'Slave_open_temp_table_definitions';
> +Variable_name Value
> +Slave_open_temp_table_definitions 1
> show status like 'Slave_open_temp_tables';
> Variable_name Value
> -Slave_open_temp_tables 1
> +Slave_open_temp_tables 0
why did it change?
> set default_master_connection = 'master1';
> +show status like 'Slave_open_temp_table_definitions';
> +Variable_name Value
> +Slave_open_temp_table_definitions 1
> show status like 'Slave_open_temp_tables';
> Variable_name Value
> -Slave_open_temp_tables 1
> +Slave_open_temp_tables 0
> set binlog_format = statement;
> create temporary table tmp1 (i int) engine=MyISAM;
> +show status like 'Slave_open_temp_table_definitions';
> +Variable_name Value
> +Slave_open_temp_table_definitions 2
> show status like 'Slave_open_temp_tables';
> Variable_name Value
> -Slave_open_temp_tables 2
> +Slave_open_temp_tables 0
> set default_master_connection = '';
> +show status like 'Slave_open_temp_table_definitions';
> +Variable_name Value
> +Slave_open_temp_table_definitions 2
> show status like 'Slave_open_temp_tables';
> Variable_name Value
> -Slave_open_temp_tables 2
> +Slave_open_temp_tables 0
> include/reset_master_slave.inc
> include/reset_master_slave.inc
> include/reset_master_slave.inc
> diff --git a/mysql-test/suite/multi_source/status_vars.test b/mysql-test/suite/multi_source/status_vars.test
> index d1cfda7..f9d2e85 100644
> --- a/mysql-test/suite/multi_source/status_vars.test
> +++ b/mysql-test/suite/multi_source/status_vars.test
> @@ -107,9 +107,11 @@ create temporary table tmp1 (i int) engine=MyISAM;
>
> --connection slave
> --sync_with_master 0,'master1'
> +show status like 'Slave_open_temp_table_definitions';
> show status like 'Slave_open_temp_tables';
You can do show
status like 'Slave_open_temp_table%';
instead. here and everywhere.
>
> set default_master_connection = 'master1';
> +show status like 'Slave_open_temp_table_definitions';
> show status like 'Slave_open_temp_tables';
>
> --connect (master2,127.0.0.1,root,,,$SERVER_MYPORT_2)
> diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
> index 7bdd9c1..426989c 100644
> --- a/sql/rpl_rli.cc
> +++ b/sql/rpl_rli.cc
> @@ -1060,24 +1061,14 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
>
> void Relay_log_info::close_temporary_tables()
> {
> - TABLE *table,*next;
> DBUG_ENTER("Relay_log_info::close_temporary_tables");
>
> - for (table=save_temporary_tables ; table ; table=next)
> + if (sql_driver_thd)
> {
> - next=table->next;
> -
> - /* Reset in_use as the table may have been created by another thd */
> - table->in_use=0;
> - /*
> - Don't ask for disk deletion. For now, anyway they will be deleted when
> - slave restarts, but it is a better intention to not delete them.
> - */
> - DBUG_PRINT("info", ("table: 0x%lx", (long) table));
> - close_temporary(table, 1, 0);
> + sql_driver_thd->temporary_tables.close_tables(true);
Hmm, really?
see that comment above in the old code:
/* Reset in_use as the table may have been created by another thd */
and now you assume that all temptables belong to sql_driver_thd?
but sql_driver_thd comment says
THD for the main sql thread, the one that starts threads to process
slave requests. If there is only one thread, then this THD is also
used for SQL processing.
and indeed in parallel replication there can be many sql threads,
all with their own temporary tables.
> }
> - save_temporary_tables= 0;
> - slave_open_temp_tables= 0;
> + save_temp_table_shares= 0;
> +
> DBUG_VOID_RETURN;
> }
>
> @@ -1753,6 +1744,10 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
> }
> m_table_map.clear_tables();
> slave_close_thread_tables(thd);
> +
> + /* Cleanup temporary tables. */
> + thd->temporary_tables.cleanup();
> +
why this was not needed before?
> if (error)
> {
> thd->mdl_context.release_transactional_locks();
> diff --git a/sql/sp_head.cc b/sql/sp_head.cc
> index cf0b8e8..45346cd 100644
> --- a/sql/sp_head.cc
> +++ b/sql/sp_head.cc
> @@ -2929,6 +2929,7 @@ void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads)
> thd->get_stmt_da()->set_overwrite_status(false);
> }
> thd_proc_info(thd, "closing tables");
> + /* main.temp_table: check this */
what does that mean?
> close_thread_tables(thd);
> thd_proc_info(thd, 0);
>
> @@ -2970,8 +2971,7 @@ void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads)
> open_tables stage.
> */
> if (!res || !thd->is_error() ||
> - (thd->get_stmt_da()->sql_errno() != ER_CANT_REOPEN_TABLE &&
can ER_CANT_REOPEN_TABLE still happen somewhere?
> - thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
> + (thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
> thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
> thd->get_stmt_da()->sql_errno() != ER_UPDATE_TABLE_USED))
> thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
> diff --git a/sql/sql_class.cc b/sql/sql_class.cc
> index 6656a84..e73ada2 100644
> --- a/sql/sql_class.cc
> +++ b/sql/sql_class.cc
> @@ -4230,7 +4234,8 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
> Before we will throw away current open tables state we want
> to be sure that it was properly cleaned up.
> */
> - DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
> + DBUG_ASSERT(open_tables == 0 &&
> + temporary_tables.is_empty() &&
temporary_tables.is_empty() is not the same as temporary_tables == 0,
because it takes into account thd->rgi_slave
> derived_tables == 0 &&
> lock == 0 &&
> locked_tables_mode == LTM_NONE &&
> diff --git a/sql/sql_class.h b/sql/sql_class.h
> index ae240ae..02b5739 100644
> --- a/sql/sql_class.h
> +++ b/sql/sql_class.h
> @@ -799,6 +800,13 @@ enum killed_type
> volatile int64 local_memory_used;
> /* Memory allocated for global usage */
> volatile int64 global_memory_used;
> +
> + /* Open temporary tables. */
> + ulong open_temporary_tables;
this one doesn't seem to be used anywhere
> + /* Number of temporary table definitions opened by THD. */
> + ulong opened_temporary_table_definitions;
> + /* Number of temporary tables opened by THD. */
> + ulong opened_temporary_tables;
> } STATUS_VAR;
>
> /*
> @@ -3506,13 +3525,13 @@ class THD :public Statement,
> */
> DBUG_PRINT("debug",
> ("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s",
> - YESNO(temporary_tables), YESNO(in_sub_stmt),
> + YESNO(temporary_tables.is_empty()), YESNO(in_sub_stmt),
> show_system_thread(system_thread)));
> if (in_sub_stmt == 0)
> {
> if (wsrep_binlog_format() == BINLOG_FORMAT_ROW)
> set_current_stmt_binlog_format_row();
> - else if (temporary_tables == NULL)
> + else if (temporary_tables.is_empty())
temporary_tables.is_empty() is not the same as temporary_tables == NULL
> set_current_stmt_binlog_format_stmt();
> }
> DBUG_VOID_RETURN;
> diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
> index e8ade81..52b14e1 100644
> --- a/sql/sql_handler.cc
> +++ b/sql/sql_handler.cc
> @@ -180,7 +180,7 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
> table->file->ha_index_or_rnd_end();
> table->query_id= thd->query_id;
> table->open_by_handler= 0;
> - mark_tmp_table_for_reuse(table);
> + //mark_tmp_table_for_reuse(table);
why?
> }
> my_free(handler->lock);
> handler->init();
> diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
> index aa2cae5..c594ef1 100644
> --- a/sql/sql_insert.cc
> +++ b/sql/sql_insert.cc
> @@ -4051,16 +4051,16 @@ static TABLE *create_table_from_items(THD *thd,
> }
> else
> {
> - if (open_temporary_table(thd, create_table))
> - {
> - /*
> - This shouldn't happen as creation of temporary table should make
> - it preparable for open. Anyway we can't drop temporary table if
> - we are unable to find it.
> - */
> - DBUG_ASSERT(0);
> - }
> - DBUG_ASSERT(create_table->table == create_info->table);
> + /*
> + TODO: Explain why we do not need to call THD::wait_for_prior_commit()
> + here?
> + */
So?
> + TABLE *tab= create_info->table;
> + tab->query_id= thd->query_id;
> + thd->thread_specific_used= true;
> + create_table->updatable= true;
> + create_table->table= tab;
> + tab->init(thd, create_table);
hmm, you don't create a table here. how comes?
> }
> }
> else
> diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
> index 52dcc7e..64227d4 100644
> --- a/sql/sql_parse.cc
> +++ b/sql/sql_parse.cc
> @@ -23,7 +23,7 @@
> // set_handler_table_locks,
> // lock_global_read_lock,
> // make_global_read_lock_block_commit
> -#include "sql_base.h" // find_temporary_table
> +#include "sql_base.h" // Temporary_tables::find_table
really? it's in temporary_tables.h, not in sql_base.h anymore
> #include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_*
> #include "sql_show.h" // mysqld_list_*, mysqld_show_*,
> // calc_sum_of_all_status
> @@ -5730,6 +5731,8 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
>
> /* Free tables */
> close_thread_tables(thd);
> + /* Do not close HANDLER tables. */
> + thd->temporary_tables.close_tables(false);
why this wasn't here before?
> #ifdef WITH_WSREP
> thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
> #endif /* WITH_WSREP */
> diff --git a/sql/sql_table.cc b/sql/sql_table.cc
> index 40032c3..f568432 100644
> --- a/sql/sql_table.cc
> +++ b/sql/sql_table.cc
> @@ -4608,13 +4602,14 @@ handler *mysql_create_frm_image(THD *thd,
> which was created.
> @param[out] key_count Number of keys in table which was created.
>
> - If one creates a temporary table, this is automatically opened
> -
> Note that this function assumes that caller already have taken
> exclusive metadata lock on table being created or used some other
> way to ensure that concurrent operations won't intervene.
> mysql_create_table() is a wrapper that can be used for this.
>
> + In case of temporary tables, the TABLE_SHARE is added to thd's
> + temporary_tables_share list.
> +
hmm, so you've decided not to open the table automatically...
> @retval 0 OK
> @retval 1 error
> @retval -1 table existed but IF EXISTS was used
> @@ -4820,17 +4813,12 @@ int create_table_impl(THD *thd,
> create_info->table= 0;
> if (!frm_only && create_info->tmp_table())
> {
> - /*
> - Open a table (skipping table cache) and add it into
> - THD::temporary_tables list.
> - */
> -
> - TABLE *table= open_table_uncached(thd, create_info->db_type, frm, path,
> - db, table_name, true, true);
> + TABLE *table=
> + thd->temporary_tables.create_and_use_table(create_info->db_type, frm,
> + path, db, table_name, true);
>
> if (!table)
> {
> - (void) rm_temporary_table(create_info->db_type, path);
why not? because create_and_use_table() is atomic?
> goto err;
> }
>
> diff --git a/sql/table.h b/sql/table.h
> index ab39603..8b7c665 100644
> --- a/sql/table.h
> +++ b/sql/table.h
> @@ -600,6 +601,7 @@ struct TABLE_STATISTICS_CB
> struct TABLE_SHARE
> {
> TABLE_SHARE() {} /* Remove gcc warning */
> + TABLE_SHARE *next, *prev;
I'd rather not add two pointers to TABLE_SHARE just for the sake
of temporary tables. Use something else, please.
May be List<> will work for you?
Or, like
struct TMP_TABLE_SHARE: TABLE_SHARE
{
TMP_TABLE_SHARE *next, *prev;
}
>
> /** Category of this table. */
> TABLE_CATEGORY table_category;
> diff --git a/sql/table_cache.cc b/sql/table_cache.cc
> index 2dd368a..b307841 100644
> --- a/sql/table_cache.cc
> +++ b/sql/table_cache.cc
> @@ -589,6 +589,7 @@ void tdc_unlock_share(TDC_element *element)
> key Table cache key
> key_length Length of key
> flags operation: what to open table or view
> + out_table TABLE for the requested table
please, put unrelated comment fixes in a separate commit
(it's very easy to do with git citool)
>
> IMPLEMENTATION
> Get a table definition from the table definition cache.
> diff --git a/sql/table_cache.h b/sql/table_cache.h
> index 2c5b0fc..ac36269 100644
> --- a/sql/table_cache.h
> +++ b/sql/table_cache.h
> @@ -1,3 +1,5 @@
> +#ifndef TABLE_CACHE_H_INCLUDED
> +#define TABLE_CACHE_H_INCLUDED
and this one too. commit all unrelated changes separately
> /* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
> Copyright (c) 2010, 2011 Monty Program Ab
> Copyright (C) 2013 Sergey Vojtovich and MariaDB Foundation
> diff --git a/storage/myisam/mi_close.c b/storage/myisam/mi_close.c
> index f0a82bc..23e01ef 100644
> --- a/storage/myisam/mi_close.c
> +++ b/storage/myisam/mi_close.c
> @@ -67,7 +67,7 @@ int mi_close(register MI_INFO *info)
> if (share->kfile >= 0 &&
> flush_key_blocks(share->key_cache, share->kfile,
> &share->dirty_part_map,
> - ((share->temporary || share->deleting) ?
> + ((share->deleting) ?
Why? in all your myisam/aria changes (this one and others)
you've enabled updating of on-disk data for temporary tables.
like, flush keycache blocks, update file header, etc. Why should
it be done persistently on disk? Temporary tables are not persistent,
if mariadb crashes nobody will care what old temporary table files will
contain. It should be enough to have all up-to-date information in memory.
> FLUSH_IGNORE_CHANGED :
> FLUSH_RELEASE)))
> error=my_errno;
> diff --git a/sql/temporary_tables.h b/sql/temporary_tables.h
> new file mode 100644
> index 0000000..c345bea
> --- /dev/null
> +++ b/sql/temporary_tables.h
> @@ -0,0 +1,112 @@
> +#ifndef TEMPORARY_TABLES_H
> +#define TEMPORARY_TABLES_H
> +/*
> + Copyright (c) 2016 MariaDB Corporation
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> +*/
> +
> +#define TMP_TABLE_KEY_EXTRA 8
> +
> +class Temporary_tables {
> +public:
> + Temporary_tables() : m_thd(0), m_table_shares(0), m_opened_tables(0) {}
> + bool init(THD *thd);
> + bool is_empty();
> + bool reset();
> + bool cleanup();
> + TABLE_SHARE *create_table(handlerton *hton, LEX_CUSTRING *frm,
> + const char *path, const char *db,
> + const char *table_name);
> + TABLE_SHARE *find_table(const TABLE_LIST *tl);
> + TABLE_SHARE *find_table_reduced_key_length(const char *key, uint key_length);
> + TABLE_SHARE *find_table(const char *db, const char *table_name);
> + TABLE *find_open_table(const char *db, const char *table_name);
> + TABLE *create_and_use_table(handlerton *hton, LEX_CUSTRING *frm,
> + const char *path, const char *db,
> + const char *table_name, bool open_in_engine);
> + TABLE *open_table(TABLE_SHARE *share, const char *alias, bool open_in_engine);
> + bool open_table(TABLE_LIST *tl);
> + bool open_tables(TABLE_LIST *tl);
> + bool close_tables(bool all);
> + bool rename_table(TABLE *table, const char *db, const char *table_name);
> + bool drop_table(TABLE *table, bool *is_trans, bool delete_in_engine);
> + void mark_tables_as_free_for_reuse();
> +
> +private:
> + uint create_table_def_key(char *key,
> + const char *db,
> + const char *table_name);
> + TABLE_SHARE *find_table(const char *key, uint key_length);
> + TABLE *find_open_table(const char *key, uint key_length);
> + TABLE *open_table(const char *db, const char *table_name);
> + bool close_table(TABLE *table);
> + bool rm_temporary_table(handlerton *hton, const char *path);
> + bool wait_for_prior_commit();
> + bool write_query_log_events();
> + void lock_tables();
> + void unlock_tables();
> +
> + /*
> + Return true if the table was created explicitly.
> + */
> + bool is_user_table(TABLE_SHARE *share)
> + {
> + const char *name= share->table_name.str;
> + return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
> + }
Is that right? I can do CREATE TEMPORARY TABLE `#sql-foo` (a int).
the correct test needs to look at, for example, table->s->path
or some flag, may be...
Why wouldn't it use share->tmp_table?
> +
> + /* List operations */
> + template <class T>
> + void link(T **list, T *element)
> + {
> + element->next= *list;
> + if (element->next)
> + element->next->prev= element;
> + *list= element;
> + (*list)->prev= 0;
> + }
> +
> + template <class T>
> + void unlink(T **list, T *element)
> + {
> + if (element->prev)
> + {
> + element->prev->next= element->next;
> + if (element->prev->next)
> + element->next->prev= element->prev;
> + }
> + else
> + {
> + DBUG_ASSERT(element == *list);
> +
> + *list= element->next;
> + if (*list)
> + element->next->prev= 0;
> + }
> + }
> +
> + uint tmpkeyval(TABLE_SHARE *share)
> + {
> + return uint4korr(share->table_cache_key.str +
> + share->table_cache_key.length - 4);
> + }
> +
> +private:
> + THD *m_thd;
Hmm, is that necessary? Temporary_tables class is instantiated in only one
place - inside the THD. So Temporary_tables always belongs to its thd, and
storing a point to the parent THD in the Temporary_tables you basically add
another void* to already big THD class, while thid pointer stores no useful
information at all.
A hackish way to fix is is to replace m_thd with
(((char*)this) - offsetof(THD, temporary_tables))
I'm not suggesting this hack, it is just to prove that m_thd is redundant.
I hope you'll find a nicer and safer solution :)
By the way, any increase in the THD size will most probably cause
a test failure on labrador, you'll need to increase the
DEFAULT_THREAD_STACK to fix it. Please check that - even if you remove
m_thd there is still a m_table_shares pointer that increases THD size.
> + TABLE_SHARE *m_table_shares;
> + TABLE *m_opened_tables;
> +};
> +
> +#endif /* TEMPORARY_TABLES_H */
> diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
> new file mode 100644
> index 0000000..5d8bce6
> --- /dev/null
> +++ b/sql/temporary_tables.cc
> @@ -0,0 +1,1265 @@
> +/*
> + Copyright (c) 2016 MariaDB Corporation
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
> +*/
> +
> +#include "sql_acl.h" /* TMP_TABLE_ACLS */
> +#include "sql_base.h" /* free_io_cache,
> + get_table_def_key,
> + tdc_create_key */
> +#include "lock.h" /* mysql_lock_remove */
> +#include "log_event.h" /* Query_log_event */
> +#include "sql_show.h" /* append_identifier */
> +#include "sql_handler.h" /* mysql_ha_rm_temporary_tables */
> +#include "temporary_tables.h" /* Temporary_tables */
> +#include "rpl_rli.h" /* rpl_group_info */
> +
> +
> +/*
> + Initialize the Temporary_tables object. Currently it always returns
> + false (success).
> +
> + @param thd [IN] Thread handle
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::init(THD *thd)
> +{
> + DBUG_ENTER("Temporary_tables::init");
> + this->m_thd= thd;
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Check whether temporary tables exist. The decision is made based on the
> + existence of TABLE_SHAREs.
> +
> + @return false Temporary tables exist
> + true No temporary table exist
> +*/
> +bool Temporary_tables::is_empty()
> +{
> + bool result;
> +
> + if (!m_thd)
> + {
> + return true;
> + }
> +
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> +
> + if (rgi_slave)
> + {
> + result= (rgi_slave->rli->save_temp_table_shares == NULL) ? true : false;
> + }
> + else
> + {
> + result= (m_table_shares == NULL) ? true : false;
> + }
> +
> + return result;
> +}
> +
> +
> +/*
> + Reset the Temporary_tables object. Currently, it always returns
> + false (success).
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::reset()
> +{
> + DBUG_ENTER("Temporary_tables::reset");
> + m_table_shares= 0;
> + m_opened_tables= 0;
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Cleanup the session's temporary tables by closing all open temporary tables
> + as well as freeing the respective TABLE_SHAREs.
> + It also writes "DROP TEMPORARY TABLE .." query log events to the binary log.
> +
> + Currently, it always returns false (success).
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::cleanup()
> +{
> + DBUG_ENTER("Temporary_tables::cleanup");
> +
> + TABLE_SHARE *share;
> + TABLE_SHARE *next;
> + lock_tables();
old code used to have here:
if (!thd->temporary_tables)
DBUG_RETURN(FALSE);
DBUG_ASSERT(!thd->rgi_slave);
you don't do either, instead you do lock_tables() (which is only needed
if thd->rgi_slave != NULL). How comes?
> +
> + /*
> + Ensure we don't have open HANDLERs for tables we are about to close.
> + This is necessary when close_temporary_tables() is called as part
s/close_temporary_tables/Temporary_tables::cleanup/ in the comment
> + of execution of BINLOG statement (e.g. for format description event).
> + */
> + mysql_ha_rm_temporary_tables(m_thd);
> +
> + // Close all open temporary tables.
> + close_tables(true);
> +
> + // Write DROP TEMPORARY TABLE query log events to binary log.
> + if (!m_thd->rgi_slave)
another if (!m_thd->rgi_slave) while the old code had DBUG_ASSERT instead
> + {
> + write_query_log_events();
> + }
> +
> + // Free all TABLE_SHARES.
> + share= m_table_shares;
> +
> + while (share) {
> + next= share->next;
> + rm_temporary_table(share->db_type(), share->path.str);
> +
> + /* Delete the share from table share list */
> + unlink<TABLE_SHARE>(&m_table_shares, share);
> +
> + free_table_share(share);
> + my_free(share);
> +
> + /* Decrement Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_decrement32(&slave_open_temp_table_definitions);
> + }
why did you put write_query_log_events() in a separate function?
now you need to iterate the list of tables twice.
> +
> + share= next;
> + }
> +
> + reset();
> +
> + unlock_tables();
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Create a temporary table.
> +
> + @param hton [in] Handlerton
> + @param frm [in] Binary frm image
> + @param path [in] File path (without extension)
> + @param db [in] Schema name
> + @param table_name [in] Table name
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE_SHARE *Temporary_tables::create_table(handlerton *hton, LEX_CUSTRING *frm,
> + const char *path, const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::create_table");
> +
> + TABLE_SHARE *share= NULL;
> + char key_cache[MAX_DBKEY_LENGTH], *saved_key_cache, *tmp_path;
> + uint key_length;
> + int res;
> +
> + lock_tables();
> +
> + if (wait_for_prior_commit())
> + {
> + goto end; /* Failure */
> + }
> +
> + /* Create the table definition key for the temporary table. */
> + key_length= create_table_def_key(key_cache, db, table_name);
> +
> + if (!(share= (TABLE_SHARE *) my_malloc(sizeof(TABLE_SHARE) + strlen(path) +
> + 1 + key_length, MYF(MY_WME))))
> + {
> + goto end; /* Out of memory */
> + }
> +
> + tmp_path= (char *)(share + 1);
> + saved_key_cache= strmov(tmp_path, path) + 1;
> + memcpy(saved_key_cache, key_cache, key_length);
> +
> + init_tmp_table_share(m_thd, share, saved_key_cache, key_length,
> + strend(saved_key_cache) + 1, tmp_path);
> +
> + share->db_plugin= ha_lock_engine(m_thd, hton);
> +
> + /*
> + Prefer using frm image over file. The image might not be available in
> + ALTER TABLE, when the discovering engine took over the ownership (see
> + TABLE::read_frm_image).
> + */
> + res= (frm->str)
> + ? share->init_from_binary_frm_image(m_thd, false, frm->str, frm->length)
> + : open_table_def(m_thd, share, GTS_TABLE | GTS_USE_DISCOVERY);
> +
> + if (res)
> + {
> + /*
> + No need to lock share->mutex as this is not needed for temporary tables.
> + */
> + free_table_share(share);
> + my_free(share);
> + share= NULL;
> + goto end;
> + }
> +
> + share->m_psi= PSI_CALL_get_table_share(true, share);
> +
> + /* Add share to the head of table share list. */
> + link<TABLE_SHARE>(&m_table_shares, share);
> +
> + /* Increment Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_increment32(&slave_open_temp_table_definitions);
> + }
> +
> +end:
> + unlock_tables();
> +
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Lookup the TABLE_SHARE using the given db/table_name.The server_id and
> + pseudo_thread_id used to generate table definition key is taken from
> + m_thd (see create_table_def_key()). Return NULL is none found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE_SHARE *Temporary_tables::find_table(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> +
> + TABLE_SHARE *share;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> +
> + key_length= create_table_def_key(key, db, table_name);
> + share= find_table(key, key_length);
> +
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Lookup TABLE_SHARE using the specified TABLE_LIST element. Return NULL is none
> + found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE_SHARE *Temporary_tables::find_table(const TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> +
> + TABLE_SHARE *share;
> + const char *tmp_key;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> +
> + key_length= get_table_def_key(tl, &tmp_key);
> + memcpy(key, tmp_key, key_length);
> + int4store(key + key_length, m_thd->variables.server_id);
> + int4store(key + key_length + 4, m_thd->variables.pseudo_thread_id);
> + key_length += TMP_TABLE_KEY_EXTRA;
> +
> + share= find_table(key, key_length);
> +
> + DBUG_RETURN(share);
> +}
> +
> +
> +/*
> + Lookup TABLE_SHARE using the specified table definition key. Return NULL is
> + none found.
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE_SHARE *Temporary_tables::find_table(const char *key,
> + uint key_length)
> +{
> + DBUG_ENTER("Temporary_tables::find_table");
> +
> + TABLE_SHARE *share;
> + TABLE_SHARE *result= NULL;
> +
> + lock_tables();
> +
> + for (share= m_table_shares; share; share= share->next)
> + {
> + if (share->table_cache_key.length == key_length &&
> + !(memcmp(share->table_cache_key.str, key, key_length)))
> + {
> + result= share;
> + break;
> + }
> + }
> +
> + unlock_tables();
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Lookup TABLE_SHARE based on the specified key. This key, however, is not
> + the usual key used for temporary tables. It does not contain server_id &
> + pseudo_thread_id. This function is essentially used use to check whether
> + there is any temporary table which _shadows_ a base table.
> + (see: Query_cache::send_result_to_client())
> +
> + @return Success A pointer to table share object
> + Failure NULL
> +*/
> +TABLE_SHARE *Temporary_tables::find_table_reduced_key_length(const char *key,
> + uint key_length)
> +{
> + DBUG_ENTER("Temporary_tables::find_table_reduced_key_length");
> +
> + TABLE_SHARE *share;
> + TABLE_SHARE *result= NULL;
> +
> + lock_tables();
> +
> + for (share= m_table_shares; share; share= share->next)
> + {
> + if ((share->table_cache_key.length - TMP_TABLE_KEY_EXTRA) == key_length &&
> + !(memcmp(share->table_cache_key.str, key, key_length)))
> + {
> + result= share;
> + break;
> + }
> + }
> +
> + unlock_tables();
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Lookup the list of opened temporary tables using the specified
> + db/table_name. Return NULL is none found.
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_open_table(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::find_open_table");
> +
> + TABLE *table;
> + char key[MAX_DBKEY_LENGTH];
> + uint key_length;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL); /* Failure */
> + }
> +
> + key_length= create_table_def_key(key, db, table_name);
> +
> + table= find_open_table(key, key_length);
> +
> + DBUG_RETURN(table);
> +}
> +
> +
> +/*
> + Lookup the list of opened temporary tables using the specified
> + key. Return NULL is none found.
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::find_open_table(const char *key,
> + uint key_length)
> +{
> + DBUG_ENTER("Temporary_tables::find_open_table");
> +
> + TABLE *table, *result= NULL;
> +
> + for (table= m_opened_tables; table; table= table->next)
> + {
> + if (table->s->table_cache_key.length == key_length &&
> + !(memcmp(table->s->table_cache_key.str, key, key_length)))
> + {
> + result= table;
> + break;
> + }
> + }
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Create a temporary table, open it and return the TABLE handle.
> +
> + @param hton [in] Handlerton
> + @param frm [in] Binary frm image
> + @param path [in] File path (without extension)
> + @param db [in] Schema name
> + @param table_name [in] Table name
> + @param open_in_engine [in] Whether open table in SE
> +
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::create_and_use_table(handlerton *hton,
better create_and_open()
> + LEX_CUSTRING *frm,
> + const char *path,
> + const char *db,
> + const char *table_name,
> + bool open_in_engine)
> +{
> + DBUG_ENTER("Temporary_tables::create_and_use_table");
> +
> + TABLE_SHARE *share;
> + TABLE *table;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL); /* Failure */
> + }
> +
> + if (!(share= create_table(hton, frm, path, db, table_name)))
> + {
> + DBUG_RETURN(NULL);
> + }
> +
> + if ((table= open_table(share, table_name, open_in_engine)))
> + {
> + DBUG_RETURN(table);
> + }
> +
> + DBUG_RETURN(NULL);
> +}
> +
> +
> +/*
> + Open a table from the specified TABLE_SHARE with the given alias.
> +
> + @param share [in] Table share
> + @param alias [in] Table alias
> + @param open_in_engine [in] Whether open table in SE
> +
> + @return Success A pointer to table object
> + Failure NULL
> +*/
> +TABLE *Temporary_tables::open_table(TABLE_SHARE *share,
> + const char *alias,
> + bool open_in_engine)
> +{
> + DBUG_ENTER("Temporary_tables::open_table");
> +
> + TABLE *table;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL); /* Failure */
> + }
> +
> + if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
> + {
> + DBUG_RETURN(NULL); /* Out of memory */
> + }
> +
> + if (open_table_from_share(m_thd, share, alias,
> + (open_in_engine) ?
> + (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
> + HA_GET_INDEX) : 0,
> + (uint) (READ_KEYINFO | COMPUTE_TYPES |
> + EXTRA_RECORD),
> + ha_open_options,
> + table,
> + open_in_engine ? false : true))
> + {
> + my_free(table);
> + DBUG_RETURN(NULL);
> + }
> +
> + table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
> + table->grant.privilege= TMP_TABLE_ACLS;
> + share->tmp_table= (table->file->has_transactions() ?
> + TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
> +
> + table->pos_in_table_list= 0;
> + table->query_id= m_thd->query_id;
> +
> + lock_tables();
> +
> + /* Add table to the head of table list. */
> + link<TABLE>(&m_opened_tables, table);
> +
> + /* Increment Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_increment32(&slave_open_temp_tables);
> + }
> +
> + unlock_tables();
> +
> + DBUG_PRINT("tmptable", ("Opened table: '%s'.'%s' 0x%lx", table->s->db.str,
> + table->s->table_name.str, (long) table));
> + DBUG_RETURN(table);
> +}
> +
> +
> +/*
> + Lookup the table share list and open a table based on db/table_name.
> + Return NULL if none found.
> +
> + @param db [in] Schema name
> + @param table_name [in] Table name
> +
> + @return Success A pointer to table object
> + Failure 0
> +*/
> +TABLE *Temporary_tables::open_table(const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::open_table");
> +
> + TABLE *result= 0;
> + TABLE_SHARE *share;
> +
> + if ((share= find_table(db, table_name)))
> + {
> + result= open_table(share, table_name, true);
> + }
why this open_table(db, table_name) is doing so much less than
open_table(table_list) below?
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Lookup the table share list and open a table based on the specified
> + TABLE_LIST element. Return false if the table was opened successfully.
> +
> + @param tl [in] TABLE_LIST
> +
> + @return false Success
> + true Failure
> +*/
> +bool Temporary_tables::open_table(TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::open_table");
> +
> + TABLE *table= NULL;
> + TABLE_SHARE *share;
> +
> + /*
> + Code in open_table() assumes that TABLE_LIST::table can be non-zero only
> + for pre-opened temporary tables.
> + */
> + DBUG_ASSERT(tl->table == NULL);
> +
> + /*
> + This function should not be called for cases when derived or I_S
> + tables can be met since table list elements for such tables can
> + have invalid db or table name.
> + Instead Temporary_tables::open_tables() should be used.
> + */
> + DBUG_ASSERT(!tl->derived && !tl->schema_table);
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(true); /* Failure */
> + }
why do you have it here? The old open_temporary_table()
only had it below, you have it twice.
> +
> + lock_tables();
> +
> + if (tl->open_type == OT_BASE_ONLY || m_table_shares == NULL)
> + {
> + DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
> + unlock_tables();
> + DBUG_RETURN(false);
> + }
> +
> + unlock_tables();
> +
> + if ((share= find_table(tl)) &&
> + (table= open_table(share, tl->get_table_name(), true)))
Why would you open temporary tables all the time?
old code didn't do that. temporary tables were automatically opened
when created and automatically dropped when closed.
I understand that you *might* need to open a second instance of a temporary
table (but perhaps this can be avoided too), but why would close them again?
just keep them open for reuse.
In that case your open_table will just pick one unused TABLE from the
m_opened_tables list
> + {
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(true); /* Failure */
> + }
> +
> +#ifdef WITH_PARTITION_STORAGE_ENGINE
> + if (tl->partition_names)
> + {
> + /* Partitioned temporary tables is not supported. */
> + DBUG_ASSERT(!table->part_info);
> + my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
> + DBUG_RETURN(true);
> + }
> +#endif
> +
> + table->query_id= m_thd->query_id;
> + m_thd->thread_specific_used= true;
> + /* It is neither a derived table nor non-updatable view. */
> + tl->updatable= true;
> + tl->table= table;
> + table->init(m_thd, tl);
> + DBUG_RETURN(false);
> + }
> +
> + if (!table &&
> + tl->open_type == OT_TEMPORARY_ONLY &&
> + tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
> + {
> + my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name);
> + DBUG_RETURN(true);
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Pre-open temporary tables corresponding to table list elements.
> +
> + @note One should finalize process of opening temporary tables
> + by calling open_tables(). This function is responsible
> + for table version checking and handling of merge tables.
> +
> + @param tl [in] TABLE_LIST
> +
> + @return false On success. If a temporary table exists
> + for the given element, tl->table is set.
> + true On error. my_error() has been called.
> +*/
> +bool Temporary_tables::open_tables(TABLE_LIST *tl)
> +{
> + DBUG_ENTER("Temporary_tables::open_tables");
> +
> + TABLE_LIST *first_not_own;
> +
> + if (wait_for_prior_commit())
> + {
> + DBUG_RETURN(NULL); /* Failure */
> + }
why do you wait_for_prior_commit here? old open_temporary_tables
didn't do that, because every individual open_table() below
waits for prior commit too.
> +
> + first_not_own= m_thd->lex->first_not_own_table();
> +
> + for (TABLE_LIST *table= tl;
> + table && table != first_not_own;
> + table= table->next_global)
> + {
> + if (table->derived || table->schema_table)
> + {
> + /*
> + Derived and I_S tables will be handled by a later call to open_tables().
> + */
> + continue;
> + }
> +
> + if ((m_thd->temporary_tables.open_table(table)))
> + {
> + DBUG_RETURN(true);
> + }
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Close a temporary table.
> +
> + @param table [in] Table handle
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::close_table(TABLE *table)
> +{
> + DBUG_ENTER("Temporary_tables::close_table");
> + DBUG_PRINT("tmptable", ("closing table: '%s'.'%s' 0x%lx alias: '%s'",
> + table->s->db.str, table->s->table_name.str,
> + (long) table, table->alias.c_ptr()));
> +
> + /* Delete the table from table list */
> + unlink<TABLE>(&m_opened_tables, table);
> +
> + free_io_cache(table);
> + closefrm(table, false);
> + my_free(table);
> +
> + /* Decrement Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_decrement32(&slave_open_temp_tables);
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +/*
> + Close all the opened table. When 'all' is set to false, tables opened by
> + handlers and ones with query_id different than that of m_thd will not be
> + be closed. Currently, false (success) is always returned.
> +
> + @param all [in] Whether to close all tables?
> +
> + @return false Success
> + true Failure
> +*/
> +bool Temporary_tables::close_tables(bool all)
> +{
> + TABLE *table;
> + TABLE *next;
> +
> + table= m_opened_tables;
> +
> + while(table) {
> + next= table->next;
> +
> + if (all || ((table->query_id == m_thd->query_id) &&
> + !(table->open_by_handler)))
> + {
> + mysql_lock_remove(m_thd, m_thd->lock, table);
> + close_table(table);
> + }
> +
> + table= next;
> + }
> + return false;
> +}
> +
> +
> +/*
> + Write query log events with "DROP TEMPORARY TABLES .." for each pseudo
> + thread to the binary log.
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::write_query_log_events()
> +{
> + DBUG_ENTER("Temporary_tables::write_query_log_events");
> + DBUG_ASSERT(!m_thd->rgi_slave);
> +
> + TABLE_SHARE *share;
> + TABLE_SHARE *next;
> + TABLE_SHARE *prev_share;
> + // Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE.
> + bool was_quote_show= true;
> + bool error= 0;
> + bool found_user_tables= false;
> + // Better add "IF EXISTS" in case a RESET MASTER has been done.
> + const char stub[]= "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ";
> + char buf[FN_REFLEN];
> +
> + /*
> + Return in case there are no temporary tables or binary logging is
> + disabled.
> + */
> + if (!(m_table_shares && mysql_bin_log.is_open()))
> + {
> + DBUG_RETURN(false);
> + }
> +
> + String s_query(buf, sizeof(buf), system_charset_info);
> + s_query.copy(stub, sizeof(stub) - 1, system_charset_info);
> +
> + /*
> + Insertion sort of temporary tables by pseudo_thread_id to build ordered
> + list of sublists of equal pseudo_thread_id.
> + */
why does it have to be sorted?
(I know that the old code also sorted the list, but I still cannot
understand why)
> +
> + for (prev_share= m_table_shares, share= prev_share->next;
> + share;
> + prev_share= share, share= share->next)
> + {
> + TABLE_SHARE *prev_sorted; /* Same as for prev_share */
> + TABLE_SHARE *sorted;
> +
> + if (is_user_table(share))
> + {
> + if (!found_user_tables)
> + found_user_tables= true;
> +
> + for (prev_sorted= NULL, sorted= m_table_shares;
> + sorted != share;
> + prev_sorted= sorted, sorted= sorted->next)
> + {
> + if (!is_user_table(sorted) ||
> + tmpkeyval(sorted) > tmpkeyval(share))
> + {
> + /*
> + Move into the sorted part of the list from the unsorted.
> + */
> + prev_share->next= share->next;
> + share->next= sorted;
> + if (prev_sorted)
> + {
> + prev_sorted->next= share;
> + }
> + else
> + {
> + m_table_shares= share;
> + }
> + share= prev_share;
> + break;
> + }
> + }
> + }
> + }
> +
> + /*
> + We always quote db, table names though it is slight overkill.
> + */
it's not an overkill, it's really needed
> + if (found_user_tables &&
> + !(was_quote_show= MY_TEST(m_thd->variables.option_bits &
> + OPTION_QUOTE_SHOW_CREATE)))
> + {
> + m_thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
> + }
> +
> + /*
> + Scan sorted temporary tables to generate sequence of DROP.
> + */
> + for (share= m_table_shares; share; share= next)
> + {
> + if (is_user_table(share))
> + {
> + bool save_thread_specific_used= m_thd->thread_specific_used;
> + my_thread_id save_pseudo_thread_id= m_thd->variables.pseudo_thread_id;
> + char db_buf[FN_REFLEN];
> + String db(db_buf, sizeof(db_buf), system_charset_info);
> +
> + /*
> + Set pseudo_thread_id to be that of the processed table.
> + */
> + m_thd->variables.pseudo_thread_id= tmpkeyval(share);
> +
> + db.copy(share->db.str, share->db.length, system_charset_info);
> + /*
> + Reset s_query() if changed by previous loop.
> + */
> + s_query.length(sizeof(stub) - 1);
> +
> + /*
> + Loop forward through all tables that belong to a common database
> + within the sublist of common pseudo_thread_id to create single
> + DROP query.
> + */
> + for (;
> + share && is_user_table(share) &&
> + tmpkeyval(share) == m_thd->variables.pseudo_thread_id &&
> + share->db.length == db.length() &&
> + memcmp(share->db.str, db.ptr(), db.length()) == 0;
> + share= next)
> + {
> + /*
> + We are going to add ` around the table names and possible more
> + due to special characters.
> + */
> + append_identifier(m_thd, &s_query, share->table_name.str,
> + strlen(share->table_name.str));
you don't need strlen(share->table_name.str) because you can write
share->table_name.length instead :)
(may be this applies to other strlen's too)
> + s_query.append(',');
> + next= share->next;
> + }
> +
> + m_thd->clear_error();
> + CHARSET_INFO *cs_save= m_thd->variables.character_set_client;
> + m_thd->variables.character_set_client= system_charset_info;
> + m_thd->thread_specific_used= true;
> +
> + Query_log_event qinfo(m_thd, s_query.ptr(),
> + s_query.length() - 1 /* to remove trailing ',' */,
> + false, true, false, 0);
> + qinfo.db= db.ptr();
> + qinfo.db_len= db.length();
> + m_thd->variables.character_set_client= cs_save;
> +
> + m_thd->get_stmt_da()->set_overwrite_status(true);
> + if ((error= (mysql_bin_log.write(&qinfo) || error)))
> + {
> + /*
> + If we're here following THD::cleanup, thence the connection
> + has been closed already. So lets print a message to the
> + error log instead of pushing yet another error into the
> + stmt_da.
> +
> + Also, we keep the error flag so that we propagate the error
> + up in the stack. This way, if we're the SQL thread we notice
> + that close_temporary_tables failed. (Actually, the SQL
> + thread only calls close_temporary_tables while applying old
s/close_temporary_tables/Temporary_tables::cleanup/ in the comment
> + Start_log_event_v3 events.)
> + */
> + sql_print_error("Failed to write the DROP statement for "
> + "temporary tables to binary log");
> + }
> +
> + m_thd->get_stmt_da()->set_overwrite_status(false);
> + m_thd->variables.pseudo_thread_id= save_pseudo_thread_id;
> + m_thd->thread_specific_used= save_thread_specific_used;
> + }
> + else
> + {
> + next= share->next;
> + }
> + }
> +
> + if (!was_quote_show)
> + {
> + /*
> + Restore option.
> + */
> + m_thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
> + }
> +
> + DBUG_RETURN(error);
> +}
> +
> +
> +/*
> + Rename a temporary table.
> +
> + @param table [in] Table handle
> + @param db [in] New schema name
> + @param table_name [in] New table name
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::rename_table(TABLE *table,
> + const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::rename_table");
> +
> + char *key;
> + uint key_length;
> + TABLE_SHARE *share= table->s;
> +
> + if (!(key= (char *) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
> + {
> + DBUG_RETURN(true);
> + }
> +
> + /*
> + Temporary tables are renamed by simply changing their table definition key.
> + */
> + key_length= create_table_def_key(key, db, table_name);
> + share->set_table_cache_key(key, key_length);
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +/*
> + Drop a temporary table.
> +
> + Try to locate the table in the list of thd->temporary_tables.
> + If the table is found:
> + - If the table is being used by some outer statement, i.e.
> + ref_count > 1, we only close the given table and return.
> + - If the table is locked with LOCK TABLES or by prelocking,
> + unlock it and remove it from the list of locked tables
> + (THD::lock). Currently only transactional temporary tables
> + are locked.
> + - Close the temporary table, remove its .FRM.
> + - Remove the table share from the list of temporary table shares.
> +
> + This function is used to drop user temporary tables, as well as
> + internal tables created in CREATE TEMPORARY TABLE ... SELECT
> + or ALTER TABLE. Even though part of the work done by this function
> + is redundant when the table is internal, as long as we
> + link both internal and user temporary tables into the same
> + temporary tables list, it's impossible to tell here whether
> + we're dealing with an internal or a user temporary table.
why is it impossible? there is is_user_table() function.
I know that it's broken, but it should be fixed - it's used elsewhere.
> +
> + @param thd [in] Thread handler
> + @param table [in] Temporary table to be deleted
> + @param is_trans [out] Is set to the type of the table:
> + transactional (e.g. innodb) as true or
> + non-transactional (e.g. myisam) as false.
fix the comment to match the function prototype, please.
don't forget to explain why do you need delete_in_engine parameter
(e.g. delete_in_engine is false in ALTER TABLE)
> +
> + @retval 0 the table was found and dropped successfully.
> + @retval -1 the table is in use by a outer query
retval is wrong
> +*/
> +
> +
> +/*
> + @return false Table was either dropped or closed in
> + case multiple open tables were found
> + referring the table share.
> + true Error
> +*/
> +bool Temporary_tables::drop_table(TABLE *table,
> + bool *is_trans,
> + bool delete_in_engine)
rename 'delete_in_engine' please, because it also includes
removing the frm file, not only "in engine".
I cannot think of a good name, sorry :(
> +{
> + DBUG_ENTER("Temporary_tables::drop_table");
> +
> + TABLE_SHARE *share;
> + handlerton *hton;
> + uint ref_count= 0;
> + bool result;
> +
> + DBUG_ASSERT(table);
> + DBUG_PRINT("tmptable", ("Dropping table: '%s'.'%s'",
> + table->s->db.str, table->s->table_name.str));
> +
old code had here
/* Table might be in use by some outer statement. */
if (table->query_id && table->query_id != thd->query_id)
{
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
DBUG_RETURN(-1);
}
the intention is not to allow a temporary table being dropped
from a trigger or a stored function, if the table is used by the
outer statement.
How do you solve this now? Apparently you do, somehow, because there is
a test case for it.
> + lock_tables();
> +
> + if (is_trans)
> + *is_trans= table->file->has_transactions();
> +
> + share= table->s;
> + hton= share->db_type();
> +
> + /*
> + Iterate over the list of open tables to find the number of tables
> + referencing this table share.
> + */
> + for (TABLE *tab= m_opened_tables; tab; tab= tab->next)
> + {
> + if (tab->s == share)
> + {
> + ref_count ++;
Strictly speaking, you can store a list of TABLE's in the TABLE_SHARE -
that is, make TABLE_SHARE have a pointer to the list of its own TABLE's.
That'd be particularly easy, if you introduce TMP_TABLE_SHARE as I've
mentioned above
But on the other hand, we don't expect many temporary tables opened
at the same time, so this list is supposed to be short, right?
Then this optimization doesn't matter practically.
> + }
> + }
> +
> + DBUG_ASSERT(ref_count > 0);
> +
> + /*
> + If LOCK TABLES list is not empty and contains this table, unlock the table
> + and remove the table from this list.
> + */
> + mysql_lock_remove(m_thd, m_thd->lock, table);
> +
> + if (close_table(table))
> + {
> + result= true;
> + goto end;
> + }
> +
> + /* There are other tables referencing this table share. */
> + if (ref_count > 1)
> + {
> + result= false;
so, it is not considered an error? why?
> + goto end;
> + }
> +
> + if (delete_in_engine)
> + {
> + rm_temporary_table(hton, share->path.str);
> + }
> +
> + /* Delete the share from table share list */
> + unlink<TABLE_SHARE>(&m_table_shares, share);
> +
> + free_table_share(share);
> + my_free(share);
> +
> + /* Decrement Slave_open_temp_table_definitions status variable count. */
> + if (m_thd->rgi_slave)
> + {
> + thread_safe_decrement32(&slave_open_temp_table_definitions);
> + }
> +
> + result= false;
> +
> +end:
> + unlock_tables();
> +
> + DBUG_RETURN(result);
> +}
> +
> +
> +/*
> + Create a table definition key.
> +
> + @param key [out] Buffer for the key to be created (must
> + be of size MAX_DBKRY_LENGTH)
> + @param db [in] Database name
> + @param table_name [in] Table name
> +
> + @return Key length.
> +
> + @note
> + The table key is create from:
> + db + \0
> + table_name + \0
> +
> + Additionally, we add the following to make each temporary table unique on
> + the slave.
> +
> + 4 bytes of master thread id
> + 4 bytes of pseudo thread id
> +*/
> +
> +uint Temporary_tables::create_table_def_key(char *key, const char *db,
> + const char *table_name)
> +{
> + DBUG_ENTER("Temporary_tables::create_table_def_key");
> +
> + uint key_length;
> +
> + key_length= tdc_create_key(key, db, table_name);
> + int4store(key + key_length, m_thd->variables.server_id);
> + int4store(key + key_length + 4, m_thd->variables.pseudo_thread_id);
> + key_length += TMP_TABLE_KEY_EXTRA;
> +
> + DBUG_RETURN(key_length);
> +}
> +
> +
> +/**
> + Delete a temporary table.
> +
> + @param base [in] Handlerton for table to be deleted.
> + @param path [in] Path to the table to be deleted (i.e. path
> + to its .frm without an extension).
> +
> + @return false Success
> + true Error
> +*/
> +bool Temporary_tables::rm_temporary_table(handlerton *base, const char *path)
> +{
> + bool error= false;
> + handler *file;
> + char frm_path[FN_REFLEN + 1];
> +
> + DBUG_ENTER("Temporary_tables::rm_temporary_table");
> +
> + strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
> + if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
> + error= true;
> +
> + file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
> + if (file && file->ha_delete_table(path))
> + {
> + error= true;
> + sql_print_warning("Could not remove temporary table: '%s', error: %d",
> + path, my_errno);
> + }
> +
> + delete file;
> + DBUG_RETURN(error);
> +}
> +
> +
> +bool Temporary_tables::wait_for_prior_commit()
> +{
> + DBUG_ENTER("Temporary_tables::wait_for_prior_commit");
> +
> + /*
> + Temporary tables are not safe for parallel replication. They were
> + designed to be visible to one thread only, so have no table locking.
> + Thus there is no protection against two conflicting transactions
> + committing in parallel and things like that.
> +
> + So for now, anything that uses temporary tables will be serialised
> + with anything before it, when using parallel replication.
> +
> + TODO: We might be able to introduce a reference count or something
> + on temp tables, and have slave worker threads wait for it to reach
> + zero before being allowed to use the temp table. Might not be worth
> + it though, as statement-based replication using temporary tables is
> + in any case rather fragile.
> + */
> + if (m_thd->rgi_slave &&
> + m_thd->rgi_slave->is_parallel_exec &&
> + m_thd->wait_for_prior_commit())
> + {
> + DBUG_RETURN(true);
> + }
> +
> + DBUG_RETURN(false);
> +}
> +
> +
> +void Temporary_tables::mark_tables_as_free_for_reuse() {
> + TABLE *table;
> + TABLE *next;
> +
> + DBUG_ENTER("mark_temp_tables_as_free_for_reuse");
> +
> + if (m_thd->query_id == 0)
> + {
> + /* Thread has not executed any statement and has not used any tmp tables */
> + DBUG_VOID_RETURN;
> + }
> +
> + lock_tables();
> +
> + if (!m_thd->temporary_tables.is_empty())
do you need that? it checks whether m_table_shares or
rgi_slave->rli->save_temp_table_shares is NULL. But after lock_tables()
m_table_shares is equal to rgi_slave->rli->save_temp_table_shares,
so it's enough to check m_table_shares. And if m_table_shares is NULL
then m_opened_tables is also NULL, so the whole if() condition
is redundant it only duplicates this while() below.
> + {
> +
> + table= m_opened_tables;
> +
> + while(table) {
> + next= table->next;
> +
> + if ((table->query_id == m_thd->query_id) && ! table->open_by_handler)
> + {
> + mysql_lock_remove(m_thd, m_thd->lock, table);
> + close_table(table);
I don't get it. Old code had mark_tmp_table_for_reuse() here, you have
mysql_lock_remove and close_table.
But temporary tables aren't locked, are they?
And constantly closing/reopening temporary tables is a waste of resources,
you can keep them open until they're dropped.
> + }
> +
> + table= next;
> + }
> + }
> +
> + unlock_tables();
Old code had here:
if (rgi_slave)
{
/*
Temporary tables are shared with other by sql execution threads.
As a safety messure, clear the pointer to the common area.
*/
thd->temporary_tables= 0;
}
shouldn't you have put it into unlock_tables() ?
(yes, it was "messure" in the old comment, not my typo :)
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +void Temporary_tables::lock_tables()
> +{
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> + if (rgi_slave)
> + {
> + mysql_mutex_lock(&rgi_slave->rli->data_lock);
> + m_table_shares= rgi_slave->rli->save_temp_table_shares;
> + }
> +}
> +
> +
> +void Temporary_tables::unlock_tables()
> +{
> + rpl_group_info *rgi_slave= m_thd->rgi_slave;
> + if (rgi_slave)
> + {
> + rgi_slave->rli->save_temp_table_shares= m_table_shares;
> + mysql_mutex_unlock(&rgi_slave->rli->data_lock);
> + }
> +}
> +
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
4
Hi Wlad,
Last week I promised to benchmark effect of various gcc optimization levels.
Here're results:
-O0 418TPS
-Os 663TPS
-O1 759TPS
-O2 807TPS
-O3 812TPS
-O3 -unroll2 -ip 812TPS (default)
-Ofast 819TPS
Benchmark was done on a Sandy Bridge system running Ubuntu 12.10. Single
thread OLTP RO, performance schema off.
Regards,
Sergey
1
0

[Maria-developers] Please review MDEV-6353 my_ismbchar() and my_mbcharlen() refactoring
by Alexander Barkov 17 May '16
by Alexander Barkov 17 May '16
17 May '16
Hi Sergei,
Please review a partial patch for MDEV-6353.
It removes my_mbcharlen() in all remaining pieces of the code,
except LOAD DATA and SELECT INTO OUTFILE.
I'll do LOAD DATA and SELECT INTO OUTFILE in a separate patch.
Thanks.
2
4

[Maria-developers] MDEV-10035 DBUG_ASSERT on CREATE VIEW v1 AS SELECT * FROM t1 FOR UPDATE
by Alexander Barkov 16 May '16
by Alexander Barkov 16 May '16
16 May '16
Hi Sanja, Sergei,
Sanja, you asked me to review a patch for MDEV-10035:
revision-id: 4ffe2295e78538dde93df078421726f0c5a7d2a2
(mariadb-10.2.0-29-g4ffe229)
parent(s): b79944950e5e5db40cf7ad49061edf5f105512c4
committer: Oleksandr Byelkin
timestamp: 2016-05-15 15:25:33 +0200
message:
I earlier also proposed the same idea to disallow FOR UPDATE,
and even created a patch disallowing this in the parser
syntactically (see attached).
But Sergei worried that we should not do it this way and proposed some
other solutions. Please see Sergei's comments in:
MDEV-10063 VIEWs and subqueries with FOR UPDATE
Sergei, are you still in doubts?
Thanks.
3
9

14 May '16
Hi, Sanja!
Looks ok to me. You're simply disable net_flush as far as I can see.
For comments and questions, see below...
On May 09, Oleksandr Byelkin wrote:
> revision-id: 7e70658d1e88b4a255e3c28426f8259e5e596a85 (mariadb-10.2.0-20-g7e70658)
> parent(s): c0a59b46be5be341bd6ffc9fe188a236ced46522
> committer: Oleksandr Byelkin
> timestamp: 2016-05-09 15:26:18 +0200
> message:
>
> MDEV-9947: COM_MULTI united response
> diff --git a/sql/protocol.cc b/sql/protocol.cc
> index 9e52870..0bf2a05 100644
> --- a/sql/protocol.cc
> +++ b/sql/protocol.cc
> @@ -208,7 +208,8 @@ bool net_send_error(THD *thd, uint sql_errno,
> const char *err,
> bool
> net_send_ok(THD *thd,
> uint server_status, uint statement_warn_count,
> - ulonglong affected_rows, ulonglong id, const char
> *message)
> + ulonglong affected_rows, ulonglong id, const char
> *message,
> + bool postpond_flush)
"postpone" or "delay" or even (looking at how you use it) "skip"
> {
> NET *net= &thd->net;
> uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
> diff --git a/sql/sql_error.h b/sql/sql_error.h
> index e03c3dd..98c8d77 100644
> --- a/sql/sql_error.h
> +++ b/sql/sql_error.h
> @@ -704,6 +704,12 @@ class Diagnostics_area
> const char *message() const
> { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return
> m_message; }
>
> + bool postpond_flush() const
> + { DBUG_ASSERT(m_status == DA_OK); return m_postpond_flush; }
What if one uses COM_MULTI to send two commands and the first
fails (results in an error). Will the second be executed?
(This is a question about *sending*, client-to-server, not about
MDEV-9947)
> +
> + void set_postpond_flush()
> + { m_postpond_flush= TRUE; }
> +
> uint sql_errno() const
> { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
>
> @@ -857,6 +863,8 @@ class Diagnostics_area
> /** Set to make set_error_status after set_{ok,eof}_status
> possible. */
> bool m_can_overwrite_status;
A comment would've been nice...
> + bool m_postpond_flush;
> +
> /** Message buffer. Can be used by OK or ERROR status. */
> char m_message[MYSQL_ERRMSG_SIZE];
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1
Hi everyone,
I am not used to blogging but I have created a wordpress blog on which I
will be making posts about my progress on this project with Spetrunia.
Please feel free to criticize and suggest corrections.
So far I have forked the code to my Github and my work will be based on
10.1. <https://github.com/charlesmuurmu/server>
I have build the code on OS X El Capitan.
I have also created a branch in which I will be working.
I am currently going through some documentation and tutorials on the
Datastax C++ driver.
Regards,
Biggy
2
3
On Wed, May 11, 2016 at 08:26:49PM +0300, Sergey Petrunia wrote:
> On Wed, Apr 27, 2016 at 07:44:11AM +0100, Charles Muurmu wrote:
> > I am Charles Muurmu also known as laserlight on IRC and I was selected for
> > GSoC 2016 to work on migrating MariaDB Cassandra storage engine from the
> > Thrift API to Datastax driver. I will be mentored by Spetrunia. My goals
> > for this community bonding period will be get myself ready by accomplishing
> > the following tasks:
> >
> > Fork and clone the server code.
> >
> > Try to build the code.
> >
> > Setup a blog on which I will post weekly reports on progress.
> >
> > Study the Datastax C++ driver API and documentation.
> >
When CassandraSE V1 was in development, I was unable to use CQL, because
Thrift API was such that the entire CQL resultset had to be materialized before
it could be sent to the client.
That is, running "select * from column_family" would cause the cassandra node
and/or MySQL to die from attempting to use too much memory.
I'm interested to learn how this is resolved in the Datastax driver.
> > Study the ha_cassandra.xx code and storage engines architecture.
> >
> Good plan.
>
> Hopefully the last two items will give a better idea about how to structure the
> coding (what to do as the first, second, etc milestone).
Another question to think about is data models.
Use of Thrift API calls in CassandraSE V1 meant that
- secondary indexes could not be used
- there were some other limitation on what datatypes could be used (I don't
remember what they were exactly).
Now, if we are using CQL, it should be possible to use those. Maybe there's
also something else that I'm not aware of.
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
1
0

[Maria-developers] Please review MDEV-10051 Fix subselect to return a syntax error instead of "Incorrect usage of UNION and LIMIT"
by Alexander Barkov 11 May '16
by Alexander Barkov 11 May '16
11 May '16
Hello Sergei,
Please review the next step for
MDEV-8909 union parser cleanup
Now it's MDEV-10051, and it fixes subselects.
Note, I decided to fix derived tables in a separate patch later.
So there will be one more patch blocking MDEV-8909.
Thanks!
2
1

[Maria-developers] MDEV-10036 sql_yacc.yy: Split select_part2 to disallow syntactically bad constructs with INTO, PROCEDURE, UNION
by Alexander Barkov 10 May '16
by Alexander Barkov 10 May '16
10 May '16
Hi Sergei,
Please review a patch for MDEV-10036.
This is the next step for:
MDEV-8909 union parser cleanup
Thanks!
2
2

[Maria-developers] Please review MDEV-10030 sql_yacc.yy: Split table_expression and remove PROCEDURE from create_select, select_paren_derived, select_derived2, query_specification
by Alexander Barkov 09 May '16
by Alexander Barkov 09 May '16
09 May '16
Hi Sergei,
Please review a patch for MDEV-10030, which is a part of
MDEV-8909 union parser cleanup.
Thanks!
2
4
Hi everyone,
I have created blog, where I'm going to write everything about my progress
on project "Pushing conditions into non-mergeable views and derived tables
in MariaDB".
I've already written a post on what I've done, so if you are interested,
you can check it out HERE <http://gsocmariadbshagalla.blogspot.ru/>.
Best wishes,
Galina Shalygina.
1
0
Hi Monty,
I vaguely recall you told me once that you like detached threads more than
joinable, specifically for service threads. Could you remind me what were
the reasons behind it?
I need this to fix https://jira.mariadb.org/browse/MDEV-9994 properly.
Thanks,
Sergey
2
4

Re: [Maria-developers] [MariaDB/server] MDEV-9857: line cache size (to 10.1) (#169)
by Kristian Nielsen 06 May '16
by Kristian Nielsen 06 May '16
06 May '16
Sergey Vojtovich <notifications(a)github.com> writes:
> Since Alexey's patch wasn't yet accepted by MySQL we need his
> confirmation that we can take it under BSD-new or MCA
> terms. @akopytov, could you confirm?
That _really_ is completely ridiculous! So here we have a patch. You will
gladly accept it from MySQL/Oracle under GPL v2, without any special
conditions. But the very same patch, submitted directly by the original
author, you will not take??!?
Now, obviously this is not something Sergey Vojtovich invented on his own, I
doubt he even agrees personally. So Otto, you specifically asked me to be
more vocal about MariaDB Foundation issue. Can we please have your personal
opinions on this idea of BSD/contributer agreement for selected potential
contributers?
I have from the very start of MariaDB been strongly opposed to any form of
contributer agreements or reciprocal licensing. Fortunately, we never really
had it in practice, since all major contributions (MySQL@Oracle, XtraDB,
oqgraph, sphinx, tokudb, galera, ...) were GPL only.
But recently we have this message to potential contributers that the MariaDB
Foundation "needs" to have BSD or MCA. This is extremely disrespectful for
at least two reasons:
- There clearly is no such "need", since the Foundation receives GPL rights
only to a large part of new contributions to MariaDB - for example from
Galera, MySQL@Oracle, Percona.
- Selected contributers are except from this requirement. For example,
according to my information, large parts of the MariaDB code to which
the company SkySQL/MariaDB Corporation has full rights, they are _not_
required to provide BSD/MCA for to the MariaDB Foundation.
Otto, I strongly encourage you to look to other successful Free Software
projects such as the Linux Kernel, where an important part of the success
is the deliberate choice of GPL-only _without_ any reciprocal licensing. I
urge you to re-consider, and take a personal stance against this BSD/MCA
nonsense, and work strongly for an official GPL-only contributer policy.
Meanwhile, I encourage all contributers to _not_ accept this BSD/MCA
requirement. If you disagree, be aware that there is no such strong
requirement, and the major part of contributions to MariaDB are done without
this requirement. If your contribution is accepted, but under condition of
BSD/MCA, and you disagree, ask on the mailing list for someone else with
commit rights to push your patch. I am sure there are several that will be
willing to do so.
- Kristian.
3
5

[Maria-developers] MDEV-9712 Performance degradation of nested NULLIF
by Alexander Barkov 05 May '16
by Alexander Barkov 05 May '16
05 May '16
Hello Sergei,
Please have a look into a draft fix for MDEV-9712.
The problem happens because in 10.1 execution time for various
recursive stages (like walk, update_used_table and
propagate_equal_fields) in NULLIF is O(recursion_level^2),
because complexity is doubled on every recursion level
when we copy args[0] to args[2].
This fix simplifies some stages to make them faster.
It reduced execution time from 144 seconds to 12 seconds.
The idea is just not to iterate through args[2] when
it's just a copy of args[0] in these stages:
- Item_func_nullif::walk
- Item_func_nullif::update_used_tables
I'm not fully sure that skipping args[2] is always correct though.
For some transformers it may not be correct.
<offtopic>
Btw, walk() can be called from the partitioning code when arg_count is
still 2 rather than 3. Looks suspicious. Perhaps there is a hidden bug
behind that.
</offtopic>
The remaining heavy piece of the code is:
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL
*cond)
{
Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation());
args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
cond, &args[0]);
args[1]->propagate_equal_fields_and_change_item_tree(thd, cmpctx,
cond, &args[1]);
args[2]->propagate_equal_fields_and_change_item_tree(thd,
Context_identity(),
cond, &args[2]);
return this;
}
We cannot use the same approach here, because args[0] and args[2]
are propagated with a different context (ANY_SUBST vs IDENTITY_SUBST).
Not sure what to do with this. Perhaps, data types could have an extra
attribute to tell that ANY_SUBST and IDENITTY_SUBST are equal.
So, for example, for non-ZEROFILL integers, ANY_SUBST and IDENTITY_SUBST
do the same thing and thus propagation could be done one time only.
But this would be a partial solution, for a limited number of data types.
Another option is to leave propagation as is.
Another option is do nothing at all.
If we rewrite the reported query as CASE, it will look heavy indeed.
So it's Ok to expect heavy execution complexity.
Thanks.
2
3
Hi Sergei,
If I run this query:
SELECT 1 UNION SELECT 2 INTO OUTFILE 'test.txt';
It creates a file 'test.txt' with this content:
1
2
Looks fine so far.
Now if I do "rm test.txt" and execute another query (with parentheses):
(SELECT 1) UNION (SELECT 2 INTO OUTFILE 'test.txt');
it still puts the same two records from both UNION parts!
This looks confusing, as parentheses a kind of assume that only
the right UNION part is to be exported.
Exporting only one UNION part is not supported, IIRC.
So the above query should probably return a syntax error.
But now there is a new trouble. There is no syntax like this:
(SELECT ...) UNION (SELECT ...) INTO OUTFILE 'test.txt';
So instead of:
(SELECT ...) UNION (SELECT ... INTO OUTFILE 'test.txt');
one will have to write this:
SELECT * INTO OUTFILE 'test.txt'
FROM ((SELECT ...) UNION (SELECT ...)) AS t1;
The latter looks longer, but at least it's not confusing as the former.
2
2

Re: [Maria-developers] [Commits] b1ddc7d: MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops with UNION in ALL subquery
by Sergey Petrunia 04 May '16
by Sergey Petrunia 04 May '16
04 May '16
Hi Sanja,
I have one comment: the patch introduces JOIN::in_optimize which can have
values of 1) not initialized and 2) false. This is clearly redundant.
Ok to push after the above is addressed.
On Sun, Feb 21, 2016 at 10:12:25PM +0100, OleksandrByelkin wrote:
> revision-id: b1ddc7d546e6b147838af72dd03f86a8b272fdf0 (mariadb-10.1.11-18-gb1ddc7d)
> parent(s): fd8e846a3b049903706267d58e6d8e61eea97df8
> committer: Oleksandr Byelkin
> timestamp: 2016-02-21 22:12:25 +0100
> message:
>
> MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops with UNION in ALL subquery
>
> Do not mark subquery as inexpensive when it is not optimized.
>
> ---
> mysql-test/r/derived_view.result | 6 +++---
> mysql-test/r/subselect.result | 20 +++++++++++++++++---
> mysql-test/r/subselect_no_exists_to_in.result | 20 +++++++++++++++++---
> mysql-test/r/subselect_no_mat.result | 20 +++++++++++++++++---
> mysql-test/r/subselect_no_opts.result | 20 +++++++++++++++++---
> mysql-test/r/subselect_no_scache.result | 20 +++++++++++++++++---
> mysql-test/r/subselect_no_semijoin.result | 20 +++++++++++++++++---
> mysql-test/r/type_year.result | 1 +
> mysql-test/t/subselect.test | 11 +++++++++++
> sql/item_subselect.cc | 20 ++++++++++++++++----
> sql/sql_select.h | 2 ++
> 11 files changed, 135 insertions(+), 25 deletions(-)
>
> diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result
> index 639942f..5783247 100644
> --- a/mysql-test/r/derived_view.result
> +++ b/mysql-test/r/derived_view.result
> @@ -1101,7 +1101,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
> 1 PRIMARY t3 system NULL NULL NULL NULL 1 100.00
> 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
> Warnings:
> -Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where (not(<expr_cache><6,5>(<in_optimizer>((6,5),<exists>(select 7,5 having (trigcond(((<cache>(6) = 7) or isnull(7))) and trigcond(((<cache>(5) = 5) or isnull(5))) and trigcond(<is_not_null_test>(7)) and trigcond(<is_not_null_test>(5))))))))
> +Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where 1
> SELECT t.a,t.b FROM t3 RIGHT JOIN ((SELECT * FROM t1) AS t, t2) ON t2.b != 0
> WHERE (t.a,t.b) NOT IN (SELECT 7, 5);
> a b
> @@ -1115,7 +1115,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
> 1 PRIMARY t3 system NULL NULL NULL NULL 1 100.00
> 3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
> Warnings:
> -Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where (not(<expr_cache><6,5>(<in_optimizer>((6,5),<exists>(select 7,5 having (trigcond(((<cache>(6) = 7) or isnull(7))) and trigcond(((<cache>(5) = 5) or isnull(5))) and trigcond(<is_not_null_test>(7)) and trigcond(<is_not_null_test>(5))))))))
> +Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where 1
> SELECT t.a,t.b FROM t3 RIGHT JOIN (v1 AS t, t2) ON t2.b != 0
> WHERE (t.a,t.b) NOT IN (SELECT 7, 5);
> a b
> @@ -1129,7 +1129,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
> 1 PRIMARY t3 system NULL NULL NULL NULL 1 100.00
> 2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
> Warnings:
> -Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where (not(<expr_cache><6,5>(<in_optimizer>((6,5),<exists>(select 7,5 having (trigcond(((<cache>(6) = 7) or isnull(7))) and trigcond(((<cache>(5) = 5) or isnull(5))) and trigcond(<is_not_null_test>(7)) and trigcond(<is_not_null_test>(5))))))))
> +Note 1003 select 6 AS `a`,5 AS `b` from `test`.`t3` where 1
> DROP VIEW v1;
> DROP TABLE t1,t2,t3;
> #
> diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
> index 75c8597..90d8f9f 100644
> --- a/mysql-test/r/subselect.result
> +++ b/mysql-test/r/subselect.result
> @@ -6805,7 +6805,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6817,8 +6819,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7133,3 +7135,15 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result
> index e6238af..dcceb61 100644
> --- a/mysql-test/r/subselect_no_exists_to_in.result
> +++ b/mysql-test/r/subselect_no_exists_to_in.result
> @@ -6805,7 +6805,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6817,8 +6819,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7133,6 +7135,18 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> set optimizer_switch=default;
> select @@optimizer_switch like '%exists_to_in=off%';
> @@optimizer_switch like '%exists_to_in=off%'
> diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result
> index 70edc64..4e9f750 100644
> --- a/mysql-test/r/subselect_no_mat.result
> +++ b/mysql-test/r/subselect_no_mat.result
> @@ -6800,7 +6800,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6812,8 +6814,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7126,6 +7128,18 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> set optimizer_switch=default;
> select @@optimizer_switch like '%materialization=on%';
> @@optimizer_switch like '%materialization=on%'
> diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result
> index c89fd13..4b09fd2 100644
> --- a/mysql-test/r/subselect_no_opts.result
> +++ b/mysql-test/r/subselect_no_opts.result
> @@ -6796,7 +6796,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6808,8 +6810,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7124,4 +7126,16 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> set @optimizer_switch_for_subselect_test=null;
> diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result
> index b12bf21..322e64d 100644
> --- a/mysql-test/r/subselect_no_scache.result
> +++ b/mysql-test/r/subselect_no_scache.result
> @@ -6811,7 +6811,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6823,8 +6825,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7139,6 +7141,18 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> set optimizer_switch=default;
> select @@optimizer_switch like '%subquery_cache=on%';
> @@optimizer_switch like '%subquery_cache=on%'
> diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result
> index 54f145d..d380528 100644
> --- a/mysql-test/r/subselect_no_semijoin.result
> +++ b/mysql-test/r/subselect_no_semijoin.result
> @@ -6796,7 +6796,9 @@ FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR alias1.a = 'y'
> HAVING field>'B' AND ( 'Moscow' ) IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> +1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a ) AS field
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> @@ -6808,8 +6810,8 @@ SELECT MAX( alias2.a )
> FROM t1 AS alias1, t1 AS alias2, t1 AS alias3
> WHERE alias1.a = alias2.a OR ('Moscow') IN ( SELECT a FROM t1 );
> id select_type table type possible_keys key key_len ref rows Extra
> -1 PRIMARY alias1 index a a 19 NULL 11 Using where; Using index
> -1 PRIMARY alias2 ref a a 19 test.alias1.a 2 Using index
> +1 PRIMARY alias1 index a a 19 NULL 11 Using index
> +1 PRIMARY alias2 index a a 19 NULL 11 Using where; Using index; Using join buffer (flat, BNL join)
> 1 PRIMARY alias3 index NULL a 19 NULL 11 Using index; Using join buffer (flat, BNL join)
> 2 SUBQUERY t1 index_subquery a a 19 const 1 Using index; Using where
> SELECT MAX( alias2.a )
> @@ -7124,5 +7126,17 @@ sq
> NULL
> drop view v2;
> drop table t1,t2;
> +#
> +# MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +# with UNION in ALL subquery
> +#
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +f
> +foo
> +drop table t1;
> +SET NAMES default;
> set @optimizer_switch_for_subselect_test=null;
> set @join_cache_level_for_subselect_test=NULL;
> diff --git a/mysql-test/r/type_year.result b/mysql-test/r/type_year.result
> index 842a16e..204cec2 100644
> --- a/mysql-test/r/type_year.result
> +++ b/mysql-test/r/type_year.result
> @@ -387,6 +387,7 @@ a
> 00
> select a from t1 where a=(select 2000 from dual where 1);
> a
> +00
> select a from t1 where a=y2k();
> a
> 00
> diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
> index a862870..f71c215 100644
> --- a/mysql-test/t/subselect.test
> +++ b/mysql-test/t/subselect.test
> @@ -5989,3 +5989,14 @@ SELECT ( SELECT MIN(v2.f2) FROM t1 ) AS sq FROM v2 GROUP BY sq;
>
> drop view v2;
> drop table t1,t2;
> +
> +--echo #
> +--echo # MDEV-9487: Server crashes in Time_and_counter_tracker::incr_loops
> +--echo # with UNION in ALL subquery
> +--echo #
> +SET NAMES utf8;
> +CREATE TABLE t1 (f VARCHAR(8)) ENGINE=MyISAM;
> +INSERT INTO t1 VALUES ('foo');
> +SELECT f FROM t1 WHERE f > ALL ( SELECT 'bar' UNION SELECT 'baz' );
> +drop table t1;
> +SET NAMES default;
> diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
> index d4e3e6c..f45d83d 100644
> --- a/sql/item_subselect.cc
> +++ b/sql/item_subselect.cc
> @@ -561,22 +561,34 @@ bool Item_subselect::is_expensive()
> for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
> {
> JOIN *cur_join= sl->join;
> +
> + /* not optimized subquery */
> if (!cur_join)
> - continue;
> + return true;
> +
> + /* very simple subquery */
> + if (!cur_join->tables_list && !sl->first_inner_unit())
> + return false;
> +
> + /*
> + If the subquery is not optimised or in the process of optimization
> + it supposed to be expensive
> + */
> + if (!cur_join->optimized || cur_join->in_optimize)
> + return true;
>
> /*
> Subqueries whose result is known after optimization are not expensive.
> Such subqueries have all tables optimized away, thus have no join plan.
> */
> - if (cur_join->optimized &&
> - (cur_join->zero_result_cause || !cur_join->tables_list))
> + if ((cur_join->zero_result_cause || !cur_join->tables_list))
> return false;
>
> /*
> If a subquery is not optimized we cannot estimate its cost. A subquery is
> considered optimized if it has a join plan.
> */
> - if (!(cur_join->optimized && cur_join->join_tab))
> + if (!cur_join->join_tab)
> return true;
>
> if (sl->first_inner_unit())
> diff --git a/sql/sql_select.h b/sql/sql_select.h
> index 9f90473..e57844fd 100644
> --- a/sql/sql_select.h
> +++ b/sql/sql_select.h
> @@ -1292,6 +1292,7 @@ class JOIN :public Sql_alloc
> OPTIMIZATION_IN_PROGRESS=1,
> OPTIMIZATION_DONE=2};
> bool optimized; ///< flag to avoid double optimization in EXPLAIN
> + bool in_optimize;
> bool initialized; ///< flag to avoid double init_execution calls
>
> Explain_select *explain;
> @@ -1380,6 +1381,7 @@ class JOIN :public Sql_alloc
> ref_pointer_array_size= 0;
> zero_result_cause= 0;
> optimized= 0;
> + in_optimize= 0;
> have_query_plan= QEP_NOT_PRESENT_YET;
> initialized= 0;
> cleaned= 0;
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
3
2

Re: [Maria-developers] c75cbc1: MDEV-717 LP:1003679 - Wrong binlog order on concurrent DROP schema and
by Sergei Golubchik 04 May '16
by Sergei Golubchik 04 May '16
04 May '16
Hi, Alexey!
convention below: when I start a comment with "btw" it means that you
don't need to change anything, it's just a thought I had when looking at
that line in the patch.
On May 03, Alexey Botchkov wrote:
> revision-id: c75cbc19806223be8e750c27ba0bd4b17ef61f54 (mariadb-10.1.13-24-gc75cbc1)
> parent(s): 94cd0f6c9b3b04db67501ef29d470f32527ceda2
> committer: Alexey Botchkov
> timestamp: 2016-05-03 13:20:12 +0400
> message:
>
> MDEV-717 LP:1003679 - Wrong binlog order on concurrent DROP schema and
> CREATE function.
>
> Test case added. That required setting some DEBUG_SYNC points.
>
> diff --git a/mysql-test/suite/binlog/t/binlog_mdev717.test b/mysql-test/suite/binlog/t/binlog_mdev717.test
> new file mode 100644
> index 0000000..5e8cce6
> --- /dev/null
> +++ b/mysql-test/suite/binlog/t/binlog_mdev717.test
> @@ -0,0 +1,49 @@
> +# MDEV-717 LP:1003679 - Wrong binlog order on concurrent DROP schema and CREATE function.
> +
> +--source include/have_log_bin.inc
> +RESET MASTER;
> +
> +disable_warnings;
> +DROP DATABASE IF EXISTS mysqltest;
> +enable_warnings;
btw, generally this isn't necessary anymore, after-test checks verify that
every test cleans up after itself.
> +connect(con1,localhost,root,,);
btw, this works also without extra commas:
connect(con1,localhost,root);
> +connection default;
> +
> +CREATE DATABASE mysqltest;
> +SET DEBUG_SYNC= "wait_drop_db_name_locked SIGNAL locked WAIT_FOR release";
> +--send DROP DATABASE mysqltest;
> +connection con1;
> +SET DEBUG_SYNC= "now WAIT_FOR locked";
Hm... If the default connection signals "locked" before you start
waiting for it now - will your test still work? You can test it by
adding a sleep right after "connection con1". (don't commit the sleep,
of course, it's just to way to verify whether the test case is valid)
Alternatively, in many places tests wait for a connection to reach the
certain debug_sync point with, like:
let $wait_condition= select count(*) = 1 from information_schema.processlist
where state like "debug sync point: wait_drop_db_name_locked%";
source include/wait_condition.inc;
> +SET DEBUG_SYNC= "sp_create_routine_started SIGNAL release";
> +--error 0,ER_BAD_DB_ERROR
why? Do you mean that even with your sync points the specific execution
order if not guaranteed?
> +CREATE FUNCTION mysqltest.f1() RETURNS INT RETURN 1;
> +connection default;
> +--reap
> +
> +CREATE DATABASE mysqltest;
> +SET DEBUG_SYNC= "wait_drop_db_name_locked SIGNAL locked1 WAIT_FOR release1";
> +--send DROP DATABASE mysqltest;
> +connection con1;
> +SET DEBUG_SYNC= "now WAIT_FOR locked1";
> +SET DEBUG_SYNC= "create_event_started SIGNAL release1";
> +--error 0,ER_BAD_DB_ERROR
> +CREATE EVENT mysqltest.e1 ON SCHEDULE EVERY 15 MINUTE DO BEGIN END;
> +connection default;
> +--reap
> +
> +CREATE DATABASE mysqltest;
> +CREATE EVENT mysqltest.e1 ON SCHEDULE EVERY 15 MINUTE DO BEGIN END;
> +SET DEBUG_SYNC= "wait_drop_db_name_locked SIGNAL locked1 WAIT_FOR release1";
> +--send DROP DATABASE mysqltest;
> +connection con1;
> +SET DEBUG_SYNC= "now WAIT_FOR locked1";
> +SET DEBUG_SYNC= "update_event_started SIGNAL release1";
> +--error 0,ER_BAD_DB_ERROR
> +ALTER EVENT mysqltest.e1 ON SCHEDULE EVERY 20 MINUTE DO BEGIN END;
> +connection default;
> +--reap
> +
> +SET DEBUG_SYNC= "RESET";
> +--source include/show_binlog_events.inc
> +
> diff --git a/sql/events.cc b/sql/events.cc
> index 77dbb8b..3c50d6e 100644
> diff --git a/sql/sp.cc b/sql/sp.cc
> index a518b52..c2e3fd2 100644
> --- a/sql/sp.cc
> +++ b/sql/sp.cc
> @@ -1043,6 +1043,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
> DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
> type == TYPE_ENUM_FUNCTION);
>
> + DEBUG_SYNC(thd, "sp_create_routine_started");
> +
could you use "before_wait_locked_pname" sync point instead?
It's at the beginning of lock_object_name().
> /* Grab an exclusive MDL lock. */
> if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
> {
> diff --git a/sql/sql_db.cc b/sql/sql_db.cc
> index 2ba67cb..fdb49f2 100644
> --- a/sql/sql_db.cc
> +++ b/sql/sql_db.cc
> @@ -837,6 +837,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
> if (lock_schema_name(thd, dbnorm))
> DBUG_RETURN(true);
>
> + DEBUG_SYNC(thd, "wait_drop_db_name_locked");
> +
Could you use "after_wait_locked_schema_name" sync point instead?
It's the one at the end of lock_schema_name().
> length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
> strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
> del_dbopt(path); // Remove dboption hash entry
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
3

Re: [Maria-developers] [Commits] a6fb98f: MDEV-6368: assertion xid_seqno > trx_sys_cur_xid_seqno
by Jan Lindström 03 May '16
by Jan Lindström 03 May '16
03 May '16
Hi Nirbhay,
I'm not expert on this code area but some questions, comments below.
On Tue, May 3, 2016 at 1:26 AM, Nirbhay Choubey <nirbhay(a)mariadb.com> wrote:
> revision-id: a6fb98fd74ab6f14fab0c1ef4630b010ff28880f
> (mariadb-10.1.13-6-ga6fb98f)
> parent(s): f8cdb9e7e8fb692f8eb3b9bb4792cd60653e0eb8
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-05-02 18:26:58 -0400
> message:
>
> MDEV-6368: assertion xid_seqno > trx_sys_cur_xid_seqno
>
> - Validate the specified wsrep_start_position value by also
> checking the return status of wsrep->sst_received. This also
> ensures that changes in wsrep_start_position is not allowed
> when the node is not in JOINING state.
> - Do not allow decrease in seqno within same UUID.
> - The initial checkpoint in SEs should be [0...:-1].
>
>
> diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
> index 4f6cc51..44fdd03 100644
> --- a/sql/wsrep_sst.cc
> +++ b/sql/wsrep_sst.cc
> @@ -264,42 +264,91 @@ void wsrep_sst_complete (const wsrep_uuid_t*
> sst_uuid,
> mysql_mutex_unlock (&LOCK_wsrep_sst);
> }
>
> -void wsrep_sst_received (wsrep_t* const wsrep,
> +/*
> + If wsrep provider is loaded, inform that the new state snapshot
> + has been received. Also update the local checkpoint.
> +
> + @param wsrep [IN] wsrep handle
> + @param uuid [IN] Initial state UUID
> + @param seqno [IN] Initial state sequence number
> + @param state [IN] Always NULL, also ignored by wsrep
> provider (?)
> + @param state_len [IN] Always 0, also ignored by wsrep
> provider (?)
> + @param implicit [IN] Whether invoked implicitly due to
> SST
> + (true) or explicitly because if
> change
> + in wsrep_start_position by user
> (false).
> + @return false Success
> + true Error
> +
> +*/
> +bool wsrep_sst_received (wsrep_t* const wsrep,
> const wsrep_uuid_t& uuid,
> wsrep_seqno_t const seqno,
> const void* const state,
> - size_t const state_len)
>
Why not const size_t state_len ? Use of const here is confusing.
> + size_t const state_len,
> + bool const implicit)
> {
> - wsrep_get_SE_checkpoint(local_uuid, local_seqno);
> + /*
> + To keep track of whether the local uuid:seqno should be updated.
> Also, note
> + that local state (uuid:seqno) is updated/checkpointed only after we
> get an
> + OK from wsrep provider. By doing so, the values remain consistent
> across
> + the server & wsrep provider.
> + */
> + bool do_update= false;
> +
> + // Get the locally stored uuid:seqno.
> + wsrep_get_SE_checkpoint(local_uuid, local_seqno);
>
What if this fails, no error handling ?
>
> - if (memcmp(&local_uuid, &uuid, sizeof(wsrep_uuid_t)) ||
> - local_seqno < seqno || seqno < 0)
> + if (memcmp(&local_uuid, &uuid, sizeof(wsrep_uuid_t)) ||
> + local_seqno < seqno)
> + {
> + do_update= true;
> + }
> + else if (local_seqno > seqno)
> + {
> + WSREP_WARN("SST position can't be set in past. Requested: %lld,
> Current: "
> + " %lld.", (long long)seqno, (long long)local_seqno);
> + /*
> + If we are here because of SET command, simply return true (error)
> instead of
> + aborting.
> + */
> + if (implicit)
> {
> - wsrep_set_SE_checkpoint(uuid, seqno);
> - local_uuid = uuid;
> - local_seqno = seqno;
> + WSREP_WARN("Can't continue.");
> + unireg_abort(1);
> }
> - else if (local_seqno > seqno)
> + else
> {
> - WSREP_WARN("SST postion is in the past: %lld, current: %lld. "
> - "Can't continue.",
> - (long long)seqno, (long long)local_seqno);
> - unireg_abort(1);
> + return true;
> }
> + }
>
> #ifdef GTID_SUPPORT
> - wsrep_init_sidno(uuid);
> + wsrep_init_sidno(uuid);
> #endif /* GTID_SUPPORT */
>
> - if (wsrep)
> - {
> - int const rcode(seqno < 0 ? seqno : 0);
> - wsrep_gtid_t const state_id = {
> - uuid, (rcode ? WSREP_SEQNO_UNDEFINED : seqno)
> - };
> + if (wsrep)
> + {
> + int const rcode(seqno < 0 ? seqno : 0);
>
When seqno can be < 0 ?
> + wsrep_gtid_t const state_id= {uuid,
> + (rcode ? WSREP_SEQNO_UNDEFINED : seqno)};
> +
> + wsrep_status_t ret= wsrep->sst_received(wsrep, &state_id, state,
> + state_len, rcode);
>
> - wsrep->sst_received(wsrep, &state_id, state, state_len, rcode);
> + if (ret != WSREP_OK)
> + {
> + return true;
> }
> + }
> +
> + // Now is the good time to update the local state and checkpoint.
> + if (do_update) {
> + wsrep_set_SE_checkpoint(uuid, seqno);
>
Again, I would add error handling.
> + local_uuid= uuid;
> + local_seqno= seqno;
> + }
> +
> + return false;
> }
>
> // Let applier threads to continue
> @@ -308,7 +357,7 @@ void wsrep_sst_continue ()
> if (sst_needed)
> {
> WSREP_INFO("Signalling provider to continue.");
> - wsrep_sst_received (wsrep, local_uuid, local_seqno, NULL, 0);
> + wsrep_sst_received (wsrep, local_uuid, local_seqno, NULL, 0, true);
>
You should check the return code.
> }
> }
>
> diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
> index 9c01e54..bd94190 100644
>
>
> bool wsrep_start_position_update (sys_var *self, THD* thd, enum_var_type
> type)
> {
> - WSREP_INFO ("wsrep_start_position var submitted: '%s'",
> - wsrep_start_position);
> - // since this value passed wsrep_start_position_check, don't check
> anything
> - // here
> - wsrep_set_local_position (wsrep_start_position, true);
> - return 0;
> + // Print a confirmation that wsrep_start_position has been updated.
> + WSREP_INFO ("wsrep_start_position set to '%s'", wsrep_start_position);
> + return false;
>
Hmm, normally I thought that system variable set would go like if
(variable_check()) variable_update()
but now update does not do nothing ?
> }
> void wsrep_start_position_init (const char* val)
> @@ -164,7 +194,7 @@ void wsrep_start_position_init (const char* val)
> return;
> }
>
> - wsrep_set_local_position (val, false);
> + wsrep_set_local_position (val, strlen(val), false);
>
Add error handling.
> }
>
> static bool refresh_provider_options()
> diff --git a/storage/innobase/trx/trx0sys.cc
> b/storage/innobase/trx/trx0sys.cc
> index 76c8613..1625e8d 100644
> --- a/storage/innobase/trx/trx0sys.cc
> +++ b/storage/innobase/trx/trx0sys.cc
> @@ -349,10 +349,10 @@ void read_wsrep_xid_uuid(const XID* xid, unsigned
> char* buf)
> unsigned char xid_uuid[16];
> long long xid_seqno = read_wsrep_xid_seqno(xid);
> read_wsrep_xid_uuid(xid, xid_uuid);
> - if (!memcmp(xid_uuid, trx_sys_cur_xid_uuid, 8))
> + if (!memcmp(xid_uuid, trx_sys_cur_xid_uuid, 16) &&
> + (xid_seqno > -1))
>
Does this mean that you ignore uuid if it is exactly the same, or is there
error handling on else?
R: Jan
2
1
Hi everyone,
I have created a wordpress blog HERE
<https://mariadbmhagtidgsoc2016.wordpress.com/> on which I will post
regularly about my progress with this project.
Feel free to check it out.
Cheers,
Charles.
1
0
Hi Everyone,
I am Becca and I will be working with Lixun Peng as mentor on adding support for GTID in the mysqlbinlog tool for GSoC 2016. I will base my work on 10.1 which I have cloned and build successfully on OS X El Capitan. I am currently studying the mysqlbinlog.cc code to see how we can extend the my_options array for GTID support and next I will study the GTID code to understand its data structures. I look forward to an awesome summer at the MariaDB foundation and thanks for giving me this opportunity.
Cheers,
Becca.
2
1

Re: [Maria-developers] ad8b439: MDEV-9618 solaris sparc build fails on 10.1.
by Sergei Golubchik 03 May '16
by Sergei Golubchik 03 May '16
03 May '16
Hi, Alexey!
On May 02, Alexey Botchkov wrote:
> revision-id: ad8b4399f2fcd1d5e9dcdb8b2a8832cb3a6745ae (mariadb-10.1.13-23-gad8b439)
> parent(s): ad4239cc3dc7ad5f6f264e1fb3cf6d24084bda90
> committer: Alexey Botchkov
> timestamp: 2016-05-02 12:03:39 +0400
> message:
>
> MDEV-9618 solaris sparc build fails on 10.1.
>
> Compiler on Solaris is sensitive to C/C++ call models
> differences, so it fails if we try to mix these two in
> '?' operator.
> Fixed by trying to keep the call models uniformity with the
> proper amount of 'extern "C"' hints.
>
> ---
> include/my_crypt.h | 6 ++++++
> include/mysql/plugin_audit.h | 7 +++++++
> include/mysql/plugin_encryption.h | 9 +++++++++
> .../example_key_management_plugin.cc | 2 +-
> plugin/file_key_management/file_key_management_plugin.cc | 2 +-
> sql/encryption.cc | 14 ++++++++++----
> unittest/sql/mf_iocache-t.cc | 2 +-
> 7 files changed, 35 insertions(+), 7 deletions(-)
>
> diff --git a/include/my_crypt.h b/include/my_crypt.h
> index e1e94c9..db280ca 100644
> --- a/include/my_crypt.h
> +++ b/include/my_crypt.h
> @@ -82,6 +82,12 @@ static inline uint my_aes_ctx_size(enum my_aes_mode mode __attribute__((unused))
> return MY_AES_CTX_SIZE;
> }
>
> +static inline uint my_aes_ctx_size_for_handler(unsigned int a,
> + unsigned int b __attribute__((unused)))
> +{
> + return my_aes_ctx_size((enum my_aes_mode) a);
> +}
> +
This won't be inlined, because you store a pointer to this function in a
structure. Better use the cast, as before.
> int my_random_bytes(uchar* buf, int num);
>
> #ifdef __cplusplus
> diff --git a/include/mysql/plugin_audit.h b/include/mysql/plugin_audit.h
> index 31589f0..e96f743 100644
> --- a/include/mysql/plugin_audit.h
> +++ b/include/mysql/plugin_audit.h
> @@ -23,6 +23,10 @@
>
> #include "plugin.h"
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
I agree, in general. But why does your short patch have nothing
about audit api?
> #define MYSQL_AUDIT_CLASS_MASK_SIZE 1
>
> #define MYSQL_AUDIT_INTERFACE_VERSION 0x0302
> @@ -174,5 +178,8 @@ struct st_mysql_audit
> unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
> };
>
> +#ifdef __cplusplus
> +}
> +#endif
>
> #endif
> diff --git a/include/mysql/plugin_encryption.h b/include/mysql/plugin_encryption.h
> index 3f35c2b..d748c4f 100644
> --- a/include/mysql/plugin_encryption.h
> +++ b/include/mysql/plugin_encryption.h
> @@ -27,6 +27,10 @@
>
> #include <mysql/plugin.h>
>
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
...etc
I agree that these extern "C" are the correct fix.
On the other hand, I don't see a point in moving casts from the
assignment to a new variable definition. So, I'd suggest to keep all
extern "C" hunks in the patch, but remove cast avoidance changes.
Then ok to push!
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
2

Re: [Maria-developers] 9058071: MDEV-9362: InnoDB tables using DATA_DIRECTORY created using MySQL 5.6 do not work with MariaDB 10.1
by Sergei Golubchik 02 May '16
by Sergei Golubchik 02 May '16
02 May '16
Hi, Jan!
I have various comments, see below. Nothing big, nothing that's really
the approach you've taken. In fact, I like where it's going.
But this is a huge and intrusive patch. I wonder, should we rather do it
in 10.2? See below, you yourself write:
On Feb 25, Jan Lindström wrote:
>
> Caution: Please take backups of your database before migrating 10.1.12.
This is not what users normally do when upgrading from a GA version to
the next minor GA version.
Anyway, see comments about the patch below:
> diff --git a/mysql-test/suite/encryption/r/innodb-bad-key-change.result b/mysql-test/suite/encryption/r/innodb-bad-key-change.result
> index cf97918..39e08f4 100644
> --- a/mysql-test/suite/encryption/r/innodb-bad-key-change.result
> +++ b/mysql-test/suite/encryption/r/innodb-bad-key-change.result
> @@ -36,8 +36,7 @@ SELECT * FROM t1;
> ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
> SHOW WARNINGS;
> Level Code Message
> -Warning 1812 Tablespace is missing for table 'test/t1'
> -Warning 192 Table test/t1 is encrypted but encryption service or used key_id 2 is not available. Can't continue reading table.
> +Warning 192 Table test/t1 is encrypted but encryption service or used key_id is not available. Can't continue reading table.
why did that happen?
> Error 1296 Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
> DROP TABLE t1;
> # Start server with keys.txt
> diff --git a/storage/innobase/include/dict0tableoptions.h b/storage/innobase/include/dict0tableoptions.h
> new file mode 100644
> index 0000000..def0e39
> --- /dev/null
> +++ b/storage/innobase/include/dict0tableoptions.h
> @@ -0,0 +1,127 @@
> +/*****************************************************************************
> +
> +Copyright (c) 2016, MariaDB Corporation.
> +
> +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 the Free Software
> +Foundation; version 2 of the License.
> +
> +This program is distributed in the hope that it will be useful, but WITHOUT
> +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License along with
> +this program; if not, write to the Free Software Foundation, Inc.,
> +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
> +
> +*****************************************************************************/
> +
> +/**************************************************//**
> +@file include/dict0tableoptions.h
> +Definitions for the system table SYS_TABLE_OPTIONS
> +
> +Created 22/01/2006 Jan Lindström
> +*******************************************************/
> +
> +#ifndef dict0tableoptions_h
> +#define dict0tableoptions_h
> +
> +#include "univ.i"
> +
> +/** Data structure to hold contents of SYS_TABLE_OPTIONS */
This is a move in the interesting direction. One of the ideas I was toying with
for a few years is to remove .frm files for InnoDB tables.
In MariaDB a storage engine can live without frm files if the engine supports
table discovery. This is rather easy to implement, but InnoDB does not store
the complete table definition, so the discovery will be lossy.
Now with your SYS_TABLE_OPTIONS table we're getting closer to having the complete
table definition saved inside InnoDB. And one step closer to frm-less InnoDB tables.
> +struct dict_tableoptions_t{
> + table_id_t table_id;
why do you need table_id in the option structure?
> + /* true if table page compressed */
> + bool page_compressed;
> + /* Used compression level if set */
> + ulonglong page_compression_level;
> + /* dict0types.h: ATOMIC_WRITES_DEFAULT, _ON, or _OFF */
> + atomic_writes_t atomic_writes;
> + /* fil0crypt.h: FIL_SPACE_ENCRYPTION_DEFAULT, _ON, or _OFF */
> + fil_encryption_t encryption;
> + /* Used encryption key identifier if set */
> + ulonglong encryption_key_id;
> + /* true if table options need to be stored */
why wouldn't they need to be?
> + bool need_stored;
> +};
> +
> +/********************************************************************//**
> +This function parses a SYS_TABLE_OPTIONS record, extracts necessary
> +information from the record and returns it to the caller.
> +@return error message or NULL if successfull */
> +UNIV_INTERN
> +const char*
> +dict_process_sys_tableoptions(
> +/*==========================*/
> + mem_heap_t* heap, /*!< in/out: heap memory */
> + const rec_t* rec, /*!< in: current SYS_TABLE_OPTIONS rec */
> + dict_tableoptions_t* table_options); /*!< out: table options */
> +
> +/********************************************************************//**
> +Gets the table options from SYS_TABLE_OPTIONS based on table_id
> +@return true if found, false if not found */
> +UNIV_INTERN
> +bool
> +dict_get_table_options(
> +/*===================*/
> + table_id_t table_id, /*!< in: table id */
> + dict_tableoptions_t* options, /*!< out:table options */
> + bool fixed); /*!< in: can we fix the
> + dictionary ? */
> +
> +/********************************************************************//**
> +Gets the table options from SYS_TABLE_OPTIONS
> +@return true if found, false if not found */
> +UNIV_INTERN
> +bool
> +dict_get_table_options(
> +/*===================*/
> + dict_table_t* table, /*!< in/out: table */
> + bool fixed); /*!< in: can we fix the
> + dictionary ? */
> +
> +/********************************************************************//**
> +Update the record in SYS_TABLE_OPTIONS.
> +@return DB_SUCCESS if OK, dberr_t if the update failed */
> +UNIV_INTERN
> +dberr_t
> +dict_update_tableoptions(
> +/*=====================*/
> + const dict_table_t* table); /*!< in: table object */
> +
> +/********************************************************************//**
> +Insert record into SYS_TABLE_OPTIONS
> +@return DB_SUCCESS if OK, dberr_t if the insert failed */
> +UNIV_INTERN
> +dberr_t
> +dict_insert_tableoptions(
> +/*=====================*/
> + const dict_table_t* table, /*!< in: table object */
> + bool fixed); /*!< in: can we fix the
> + dictionary ? */
> +
> +/********************************************************************//**
> +Update the table flags in SYS_TABLES.
> +@return DB_SUCCESS if OK, dberr_t if the update failed */
> +UNIV_INTERN
> +dberr_t
> +dict_update_table_flags(
> +/*=====================*/
> + const dict_table_t* table, /*!< in: table object */
> + bool fixed); /*!< in: can we fix the
> + dictionary ? */
> +
> +/********************************************************************//**
> +Delete the record in SYS_TABLE_OPTIONS.
> +@return DB_SUCCESS if OK, dberr_t if the update failed */
> +UNIV_INTERN
> +dberr_t
> +dict_delete_tableoptions(
> +/*=====================*/
> + const dict_table_t* table, /*!< in: table object */
> + trx_t* trx, /*!< in: trx */
> + bool fixed); /*!< in: can we fix the
> + dictionary ? */
> +
> +#endif /* dict0tableoptions_h */
> +
> diff --git a/storage/innobase/dict/dict0tableoptions.cc b/storage/innobase/dict/dict0tableoptions.cc
> new file mode 100644
> index 0000000..527c5e3
> --- /dev/null
> +++ b/storage/innobase/dict/dict0tableoptions.cc
> @@ -0,0 +1,482 @@
> +/*****************************************************************************
> +
> +Copyright (c) 2016, MariaDB Corporation.
> +
> +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 the Free Software
> +Foundation; version 2 of the License.
> +
> +This program is distributed in the hope that it will be useful, but WITHOUT
> +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
> +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License along with
> +this program; if not, write to the Free Software Foundation, Inc.,
> +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
> +
> +*****************************************************************************/
> +
> +/**************************************************//**
> +@file dict/dict0tableoptions.cc
> +Function implementations for the system table SYS_TABLE_OPTIONS
> +
> +Created 22/01/2006 Jan Lindström
> +*******************************************************/
> +
> +#include "mysql_version.h"
> +#include "btr0pcur.h"
> +#include "btr0btr.h"
> +#include "page0page.h"
> +#include "mach0data.h"
> +#include "dict0dict.h"
> +#include "dict0boot.h"
> +#include "dict0stats.h"
> +#include "dict0mem.h"
> +#include "rem0cmp.h"
> +#include "srv0start.h"
> +#include "srv0srv.h"
> +#include "dict0crea.h"
> +#include "dict0priv.h"
> +#include "ha_prototypes.h" /* innobase_casedn_str() */
> +#include "fts0priv.h"
> +#include "dict0tableoptions.h"
> +#include "dict0load.h"
> +#include "row0mysql.h"
> +
> +/********************************************************************//**
> +This function parses a SYS_TABLE_OPTIONS record, extracts necessary
> +information from the record and returns it to the caller.
> +@return error message or NULL if successfull */
> +UNIV_INTERN
> +const char*
> +dict_process_sys_tableoptions(
> +/*==========================*/
> + mem_heap_t* heap, /*!< in/out: heap memory */
> + const rec_t* rec, /*!< in: current SYS_TABLE_OPTIONS rec */
> + dict_tableoptions_t* table_options) /*!< out: table options */
> +{
> + const byte* field;
> + ulint len=0;
> +
> + if (rec_get_deleted_flag(rec, 0)) {
> + return("delete-marked record in SYS_TABLE_OPTIONS");
> + }
> +
> + if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_TABLEOPTIONS) {
> + return("wrong number of columns in SYS_TABLE_OPTIONS record");
> + }
How does it work future-wise? How do you add new options to the table, say, in 10.2?
> diff --git a/storage/innobase/api/api0api.cc b/storage/innobase/api/api0api.cc
> index 739ea9f..4af32a5 100644
> --- a/storage/innobase/api/api0api.cc
> +++ b/storage/innobase/api/api0api.cc
> @@ -270,7 +271,7 @@ ib_open_table_by_name(
> dict_table_t* table;
>
> table = dict_table_open_on_name(name, FALSE, FALSE,
> - DICT_ERR_IGNORE_NONE);
> + DICT_ERR_IGNORE_NONE, NULL);
What does that mean that you pass NULL as the last argument?
You won't know whether the table is encrypted or compressed - so you,
basically won't be able to read the table?
>
> if (table != NULL && table->ibd_file_missing) {
> table = NULL;
> diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
> index f4e7c0d..f1953af 100644
> --- a/storage/innobase/buf/buf0buf.cc
> +++ b/storage/innobase/buf/buf0buf.cc
> @@ -4677,7 +4678,7 @@ buf_page_io_complete(
> "However key management plugin or used key_id %lu is not found or"
> " used encryption algorithm or method does not match."
> " Can't continue opening the table.",
> - bpage->key_version);
> + bpage->space, bpage->key_version);
This seems to be an unrelated bugfix. And it seems to be already fixed
in the latest 10.1
>
> if (bpage->space > TRX_SYS_SPACE) {
> if (corrupted) {
> diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
> index 2b72835..13f1ee6 100644
> --- a/storage/innobase/dict/dict0dict.cc
> +++ b/storage/innobase/dict/dict0dict.cc
> @@ -1187,6 +1189,16 @@ dict_table_open_on_name(
>
> /* If table is encrypted return table */
> if (ignore_err == DICT_ERR_IGNORE_NONE
> + && !table->is_encrypted && options) {
hmm, where else table->is_encrypted can be set?
> + if ((options->encryption == FIL_SPACE_ENCRYPTION_ON ||
> + (options->encryption == FIL_SPACE_ENCRYPTION_DEFAULT && srv_encrypt_tables))
> + && !encryption_key_id_exists((unsigned int)options->encryption_key_id)) {
> + table->is_encrypted = true;
> + }
> + }
> +
> + /* If table is encrypted return table */
> + if (ignore_err == DICT_ERR_IGNORE_NONE
> && table->is_encrypted) {
> /* Make life easy for drop table. */
> if (table->can_be_evicted) {
> diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc
> index d8bd0a6..2b732f1 100644
> --- a/storage/innobase/dict/dict0load.cc
> +++ b/storage/innobase/dict/dict0load.cc
> @@ -56,7 +64,8 @@ static const char* SYSTEM_TABLE_NAME[] = {
> "SYS_FOREIGN",
> "SYS_FOREIGN_COLS",
> "SYS_TABLESPACES",
> - "SYS_DATAFILES"
> + "SYS_DATAFILES",
> + "SYS_TABLE_OPTIONS"
May be "SYS_MARIADB_OPTIONS" ? Not really InnoDB sys table style,
I agree, but guarantees no conflicts in the future.
> };
>
> /* If this flag is TRUE, then we will load the cluster index's (and tables')
> @@ -2365,6 +2386,11 @@ dict_load_table(
> btr_pcur_close(&pcur);
> mtr_commit(&mtr);
>
> + if (table && options) {
no need to do if (table), because table is guaranteed to be not NULL here
> + ut_ad(table->table_options);
> + memcpy(table->table_options, options, sizeof(dict_tableoptions_t));
> + }
> +
> if (table->space == 0) {
> /* The system tablespace is always available. */
> } else if (table->flags2 & DICT_TF2_DISCARDED) {
> diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc
> index 1724ac0..39d0d55 100644
> --- a/storage/innobase/dict/dict0mem.cc
> +++ b/storage/innobase/dict/dict0mem.cc
> @@ -85,7 +86,6 @@ dict_mem_table_create(
> mem_heap_t* heap;
>
> ut_ad(name);
> - ut_a(dict_tf_is_valid(flags));
why?
> ut_a(!(flags2 & ~DICT_TF2_BIT_MASK));
>
> heap = mem_heap_create(DICT_HEAP_SIZE);
> diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
> index 928c72c..4f75465 100644
> --- a/storage/innobase/fil/fil0fil.cc
> +++ b/storage/innobase/fil/fil0fil.cc
> @@ -716,27 +717,6 @@ fil_node_open_file(
> ut_error;
> }
>
> - if (UNIV_UNLIKELY(space->flags != flags)) {
> - fprintf(stderr,
> - "InnoDB: Error: table flags are 0x%lx"
> - " in the data dictionary\n"
> - "InnoDB: but the flags in file %s are 0x%lx!\n",
> - space->flags, node->name, flags);
do you still the check somewhere
where SYS_TABLE_OPTIONS is compared with frm?
> -
> - ut_error;
> - }
> -
> - if (UNIV_UNLIKELY(space->flags != flags)) {
> - if (!dict_tf_verify_flags(space->flags, flags)) {
> - fprintf(stderr,
> - "InnoDB: Error: table flags are 0x%lx"
> - " in the data dictionary\n"
> - "InnoDB: but the flags in file %s are 0x%lx!\n",
> - space->flags, node->name, flags);
> - ut_error;
> - }
> - }
> -
> if (size_bytes >= FSP_EXTENT_SIZE * UNIV_PAGE_SIZE) {
> /* Truncate the size to whole extent size. */
> size_bytes = ut_2pow_round(size_bytes,
> @@ -1917,11 +1909,6 @@ fil_check_first_page(
> flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
>
> if (UNIV_PAGE_SIZE != fsp_flags_get_page_size(flags)) {
> - fprintf(stderr,
> - "InnoDB: Error: Current page size %lu != "
> - " page size on page %lu\n",
> - UNIV_PAGE_SIZE, fsp_flags_get_page_size(flags));
> -
why?
> return("innodb-page-size mismatch");
> }
>
> @@ -3379,8 +3362,7 @@ fil_create_new_single_table_tablespace(
> ulint size, /*!< in: the initial size of the
> tablespace file in pages,
> must be >= FIL_IBD_FILE_INITIAL_SIZE */
> - fil_encryption_t mode, /*!< in: encryption mode */
> - ulint key_id) /*!< in: encryption key_id */
> + const dict_table_t* table) /*!< in: table or NULL */
what does NULL mean here?
> {
> os_file_t file;
> ibool ret;
> @@ -3640,6 +3627,140 @@ fil_report_bad_tablespace(
> (ulong) expected_id, (ulong) expected_flags);
> }
>
> +/******************************************************************
> +Set flags for a tablespace */
> +static
> +void
> +fil_space_set_fsp_flags(
> +/*=====================*/
> + ulint id, /*!< in: space id */
> + uint flags) /*!< in: fsp flags */
> +{
> + fil_space_t* space;
> +
> + ut_ad(fil_system);
> +
> + mutex_enter(&fil_system->mutex);
> +
> + space = fil_space_get_by_id(id);
> +
> + if (space != NULL) {
> + space->flags = flags;
> + }
> +
> + mutex_exit(&fil_system->mutex);
> +}
> +
> +/******************************************************************
> +Update tablespace (fsp) flags on page 0
a bit more verbose comment would've been be nice.
like, say that it's used to migrate from mariadb-flags-in-page-0
to sys_table_options approach.
> +@return true if successfull, false if not */
> +static
> +void
> +fil_update_page0(
> +/*=============*/
> + byte* page0, /*!< in: page 0 or NULL */
why do you need this page0 argument if it is *always* NULL?
> + os_file_t data_file, /*!< in: data file */
> + ulint space, /*!< in: space id */
> + ulint flags, /*!< in: old fsp flags */
> + ulint fsp_flags)/*!< in: new fsp flags */
> +{
> + ulint psize = FSP_FLAGS_GET_PAGE_SSIZE_MARIADB(flags);
> + ulint zip_size = FSP_FLAGS_GET_ZIP_SSIZE(flags);
> +
> + if (!psize) {
> + psize = UNIV_PAGE_SIZE_ORIG;
> + }
> +
> + fsp_flags = fsp_flags_set_page_size(fsp_flags, psize);
> +
> + if (flags != fsp_flags) {
> +
> + ib_logf(IB_LOG_LEVEL_INFO,
> + "InnoDB: Adjusted space_id %lu tablespace flags from %lu to %lu.\n",
> + space, flags, fsp_flags);
> +
> + mtr_t mtr;
> + mtr_start(&mtr);
> +
> + if (!page0) {
> + buf_block_t* block = buf_page_get_gen(space,
> + zip_size, 0,
> + RW_X_LATCH,
> + NULL,
> + BUF_GET,
> + __FILE__, __LINE__,
> + &mtr);
> + page0 = buf_block_get_frame(block);
> + }
> +
> + mlog_write_ulint(page0 + FSP_HEADER_OFFSET + FSP_SPACE_FLAGS,
> + fsp_flags, MLOG_4BYTES, &mtr);
> + /* Redo log this as bytewise update to page 0
> + followed by an MLOG_FILE_WRITE_FSP_FLAGS */
> + byte* log_ptr = mlog_open(&mtr, 11 + 8);
> + if (log_ptr != NULL) {
> + log_ptr = mlog_write_initial_log_record_fast(
> + page0,
> + MLOG_FILE_WRITE_FSP_FLAGS,
> + log_ptr, &mtr);
> + mach_write_to_4(log_ptr, fsp_flags);
> + log_ptr += 4;
> + mach_write_to_4(log_ptr, space);
> + log_ptr += 4;
> + mlog_close(&mtr, log_ptr);
> + }
> +
> + mtr_commit(&mtr);
> + lsn_t end_lsn = mtr.end_lsn;
> + buf_flush_init_for_writing(page0, NULL, end_lsn);
> + flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page0);
> + ut_ad(flags == fsp_flags);
> +
> + /* Flush dirty page to the storage */
> + ulint n_pages = 0;
> + ulint sum_pages = 0;
> + bool success = false;
> + do {
> + success = buf_flush_list(ULINT_MAX, end_lsn, &n_pages);
> + buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST);
> + sum_pages += n_pages;
> + } while (!success);
> +
> + fil_space_set_fsp_flags(space, fsp_flags);
> + }
> +}
> +
> +/******************************************************************
> +Parse a MLOG_FILE_WRITE_FSP_FLAGS log entry
> +@return position on log buffer */
> +UNIV_INTERN
> +byte*
> +fil_parse_write_fsp_flags(
> +/*======================*/
> + byte* ptr, /*!< in: Log entry start */
> + byte* end_ptr,/*!< in: Log entry end */
> + buf_block_t* block) /*!< in: buffer block */
> +{
> + /* check that redo log entry is complete */
> + uint entry_size = 4 + 4; // size of flags + space_id
> +
> + if (end_ptr - ptr < entry_size){
> + return NULL;
> + }
> +
> + ulint flags = mach_read_from_4(ptr);
> + ptr += 4;
> + ulint space_id = mach_read_from_4(ptr);
> + ptr += 4;
> +
> + ut_a(fsp_flags_is_valid(flags));
> +
> + /* update fil_space memory cache with flags */
> + fil_space_set_fsp_flags(space_id, flags);
> +
> + return ptr;
> +}
> +
> /********************************************************************//**
> Tries to open a single-table tablespace and optionally checks that the
> space id in it is correct. If this does not succeed, print an error message
> diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc
> index cc4a4f9..f81fd31 100644
> --- a/storage/innobase/fts/fts0fts.cc
> +++ b/storage/innobase/fts/fts0fts.cc
> @@ -1981,7 +1982,13 @@ fts_create_one_index_table(
> dict_mem_table_add_col(new_table, heap, "ilist", DATA_BLOB,
> 4130048, 0);
>
> - error = row_create_table_for_mysql(new_table, trx, false, FIL_SPACE_ENCRYPTION_DEFAULT, FIL_DEFAULT_ENCRYPTION_KEY);
> + /* Get default encryption key id if set */
> + if (new_table && new_table->table_options &&
new_table cannot be NULL here
> + new_table->table_options->encryption_key_id == 0) {
> + new_table->table_options->encryption_key_id = innobase_get_default_encryption_key_id(trx);
do you want to do it here? may be better do it in row_create_table_for_mysql?
> + }
> +
> + error = row_create_table_for_mysql(new_table, trx, false);
>
> if (error != DB_SUCCESS) {
> trx->error_state = error;
> diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
> index 527c5be..3d5aaee 100644
> --- a/storage/innobase/handler/ha_innodb.cc
> +++ b/storage/innobase/handler/ha_innodb.cc
> @@ -5459,6 +5460,43 @@ ha_innobase::innobase_initialize_autoinc()
> }
>
> /*****************************************************************//**
> +Set up the table options structure based on frm. */
> +UNIV_INTERN
> +void
> +ha_innobase::set_table_options(
> +/*===========================*/
> + THD* thd, /*!< in: thd */
> + TABLE* table, /*!< in: table */
> + dict_tableoptions_t* options) /*!< in: table options */
> +{
> + ha_table_option_struct* moptions = table->s->option_struct;
> +
> + options->page_compressed = moptions->page_compressed;
> + options->page_compression_level = moptions->page_compression_level;
> + options->atomic_writes = (atomic_writes_t)moptions->atomic_writes;
> + options->encryption = (fil_encryption_t)moptions->encryption;
> + options->encryption_key_id = moptions->encryption_key_id;
> +
> + if (options->encryption_key_id == 0) {
> + options->encryption_key_id = THDVAR(thd, default_encryption_key_id);
> + }
No, this is not how it works. The *server* sets the encryption_key_id
based on the default_encryption_key_id. The engine does not
need to bother.
> +
> + if (options->page_compression_level == 0) {
> + options->page_compression_level = page_zip_level;
> + }
same here
> +
> + /* Table options should be stored if they are not same as
> + defaults */
> + if (moptions->page_compressed ||
> + (options->atomic_writes == ATOMIC_WRITES_ON ||
> + options->atomic_writes == ATOMIC_WRITES_OFF) ||
> + (options->encryption == FIL_SPACE_ENCRYPTION_OFF ||
> + options->encryption == FIL_SPACE_ENCRYPTION_ON)) {
> + options->need_stored = true;
> + }
> +}
> +
> +/*****************************************************************//**
> Creates and opens a handle to a table which already exists in an InnoDB
> database.
> @return 1 if error, 0 if success */
> @@ -11603,6 +11647,10 @@ ha_innobase::check_table_options(
> return "ENCRYPTION_KEY_ID";
>
> }
> +
> + table_options->need_stored = true;
> +
> + table_options->need_stored = true;
typo
> }
>
> /* Check atomic writes requirements */
> @@ -20422,3 +20483,25 @@ ib_push_warning(
> my_free(buf);
> va_end(args);
> }
> +
> +/********************************************************************//**
> +Helper function to get default_encryption_key_id from THD
> +(trx->mysql_thd).
> +@return default_encryption_key_id from THD or
> +FIL_DEFAULT_ENCRYPTION_KEY */
> +UNIV_INTERN
> +ulint
> +innobase_get_default_encryption_key_id(
See comment above. You _probably_ don't need this method
> +/*===================================*/
> + trx_t* trx) /*! in: trx */
> +{
> + ulint key_id = FIL_DEFAULT_ENCRYPTION_KEY;
> +
> + if (trx && trx->mysql_thd) {
> + THD *thd = (THD *)trx->mysql_thd;
> +
> + key_id = THDVAR(thd, default_encryption_key_id);
> + }
> +
> + return (key_id);
> +}
> diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc
> index ef69e7d..75baa9d 100644
> --- a/storage/innobase/handler/i_s.cc
> +++ b/storage/innobase/handler/i_s.cc
> @@ -9130,3 +9129,230 @@ UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_semaphore_waits =
> STRUCT_FLD(version_info, INNODB_VERSION_STR),
> STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_BETA),
> };
> +
> +/** SYS_TABLE_OPTIONS ************************************************/
> +/* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_TABLE_OPTIONS */
> +static ST_FIELD_INFO innodb_sys_tableoptions_fields_info[] =
Good idea!
> +{
> + // SYS_TABLE_OPTIONS_TABLE_ID 0
> + {STRUCT_FLD(field_name, "TABLE_ID"),
> + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS),
> + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + // SYS_TABLE_OPTIONS_PAGE_COMPRESSED 1
> + {STRUCT_FLD(field_name, "PAGE_COMPRESSED"),
> + STRUCT_FLD(field_length, 7),
> + STRUCT_FLD(field_type, MYSQL_TYPE_STRING),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, 0),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + // SYS_TABLE_OPTIONS_PAGE_COMPRESSION_LEVEL 2
> + {STRUCT_FLD(field_name, "PAGE_COMPRESSION_LEVEL"),
> + STRUCT_FLD(field_length, MY_INT32_NUM_DECIMAL_DIGITS),
> + STRUCT_FLD(field_type, MYSQL_TYPE_LONG),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + // SYS_TABLE_OPTIONS_ATOMIC_WRITES 3
> + {STRUCT_FLD(field_name, "ATOMIC_WRITES"),
> + STRUCT_FLD(field_length, 9),
> + STRUCT_FLD(field_type, MYSQL_TYPE_STRING),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, 0),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + // SYS_TABLE_OPTIONS_ENCRYPTED 4
> + {STRUCT_FLD(field_name, "ENCRYPTED"),
> + STRUCT_FLD(field_length, 9),
> + STRUCT_FLD(field_type, MYSQL_TYPE_STRING),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, 0),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + // SYS_TABLE_OPTIONS_ENCRYPTION_KEY_ID 5
> + {STRUCT_FLD(field_name, "ENCRYPTION_KEY_ID"),
> + STRUCT_FLD(field_length, MY_INT32_NUM_DECIMAL_DIGITS),
> + STRUCT_FLD(field_type, MYSQL_TYPE_LONG),
> + STRUCT_FLD(value, 0),
> + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED),
> + STRUCT_FLD(old_name, ""),
> + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)},
> +
> + END_OF_ST_FIELD_INFO
> +};
> +
> +/*******************************************************************//**
> +Function to go through each record in SYS_TABLE_OPTIONS table, and fill the
> +information_schema.innodb_sys_table_options table with related table information
> +@return 0 on success */
> +static
> +int
> +i_s_dict_fill_sys_table_options(
> +/*============================*/
> + THD* thd, /*!< in: thread */
> + TABLE_LIST* tables, /*!< in/out: tables to fill */
> + Item* ) /*!< in: condition (not used) */
> +{
> + btr_pcur_t pcur;
> + const rec_t* rec;
> + mem_heap_t* heap;
> + mtr_t mtr;
> +
> + DBUG_ENTER("i_s_sys_table_options_fill_table");
> + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
> +
> + /* deny access to user without PROCESS_ACL privilege */
> + if (check_global_access(thd, PROCESS_ACL, true)) {
> + DBUG_RETURN(0);
> + }
> +
> + heap = mem_heap_create(1000);
> + mutex_enter(&(dict_sys->mutex));
> + mtr_start(&mtr);
> +
> + rec = dict_startscan_system(&pcur, &mtr, SYS_TABLE_OPTIONS);
> +
> + while (rec) {
> + const char* err_msg;
> + dict_tableoptions_t options;
> +
> + /* Create and populate a dict_tableoptions_t structure with
> + information from SYS_TABLE_OPTIONS row */
> + memset(&options, 0, sizeof(dict_tableoptions_t));
> +
> + err_msg = dict_process_sys_tableoptions(
> + heap, rec, &options);
> +
> + mtr_commit(&mtr);
> + mutex_exit(&dict_sys->mutex);
> +
> + if (!err_msg) {
> + Field** fields = tables->table->field;
> +
> + OK(fields[SYS_TABLE_OPTIONS_TABLE_ID]->store((longlong) options.table_id));
> + OK(fields[SYS_TABLE_OPTIONS_PAGE_COMPRESSION_LEVEL]->store((ulint)options.page_compression_level));
> + OK(fields[SYS_TABLE_OPTIONS_ENCRYPTION_KEY_ID]->store((ulint)options.encryption_key_id));
> +
> + if (options.page_compressed) {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_PAGE_COMPRESSED], "YES"));
> + } else {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_PAGE_COMPRESSED], "NO"));
> + }
> +
> + if (options.encryption == FIL_SPACE_ENCRYPTION_DEFAULT) {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ENCRYPTED], "DEFAULT"));
> + } else if (options.encryption == FIL_SPACE_ENCRYPTION_ON) {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ENCRYPTED], "ON"));
> + } else {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ENCRYPTED], "OFF"));
> + }
> +
> + if (options.atomic_writes == ATOMIC_WRITES_DEFAULT) {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ATOMIC_WRITES], "DEFAULT"));
> + } else if (options.atomic_writes == ATOMIC_WRITES_ON) {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ATOMIC_WRITES], "ON"));
> + } else {
> + OK(field_store_string(fields[SYS_TABLE_OPTIONS_ATOMIC_WRITES], "OFF"));
> + }
> +
> + OK(schema_table_store_record(thd, tables->table));
> +
> + } else {
> + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
> + ER_CANT_FIND_SYSTEM_REC, "%s",
> + err_msg);
> + }
> +
> + mem_heap_empty(heap);
> +
> + /* Get the next record */
> + mutex_enter(&dict_sys->mutex);
> + mtr_start(&mtr);
> + rec = dict_getnext_system(&pcur, &mtr);
> + }
> +
> + mtr_commit(&mtr);
> + mutex_exit(&dict_sys->mutex);
> + mem_heap_free(heap);
> +
> + DBUG_RETURN(0);
> +}
> +/*******************************************************************//**
> +Bind the dynamic table INFORMATION_SCHEMA.INNODB_SYS_TABLE_OPTIONS
> +@return 0 on success */
> +static
> +int
> +innodb_sys_table_options_init(
> +/*============================*/
> + void* p) /*!< in/out: table schema object */
> +{
> + ST_SCHEMA_TABLE* schema;
> +
> + DBUG_ENTER("innodb_sys_table_options_init");
> +
> + schema = (ST_SCHEMA_TABLE*) p;
> +
> + schema->fields_info = innodb_sys_tableoptions_fields_info;
> + schema->fill_table = i_s_dict_fill_sys_table_options;
> +
> + DBUG_RETURN(0);
> +}
> +
> +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_table_options =
> +{
> + /* the plugin type (a MYSQL_XXX_PLUGIN value) */
> + /* int */
> + STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN),
> +
> + /* pointer to type-specific plugin descriptor */
> + /* void* */
> + STRUCT_FLD(info, &i_s_info),
> +
> + /* plugin name */
> + /* const char* */
> + STRUCT_FLD(name, "INNODB_SYS_TABLE_OPTIONS"),
> +
> + /* plugin author (for SHOW PLUGINS) */
> + /* const char* */
> + STRUCT_FLD(author, maria_plugin_author),
> +
> + /* general descriptive text (for SHOW PLUGINS) */
> + /* const char* */
> + STRUCT_FLD(descr, "InnoDB SYS_TABLE_OPTIONS"),
> +
> + /* the plugin license (PLUGIN_LICENSE_XXX) */
> + /* int */
> + STRUCT_FLD(license, PLUGIN_LICENSE_GPL),
> +
> + /* the function to invoke when plugin is loaded */
> + /* int (*)(void*); */
> + STRUCT_FLD(init, innodb_sys_table_options_init),
> +
> + /* the function to invoke when plugin is unloaded */
> + /* int (*)(void*); */
> + STRUCT_FLD(deinit, i_s_common_deinit),
> +
> + /* plugin version (for SHOW PLUGINS) */
> + /* unsigned int */
> + STRUCT_FLD(version, INNODB_VERSION_SHORT),
> +
> + /* struct st_mysql_show_var* */
> + STRUCT_FLD(status_vars, NULL),
> +
> + /* struct st_mysql_sys_var** */
> + STRUCT_FLD(system_vars, NULL),
> +
> + /* Maria extension */
> + STRUCT_FLD(version_info, INNODB_VERSION_STR),
> + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_BETA),
> +};
> diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h
> index b15d364..fa65884 100644
> --- a/storage/innobase/include/dict0dict.h
> +++ b/storage/innobase/include/dict0dict.h
> @@ -944,15 +948,8 @@ dict_tf_set(
> ulint* flags, /*!< in/out: table */
> rec_format_t format, /*!< in: file format */
> ulint zip_ssize, /*!< in: zip shift size */
> - bool remote_path, /*!< in: table uses DATA DIRECTORY
> + bool remote_path); /*!< in: table uses DATA DIRECTORY
> */
> - bool page_compressed,/*!< in: table uses page compressed
> - pages */
> - ulint page_compression_level, /*!< in: table page compression
> - level */
> - ulint atomic_writes) /*!< in: table atomic
> - writes option value*/
> - __attribute__((nonnull));
Why do you remove nonnull attributes? They help gcc to optimize the code
> /********************************************************************//**
> Convert a 32 bit integer table flags to the 32 bit integer that is
> written into the tablespace header at the offset FSP_SPACE_FLAGS and is
> diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic
> index a3a3446..3580081 100644
> --- a/storage/innobase/include/dict0dict.ic
> +++ b/storage/innobase/include/dict0dict.ic
> @@ -685,11 +629,7 @@ dict_sys_tables_type_validate(
> ulint zip_ssize = DICT_TF_GET_ZIP_SSIZE(type);
> ulint atomic_blobs = DICT_TF_HAS_ATOMIC_BLOBS(type);
> ulint unused = DICT_TF_GET_UNUSED(type);
> - ulint page_compression = DICT_TF_GET_PAGE_COMPRESSION(type);
> - ulint page_compression_level = DICT_TF_GET_PAGE_COMPRESSION_LEVEL(type);
By the way, what about page compression in MySQL InnoDB?
where is that stored? will you support those tables?
> - ulint atomic_writes = DICT_TF_GET_ATOMIC_WRITES(type);
> -
> - ut_a(atomic_writes <= ATOMIC_WRITES_OFF);
> + ulint unused_mariadb = DICT_TF_GET_UNUSED_MARIADB(type);
>
> /* The low order bit of SYS_TABLES.TYPE is always set to 1.
> If the format is UNIV_FORMAT_B or higher, this field is the same
> diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h
> index 7fe0cc8..bc7f317 100644
> --- a/storage/innobase/include/fil0fil.h
> +++ b/storage/innobase/include/fil0fil.h
> @@ -39,7 +39,7 @@ Created 10/25/1995 Heikki Tuuri
> #include "ibuf0types.h"
> #include "log0log.h"
> #endif /* !UNIV_HOTBACKUP */
> -
> +#include "my_crypt.h"
why?
> #include <list>
>
> // Forward declaration
> diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc
> index d7b37b5..29ce93d 100644
> --- a/storage/innobase/srv/srv0start.cc
> +++ b/storage/innobase/srv/srv0start.cc
> @@ -2537,7 +2543,16 @@ innobase_start_or_create_for_mysql(void)
>
> sys_datafiles_created = true;
>
> - /* This function assumes that SYS_DATAFILES exists */
> + err = dict_create_or_check_sys_table_options();
> +
> + if (err != DB_SUCCESS) {
> + return(err);
> + }
> +
> + sys_table_options_created = true;
> +
> + /* This function assumes that SYS_DATAFILES
> + and SYS_TABLE_OPTIONS exists */
"exist"
> dict_check_tablespaces_and_store_max_id(dict_check);
> }
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
3
Hi everyone,
I am Charles Dirk, also Razor on IRC and am glad to be selected as a
Google summer of Code student with MariaDB. I will be working on Providing
GTID support in MHA and I will be mentored by Byte. I look forward to
contributing to and learning much from the MariaDB developer community this
summer. I am currently reading some documentation on KB to get more
pointers on this task while waiting to hear from my Mentor.
Cheers,
Charles.
2
1
Hello everyone!
I was selected for GSoC to work with MariaDB, with task NO PAD Collatons.
Me profile on github is medvdanil, and i'm planning to code in my own
branch 10.2-MDEV-9711 or 10.1-MDEV-9711.
Which version I should select 10.1 or 10.2?
And I ask to give me access to create one of those branches and commit to
it.
Regards,
Daniil Medvedev
4
4

Re: [Maria-developers] 95e1c46: MDEV-9898 SET ROLE NONE can crash mysqld.
by Sergei Golubchik 02 May '16
by Sergei Golubchik 02 May '16
02 May '16
Hi, Alexey!
On May 02, Alexey Botchkov wrote:
> Fine.
> But can you please explain the logic here? When connected to the server,
> the user is authenticated with the
> find_user_or_anon(). But when we check permissions for the SET ROLE NONE,
> we use find_user_wild().
> Why is that?
Because 'user' is thd->security_ctx->priv_user in this case, not
thd->security_ctx->user. find_user_or_anon() works with user/host pair,
using wildcard matching for both user and host values.
While find_user_exact() works with priv_user/priv_host pairs, using
exact matching for both user and host values.
The very weird find_user_wild() works with inconsistent priv_user/host
pairs, using wildcards only for the host, not for the user.
This pair make no sense to me, but this is the historical MySQL behavior
that I didn't risk to break :(
It's MDEV-5233
Regards,
Sergei
1
0

01 May '16
Hi everyone!
My name is Galina Shalygina and I was selected for GSoC to work with
MariaDB company. I'm from St Petersburg, at Russia and I study at St
Petersburg State University with major in Analytical Information Systems.
I'm in my third year at the University. My nick on IRC is galina and on
github it's shagalla.
I've already worked with MariaDB Server, and my contribution on
non-recursive CTE was accepted by MariaDB. Now I'm finishing and
implementation of recursive CTE.
My project for GsoC 2016 is called "Pushing conditions into non-mergeable
views and derived tables in MariaDB ". I plan to implement the
optimizations that push conditions into non-mergeable (such as grouping)
views/derived tables. I will be mentored by igor_seattle and spetrunia.
During the community bonding period I'm going to:
1. Clone server code and build it;
2. Read documentation;
3. Study all developments on my theme;
4. Start some kind of blog where I'll write reports during work period;
5. For this project I will need to be able to clone expressions, so I'll
start to work on solving this problem.
I'm looking forward to working on this project. It would be a great
experience for me!
Best wishes,
Galina Shalygina.
1
0

Re: [Maria-developers] f9f290b: MDEV-9851: CREATE USER w/o IDENTIFIED BY clause causes crash when using cracklib plugin
by Sergei Golubchik 29 Apr '16
by Sergei Golubchik 29 Apr '16
29 Apr '16
Hi, Nirbhay!
On Mar 31, Nirbhay Choubey wrote:
> revision-id: f9f290b6828eeb57cba611d006d2a9301dc52244 (mariadb-10.1.13-3-gf9f290b)
> parent(s): f4d5fe277599da4549c97c660f324c88cf9a2542
> author: Nirbhay Choubey
> committer: Nirbhay Choubey
> timestamp: 2016-03-31 18:03:44 -0400
> message:
>
> MDEV-9851: CREATE USER w/o IDENTIFIED BY clause causes crash when using cracklib plugin
>
> Add a check for NULL password.
>
> diff --git a/plugin/cracklib_password_check/cracklib_password_check.c b/plugin/cracklib_password_check/cracklib_password_check.c
> index c593173..c192cdf 100644
> --- a/plugin/cracklib_password_check/cracklib_password_check.c
> +++ b/plugin/cracklib_password_check/cracklib_password_check.c
> @@ -33,7 +33,8 @@ static int crackme(MYSQL_LEX_STRING *username, MYSQL_LEX_STRING *password)
> if ((host= strchr(user, '@')))
> *host++= 0;
>
> - if ((res= FascistCheckUser(password->str, dictionary, user, host)))
> + if ((password->str == NULL) || // No password
> + (res= FascistCheckUser(password->str, dictionary, user, host)))
> {
> my_printf_error(ER_NOT_VALID_PASSWORD, "cracklib: %s",
> MYF(ME_JUST_WARNING), res);
You forgot to fix the simple_password_check plugin. And if all plugins
need to do the same check - it's a strong indication that this should've
been done in the server.
So, please, fix this in sql_acl.cc instead. Like this:
- struct validation_data data= { &user->user, &user->pwtext };
+ struct validation_data data= { &user->user, user->pwtext.str ? &user->pwtext : &empy_lex_str };
Ok to push with this fix and your test case.
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1

Re: [Maria-developers] 95e1c46: MDEV-9898 SET ROLE NONE can crash mysqld.
by Sergei Golubchik 29 Apr '16
by Sergei Golubchik 29 Apr '16
29 Apr '16
Hi, Alexey!
On Apr 28, Alexey Botchkov wrote:
> revision-id: 95e1c46c1709e7244332a025a527cacceab7da60 (mariadb-10.1.13-14-g95e1c46)
> parent(s): 646c4cea58afbb369021a3d7b5ccbbf6aed708d4
> committer: Alexey Botchkov
> timestamp: 2016-04-28 13:36:36 +0400
> message:
>
> MDEV-9898 SET ROLE NONE can crash mysqld.
> The check_user_can_set_role() used find_user_exact() to get the
> permissions for the SET ROLE NONE command.
> Which returned NULL too often, for instance when user
> authenticated as 'user'@'%'.
> Now we use find_user_or_anon() instead.
No, this is wrong. SET ROLE NONE should use the same rule as
SET ROLE name.
And the latter uses acl_user->wild_eq(user, host, ip).
Thus SET ROLE NONE should find the user with ACL_USER::wild_eq, that is,
it should use find_user_wild().
Ok to push with find_user_wild().
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
Hello,
there is an outstanding request in docker-library/mariadb for Alpine
support:
https://github.com/docker-library/mariadb/issues/53
What I know is that we currently don't build packages for Alpine. Alpine is
going to be the platform of choice for Docker containers in the future (you
can read about the various advantages of Alpine here:
https://www.brianchristner.io/docker-is-moving-to-alpine-linux/)
Federico Razzoli has managed to make MariaDB 10.1 work in Alpine with some
caveats (no tokudb, no oqgraph, no galera). You can look at his work here
https://hub.docker.com/r/santec/mariadb-alpine/. I admit I don't know about
portability of Galera lib on Alpine - should probably talk to Codership
about that.
Is there any way we can make this happen?
1
0

[Maria-developers] Why reserved keywords: UTC_TIME, UTC_DATE, UTC_DATETIME
by Alexander Barkov 29 Apr '16
by Alexander Barkov 29 Apr '16
29 Apr '16
Hello,
I noticed that UTC_TIME, UTC_DATE, UTC_DATETIME are reserved keywords:
MariaDB [test]> SELECT * FROM utc_time;
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MariaDB server version for the right
syntax to use near 'utc_time' at line 1
I think they should not.
- These keywords are not mentioned in the SQL standard
- They are not involved in any complex grammar that would prevent them
from being non-reserved keywords
Looks like a bug...
Any objections to make them non-reserved keywords in 10.2?
6
8

27 Apr '16
Hi, Shubham!
On Apr 24, Shubham Barai wrote:
> Hi Sergei,
>
> I have created github repository with a new branch for my prototype.
Great, what's the url?
btw, please, use "Reply to all", so that the whole list could see your
progress. Also google recommends you to blog regularly about what you're
doing:
http://write.flossmanuals.net/gsocstudentguide/time-management-for-students/
but if you don't like that, you can send weekly progress report emails
instead. Whatever suits you best.
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
6
Hi everyone,
I am Charles Muurmu also known as laserlight on IRC and I was selected for
GSoC 2016 to work on migrating MariaDB Cassandra storage engine from the
Thrift API to Datastax driver. I will be mentored by Spetrunia. My goals
for this community bonding period will be get myself ready by accomplishing
the following tasks:
Fork and clone the server code.
Try to build the code.
Setup a blog on which I will post weekly reports on progress.
Study the Datastax C++ driver API and documentation.
Study the ha_cassandra.xx code and storage engines architecture.
I believe these will prepare me for my first task when coding begins which
will be to integrate the Datastax C++ driver into the MariaDB Server build
system.
I equally look forward to more instructions from my Mentor on how we will
work on this task and lastly, I would love to congratulate the other
students selected with MariaDB.
Cheers,
laserlight.
1
0

Re: [Maria-developers] [Commits] c204e9b: MDEV-9610 Trigger on normal table can't insert into CONNECT engine table - Access Denied
by Alexander Barkov 26 Apr '16
by Alexander Barkov 26 Apr '16
26 Apr '16
Hi Sergei,
On 04/25/2016 08:42 PM, serg(a)mariadb.org wrote:
> revision-id: c204e9bd63e7f6ce40e83b1f7fc0b4607a131009 (mariadb-10.0.24-43-gc204e9b)
> parent(s): ce38adddfa91949b30956abd0e4cab201effcdef
> author: Sergei Golubchik
> committer: Sergei Golubchik
> timestamp: 2016-04-25 18:41:31 +0200
> message:
>
> MDEV-9610 Trigger on normal table can't insert into CONNECT engine table - Access Denied
>
> in case of prelocking, don't check table->grant.privilege
> in handler::external_lock(), do it in
> handler::start_stmt().
>
> ---
> storage/connect/ha_connect.cc | 58 ++++++++++++++++++++--
> storage/connect/ha_connect.h | 1 +
> storage/connect/mysql-test/connect/r/grant3.result | 5 ++
> storage/connect/mysql-test/connect/t/grant3.test | 11 ++++
> 4 files changed, 70 insertions(+), 5 deletions(-)
>
> diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
> index 645d000..1032567 100644
> --- a/storage/connect/ha_connect.cc
> +++ b/storage/connect/ha_connect.cc
> @@ -4054,6 +4054,14 @@ int ha_connect::delete_all_rows()
> } // end of delete_all_rows
>
>
> +static void issue_access_denied_error(THD *thd)
> +{
> + status_var_increment(thd->status_var.access_denied_errors);
> + my_error(access_denied_error_code(thd->password), MYF(0),
> + thd->security_ctx->priv_user, thd->security_ctx->priv_host,
> + (thd->password ? ER(ER_YES) : ER(ER_NO)));
> +}
> +
> bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
> {
> const char *db= (dbn && *dbn) ? dbn : NULL;
> @@ -4124,12 +4132,9 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
> */
> if (!table || !table->mdl_ticket || table->mdl_ticket->get_type() == MDL_EXCLUSIVE)
> return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
> - if (table->grant.privilege & FILE_ACL)
> + if (thd->lex->requires_prelocking() || table->grant.privilege & FILE_ACL)
> return false;
> - status_var_increment(thd->status_var.access_denied_errors);
> - my_error(access_denied_error_code(thd->password), MYF(0),
> - thd->security_ctx->priv_user, thd->security_ctx->priv_host,
> - (thd->password ? ER(ER_YES) : ER(ER_NO)));
> + issue_access_denied_error(thd);
> return true;
>
> // This is temporary until a solution is found
> @@ -4146,6 +4151,46 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
> return true;
> } // end of check_privileges
>
> +bool ha_connect::check_cached_privileges(THD *thd)
> +{
> + PTOS options= GetTableOptionStruct();
> + TABTYPE type=GetRealType(options);
> +
> +#ifdef NO_EMBEDDED_ACCESS_CHECKS
> + return false;
> +#endif
> +
> + switch (type) {
> + case TAB_DOS:
> + case TAB_FIX:
> + case TAB_BIN:
> + case TAB_CSV:
> + case TAB_FMT:
> + case TAB_DBF:
> + case TAB_XML:
> + case TAB_INI:
> + case TAB_VEC:
> + case TAB_JSON:
> + if (!options->filename || !*options->filename)
> + return false;
> + /* Fall through to check FILE_ACL */
> + case TAB_ODBC:
> + case TAB_MYSQL:
> + case TAB_DIR:
> + case TAB_MAC:
> + case TAB_WMI:
> + case TAB_OEM:
> + if (table->grant.privilege & FILE_ACL)
> + return false;
> + issue_access_denied_error(thd);
> + return true;
> + default:
The patch looks fine.
I'd suggest two things.
1. Don't use "default"
Can you please list all possible type value instead of "default"?
Otherwise, when a new table type is added the next time, it will be
easier to forget to update check_cached_privileges() accordingly.
2. Try to avoid duplicate code.
Btw, I'd probably try to remove the duplicate switch,
by adding a new method like this:
enum Extra_acl
{
CONNECT_SECURE_FILE_PATH= 1,
CONNECT_FILE_ACL= 1<<1;
}
int ha_connect::extra_acl_needed(THD *thd, PTOS options)
{
...
switch (GetRealType(options)) {
...
case TAB_DOS:
case TAB_FIX:
case TAB_BIN:
case TAB_CSV:
case TAB_FMT:
case TAB_DBF:
case TAB_XML:
case TAB_INI:
case TAB_VEC:
case TAB_JSON:
return (options->filename && *options->filename) ?
CONNECT_SECURE_FILE_PATH : 0;
case TAB_ODBC:
case TAB_MYSQL:
case TAB_DIR:
case TAB_MAC:
case TAB_WMI:
case TAB_OEM:
#ifdef NO_EMBEDDED_ACCESS_CHECKS
return 0;
#endif
return CONNECT_FILE_ACL;
...
}
...
}
And then reuse it in both ha_connect::check_privilege()
and ha_connect::check_cached_privileges().
The latter would look about like this:
bool ha_connect::check_cached_privileges(THD *thd)
{
PTOS options= GetTableOptionStruct();
if (!(extra_acl_needed(options) & CONNECT_FILE_ACL) ||
(table->grant.privilege & FILE_ACL))
return false;
issue_access_denied_error(thd);
return true;
}
> + return false;
> + }
> + return false;
> +
> +} // end of check_cached_privileges
> +
> // Check that two indexes are equivalent
> bool ha_connect::IsSameIndex(PIXDEF xp1, PIXDEF xp2)
> {
> @@ -4308,6 +4353,9 @@ int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
> PGLOBAL g= GetPlug(thd, xp);
> DBUG_ENTER("ha_connect::start_stmt");
>
> + if (check_cached_privileges(thd))
> + DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
> +
> // Action will depend on lock_type
> switch (lock_type) {
> case TL_WRITE_ALLOW_WRITE:
> diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h
> index 05cc872..fae804b 100644
> --- a/storage/connect/ha_connect.h
> +++ b/storage/connect/ha_connect.h
> @@ -537,6 +537,7 @@ int index_prev(uchar *buf);
>
> protected:
> bool check_privileges(THD *thd, PTOS options, char *dbn);
> + bool check_cached_privileges(THD *thd);
It looks like the number of leading spaces is wrong here.
> MODE CheckMode(PGLOBAL g, THD *thd, MODE newmode, bool *chk, bool *cras);
> char *GetDBfromName(const char *name);
>
> diff --git a/storage/connect/mysql-test/connect/r/grant3.result b/storage/connect/mysql-test/connect/r/grant3.result
> new file mode 100644
> index 0000000..2f9d37b
> --- /dev/null
> +++ b/storage/connect/mysql-test/connect/r/grant3.result
> @@ -0,0 +1,5 @@
> +create table tcon (i int) engine=Connect table_type=DOS file_name='tcon.dos';
> +create table tin (i int);
> +create trigger tr after insert on tin for each row insert into tcon values (new.i);
> +insert into tin values (1);
> +drop table tin,tcon;
> diff --git a/storage/connect/mysql-test/connect/t/grant3.test b/storage/connect/mysql-test/connect/t/grant3.test
> new file mode 100644
> index 0000000..9f05ca7
> --- /dev/null
> +++ b/storage/connect/mysql-test/connect/t/grant3.test
> @@ -0,0 +1,11 @@
> +#
> +# MDEV-9610 Trigger on normal table can't insert into CONNECT engine table - Access Denied
> +#
> +create table tcon (i int) engine=Connect table_type=DOS file_name='tcon.dos';
> +create table tin (i int);
> +create trigger tr after insert on tin for each row insert into tcon values (new.i);
> +insert into tin values (1);
> +drop table tin,tcon;
> +
> +let datadir=`select @@datadir`;
> +remove_file $datadir/test/tcon.dos;
> _______________________________________________
> commits mailing list
> commits(a)mariadb.org
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
>
2
1

26 Apr '16
Hi!
>>> Any suggestions for how this is supposed to work? Or is it just broken
>>> by design, but saved because normally slave threads do not need to
>>> access SHOW STATUS or system variables?
<cut>
> Hm. So I went through all of the code that references LOCK_active_mi to try
> and understand this. It seems there are two main uses:
>
> 1. As you say, to serialise admin commands. I think the list is: end_slave()
> (server shutdown); CHANGE MASTER; START/STOP SLAVE; START/STOP ALL SLAVES;
> RESET SLAVE; FLUSH SLAVE.
>
> 2. To protect access to master_info_index, eg. to prevent a Master_info from
> disappearing while it is accessed.
When reading about this, I think it would be better to have a counter
in Master_info if someone is using it and only delete if if the
counter is zero. Would be trivial to add an increment of the counter
in get_master_info().
The counter could either be protected by the LOCK_active_mi or one of
the mutexes in Master_info like data_lock.
> There is a comment in rpl_rli.h that LOCK_active_mi protects
> Relay_log_info::inited, but I did not understand it - maybe it is wrong?
Setting inited is indirectly protected by LOCK_active_mi, as
init_all_master_info() which calls init_master_info(), is protected by
LOCK_active_mi.
Looking at the code, I didn't however see any need to protect inited
as this is an internal
flag that is always 1 after start. It's main usage is to avoid some
re-initialization of relay logs on retries.
> So the idea would be to make a new lock for (1), and keep LOCK_active_mi for
> (2).
>
> Actually, using a lock for (1) seems a bad idea - STOP SLAVE can take a long
> time to run, and a mutex makes it unkillable. A condition variable might be
> better (to make STOP SLAVE killable). But I guess that is a separate issue,
> it would not affect the possibility of deadlocks.
Agree that a condition variable would be better, but not critical as
one has already a problem if stop_slave doesn't work.
> Both LOCK_active_mi and the new LOCK_serialize_replication_admin_commands
> must by themselves prevent master_info_index from having elements added or
> removed. This is needed for start_all_slaves() and stop_all_slaves() to work
> correctly, I think.
Why both?
Isn't it enough to first take LOCK_serialize_replication_admin_commands and
then take LOCK_active_mi if one needs to access master_info_index ?
> Something will need to be done for remove_master_info(). Currently, it also
> tries to stop slave threads (from within free_key_master_info()) while
> holding LOCK_active_mi. It seems that threads should be stopped before
> attempting to remove their mi from master_info_index, probably?
To do that we would need to:
- Add a flag in Master_info that it's not usable anymore and change
this flag only under LOCK_active_mi. get_master_info() could wait (on
a condition) if this flag is set.
- Add a counter that Master_info is in use.
- Add a function to release Master_info.
- Call terminate_slave_threads() outside of free_key_master_info()
> Actually, there would still be the deadlock problem after introducing the
> new lock, because it is possible for a slave thread to run CHANGE MASTER!
> (and I think START SLAVE / RESET SLAVE as well). But that is probably a bug,
> does not seem good that this is possible. I guess these need a check to
> throw an ER_LOCK_OR_ACTIVE_TRANSACTION error, like STOP SLAVE.
Wouldn't the new lock LOCK_serialize_replication_admin_commands fix that?
> I think I also found another lock problem while reading the code, for
> MASTER_POS_WAIT() og SHOW BINLOG EVENTS. They both grab a Master_info under
> LOCK_active_mi, but then proceed to access it after releasing the lock. I do
> not see anything that prevents the Master_info to disappear under their feet
> if RESET SLAVE ALL is run in another thread? But again, that's a separate
> issue, I suppose. Probably there are some other tricky deadlock issues
> lurking here.
I checked the function mysql_show_binlog_events() but could not find
any access to
Master_info after mysql_mutex_unlock(&LOCK_active_mi) was released.
> I don't know. It seems splitting into a new
> LOCK_serialize_replication_admin_commands could solve the deadlock issue,
> maybe, if the other problems mentioned above are addressed. It seems very
> hard to convince oneself that this would not introduce new problems
> somewhere, the locking in replication is really complex and feels very
> fragile :-/ It doesn't really feel like something one would want to do in
> 10.1 (certainly not 10.0), but maybe 10.2?
>
> It's kind of a very edge-case problem (and seems to have been there for a
> _long_ time). Still, hanging the mysqld process hard, requiring kill -9, is
> also not nice...
Adding two new flags, one if master_info is in use and one if it's
unusable, shouldn't be
that hard to make reasonable safe in 10.1
I am now back in Finland and working. Feel free to call me to discuss
this any time.
Regards,
Monty
2
2

[Maria-developers] Problem that some variables can be changed without stopping the slave IO thread
by Kristian Nielsen 25 Apr '16
by Kristian Nielsen 25 Apr '16
25 Apr '16
Monty,
This patch of yours introduced a change where before both slave threads must
be stopped to change @@replicate_events_marked_for_skip, but after only the
SQL thread is checked:
-----------------------------------------------------------------------
commit 572560f38c248d5020f0e63aeb3f8905cd568208
Author: Michael Widenius <monty(a)askmonty.org>
Date: Wed Oct 3 01:44:54 2012 +0300
Changed SHOW_FUNC variabels that don't return SHOW_ARRAY to SHOW_SIMPLE_FUNC.
+ Master_info_index::give_error_if_slave_running()
+
+ @return
+ TRUE If some slave is running. An error is printed
+ FALSE No slave is running
+*/
+
+bool Master_info_index::give_error_if_slave_running()
+{
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
bool
Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
...
- /* Slave threads must be stopped to change the variable. */
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- result= true;
- }
- else
+ if (!master_info_index->give_error_if_slave_running())
-----------------------------------------------------------------------
So instead of checking both IO and SQL thread, now it checks only SQL thread
(mi->rli.slave_running). Why was this change made? Was it by mistake?
Unfortunately, I did not notice this before, so I subsequently used the same
code (give_error_if_slave_running()) for the following variables:
gtid_slave_pos
slave_parallel_threads
slave_domain_parallel_threads
replicate_events_marked_for_skip
gtid_ignore_duplicates
This means that all of the above variables can be changed while the IO
thread is still running. This was never the intention! There are some
complex interactions between the master, the IO thread, and the SQL thread,
particularly with respect to the slave's GTID position, and parts of the
code are specifically written on the assumption that certain things cannot
change without first stopping all replication threads. (I do not have the
full overview of exactly what can break, I think that would require a
careful review of large parts of the code, but I fear that it can corrupt
not just the replication GTID position and possibly even internal server
structures).
So I am wondering what to do ... it is easy to fix
give_error_if_slave_running() to check for both threads, but this is a
change in behaviour (even if it should match documentation) that could
easily break administration scripts, eg. after a 10.0 automatic security-fix
upgrade. I also know that eg. Jean François has written about how he uses
the feature of being able to not stop the IO thread in some cases.
On the other hand, this can cause serious problems for users, as they have
no real way of knowing not to attempt to change these variables without
stopping IO thread first, when there is no error given. MDEV-9138 describes
a complex scenario where slave position gets corrupted from this.
Any suggestions?
Thanks,
- Kristian.
1
0

23 Apr '16
Hello,
I just want to inform everybody about the newest mysql CVEs:
Apr 21 CVE-2016-0666
Apr 21 CVE-2016-0663
Apr 21 CVE-2016-0665
Apr 21 CVE-2016-0661
Apr 21 CVE-2016-0662
Apr 21 CVE-2016-0659
Apr 21 CVE-2016-0658
Apr 21 CVE-2016-0657
Apr 21 CVE-2016-0656
Apr 21 CVE-2016-0654
Apr 21 CVE-2016-0655
Apr 21 CVE-2016-0653
Apr 21 CVE-2016-0652
Apr 21 CVE-2016-0651
Apr 21 CVE-2016-0650
Apr 21 CVE-2016-0649
Apr 21 CVE-2016-0648
Apr 21 CVE-2016-0647
Apr 21 CVE-2016-0646
Apr 21 CVE-2016-0644
Apr 21 CVE-2016-0643
Apr 21 CVE-2016-0642
Apr 21 CVE-2016-0641
Apr 21 CVE-2016-0639
Apr 21 CVE-2016-0640
Is there a list of current CVEs for mariadb there I can compare them?
Best regards
Chris
2
1
Hello Sergei!
By adaptive hashing I refer to something like in innodb adaptive hashing
http://dev.mysql.com/doc/refman/5.7/en/innodb-adaptive-hash.html
On Tue, Apr 19, 2016 at 6:55 PM, sachin setiya <sachinsetia1001(a)gmail.com>
wrote:
> Hello
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Mon, 11 Apr 2016 15:03:24 +0530
> Subject: InnoDB blob for primary key
> Hello Developers,
> Hi this is sachin.Actually i was currently experimenting with with
> blob uniques in innodb
> there is three main problems
> 1.Unique blob
> 2.Blob Forigen key
> 3.Blob primary key
>
> 1. For blob unique we can simply store hash in unclustered index
> 2. Blob Forigen key i am currently working on it
> 3. Blob primary key :- for this i thought we create a 4 byte column
> which stores the hash of blob primary key.Internally this column will work
> as
> primary key and key for clustered index. I already successufully tested
> this
> here is my output
>
> MariaDB [sachin]> create table t4 (abc blob primary key);
> Query OK, 0 rows affected (0.10 sec)
>
> MariaDB [sachin]> insert into t4 values('sachin');
> Query OK, 1 row affected (0.01 sec)
>
> MariaDB [sachin]> insert into t4 values('sachin');
> ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
> MariaDB [sachin]> insert into t4 values('sachin setiya');
> Query OK, 1 row affected (0.00 sec)
>
> MariaDB [sachin]> insert into t4 values('sachin setiya');
> ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
>
> MariaDB [sachin]> desc t4;
> +-------+------+------+-----+---------+-------+
> | Field | Type | Null | Key | Default | Extra |
> +-------+------+------+-----+---------+-------+
> | abc | blob | NO | PRI | NULL | |
> +-------+------+------+-----+---------+-------+
> 1 row in set (0.01 sec)
>
> MariaDB [sachin]> select * from t4;
> +---------------+
> | abc |
> +---------------+
> | sachin |
> | sachin setiya |
> +---------------+
> 2 rows in set (0.01 sec)
>
> @Sergei hi! Actually i invested arround 2 months in mariadb So for me now
> it does not matter either i got selected in gsoc i want to do work in
> innodb blob unique from today.Please sir allow me to do this
>
> I am including the patch file and t4.ibd and t4.frm file
> Regards
> sachin
>
>
>
>
> ---------- Forwarded message ----------
> From: "Jan Lindström" <jan.lindstrom(a)mariadb.com>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc: "maria-developers(a)lists.launchpad.net" <
> maria-developers(a)lists.launchpad.net>
> Date: Tue, 12 Apr 2016 14:22:29 +0300
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi,
>
> Sachin, naturally you may continue, this is open source, please read
> https://mariadb.com/kb/en/mariadb/community-contributing-to-the-mariadb-pro…
> From InnoDB point of view there is issue if you add a new system generated
> column to the row. Firstly, existing tables will not have that column and
> secondly it will effect e.g maximum row size and external tools like backup.
>
> R: Jan
>
> On Mon, Apr 11, 2016 at 12:33 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
> wrote:
>
>> Hello Developers,
>> Hi this is sachin.Actually i was currently experimenting with with
>> blob uniques in innodb
>> there is three main problems
>> 1.Unique blob
>> 2.Blob Forigen key
>> 3.Blob primary key
>>
>> 1. For blob unique we can simply store hash in unclustered index
>> 2. Blob Forigen key i am currently working on it
>> 3. Blob primary key :- for this i thought we create a 4 byte column
>> which stores the hash of blob primary key.Internally this column will
>> work as
>> primary key and key for clustered index. I already successufully tested
>> this
>> here is my output
>>
>> MariaDB [sachin]> create table t4 (abc blob primary key);
>> Query OK, 0 rows affected (0.10 sec)
>>
>> MariaDB [sachin]> insert into t4 values('sachin');
>> Query OK, 1 row affected (0.01 sec)
>>
>> MariaDB [sachin]> insert into t4 values('sachin');
>> ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
>> MariaDB [sachin]> insert into t4 values('sachin setiya');
>> Query OK, 1 row affected (0.00 sec)
>>
>> MariaDB [sachin]> insert into t4 values('sachin setiya');
>> ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
>>
>> MariaDB [sachin]> desc t4;
>> +-------+------+------+-----+---------+-------+
>> | Field | Type | Null | Key | Default | Extra |
>> +-------+------+------+-----+---------+-------+
>> | abc | blob | NO | PRI | NULL | |
>> +-------+------+------+-----+---------+-------+
>> 1 row in set (0.01 sec)
>>
>> MariaDB [sachin]> select * from t4;
>> +---------------+
>> | abc |
>> +---------------+
>> | sachin |
>> | sachin setiya |
>> +---------------+
>> 2 rows in set (0.01 sec)
>>
>> @Sergei hi! Actually i invested arround 2 months in mariadb So for me now
>> it does not matter either i got selected in gsoc i want to do work in
>> innodb blob unique from today.Please sir allow me to do this
>>
>> I am including the patch file and t4.ibd and t4.frm file
>> Regards
>> sachin
>>
>>
>>
>> _______________________________________________
>> Mailing list: https://launchpad.net/~maria-developers
>> Post to : maria-developers(a)lists.launchpad.net
>> Unsubscribe : https://launchpad.net/~maria-developers
>> More help : https://help.launchpad.net/ListHelp
>>
>>
>
>
> ---------- Forwarded message ----------
> From: Sergei Golubchik <vuvova(a)gmail.com>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc:
> Date: Wed, 13 Apr 2016 00:04:22 +0200
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi, Sachin!
>
> Just a very quick comment now. We're having a company meeting
> (and a meetup -
> http://www.meetup.com/MariaDB-Developers-Berlin-Meetup/events/230026151/)
> in Berlin, I'll send you a longer reply when this is all over (in a
> couple of days).
>
> It might be an interesting option not to hack InnoDB, but to add
> this feature (long UNIQUE keys) on the upper level. That is, in sql/
> directory. Automatically add a new BIGINT column, but hide it from
> the user (not show it in SHOW COLUMNS and in SELECT *). Create
> an index for it (also hidden). Put the blob's hash into this column.
> And on collisions retrieve both rows and compare blobs. Just like MyISAM
> is doing, but not on the storage engine level, but in the
> engine-independent fashion, something that works for all engines.
>
> Want to look at this approach?
>
> MySQL 5.7 has something similar (long unique constraints on the sql
> layer), but they've done it only for internal temporary tables (like
> MyISAM's MI_UNIQUE is only used for internal temporary tables),
> and it's not directly available for the user. Still, it might give you
> some hinst about how to implement this feature on the sql layer.
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
> On Apr 11, Sachin Setia wrote:
> > Hello Developers,
> > Hi this is sachin.Actually i was currently experimenting with with
> > blob uniques in innodb
> > there is three main problems
> > 1.Unique blob
> > 2.Blob Forigen key
> > 3.Blob primary key
> >
> > 1. For blob unique we can simply store hash in unclustered index
> > 2. Blob Forigen key i am currently working on it
> > 3. Blob primary key :- for this i thought we create a 4 byte column
> > which stores the hash of blob primary key.Internally this column will
> work
> > as
> > primary key and key for clustered index. I already successufully tested
> > this
> > here is my output
> >
> > MariaDB [sachin]> create table t4 (abc blob primary key);
> > Query OK, 0 rows affected (0.10 sec)
> >
> > MariaDB [sachin]> insert into t4 values('sachin');
> > Query OK, 1 row affected (0.01 sec)
> >
> > MariaDB [sachin]> insert into t4 values('sachin');
> > ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
> > MariaDB [sachin]> insert into t4 values('sachin setiya');
> > Query OK, 1 row affected (0.00 sec)
> >
> > MariaDB [sachin]> insert into t4 values('sachin setiya');
> > ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
> >
> > MariaDB [sachin]> desc t4;
> > +-------+------+------+-----+---------+-------+
> > | Field | Type | Null | Key | Default | Extra |
> > +-------+------+------+-----+---------+-------+
> > | abc | blob | NO | PRI | NULL | |
> > +-------+------+------+-----+---------+-------+
> > 1 row in set (0.01 sec)
> >
> > MariaDB [sachin]> select * from t4;
> > +---------------+
> > | abc |
> > +---------------+
> > | sachin |
> > | sachin setiya |
> > +---------------+
> > 2 rows in set (0.01 sec)
> >
> > @Sergei hi! Actually i invested arround 2 months in mariadb So for me now
> > it does not matter either i got selected in gsoc i want to do work in
> > innodb blob unique from today.Please sir allow me to do this
> >
> > I am including the patch file and t4.ibd and t4.frm file
> > Regards
> > sachin
>
> > diff --git a/sql/sql_table.cc b/sql/sql_table.cc
> > index dfce503..efd6f22 100644
> > --- a/sql/sql_table.cc
> > +++ b/sql/sql_table.cc
> > @@ -3244,7 +3244,7 @@ mysql_prepare_create_table(THD *thd,
> HA_CREATE_INFO *create_info,
> > !(sql_field->charset= find_bin_collation(sql_field->charset)))
> > DBUG_RETURN(TRUE);
> >
> > - /*
> > + /*
> > Convert the default value from client character
> > set into the column character set if necessary.
> > */
> > @@ -3874,11 +3874,11 @@ mysql_prepare_create_table(THD *thd,
> HA_CREATE_INFO *create_info,
> > if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
> > Field::GEOM_POINT)
> > column->length= MAX_LEN_GEOM_POINT_FIELD;
> > - if (!column->length)
> > - {
> > - my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0),
> column->field_name.str);
> > - DBUG_RETURN(TRUE);
> > - }
> > +// if (!column->length) //change
> > +// {
> > +// my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0),
> column->field_name.str);
> > +// DBUG_RETURN(TRUE);
> > +// }
> > }
> > #ifdef HAVE_SPATIAL
> > if (key->type == Key::SPATIAL)
> > @@ -3992,9 +3992,9 @@ mysql_prepare_create_table(THD *thd,
> HA_CREATE_INFO *create_info,
> > }
> > else if (key_part_length == 0 && (sql_field->flags &
> NOT_NULL_FLAG))
> > {
> > - my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(),
> > - column->field_name.str);
> > - DBUG_RETURN(TRUE);
> > +// my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(),
> > +// column->field_name.str);
> > +// DBUG_RETURN(TRUE);
> > }
> > if (key_part_length > file->max_key_part_length() &&
> > key->type != Key::FULLTEXT)
> > diff --git a/storage/innobase/dict/dict0dict.cc
> b/storage/innobase/dict/dict0dict.cc
> > index c51deb0..f6e94af 100644
> > --- a/storage/innobase/dict/dict0dict.cc
> > +++ b/storage/innobase/dict/dict0dict.cc
> > @@ -1315,7 +1315,7 @@ dict_table_add_to_cache(
> > #define BIG_ROW_SIZE 1024
> >
> > ut_ad(mutex_own(&(dict_sys->mutex)));
> > -
> > +
> > dict_table_add_system_columns(table, heap);
> >
> > table->cached = TRUE;
> > diff --git a/storage/xtradb/dict/dict0dict.cc
> b/storage/xtradb/dict/dict0dict.cc
> > index 206038d..d8223d7 100644
> > --- a/storage/xtradb/dict/dict0dict.cc
> > +++ b/storage/xtradb/dict/dict0dict.cc
> > @@ -1251,7 +1251,7 @@ dict_table_open_on_name(
> > Adds system columns to a table object. */
> > UNIV_INTERN
> > void
> > -dict_table_add_system_columns(
> > +dict_table_add_system_columns( //work
> > /*==========================*/
> > dict_table_t* table, /*!< in/out: table */
> > mem_heap_t* heap) /*!< in: temporary heap */
> > @@ -1266,16 +1266,25 @@ dict_table_add_system_columns(
> > etc.) and as the last columns of the table memory object.
> > The clustered index will not always physically contain all
> > system columns. */
> > -
> > +// dict_mem_table_add_col(table,heap,"DB_BLOB_HASH",DATA_INT
> > +// ,DATA_NOT_NULL,DATA_BLOB_HASH_LEN);
> > dict_mem_table_add_col(table, heap, "DB_ROW_ID", DATA_SYS,
> > DATA_ROW_ID | DATA_NOT_NULL,
> > DATA_ROW_ID_LEN);
> > #if DATA_ROW_ID != 0
> > #error "DATA_ROW_ID != 0"
> > #endif
> > +// dict_mem_table_add_col(table,heap,"DB_BLOB_HASH",DATA_SYS,
> > +//
> DATA_BLOB_HASH|DATA_NOT_NULL,
> > +//
> DATA_BLOB_HASH_LEN);
> > + //just a rough trick to get it working
> > +
> > +// if(*table->name=='y'){
> > dict_mem_table_add_col(table, heap, "DB_TRX_ID", DATA_SYS,
> > DATA_TRX_ID | DATA_NOT_NULL,
> > DATA_TRX_ID_LEN);
> > +// }
> > +
> > #if DATA_TRX_ID != 1
> > #error "DATA_TRX_ID != 1"
> > #endif
> > @@ -1310,7 +1319,11 @@ dict_table_add_to_cache(
> > ulint row_len;
> >
> > ut_ad(dict_lru_validate());
> > -
> > +// bool x =false;//break
> > +// if(x){
> > +// dict_mem_table_add_col(table,heap,"DB_BLOB_HASH",DATA_INT
> > +//
> ,DATA_NOT_NULL,4);
> > +// }
> > /* The lower limit for what we consider a "big" row */
> > #define BIG_ROW_SIZE 1024
> >
> > @@ -3075,7 +3088,7 @@ dict_index_build_internal_clust(
> > /* Copy the fields of index */
> > dict_index_copy(new_index, index, table, 0, index->n_fields);
> >
> > - if (dict_index_is_univ(index)) {
> > + if (dict_index_is_univ(index)) { //work
> > /* No fixed number of fields determines an entry uniquely
> */
> >
> > new_index->n_uniq = REC_MAX_N_FIELDS;
> > @@ -3124,7 +3137,7 @@ dict_index_build_internal_clust(
> > DATA_ROLL_PTR),
> > 0);
> >
> > - for (i = 0; i < trx_id_pos; i++) {
> > + for (i = 0; i < trx_id_pos; i++) {//work i think i need to
> do some stuff
> >
> > ulint fixed_size = dict_col_get_fixed_size(
> > dict_index_get_nth_col(new_index, i),
> > diff --git a/storage/xtradb/dict/dict0load.cc
> b/storage/xtradb/dict/dict0load.cc
> > index d6ed8ac..8cc59f8 100644
> > --- a/storage/xtradb/dict/dict0load.cc
> > +++ b/storage/xtradb/dict/dict0load.cc
> > @@ -2276,7 +2276,7 @@ dictionary cache.
> > ibd_file_missing flag TRUE in the table object we return */
> > UNIV_INTERN
> > dict_table_t*
> > -dict_load_table(
> > +dict_load_table( //work for corruped table
> > /*============*/
> > const char* name, /*!< in: table name in the
> > databasename/tablename format */
> > @@ -2337,7 +2337,7 @@ dict_load_table(
> > btr_pcur_close(&pcur);
> > mtr_commit(&mtr);
> > mem_heap_free(heap);
> > -
> > +
> > return(NULL);
> > }
> >
> > diff --git a/storage/xtradb/dict/dict0mem.cc
> b/storage/xtradb/dict/dict0mem.cc
> > index a4f6cd6..6cbe556 100644
> > --- a/storage/xtradb/dict/dict0mem.cc
> > +++ b/storage/xtradb/dict/dict0mem.cc
> > @@ -101,11 +101,11 @@ dict_mem_table_create(
> > memcpy(table->name, name, strlen(name) + 1);
> > table->is_system_db = dict_mem_table_is_system(table->name);
> > table->space = (unsigned int) space;
> > - table->n_cols = (unsigned int) (n_cols + DATA_N_SYS_COLS);
> > -
> > + table->n_cols = (unsigned int) (n_cols + DATA_N_SYS_COLS); //work
> > +
> > table->cols = static_cast<dict_col_t*>(
> > mem_heap_alloc(heap,
> > - (n_cols + DATA_N_SYS_COLS)
> > + (n_cols + DATA_N_SYS_COLS)//work
> > * sizeof(dict_col_t)));
> >
> > ut_d(table->magic_n = DICT_TABLE_MAGIC_N);
> > diff --git a/storage/xtradb/handler/ha_innodb.cc
> b/storage/xtradb/handler/ha_innodb.cc
> > index e5edf76..3a8dc91 100644
> > --- a/storage/xtradb/handler/ha_innodb.cc
> > +++ b/storage/xtradb/handler/ha_innodb.cc
> > @@ -5983,7 +5983,7 @@ ha_innobase::open(
> > ibool par_case_name_set = FALSE;
> > char par_case_name[FN_REFLEN];
> > dict_err_ignore_t ignore_err = DICT_ERR_IGNORE_NONE;
> > -
> > + ibool is_blob_primary_key=false;
> > DBUG_ENTER("ha_innobase::open");
> >
> > UT_NOT_USED(mode);
> > @@ -6031,13 +6031,20 @@ ha_innobase::open(
> >
> > /* Get pointer to a table object in InnoDB dictionary cache */
> > ib_table = dict_table_open_on_name(norm_name, FALSE, TRUE,
> ignore_err);
> > -
> > + //int number_of_columns = dict_table_get_n_user_cols(ib_table);
> > + //if(ib_table->)
> > + if(ib_table){
> > + int number_of_columns =
> dict_table_get_n_user_cols(ib_table);
> > +
> if(!innobase_strcasecmp(ib_table->col_names,"DB_BLOB_HASH")){
> > + is_blob_primary_key=true;
> > + number_of_columns--;//stodo i think we need to add
> flag for blob primary key to make checking easier
> > + }
> > if (ib_table
> > - && ((!DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID)
> > - && table->s->stored_fields !=
> dict_table_get_n_user_cols(ib_table))
> > + && ((!DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID))
> > + && table->s->stored_fields != number_of_columns) //work
> > || (DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID)
> > && (table->s->fields
> > - != dict_table_get_n_user_cols(ib_table) - 1)))) {
> > + != dict_table_get_n_user_cols(ib_table) - 1))) {
> > ib_logf(IB_LOG_LEVEL_WARN,
> > "table %s contains %lu user defined columns "
> > "in InnoDB, but %lu columns in MySQL. Please "
> > @@ -6054,7 +6061,7 @@ ha_innobase::open(
> > ib_table = NULL;
> > is_part = NULL;
> > }
> > -
> > + }
> > if (UNIV_UNLIKELY(ib_table && ib_table->is_corrupt &&
> > srv_pass_corrupt_table <= 1)) {
> > free_share(share);
> > @@ -6254,12 +6261,12 @@ ha_innobase::open(
> > /* Looks like MySQL-3.23 sometimes has primary key number != 0 */
> > primary_key = table->s->primary_key;
> > key_used_on_scan = primary_key;
> > -
> > + if(!is_blob_primary_key){
> > if (!innobase_build_index_translation(table, ib_table, share)) {
> > sql_print_error("Build InnoDB index translation table
> for"
> > " Table %s failed", name);
> > }
> > -
> > + }
> > /* Allocate a buffer for a 'row reference'. A row reference is
> > a string of bytes of length ref_length which uniquely specifies
> > a row in our table. Note that MySQL may also compare two row
> > @@ -6314,7 +6321,11 @@ ha_innobase::open(
> > for (uint i = 0; i < table->s->keys; i++) {
> > dict_index_t* index;
> > index = innobase_get_index(i);
> > - if (dict_index_is_clust(index)) {
> > + if (dict_index_is_clust(index)) { //work
> > + if(is_blob_primary_key){
> > + ref_length=4; //hash length
> > + continue;
> > + }
> > ref_length =
> >
> table->key_info[i].key_length;
> > }
> > @@ -7795,7 +7806,8 @@ build_template_field(
> >
> > //ut_ad(field == table->field[i]);
> > ut_ad(clust_index->table == index->table);
> > -
> > +//work here we go
> > +//todo it should get the next column defs
> > col = dict_table_get_nth_col(index->table, i);
> >
> > templ = prebuilt->mysql_template + prebuilt->n_template++;
> > @@ -8138,7 +8150,8 @@ ha_innobase::build_template(
> > continue;
> > }
> > }
> > -
> > +
> if(!innobase_strcasecmp(clust_index->fields->name,"DB_BLOB_HASH"))
> > + i++;
> > build_template_field(prebuilt, clust_index, index,
> > table, field, i);
> > }
> > @@ -11065,6 +11078,7 @@ create_table_def(
> > ulint doc_id_col = 0;
> > ibool has_doc_id_col = FALSE;
> > mem_heap_t* heap;
> > + bool is_blob_primary_key=false;
> >
> > DBUG_ENTER("create_table_def");
> > DBUG_PRINT("enter", ("table_name: %s", table_name));
> > @@ -11126,6 +11140,12 @@ create_table_def(
> > table->fts->doc_col = doc_id_col;
> > }
> > } else {
> > + if(form->key_info[0].key_part->length==0 && //change
> > + form->key_info[0].key_part->key_type|MYSQL_TYPE_BLOB){
> > + s_cols++;
> > + is_blob_primary_key=true;
> > + }
> > +
> > table = dict_mem_table_create(table_name, 0, s_cols,
> > flags, flags2);
> > }
> > @@ -11143,6 +11163,13 @@ create_table_def(
> > table->data_dir_path = NULL;
> > }
> > heap = mem_heap_create(1000);
> > + //work
> > + //add one more column for hash
> > +
> > + if(is_blob_primary_key){
> > + dict_mem_table_add_col(table,heap,"DB_BLOB_HASH",
> > + DATA_INT,1283,4);
> > + }
> >
> > for (i = 0; i < n_cols; i++) {
> > Field* field = form->field[i];
> > @@ -11222,7 +11249,7 @@ create_table_def(
> > err = DB_ERROR;
> > goto error_ret;
> > }
> > -
> > +
> > dict_mem_table_add_col(table, heap,
> > field->field_name,
> > col_type,
> > @@ -11347,7 +11374,7 @@ create_index(
> > the length of the key part versus the column. */
> >
> > Field* field = NULL;
> > -
> > +//work
> > for (ulint j = 0; j < form->s->fields; j++) {
> >
> > field = form->field[j];
> > @@ -11396,7 +11423,12 @@ create_index(
> > }
> >
> > field_lengths[i] = key_part->length;
> > -
> > + if(form->key_info[0].key_part->length==0 && //change
> > + form->key_info[0].key_part->key_type|MYSQL_TYPE_BLOB){
> > + dict_mem_index_add_field(
> > + index,"DB_BLOB_HASH", 0);
> > + continue;
> > + }
> > dict_mem_index_add_field(
> > index, key_part->field->field_name, prefix_len);
> > }
> > diff --git a/storage/xtradb/include/data0type.h
> b/storage/xtradb/include/data0type.h
> > index 111664b..0f510e8 100644
> > --- a/storage/xtradb/include/data0type.h
> > +++ b/storage/xtradb/include/data0type.h
> > @@ -147,6 +147,9 @@ be less than 256 */
> > #define DATA_ROLL_PTR 2 /* rollback data pointer: 7 bytes
> */
> > #define DATA_ROLL_PTR_LEN 7
> >
> > +//#define DATA_BLOB_HASH 3 // hash coloumn for blob primay key
> > +//#define DATA_BLOB_HASH_LEN 4 //used as a clustered index
> > +
> > #define DATA_N_SYS_COLS 3 /* number of system columns
> defined above */
> >
> > #define DATA_FTS_DOC_ID 3 /* Used as FTS DOC ID column */
> > diff --git a/storage/xtradb/que/que0que.cc
> b/storage/xtradb/que/que0que.cc
> > index e2dc023..076785d 100644
> > --- a/storage/xtradb/que/que0que.cc
> > +++ b/storage/xtradb/que/que0que.cc
> > @@ -1030,7 +1030,7 @@ que_thr_step(
> > que_node_print_info(node);
> > }
> > #endif
> > - if (type & QUE_NODE_CONTROL_STAT) {
> > + if (type & QUE_NODE_CONTROL_STAT) { //improve
> > if ((thr->prev_node != que_node_get_parent(node))
> > && que_node_get_next(thr->prev_node)) {
> >
> > diff --git a/storage/xtradb/row/row0ins.cc
> b/storage/xtradb/row/row0ins.cc
> > index d02e179..c58d7fb 100644
> > --- a/storage/xtradb/row/row0ins.cc
> > +++ b/storage/xtradb/row/row0ins.cc
> > @@ -137,7 +137,7 @@ row_ins_alloc_sys_fields(
> > /*=====================*/
> > ins_node_t* node) /*!< in: insert node */
> > {
> > - dtuple_t* row;
> > + dtuple_t* row; //work
> > dict_table_t* table;
> > mem_heap_t* heap;
> > const dict_col_t* col;
> > @@ -3118,7 +3118,7 @@ row_ins_index_entry_set_vals(
> >
> > n_fields = dtuple_get_n_fields(entry);
> >
> > - for (i = 0; i < n_fields; i++) {
> > + for (i = 0; i < n_fields; i++) {//see
> > dict_field_t* ind_field;
> > dfield_t* field;
> > const dfield_t* row_field;
> > @@ -3169,8 +3169,8 @@ row_ins_index_entry_step(
> >
> > ut_ad(dtuple_check_typed(node->row));
> >
> > - row_ins_index_entry_set_vals(node->index, node->entry, node->row);
> > -
> > + row_ins_index_entry_set_vals(node->index, node->entry,
> node->row);//explore
> > +//need to see who sets the entry in row
> > ut_ad(dtuple_check_typed(node->entry));
> >
> > err = row_ins_index_entry(node->index, node->entry, thr);
> > @@ -3208,7 +3208,7 @@ row_ins_alloc_row_id_step(
> >
> > /* Fill in row id value to row */
> >
> > - row_id = dict_sys_get_new_row_id();
> > + row_id = dict_sys_get_new_row_id(); //work
> >
> > dict_sys_write_row_id(node->row_id_buf, row_id);
> > }
> > diff --git a/storage/xtradb/row/row0mysql.cc
> b/storage/xtradb/row/row0mysql.cc
> > index 9427b20..b28e778 100644
> > --- a/storage/xtradb/row/row0mysql.cc
> > +++ b/storage/xtradb/row/row0mysql.cc
> > @@ -510,7 +510,15 @@ row_mysql_store_col_in_innobase_format(
> >
> > return(buf);
> > }
> > -
> > +//this is just dummy function
> > +int dummuy_hash(byte * ptr,int length){
> > + int val=0;
> > + for(int i=0;i<length;i++){
> > + val =val*8+(int )*ptr;
> > + ptr++;
> > + }
> > + return val;
> > +}
> > /**************************************************************//**
> > Convert a row in the MySQL format to a row in the Innobase format. Note
> that
> > the function to convert a MySQL format key value to an InnoDB dtuple is
> > @@ -532,15 +540,22 @@ row_mysql_convert_row_to_innobase(
> > const mysql_row_templ_t*templ;
> > dfield_t* dfield;
> > ulint i;
> > -
> > + ibool is_blob_primary_key=false;
> > + ibool is_first_template=true;
> > ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
> > - ut_ad(prebuilt->mysql_template);
> > -
> > - for (i = 0; i < prebuilt->n_template; i++) {
> > + ut_ad(prebuilt->mysql_template);//work
> > +
> if(!innobase_strcasecmp(prebuilt->table->col_names,"DB_BLOB_HASH")){
> > + is_blob_primary_key =true;
> > + }
> > + for (i = 0; i < prebuilt->n_template; i++) { //change add one
> >
> > templ = prebuilt->mysql_template + i;
> > + if(is_blob_primary_key&&is_first_template){
> > + dfield = dtuple_get_nth_field(row, i+1);
> > + is_first_template=false;
> > + }else{
> > dfield = dtuple_get_nth_field(row, i);
> > -
> > + }
> > if (templ->mysql_null_bit_mask != 0) {
> > /* Column may be SQL NULL */
> >
> > @@ -565,6 +580,17 @@ row_mysql_convert_row_to_innobase(
> > next_column:
> > ;
> > }
> > + if(is_blob_primary_key){
> > + //get the first field and set the hash
> > + dfield_t * hash_field=dtuple_get_nth_field(row,0);
> > + dfield_t * blob_field=dtuple_get_nth_field(row,1);
> > + byte * hash = static_cast<byte
> *>(mem_heap_zalloc(prebuilt->heap,4));
> > + mach_write_to_4(hash,dummuy_hash((byte
> *)blob_field->data,blob_field->len));
> > + //4 is hash length
> > + hash_field->len=4;
> > + hash_field->data=hash;
> > + //hash_field->type
> > + }
> >
> > /* If there is a FTS doc id column and it is not user supplied (
> > generated by server) then assign it a new doc id. */
> > @@ -831,7 +857,7 @@ row_create_prebuilt(
> >
> > prebuilt->search_tuple = dtuple_create(heap,
> search_tuple_n_fields);
> >
> > - ref = dtuple_create(heap, ref_len);
> > + ref = dtuple_create(heap, ref_len);//work
> >
> > dict_index_copy_types(ref, clust_index, ref_len);
> >
> > @@ -1057,7 +1083,7 @@ row_get_prebuilt_insert_row(
> >
> > dict_table_copy_types(row, table);
> >
> > - ins_node_set_new_row(node, row);
> > + ins_node_set_new_row(node, row);//explore
> >
> > prebuilt->ins_graph = static_cast<que_fork_t*>(
> > que_node_get_parent(
> > @@ -1353,7 +1379,7 @@ row_insert_for_mysql(
> > row_get_prebuilt_insert_row(prebuilt);
> > node = prebuilt->ins_node;
> >
> > - row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);
> > + row_mysql_convert_row_to_innobase(node->row, prebuilt,
> mysql_rec);//debug
> >
> > savept = trx_savept_take(trx);
> >
> > diff --git a/storage/xtradb/row/row0row.cc
> b/storage/xtradb/row/row0row.cc
> > index be786f9..a1dd465 100644
> > --- a/storage/xtradb/row/row0row.cc
> > +++ b/storage/xtradb/row/row0row.cc
> > @@ -83,7 +83,7 @@ row_build_index_entry_low(
> > entry, dict_index_get_n_unique_in_tree(index));
> > }
> >
> > - for (i = 0; i < entry_len; i++) {
> > + for (i = 0; i < entry_len; i++) { //explore need to see how it
> works for simple
> > const dict_field_t* ind_field
> > = dict_index_get_nth_field(index, i);
> > const dict_col_t* col
>
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Wed, 13 Apr 2016 21:17:03 +0530
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hello Jan!
>
> Actually I was going through the problems raised by you
>
> 1>Firstly, existing tables will not have that column
>
> Yes Sir, you are right that existing table woudn't have DB_BLOB_HASH
> column, but this is fine because they indexed there blob column then
> they must have provided blob length, and if so then length of key_part
> would not be zero and so need to make changes in patch to detect this.
> And also by doing I can also eliminate another problem what if existing
> table have a coloumn named 'DB_BLOB_HASH'
>
> 2>secondly it will effect e.g maximum row size
> For this i can increase reclength by 4 in pack_header function(but i
> am not sure whether this will work or not)
>
> 3>and external tools like backup
> Here, I am assuming that by backup you mean mysqldump actually i tried
> this by create a table with blob coloum (length was given ) and than
> changing mariadb binary(to changed code) and .sql file of backup was
> working fine
>
> But I think there are more problems with this this prototype
> 1>Existing table with coloumn named 'DB_BLOB_HASH' .We can solve this
> as written above but some code in patch compares first coloumn name
> with 'DB_BLOB_HASH' so this will create problem Sir, can we add a flag
> in dict_table_t->flag so that we can reliable check for blob hash coloumn
>
> 2>This is very bad problem , what happen if if data is different and crc
> hash of data is same.And this will occur when we have medium amount of
> data like 100 thousand records.To avoid this we can do like
>
> 2.1> use crc 64 reduces probability
> 2.2> use two different algorithm for hash and treat these two algoriths
> hash for uniquesness.Make collusion probability very very less
>
> But the main problem is what would happen if collusion occurs to solve
> this i have two ideas
> 2.3> like 2.2 but instead of using two algorithm use one and add column
> for number of times this hash is repeated collusion frequency will be zero
> i think this will work fine but need to test it
> 2.4> whole prototype is crap start again with some different method. Can
> you
> please suggest some differnt way of doing this
> Regards
> sachin
>
> On Tue, Apr 12, 2016 at 4:52 PM, Jan Lindström
> <jan.lindstrom(a)mariadb.com> wrote:
> > Hi,
> >
> > Sachin, naturally you may continue, this is open source, please read
> >
> https://mariadb.com/kb/en/mariadb/community-contributing-to-the-mariadb-pro…
> > From InnoDB point of view there is issue if you add a new system
> generated
> > column to the row. Firstly, existing tables will not have that column and
> > secondly it will effect e.g maximum row size and external tools like
> backup.
> >
> > R: Jan
> >
> > On Mon, Apr 11, 2016 at 12:33 PM, Sachin Setia <
> sachinsetia1001(a)gmail.com>
> > wrote:
> >>
> >> Hello Developers,
> >> Hi this is sachin.Actually i was currently experimenting with with
> >> blob uniques in innodb
> >> there is three main problems
> >> 1.Unique blob
> >> 2.Blob Forigen key
> >> 3.Blob primary key
> >>
> >> 1. For blob unique we can simply store hash in unclustered index
> >> 2. Blob Forigen key i am currently working on it
> >> 3. Blob primary key :- for this i thought we create a 4 byte column
> >> which stores the hash of blob primary key.Internally this column will
> work
> >> as
> >> primary key and key for clustered index. I already successufully tested
> >> this
> >> here is my output
> >>
> >> MariaDB [sachin]> create table t4 (abc blob primary key);
> >> Query OK, 0 rows affected (0.10 sec)
> >>
> >> MariaDB [sachin]> insert into t4 values('sachin');
> >> Query OK, 1 row affected (0.01 sec)
> >>
> >> MariaDB [sachin]> insert into t4 values('sachin');
> >> ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
> >> MariaDB [sachin]> insert into t4 values('sachin setiya');
> >> Query OK, 1 row affected (0.00 sec)
> >>
> >> MariaDB [sachin]> insert into t4 values('sachin setiya');
> >> ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
> >>
> >> MariaDB [sachin]> desc t4;
> >> +-------+------+------+-----+---------+-------+
> >> | Field | Type | Null | Key | Default | Extra |
> >> +-------+------+------+-----+---------+-------+
> >> | abc | blob | NO | PRI | NULL | |
> >> +-------+------+------+-----+---------+-------+
> >> 1 row in set (0.01 sec)
> >>
> >> MariaDB [sachin]> select * from t4;
> >> +---------------+
> >> | abc |
> >> +---------------+
> >> | sachin |
> >> | sachin setiya |
> >> +---------------+
> >> 2 rows in set (0.01 sec)
> >>
> >> @Sergei hi! Actually i invested arround 2 months in mariadb So for me
> now
> >> it does not matter either i got selected in gsoc i want to do work in
> >> innodb blob unique from today.Please sir allow me to do this
> >>
> >> I am including the patch file and t4.ibd and t4.frm file
> >> Regards
> >> sachin
> >>
> >>
> >>
> >> _______________________________________________
> >> Mailing list: https://launchpad.net/~maria-developers
> >> Post to : maria-developers(a)lists.launchpad.net
> >> Unsubscribe : https://launchpad.net/~maria-developers
> >> More help : https://help.launchpad.net/ListHelp
> >>
> >
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Wed, 13 Apr 2016 22:19:44 +0530
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hello Sergei
> Sorry I did not see your mail. Actually i was thinking something like
> this before implementing the prototype but if i am more closer to
> innodb the more performance i will i get. I will definitively think
> about it.
> Regards
> sachin
>
> On Wed, Apr 13, 2016 at 9:17 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
> wrote:
> > Hello Jan!
> >
> > Actually I was going through the problems raised by you
> >
> > 1>Firstly, existing tables will not have that column
> >
> > Yes Sir, you are right that existing table woudn't have DB_BLOB_HASH
> > column, but this is fine because they indexed there blob column then
> > they must have provided blob length, and if so then length of key_part
> > would not be zero and so need to make changes in patch to detect this.
> > And also by doing I can also eliminate another problem what if existing
> > table have a coloumn named 'DB_BLOB_HASH'
> >
> > 2>secondly it will effect e.g maximum row size
> > For this i can increase reclength by 4 in pack_header function(but i
> > am not sure whether this will work or not)
> >
> > 3>and external tools like backup
> > Here, I am assuming that by backup you mean mysqldump actually i tried
> > this by create a table with blob coloum (length was given ) and than
> > changing mariadb binary(to changed code) and .sql file of backup was
> > working fine
> >
> > But I think there are more problems with this this prototype
> > 1>Existing table with coloumn named 'DB_BLOB_HASH' .We can solve this
> > as written above but some code in patch compares first coloumn name
> > with 'DB_BLOB_HASH' so this will create problem Sir, can we add a flag
> > in dict_table_t->flag so that we can reliable check for blob hash coloumn
> >
> > 2>This is very bad problem , what happen if if data is different and crc
> > hash of data is same.And this will occur when we have medium amount of
> > data like 100 thousand records.To avoid this we can do like
> >
> > 2.1> use crc 64 reduces probability
> > 2.2> use two different algorithm for hash and treat these two algoriths
> > hash for uniquesness.Make collusion probability very very less
> >
> > But the main problem is what would happen if collusion occurs to solve
> > this i have two ideas
> > 2.3> like 2.2 but instead of using two algorithm use one and add column
> > for number of times this hash is repeated collusion frequency will be
> zero
> > i think this will work fine but need to test it
> > 2.4> whole prototype is crap start again with some different method. Can
> you
> > please suggest some differnt way of doing this
> > Regards
> > sachin
> >
> > On Tue, Apr 12, 2016 at 4:52 PM, Jan Lindström
> > <jan.lindstrom(a)mariadb.com> wrote:
> >> Hi,
> >>
> >> Sachin, naturally you may continue, this is open source, please read
> >>
> https://mariadb.com/kb/en/mariadb/community-contributing-to-the-mariadb-pro…
> >> From InnoDB point of view there is issue if you add a new system
> generated
> >> column to the row. Firstly, existing tables will not have that column
> and
> >> secondly it will effect e.g maximum row size and external tools like
> backup.
> >>
> >> R: Jan
> >>
> >> On Mon, Apr 11, 2016 at 12:33 PM, Sachin Setia <
> sachinsetia1001(a)gmail.com>
> >> wrote:
> >>>
> >>> Hello Developers,
> >>> Hi this is sachin.Actually i was currently experimenting with with
> >>> blob uniques in innodb
> >>> there is three main problems
> >>> 1.Unique blob
> >>> 2.Blob Forigen key
> >>> 3.Blob primary key
> >>>
> >>> 1. For blob unique we can simply store hash in unclustered index
> >>> 2. Blob Forigen key i am currently working on it
> >>> 3. Blob primary key :- for this i thought we create a 4 byte column
> >>> which stores the hash of blob primary key.Internally this column will
> work
> >>> as
> >>> primary key and key for clustered index. I already successufully tested
> >>> this
> >>> here is my output
> >>>
> >>> MariaDB [sachin]> create table t4 (abc blob primary key);
> >>> Query OK, 0 rows affected (0.10 sec)
> >>>
> >>> MariaDB [sachin]> insert into t4 values('sachin');
> >>> Query OK, 1 row affected (0.01 sec)
> >>>
> >>> MariaDB [sachin]> insert into t4 values('sachin');
> >>> ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
> >>> MariaDB [sachin]> insert into t4 values('sachin setiya');
> >>> Query OK, 1 row affected (0.00 sec)
> >>>
> >>> MariaDB [sachin]> insert into t4 values('sachin setiya');
> >>> ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
> >>>
> >>> MariaDB [sachin]> desc t4;
> >>> +-------+------+------+-----+---------+-------+
> >>> | Field | Type | Null | Key | Default | Extra |
> >>> +-------+------+------+-----+---------+-------+
> >>> | abc | blob | NO | PRI | NULL | |
> >>> +-------+------+------+-----+---------+-------+
> >>> 1 row in set (0.01 sec)
> >>>
> >>> MariaDB [sachin]> select * from t4;
> >>> +---------------+
> >>> | abc |
> >>> +---------------+
> >>> | sachin |
> >>> | sachin setiya |
> >>> +---------------+
> >>> 2 rows in set (0.01 sec)
> >>>
> >>> @Sergei hi! Actually i invested arround 2 months in mariadb So for me
> now
> >>> it does not matter either i got selected in gsoc i want to do work in
> >>> innodb blob unique from today.Please sir allow me to do this
> >>>
> >>> I am including the patch file and t4.ibd and t4.frm file
> >>> Regards
> >>> sachin
> >>>
> >>>
> >>>
> >>> _______________________________________________
> >>> Mailing list: https://launchpad.net/~maria-developers
> >>> Post to : maria-developers(a)lists.launchpad.net
> >>> Unsubscribe : https://launchpad.net/~maria-developers
> >>> More help : https://help.launchpad.net/ListHelp
> >>>
> >>
>
>
> ---------- Forwarded message ----------
> From: Sergei Golubchik <serg(a)mariadb.org>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc:
> Date: Thu, 14 Apr 2016 20:53:51 +0200
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi, Sachin!
>
> On Apr 13, Sachin Setia wrote:
> > Hello Sergei
> > Sorry I did not see your mail. Actually i was thinking something like
> > this before implementing the prototype but if i am more closer to
> > innodb the more performance i will i get. I will definitively think
> > about it.
>
> Great!
>
> Could you please tell me (mailing list, that is) what you think before
> next Monday (before April 18h, that is)?
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Fri, 15 Apr 2016 18:37:15 +0530
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi Sergei!
>
> Actually I was going through the mysql source code for unique long
> constraints
> in file sql_tmp_table.cc in function create_tmp_table they make a new field
> and a new key(hash_key) and pass this table obejct to storage
> engine.They actually
> refer this field as a hash field
> On the time of insert they call bool check_unique_constraint(TABLE
> *table) function
> which first calculate the hash and store it in field then they see for
> duplicate
> hash and retrive ha_index_next_same if records are not same then record
>
> We can do the same thing in mariadb by adding one more field and key in
> mysql_prepare_create_table in this we check for blob with unlimited
> length or varchar for length
> greater then internal storage engine by doing this in
> mysql_prepare_create_table there
> will be no issues of frm file inconsistance.
>
> In case of insert first we will fill the hash field in fill_record
> function of sql_base.cc
> by first calculating the hash. Then we will retrive the index map
> using ha_index_read_map
> if returened value is zero then we will comapare two records and if
> they match then we will through error
> I am not sure where to place this code either in fill_record or later
> Or i can simple just
> fill hash in field in fill_record and then check for duplicates later on.
>
> Current I am not sure how to hide columns from user.Sir, can you
> suggest me where to look
>
> But there is one problem we can make unique key by this approch but
> not primary key because primary key
> is clustered and hashes can collide so i think we can't use hash field
> as primary key. To overcome this problem
> I have one idea instead of storing just hash we can make hash field
> length 10 bytes and in last two bytes
> we can store short int which tells how much time hash is repeated this
> can make hash unique
> in case of collusion. And also we are not doing more computation
> because we already retrive
> all records with same hashes.
> What do you think of this idea?.
> And there is one more problem how to make it foreign key.
>
> Will send you a prototype code tomorrow.
> Regards
> sachin
>
> On Fri, Apr 15, 2016 at 12:23 AM, Sergei Golubchik <serg(a)mariadb.org>
> wrote:
> > Hi, Sachin!
> >
> > On Apr 13, Sachin Setia wrote:
> >> Hello Sergei
> >> Sorry I did not see your mail. Actually i was thinking something like
> >> this before implementing the prototype but if i am more closer to
> >> innodb the more performance i will i get. I will definitively think
> >> about it.
> >
> > Great!
> >
> > Could you please tell me (mailing list, that is) what you think before
> > next Monday (before April 18h, that is)?
> >
> > Regards,
> > Sergei
> > Chief Architect MariaDB
> > and security(a)mariadb.org
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Sat, 16 Apr 2016 19:37:45 +0530
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi Sergei!
> As I already told you i was building prototype.It is some what completed
> apart from one thing comparing of two field values. the difficulty is how
> to get data length of field from table->record[1]. I will try to solve it.
> One more thing actually i got how mysql hide field. For example condsider
> three
> fields hash,data,data. mysql field pointer point at second field not at
> hash
> field and hash field ptr is stored in table->hash_field
> can we do something similar to store hash fields(if we make array of
> hashes in
> case of more than one unique).But will adding member variable cause
> problem?
> what do you think?
> Regards
> sachin
>
> On Fri, Apr 15, 2016 at 6:37 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
> wrote:
> > Hi Sergei!
> >
> > Actually I was going through the mysql source code for unique long
> constraints
> > in file sql_tmp_table.cc in function create_tmp_table they make a new
> field
> > and a new key(hash_key) and pass this table obejct to storage
> > engine.They actually
> > refer this field as a hash field
> > On the time of insert they call bool check_unique_constraint(TABLE
> > *table) function
> > which first calculate the hash and store it in field then they see for
> duplicate
> > hash and retrive ha_index_next_same if records are not same then record
> >
> > We can do the same thing in mariadb by adding one more field and key in
> > mysql_prepare_create_table in this we check for blob with unlimited
> > length or varchar for length
> > greater then internal storage engine by doing this in
> > mysql_prepare_create_table there
> > will be no issues of frm file inconsistance.
> >
> > In case of insert first we will fill the hash field in fill_record
> > function of sql_base.cc
> > by first calculating the hash. Then we will retrive the index map
> > using ha_index_read_map
> > if returened value is zero then we will comapare two records and if
> > they match then we will through error
> > I am not sure where to place this code either in fill_record or later
> > Or i can simple just
> > fill hash in field in fill_record and then check for duplicates later on.
> >
> > Current I am not sure how to hide columns from user.Sir, can you
> > suggest me where to look
> >
> > But there is one problem we can make unique key by this approch but
> > not primary key because primary key
> > is clustered and hashes can collide so i think we can't use hash field
> > as primary key. To overcome this problem
> > I have one idea instead of storing just hash we can make hash field
> > length 10 bytes and in last two bytes
> > we can store short int which tells how much time hash is repeated this
> > can make hash unique
> > in case of collusion. And also we are not doing more computation
> > because we already retrive
> > all records with same hashes.
> > What do you think of this idea?.
> > And there is one more problem how to make it foreign key.
> >
> > Will send you a prototype code tomorrow.
> > Regards
> > sachin
> >
> > On Fri, Apr 15, 2016 at 12:23 AM, Sergei Golubchik <serg(a)mariadb.org>
> wrote:
> >> Hi, Sachin!
> >>
> >> On Apr 13, Sachin Setia wrote:
> >>> Hello Sergei
> >>> Sorry I did not see your mail. Actually i was thinking something like
> >>> this before implementing the prototype but if i am more closer to
> >>> innodb the more performance i will i get. I will definitively think
> >>> about it.
> >>
> >> Great!
> >>
> >> Could you please tell me (mailing list, that is) what you think before
> >> next Monday (before April 18h, that is)?
> >>
> >> Regards,
> >> Sergei
> >> Chief Architect MariaDB
> >> and security(a)mariadb.org
>
>
> ---------- Forwarded message ----------
> From: Sachin Setia <sachinsetia1001(a)gmail.com>
> To: maria-developers(a)lists.launchpad.net
> Cc:
> Date: Mon, 18 Apr 2016 20:12:39 +0530
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hello Sergei!
> Hi Actually I was thinking about how to implement
> blob as a foreign key.Foreign has to be unique which
> we can already implement. To make it foreign key
> we can either store unique hash or the whole blob
> column.
> But I am not sure much people
> want to copy so long blob data in reference table.
> Second option would be use blob hash as a reference key.
> But user can not directly us hash as a reference key
> because that is hidden.
> What I was thinking of a clear to programmer way of
> using blob hash. Suppose user can directly create
> blob hash column ,use that column as a primary key or
> foreign key.Like
> create table t1(abc blob , blob hash(abc))//this will just create blob
> hash column
> create table t1(abc blob,unique(blob hash(abc))) // this will create
> unique blob hash column
> and similar for primary key and foreign key
> user can enter hash value if they have some good algorithm
> or if they do not give any value we will automatically
> create and store hash. What do you think? sir.
> Regards
> sachin
>
>
>
> On Sat, Apr 16, 2016 at 7:37 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
> wrote:
>
>> Hi Sergei!
>> As I already told you i was building prototype.It is some what completed
>> apart from one thing comparing of two field values. the difficulty is how
>> to get data length of field from table->record[1]. I will try to solve
>> it.
>> One more thing actually i got how mysql hide field. For example
>> condsider three
>> fields hash,data,data. mysql field pointer point at second field not at
>> hash
>> field and hash field ptr is stored in table->hash_field
>> can we do something similar to store hash fields(if we make array of
>> hashes in
>> case of more than one unique).But will adding member variable cause
>> problem?
>> what do you think?
>> Regards
>> sachin
>>
>> On Fri, Apr 15, 2016 at 6:37 PM, Sachin Setia <sachinsetia1001(a)gmail.com>
>> wrote:
>> > Hi Sergei!
>> >
>> > Actually I was going through the mysql source code for unique long
>> constraints
>> > in file sql_tmp_table.cc in function create_tmp_table they make a new
>> field
>> > and a new key(hash_key) and pass this table obejct to storage
>> > engine.They actually
>> > refer this field as a hash field
>> > On the time of insert they call bool check_unique_constraint(TABLE
>> > *table) function
>> > which first calculate the hash and store it in field then they see for
>> duplicate
>> > hash and retrive ha_index_next_same if records are not same then record
>> >
>> > We can do the same thing in mariadb by adding one more field and key in
>> > mysql_prepare_create_table in this we check for blob with unlimited
>> > length or varchar for length
>> > greater then internal storage engine by doing this in
>> > mysql_prepare_create_table there
>> > will be no issues of frm file inconsistance.
>> >
>> > In case of insert first we will fill the hash field in fill_record
>> > function of sql_base.cc
>> > by first calculating the hash. Then we will retrive the index map
>> > using ha_index_read_map
>> > if returened value is zero then we will comapare two records and if
>> > they match then we will through error
>> > I am not sure where to place this code either in fill_record or later
>> > Or i can simple just
>> > fill hash in field in fill_record and then check for duplicates later
>> on.
>> >
>> > Current I am not sure how to hide columns from user.Sir, can you
>> > suggest me where to look
>> >
>> > But there is one problem we can make unique key by this approch but
>> > not primary key because primary key
>> > is clustered and hashes can collide so i think we can't use hash field
>> > as primary key. To overcome this problem
>> > I have one idea instead of storing just hash we can make hash field
>> > length 10 bytes and in last two bytes
>> > we can store short int which tells how much time hash is repeated this
>> > can make hash unique
>> > in case of collusion. And also we are not doing more computation
>> > because we already retrive
>> > all records with same hashes.
>> > What do you think of this idea?.
>> > And there is one more problem how to make it foreign key.
>> >
>> > Will send you a prototype code tomorrow.
>> > Regards
>> > sachin
>> >
>> > On Fri, Apr 15, 2016 at 12:23 AM, Sergei Golubchik <serg(a)mariadb.org>
>> wrote:
>> >> Hi, Sachin!
>> >>
>> >> On Apr 13, Sachin Setia wrote:
>> >>> Hello Sergei
>> >>> Sorry I did not see your mail. Actually i was thinking something like
>> >>> this before implementing the prototype but if i am more closer to
>> >>> innodb the more performance i will i get. I will definitively think
>> >>> about it.
>> >>
>> >> Great!
>> >>
>> >> Could you please tell me (mailing list, that is) what you think before
>> >> next Monday (before April 18h, that is)?
>> >>
>> >> Regards,
>> >> Sergei
>> >> Chief Architect MariaDB
>> >> and security(a)mariadb.org
>>
>
>
>
> ---------- Forwarded message ----------
> From: Sergei Golubchik <serg(a)mariadb.org>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc: maria-developers(a)lists.launchpad.net
> Date: Mon, 18 Apr 2016 22:32:25 +0200
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi, Sachin!
>
> On Apr 15, Sachin Setia wrote:
> > Hi Sergei!
> >
> > Actually I was going through the mysql source code for unique long
> > constraints in file sql_tmp_table.cc in function create_tmp_table they
> > make a new field and a new key(hash_key) and pass this table obejct to
> > storage engine.They actually refer this field as a hash field On the
> > time of insert they call bool check_unique_constraint(TABLE *table)
> > function which first calculate the hash and store it in field then
> > they see for duplicate hash and retrive ha_index_next_same if records
> > are not same then record
>
> Right. Very good!
>
> > We can do the same thing in mariadb by adding one more field and key in
> > mysql_prepare_create_table in this we check for blob with unlimited
> > length or varchar for length greater then internal storage engine by
> > doing this in mysql_prepare_create_table there will be no issues of frm
> > file inconsistance.
> >
> > In case of insert first we will fill the hash field in fill_record
> > function of sql_base.cc by first calculating the hash. Then we will
> > retrive the index map using ha_index_read_map if returened value is zero
> > then we will comapare two records and if they match then we will through
> > error I am not sure where to place this code either in fill_record or
> > later Or i can simple just fill hash in field in fill_record and then
> > check for duplicates later on.
>
> MariaDB supports "virtual columns", see
> https://mariadb.com/kb/en/mariadb/virtual-computed-columns/
>
> So, it might be enough to mark this hash column as virtual, and will be
> automatically calculated when necessary (in fill_record, etc).
>
> > Current I am not sure how to hide columns from user.Sir, can you suggest
> > me where to look
>
> This would need a new flag per field, like "hidden". And in all commands
> that use a list of columns (SHOW, INFORMATION_SCHEMA tables, SELECT *,
> INSERT table VALUE (...), etc) - this column should be skipped.
>
> But don't worry, I don't think this will be a problem for you. You can,
> of course, start from not hiding this column and implement this hiddden
> flag later.
>
> > But there is one problem we can make unique key by this approch but not
> > primary key because primary key is clustered and hashes can collide so i
> > think we can't use hash field as primary key.
>
> Of course.
>
> > To overcome this problem I have one idea instead of storing just hash
> > we can make hash field length 10 bytes and in last two bytes we can
> > store short int which tells how much time hash is repeated this can
> > make hash unique in case of collusion. And also we are not doing more
> > computation because we already retrive all records with same hashes.
> > What do you think of this idea?. And there is one more problem how to
> > make it foreign key.
>
> I would say, there is no need to bother. Let's just say that UNIQUE for
> blobs is only a constraint, can not be used as a primary key, can not be
> used as a foreign key. This is a reasonable limitation, I think :)
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
>
> ---------- Forwarded message ----------
> From: Sergei Golubchik <serg(a)mariadb.org>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc: maria-developers(a)lists.launchpad.net
> Date: Mon, 18 Apr 2016 22:46:42 +0200
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi, Sachin!
>
> On Apr 16, Sachin Setia wrote:
> > Hi Sergei!
> > As I already told you i was building prototype.It is some what
> > completed apart from one thing comparing of two field values. the
> > difficulty is how to get data length of field from table->record[1].
> > I will try to solve it.
>
> Do you mean, a length of the blob value?
> Try field->get_length()
>
> > One more thing actually i got how mysql hide field. For example
> > condsider three fields hash,data,data. mysql field pointer point at
> > second field not at hash field and hash field ptr is stored in
> > table->hash_field can we do something similar to store hash fields(if
> > we make array of hashes in case of more than one unique).But will
> > adding member variable cause problem? what do you think?
>
> Well, yes, that would work.
> But I see few other features where field hiding might be useful,
> and in these cases this simple trick with moving table->field pointer
> will not work, unfortunately.
>
> So we might need something more complex. But, again, this is not a most
> important part of this project, so don't start from it.
>
> In fact, I'm rather interested to know about the adaptive hashing that
> you've mentioned. Can you tell me more about it, please?
>
> > diff --git a/sql/sql_base.cc b/sql/sql_base.cc
> > index ac2162b..291a3e2 100644
> > --- a/sql/sql_base.cc
> > +++ b/sql/sql_base.cc
> > @@ -8956,6 +8964,31 @@ fill_record(THD *thd, TABLE *table, Field **ptr,
> List<Item> &values,
> > goto err;
> > field->set_explicit_default(value);
> > }
> > + table->file->ha_index_init(0,0);
> > + res =
> table->file->ha_index_read_map(table->record[1],hash_field->ptr,HA_WHOLE_KEY,HA_READ_KEY_EXACT);
> > + while(!res){
> > + //compare the record if not sure how to compare it so just
> assume it works
> > + diff = table->record[1]-table->record[0];
> > + src_length = blob_field->data_length();
> > + //dest_length =
> blob_field->data_length(table->record[1]); // i dont know how to get the
> length from record 1
> > + // so i am enable to do this
> > + // then we can comapare records using
> > + //field->cmp_max
> > + //this is mysql code
> > + /* if (!(table->distinct ?
> > + table_rec_cmp(table) :
> > + group_rec_cmp(table->group, table->record[0],
> table->record[1])))
> > + return false; // skip it
> > + res= table->file->ha_index_next_same(table->record[1],
> > + table->hash_field->ptr,
> > + sizeof(hash)); */
> > + //fetch the next record
> > + res= table->file->ha_index_next_same(table->record[1],
> > + hash_field->ptr,
> > + 8);
> > +
> > + }
>
> not quite, you should check unique constraints in fill_record(). This
> should happen when the row is actually inserted or updated. A good place
> for this check, I'd say, is handler::ha_write_row() and
> handler::ha_update_row().
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
>
> ---------- Forwarded message ----------
> From: Sergei Golubchik <serg(a)mariadb.org>
> To: Sachin Setia <sachinsetia1001(a)gmail.com>
> Cc: maria-developers(a)lists.launchpad.net
> Date: Mon, 18 Apr 2016 22:51:57 +0200
> Subject: Re: [Maria-developers] InnoDB blob for primary key
> Hi, Sachin!
>
> On Apr 18, Sachin Setia wrote:
> > Hi Actually I was thinking about how to implement blob as a foreign
> > key.Foreign has to be unique which we can already implement. To make
> > it foreign key we can either store unique hash or the whole blob
> > column. But I am not sure much people want to copy so long blob data
> > in reference table.
>
> Agree :)
>
> > Second option would be use blob hash as a reference key.
> > But user can not directly us hash as a reference key
> > because that is hidden.
> > What I was thinking of a clear to programmer way of
> > using blob hash. Suppose user can directly create
> > blob hash column ,use that column as a primary key or
> > foreign key.Like
> > create table t1(abc blob , blob hash(abc))//this will just create blob
> hash
> > column
> > create table t1(abc blob,unique(blob hash(abc))) // this will create
> unique
> > blob hash column
> > and similar for primary key and foreign key
> > user can enter hash value if they have some good algorithm
> > or if they do not give any value we will automatically
> > create and store hash. What do you think? sir.
>
> Mixed feelings. First, I wrote in an earlier email about using virtual
> columns for that. In this line of thoughts, a user-specified hash
> function is absolutely possible and logical.
>
> On the other hand, I don't see why anyone would need that - a hash is
> not guaranteed to be unique, no matter what algorithm one uses.
>
> And, as I wrote in an earlier email, I don't think that primary/foreign
> keys on blobs is a feature worth spending time on.
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
>
>
2
6
Hello everyone,
I am Shubham Barai, a 3rd-year undergraduate student in
computer science at Maharashtra Institute of technology, Pune, India.
I am interested in the project "Unique indexes for blobs".I have read the
description of the project given on the link
https://jira.mariadb.org/browse/MDEV-371.
MyISAM and innoDB do allow users to create unique indexes on blobs with
some limitations on prefix length.
So end user can create an index on blobs using "create table" or "create
index "statement.
example:CREATE TABLE some_table (blob_column BLOB, UNIQUE
(blob_column(700)));
For MyISAM tables, the prefix limit is 1000 bytes and for innoDB limit is
767 bytes.
After creating an index, I ran some queries like
"explain select distinct blob_column from some_table"
and found out that server executes this query with the help of the
temporary table but for other data types like int, the server executes the
query with the help of the index.
I want to know what exactly is the task for this project.Any help would be
greatly appreciated
Thank You,
Shubham.
4
18

[Maria-developers] Fwd: Do we have anywhere some code that copies from one internal tmp table into another?
by Igor Babaev 22 Apr '16
by Igor Babaev 22 Apr '16
22 Apr '16
Hi,
Please help me if you are aware of such code.
In my case the tables are similar.
I understand that it's easy to write this code.
(Read rows from the source table one by one
updating the destination table).
Still I would prefer to use the existing code.
Regards,
Igor.
2
2

Re: [Maria-developers] 745b522: MDEV-8889 - Assertion `next_insert_id == 0' failed in handler::ha_external_lock
by Sergei Golubchik 22 Apr '16
by Sergei Golubchik 22 Apr '16
22 Apr '16
Hi, Sergey!
I believe this fix is ok...
On Apr 21, Sergey Vojtovich wrote:
> revision-id: 745b5226e68d376eed709d930f72c3fbc7f07d2a (mariadb-10.0.24-26-g745b522)
> parent(s): 072ca71d26487817d025ee97955e6360c3c5c086
> committer: Sergey Vojtovich
> timestamp: 2016-04-21 16:51:00 +0400
> message:
>
> MDEV-8889 - Assertion `next_insert_id == 0' failed in handler::ha_external_lock
>
> There was a race condition between delayed insert thread and connection thread
> actually performing INSERT/REPLACE DELAYED. It was triggered by concurrent
> INSERT/REPLACE DELAYED and statements that flush the same table either
> explicitely or implicitely (like FLUSH TABLE, ALTER TABLE, ...).
>
> This race condition was caused by a gap in delayed thread shutdown logic,
> which allowed concurrent connection running INSERT/REPLACE DELAYED to change
> essential data consequently leaving table in semi-consistent state.
>
> Specifically query thread could decrease "tables_in_use" reference counter in
> this gap, causing delayed insert thread to shutdown without releasing auto
> increment and table lock.
>
> Fixed by extending condition so that delayed insert thread won't shutdown
> until there're locked tables.
s/until/if/
> Also removed volatile qualifier from tables_in_use and stacked_inserts since
> they're supposed to be protected by mutexes.
It looks like stacked_inserts is accessed without a mutex at the end of
Delayed_insert::handle_inserts().
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1

Re: [Maria-developers] cd216b1: MDEV-9142 :Adding Constraint with no database reference
by Sergei Golubchik 22 Apr '16
by Sergei Golubchik 22 Apr '16
22 Apr '16
Hi, Jan!
On Apr 07, Jan Lindström wrote:
> revision-id: cd216b1ab577ffe00253ec135d6194051f7ecfa0 (mariadb-5.5.48-15-gcd216b1)
> parent(s): 11b77e9b18a8d97063b4c4a96e40bf9c75bd0e8b
> committer: Jan Lindström
> timestamp: 2016-04-07 10:47:46 +0300
> message:
>
> MDEV-9142 :Adding Constraint with no database reference
> results in ERROR 1046 (3D000) at line 13: No database selected.
>
> Use database from create table to foreign key database if
> nothing else is given.
Ok to push
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
Hi Olivier, Vladislav,
Please have a look into
MDEV-9739 Assertion `m_status == DA_ERROR || m_status == DA_OK' failed
in Diagnostics_area::message() ; connect.xml* tests fail in buildbot
The crash appeared after this commit by Vladislav:
commit 3a24f1cf8426409a69619982e84ca498d581f8a1
Author: Vladislav Vaintroub <vvaintroub(a)googlemail.com>
Date: Mon Feb 22 12:48:03 2016 +0100
MDEV-9307 - provide info about DATA/INDEX directory in
INFORMATION_SCHEMA.TA
BLES (in CREATE_OPTIONS column)
Vladislav thinks that the actual problem resides in the ConnectSE code,
in a wrong use of my_error().
Can you please take over the problem from this point?
Thanks.
On 03/21/2016 12:54 PM, Alexander Barkov wrote:
> Hi Olivier,
>
>
> On 03/20/2016 02:10 AM, Olivier Bertrand wrote:
>> Hello Alexander,
>>
>> Le 19/03/2016 14:59, Alexander Barkov a écrit :
>>> Hi Olivier,
>>>
>>> On 03/19/2016 02:18 AM, Olivier Bertrand wrote:
>>>> Hello Alexander,
>>>>
>>>> I went into linux ubuntu and pass the tests but all XML tests were
>>>> skipped saying "need libxml2". However I could check was libxml2 was
>>>> installed indeed. Don't know what to do next.
>>> Please make sure that libxml2-dev is installed.
>> It was not and I installed it. However it did not change anything.
>
> Please make sure to remove CMakeCache.txt and run cmake again from
> scratch. Does this help?
>
>
>> The problem is that libxml2 is not recognized as installed when
>> compiling Connect and then the XML type is not available.
>> (trying to create an XML table fails saying "invalid table type")
>>
>> Btw, how do the tests fail and for what reason?
>
> For some reasons it crashes in DEBUG build when doing this query:
>
>
> SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE
> TABLE_SCHEMA='test' AND TABLE_NAME='t1' AND ENGINE='CONNECT'
> AND CREATE_OPTIONS LIKE '%`table_type`=XML%' AND CREATE_OPTIONS
> LIKE '%xmlsup=libxml2%'
>
>
> with this stack trace:
>
>
> sql/sql_error.h:705(Diagnostics_area::message() const)[0x563787dda10a]
> sql/sql_select.cc:2669(JOIN::exec_inner())[0x563787dd97b5]
> sql/sql_select.cc:3453(mysql_select(THD*, Item***, TABLE_LIST*, unsigned
> int, List<Item>&, Item*, unsigned int, st_order*, st_order*, Item*,
> st_order*, unsigned long long, select_result*, st_select_lex_unit*,
> st_select_lex*))[0x563787ddcd1b]
> sql/sql_select.cc:384(handle_select(THD*, LEX*, select_result*, unsigned
> long))[0x563787dd2811]
> sql/sql_parse.cc:5936(execute_sqlcom_select)[0x563787da2ae4]
> sql/sql_parse.cc:2962(mysql_execute_command(THD*))[0x563787d98625]
> sql/sql_parse.cc:7336(mysql_parse(THD*, char*, unsigned int,
> Parser_state*))[0x563787da615d]
> sql/sql_parse.cc:1490(dispatch_command(enum_server_command, THD*, char*,
> unsigned int))[0x563787d94846]
> sql/sql_parse.cc:1109(do_command(THD*))[0x563787d93576]
> sql/sql_connect.cc:1349(do_handle_one_connection(THD*))[0x563787ec8a6c]
> sql/sql_connect.cc:1262(handle_one_connection)[0x563787ec87bb]
> perfschema/pfs.cc:1862(pfs_spawn_thread)[0x56378827a1da]
> /lib64/libpthread.so.0(+0x760a)[0x7f427b8aa60a]
> /lib64/libc.so.6(clone+0x6d)[0x7f427959fa4d]
>
>
> Can you please try to compile a DEBUG version on Linux and check
> if you get the same problem?
>
> In the meanwhile I'll try to find the commit which caused this problem.
>
> Thanks.
>
>
>>>
>>>
>>>> Olivier
>>>>
>>>> Le 18/03/2016 14:46, Alexander Barkov a écrit :
>>>>> Hello Olivier,
>>>>>
>>>>> a few tests are failing in the main 10.1 tree on Linux:
>>>>>
>>>>> connect.xml
>>>>> connect.xml_grant
>>>>> connect.xml_html
>>>>> connect.xml_mdev5261
>>>>> connect.xml_mult
>>>>>
>>>>> Can you please have a look?
>>>>>
>>>>> Thanks!
>>
3
4

21 Apr '16
https://mariadb.com/kb/en/mariadb/virtual-computed-columns/ says
the expression cannot exceed 252 characters in length.
I've got a complaint about this limitation.
Do we have any plans to remove it? (Does Oracle have it in their variant of
this feature?)
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
3
2

[Maria-developers] MDEV-9372 select 100 between 1 and 9223372036854775808 returns false
by Alexander Barkov 20 Apr '16
by Alexander Barkov 20 Apr '16
20 Apr '16
Hi Sergei,
Please review a patch for MDEV-9372.
Thanks.
2
1

Re: [Maria-developers] [Commits] 308cee547f6573a9825b5f08f938d4452d0095d9 Fixed bug mdev-9931.
by Oleksandr Byelkin 20 Apr '16
by Oleksandr Byelkin 20 Apr '16
20 Apr '16
Hi, Igor!
On 20.04.2016 00:37, Igor Babaev wrote:
> commit 308cee547f6573a9825b5f08f938d4452d0095d9
> Author: Igor Babaev <igor(a)askmonty.org>
> Commit: Igor Babaev <igor(a)askmonty.org>
>
> Fixed bug mdev-9931.
>
> When the specification of a WITH table referred to a view
> that used a based table with the same name as the WITH table
> the server went into an infinite loop because it erroneously
> resolved the reference to the base table as the reference to
> the WITH table.
>
> With tables used in a view cannot be searched for beyond the
> scope the view.
> ---
It looks OK to push. I'd name it 'is_view_border' (because it can be
more units which belon to a view), but if you think 'is_view' is better,
OK to leave it as is.
> mysql-test/r/cte_nonrecursive.result | 13 +++++++++++++
> mysql-test/t/cte_nonrecursive.test | 14 ++++++++++++++
> sql/sql_cte.cc | 5 ++++-
> sql/sql_lex.cc | 1 +
> sql/sql_lex.h | 1 +
> sql/sql_view.cc | 2 ++
> 6 files changed, 35 insertions(+), 1 deletions(-)
>
> diff --git a/mysql-test/r/cte_nonrecursive.result b/mysql-test/r/cte_nonrecursive.result
> index df64115..a9c13f3 100644
> --- a/mysql-test/r/cte_nonrecursive.result
> +++ b/mysql-test/r/cte_nonrecursive.result
> @@ -746,3 +746,16 @@ with t(f1,f1) as (select * from t1 where b >= 'c')
> select t1.b from t2,t1 where t1.a = t2.c;
> ERROR 42S21: Duplicate column name 'f1'
> drop table t1,t2;
> +#
> +# Bug mdev-9937: View used in the specification of with table
> +# refers to the base table with the same name
> +#
> +create table t1 (a int);
> +insert into t1 values (20), (30), (10);
> +create view v1 as select * from t1 where a > 10;
> +with t1 as (select * from v1) select * from t1;
> +a
> +20
> +30
> +drop view v1;
> +drop table t1;
> diff --git a/mysql-test/t/cte_nonrecursive.test b/mysql-test/t/cte_nonrecursive.test
> index 5a6e07e..e3164f5 100644
> --- a/mysql-test/t/cte_nonrecursive.test
> +++ b/mysql-test/t/cte_nonrecursive.test
> @@ -434,3 +434,17 @@ with t(f1,f1) as (select * from t1 where b >= 'c')
> select t1.b from t2,t1 where t1.a = t2.c;
>
> drop table t1,t2;
> +
> +--echo #
> +--echo # Bug mdev-9937: View used in the specification of with table
> +--echo # refers to the base table with the same name
> +--echo #
> +
> +create table t1 (a int);
> +insert into t1 values (20), (30), (10);
> +create view v1 as select * from t1 where a > 10;
> +
> +with t1 as (select * from v1) select * from t1;
> +
> +drop view v1;
> +drop table t1;
> diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
> index 1203a4c..77f0bcf 100644
> --- a/sql/sql_cte.cc
> +++ b/sql/sql_cte.cc
> @@ -512,7 +512,10 @@ bool With_element::prepare_unreferenced(THD *thd)
> {
> With_clause *with_clause=sl->get_with_clause();
> if (with_clause && (found= with_clause->find_table_def(table)))
> - return found;
> + return found;
> + /* Do not look for the table's definition beyond the scope of the view */
> + if (sl->master_unit()->is_view)
> + break;
> }
> return found;
> }
> diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
> index 6056b03..de345b4 100644
> --- a/sql/sql_lex.cc
> +++ b/sql/sql_lex.cc
> @@ -2078,6 +2078,7 @@ void st_select_lex_unit::init_query()
> found_rows_for_union= 0;
> insert_table_with_stored_vcol= 0;
> derived= 0;
> + is_view= false;
> with_clause= 0;
> with_element= 0;
> columns_are_renamed= false;
> diff --git a/sql/sql_lex.h b/sql/sql_lex.h
> index 0b4f0da..10247bd 100644
> --- a/sql/sql_lex.h
> +++ b/sql/sql_lex.h
> @@ -645,6 +645,7 @@ class st_select_lex_unit: public st_select_lex_node {
> derived tables/views handling.
> */
> TABLE_LIST *derived;
> + bool is_view;
> /* With clause attached to this unit (if any) */
> With_clause *with_clause;
> /* With element where this unit is used as the specification (if any) */
> diff --git a/sql/sql_view.cc b/sql/sql_view.cc
> index 41fd5b7..b66f678 100644
> --- a/sql/sql_view.cc
> +++ b/sql/sql_view.cc
> @@ -1612,6 +1612,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
> sl->context.error_processor_data= (void *)table;
> }
>
> + table->select_lex->master_unit()->is_view= true;
> +
> /*
> check MERGE algorithm ability
> - algorithm is not explicit TEMPORARY TABLE
> _______________________________________________
> commits mailing list
> commits(a)mariadb.org
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
1
0

[Maria-developers] MDEV-9745 Crash with CASE WHEN TRUE THEN COALESCE(CAST(NULL AS UNSIGNED)) ELSE 4 END
by Alexander Barkov 19 Apr '16
by Alexander Barkov 19 Apr '16
19 Apr '16
Hello Sergei,
please review a patch for MDEV-9745.
This is actually a backport of MDEV-9653 from 10.1 to 5.5.
Thanks.
2
1

Re: [Maria-developers] 21265d5: MDEV-9943 - TokuDB fails to compile with gcc 5.2.1
by Sergei Golubchik 19 Apr '16
by Sergei Golubchik 19 Apr '16
19 Apr '16
Hi, Sergey!
On Apr 19, Sergey Vojtovich wrote:
> revision-id: 21265d58c1ea65f5c21a56cf669321f39066d7b5 (mariadb-5.5.48-19-g21265d5)
> parent(s): 6c0e231c0282b43d6a46a4983f5971e960d3b8ca
> committer: Sergey Vojtovich
> timestamp: 2016-04-19 16:16:13 +0400
> message:
>
> MDEV-9943 - TokuDB fails to compile with gcc 5.2.1
>
> For some reason check_cxx_compiler_flag() passes result variable name down to
> compiler:
> https://github.com/Kitware/CMake/blob/master/Modules/CheckCXXSourceCompiles…
>
> But compiler doesn't permit dashes in macro name, like in
> -DHAVE_CXX_-fimplicit-templates.
>
> Workarounded by renaming HAVE_CXX_-fimplicit-templates to
> HAVE_CXX_IMPLICIT_TEMPLAES.
>
> diff --git a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake
> index 99629e4..4ae7b63 100644
> --- a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake
> +++ b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake
> @@ -103,8 +103,8 @@ set_cflags_if_supported(
>
> if (CMAKE_CXX_FLAGS MATCHES -fno-implicit-templates)
> # must append this because mysql sets -fno-implicit-templates and we need to override it
> - check_cxx_compiler_flag(-fimplicit-templates HAVE_CXX_-fimplicit-templates)
> - if (HAVE_CXX_-fimplicit-templates)
> + check_cxx_compiler_flag(-fimplicit-templates HAVE_CXX_IMPLICIT_TEMPLATES)
> + if (HAVE_CXX_IMPLICIT_TEMPLATES)
> set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fimplicit-templates")
> endif ()
> endif()
I'd rather fix other similar places too. If you agree, ok to push with
the patch below.
--- a/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake
+++ b/storage/tokudb/ft-index/cmake_modules/TokuSetupCompiler.cmake
@@ -61,12 +61,13 @@ endmacro(set_cflags_if_supported_named)
## adds a compiler flag if the compiler supports it
macro(set_cflags_if_supported)
foreach(flag ${ARGN})
- check_c_compiler_flag(${flag} HAVE_C_${flag})
- if (HAVE_C_${flag})
+ STRING(REGEX REPLACE "[-,= ]" "_" res ${flag})
+ check_c_compiler_flag(${flag} HAVE_C_${res})
+ if (HAVE_C_${res})
set(CMAKE_C_FLAGS "${flag} ${CMAKE_C_FLAGS}")
endif ()
- check_cxx_compiler_flag(${flag} HAVE_CXX_${flag})
- if (HAVE_CXX_${flag})
+ check_cxx_compiler_flag(${flag} HAVE_CXX_${res})
+ if (HAVE_CXX_${res})
set(CMAKE_CXX_FLAGS "${flag} ${CMAKE_CXX_FLAGS}")
endif ()
endforeach(flag)
@@ -75,8 +76,9 @@ endmacro(set_cflags_if_supported)
## adds a linker flag if the compiler supports it
macro(set_ldflags_if_supported)
foreach(flag ${ARGN})
- check_cxx_compiler_flag(${flag} HAVE_${flag})
- if (HAVE_${flag})
+ STRING(REGEX REPLACE "[-,= ]" "_" res ${flag})
+ check_cxx_compiler_flag(${flag} HAVE_${res})
+ if (HAVE_${res})
set(CMAKE_EXE_LINKER_FLAGS "${flag} ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${flag} ${CMAKE_SHARED_LINKER_FLAGS}")
endif ()
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1

Re: [Maria-developers] 6c0e231: MDEV-9945 - main.kill_processlist-6619 fails sporadically
by Sergei Golubchik 19 Apr '16
by Sergei Golubchik 19 Apr '16
19 Apr '16
Hi, Sergey!
On Apr 19, Sergey Vojtovich wrote:
> revision-id: 6c0e231c0282b43d6a46a4983f5971e960d3b8ca (mariadb-5.5.48-18-g6c0e231)
> parent(s): 18ff6f654b1ca595fc5c0eebddbc1d0c5ee0d09e
> committer: Sergey Vojtovich
> timestamp: 2016-04-19 14:05:52 +0400
> message:
>
> MDEV-9945 - main.kill_processlist-6619 fails sporadically
>
> SHOW PROCESSLIST output can be affected by not completed concurrent queries.
> Removed this affected SHOW PROCESSLIST since it doesn't seem to affect original
> problem.
ok to push
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0

Re: [Maria-developers] 18ff6f6: MDEV-9944 - main.events_2 fails sporadically
by Sergei Golubchik 19 Apr '16
by Sergei Golubchik 19 Apr '16
19 Apr '16
Hi, Sergey!
On Apr 19, Sergey Vojtovich wrote:
> revision-id: 18ff6f654b1ca595fc5c0eebddbc1d0c5ee0d09e (mariadb-5.5.48-17-g18ff6f6)
> parent(s): 6fd54c01bb5bc480497b143d63181837148ff47f
> committer: Sergey Vojtovich
> timestamp: 2016-04-19 12:38:00 +0400
> message:
>
> MDEV-9944 - main.events_2 fails sporadically
>
> Fixed wait condition in test case to actually wait for get_lock() completion
> (not for lock acquisition as it was before). This removes sporadic extra row in
> subsequent processlist queries.
ok to push
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
Hi Sergei!
Actually I was going through the mysql source code for unique long constraints
in file sql_tmp_table.cc in function create_tmp_table they make a new field
and a new key(hash_key) and pass this table obejct to storage
engine.They actually
refer this field as a hash field
On the time of insert they call bool check_unique_constraint(TABLE
*table) function
which first calculate the hash and store it in field then they see for duplicate
hash and retrive ha_index_next_same if records are not same then record
We can do the same thing in mariadb by adding one more field and key in
mysql_prepare_create_table in this we check for blob with unlimited
length or varchar for length
greater then internal storage engine by doing this in
mysql_prepare_create_table there
will be no issues of frm file inconsistance.
In case of insert first we will fill the hash field in fill_record
function of sql_base.cc
by first calculating the hash. Then we will retrive the index map
using ha_index_read_map
if returened value is zero then we will comapare two records and if
they match then we will through error
I am not sure where to place this code either in fill_record or later
Or i can simple just
fill hash in field in fill_record and then check for duplicates later on.
Current I am not sure how to hide columns from user.Sir, can you
suggest me where to look
But there is one problem we can make unique key by this approch but
not primary key because primary key
is clustered and hashes can collide so i think we can't use hash field
as primary key. To overcome this problem
I have one idea instead of storing just hash we can make hash field
length 10 bytes and in last two bytes
we can store short int which tells how much time hash is repeated this
can make hash unique
in case of collusion. And also we are not doing more computation
because we already retrive
all records with same hashes.
What do you think of this idea?.
And there is one more problem how to make it foreign key.
Will send you a prototype code tomorrow.
Regards
sachin
On Fri, Apr 15, 2016 at 12:23 AM, Sergei Golubchik <serg(a)mariadb.org> wrote:
> Hi, Sachin!
>
> On Apr 13, Sachin Setia wrote:
>> Hello Sergei
>> Sorry I did not see your mail. Actually i was thinking something like
>> this before implementing the prototype but if i am more closer to
>> innodb the more performance i will i get. I will definitively think
>> about it.
>
> Great!
>
> Could you please tell me (mailing list, that is) what you think before
> next Monday (before April 18h, that is)?
>
> Regards,
> Sergei
> Chief Architect MariaDB
> and security(a)mariadb.org
2
5

18 Apr '16
So while looking at MDEV-9573, I found this code in mysql_execute_command()
for STOP SLAVE:
mysql_mutex_lock(&LOCK_active_mi);
if ((mi= (master_info_index->
get_master_info(&lex_mi->connection_name,
Sql_condition::WARN_LEVEL_ERROR))))
if (!stop_slave(thd, mi, 1/* net report*/))
my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
So basically, this code is holding LOCK_active_mi for the entire duration of
the STOP SLAVE operation.
This seems completely broken. Anything in a slave thread that tries to take
LOCK_active_mi will then deadlock with the STOP SLAVE operation. It is
simple enough to trigger, testcase (with sleep-injecting patch) at the end
of the email. This uses INFORMATION_SCHEMA.SESSSION_STATUS; I could imagine
accessing a number of system variables could trigger it as well.
>From a quick check, it looks like this has been like this forever, eg. 5.1
has similar code.
Any suggestions for how this is supposed to work? Or is it just broken by
design, but saved because normally slave threads do not need to access SHOW
STATUS or system variables?
- Kristian.
-----------------------------------------------------------------------
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index ced9225..2358d6b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -7765,6 +7765,7 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff,
var->type= SHOW_CHAR;
var->value= buff;
+if (thd->slave_thread) my_sleep(5000000);
mysql_mutex_lock(&LOCK_active_mi);
if (master_info_index)
{
-----------------------------------------------------------------------
--source include/have_innodb.inc
--source include/master-slave.inc
--connection master
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
--save_master_pos
--connection slave
--sync_with_master
--source include/stop_slave.inc
--connection master
INSERT INTO t1
SELECT VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME='COM_COMMIT';
--save_master_pos
--connection slave
--source include/start_slave.inc
# Wait a bit to hit a sleep() in code after taking LOCK_active_mi.
SELECT SLEEP(2);
STOP SLAVE;
SELECT * FROM t1 ORDER BY a;
START SLAVE;
--sync_with_master
SELECT * FROM t1 ORDER BY a;
# Clean up.
--connection master
DROP TABLE t1;
--source include/rpl_end.inc
-----------------------------------------------------------------------
2
2

[Maria-developers] MDEV-9521 Least function returns 0000-00-00 for null date columns instead of null
by Alexander Barkov 18 Apr '16
by Alexander Barkov 18 Apr '16
18 Apr '16
Hi Sergei,
Please review a patch for MDEV-9521.
Thanks.
2
3
Hi all :-)
How can I enable all those nice DBUG_* macros and/or see their output?
I'm configuring MariaDB with cmake -DCMAKE_BUILD_TYPE=Debug,
but it seems I don't have those macros.
2
1

17 Apr '16
if openssl works for othres i like to know a working my.cnf to make it
work, i have added my ssql same way as used in dovecot / postfix, no ssl
error in mysql, but openssl s_client -showcerts -connect 127.0.0.1:3306
says ssl23 fails, at best i see ssl3 tlsv1 fails, output is
CONNECTED(00000003)
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 308 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1460845475
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
how to debug it more from here ?
3
5

Re: [Maria-developers] 56ef213: MDEV-9836 Connection lost when using SSL
by Sergei Golubchik 16 Apr '16
by Sergei Golubchik 16 Apr '16
16 Apr '16
Hi, Vlad!
On Mar 30, wlad(a)mariadb.com wrote:
> revision-id: 56ef213b8be7c91331168df002c4f69d54a26081 (mariadb-5.5.48-15-g56ef213)
> parent(s): 11b77e9b18a8d97063b4c4a96e40bf9c75bd0e8b
> author: Vladislav Vaintroub
> committer: Vladislav Vaintroub
> timestamp: 2016-03-30 23:09:57 +0200
> message:
>
> MDEV-9836 Connection lost when using SSL
>
> Don't read from socket in yassl in SSL_pending().
> Just return size of the buffered unprocessed data.
As far as I understand, you make it to return the size of buffered
*processed* data.
Which is correct, according to OpenSSL documentation:
SSL_pending() returns the number of bytes which have been processed,
buffered and are available inside ssl for immediate read.
If you agree with my understanding, please fix the commit comment to say
"processed" and add a reference to the manual (a quote as above or "as
specified in the OpenSSL manual" or whatever you prefer).
Then ok push.
> diff --git a/extra/yassl/src/ssl.cpp b/extra/yassl/src/ssl.cpp
> index 9516e8b..2346533 100644
> --- a/extra/yassl/src/ssl.cpp
> +++ b/extra/yassl/src/ssl.cpp
> @@ -1471,10 +1471,6 @@ int SSL_peek(SSL* ssl, void* buffer, int sz)
>
> int SSL_pending(SSL* ssl)
> {
> - // Just in case there's pending data that hasn't been processed yet...
> - char c;
> - SSL_peek(ssl, &c, 1);
> -
> return ssl->bufferedData();
> }
>
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0
I am having trouble compiling MariaDB as a conda package on Ubuntu 14.04.
I pushed the conda recipe that I am using as well as the the stdout and
stderr output to a github repo
<https://github.com/ostrokach/conda-recipes-extra/tree/master/mariadb-10>.
In brief, I am compiling MariaDB using
CFLAGS="-I$PREFIX/include -I$PREFIX/include/ncurses" \
CPPFLAGS="-I$PREFIX/include -I$PREFIX/include/ncurses" \
CXXFLAGS="-I$PREFIX/include -I$PREFIX/include/ncurses" \
LDFLAGS="-L$PREFIX/lib" \
cmake . \
-DBUILD_CONFIG=mysql_release \
-DCMAKE_PREFIX_PATH:PATH="$PREFIX" \
-DCMAKE_INSTALL_PREFIX:PATH="$PREFIX" \
-DMYSQL_DATADIR:PATH="$PREFIX/data"
make
make install
where $PREFIX is the location of the conda build environment containing the
following packages:
requirements:
build:
- cmake
- bison
- ncurses
- zlib
- lzo
- jemalloc ==3.6.0
- aio
- readline
- openssl
- zeromq
I am using the system gcc compiler:
gcc (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
I get the following error at 91% during make:
+ CFLAGS='-I/home/user/anaconda3/envs/_build/include
-I/home/user/anaconda3/envs/_build/include/ncurses'
+ CPPFLAGS='-I/home/user/anaconda3/envs/_build/include
-I/home/user/anaconda3/envs/_build/include/ncurses'
+ CXXFLAGS='-I/home/user/anaconda3/envs/_build/include
-I/home/user/anaconda3/envs/_build/include/ncurses'
+ LDFLAGS=-L/home/user/anaconda3/envs/_build/lib
+ cmake . -DBUILD_CONFIG=mysql_release
-DCMAKE_PREFIX_PATH:PATH=/home/user/anaconda3/envs/_build
-DCMAKE_INSTALL_PREFIX:PATH=/home/user/anaconda3/envs/_build
-DMYSQL_DATADIR:PATH=/home/user/anaconda3/envs/_build/data
+ make
/usr/bin/ar: creating
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/libmysql/libmysqlclient.a
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/sql/sql_yacc.yy:1022.1-12:
warning: deprecated directive, use ‘%pure-parser’ [-Wdeprecated]
%pure_parser /* We have threads */
^^^^^^^^^^^^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/mroonga/vendor/groonga/lib/expr.c:
In function ‘grn_expr_exec’:
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/mroonga/vendor/groonga/lib/expr.c:3543:1:
warning: const/copy propagation disabled: 21688 basic blocks and 63352
registers [-Wdisabled-optimization]
}
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/mroonga/vendor/groonga/lib/expr.c:3543:1:
warning: PRE disabled: 21688 basic blocks and 63352 registers
[-Wdisabled-optimization]
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/mroonga/vendor/groonga/lib/expr.c:3543:1:
warning: const/copy propagation disabled: 21688 basic blocks and 63352
registers [-Wdisabled-optimization]
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/mroonga/vendor/groonga/lib/expr.c:3543:1:
warning: const/copy propagation disabled: 19113 basic blocks and 64360
registers [-Wdisabled-optimization]
CMake Warning (dev) in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.5)
should be added at the top of the file. The version specified may be lower
if you wish to support older CMake versions for this project. For more
information run "cmake --help-policy CMP0000".
This warning is for project developers. Use -Wno-dev to suppress it.
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/tokudb/PerconaFT/xz/src/build_lzma/src/liblzma/lz/lz_encoder.c:
In function ‘fill_window’:
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/storage/tokudb/PerconaFT/xz/src/build_lzma/src/liblzma/lz/lz_encoder.c:89:9:
warning: variable ‘in_used’ set but not used
[-Wunused-but-set-variable]
size_t in_used;
^
In file included from
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/plugin/auth_gssapi/client_plugin.cc:37:0:
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/include/mysql/client_plugin.h:64:9:
warning: ‘_mysql_client_plugin_declaration_’ initialized and declared
‘extern’ [enabled by default]
_mysql_client_plugin_declaration_ = { \
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/plugin/auth_gssapi/client_plugin.cc:101:1:
note: in expansion of macro ‘mysql_declare_client_plugin’
mysql_declare_client_plugin(AUTHENTICATION)
^
troff: fatal error: can't find macro file m
troff: fatal error: can't find macro file m
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:
In function ‘int com_status(String*, char*)’:
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4738:13:
error: ‘A_BOLD’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4738:19:
error: ‘vidattr’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4740:13:
error: ‘A_NORMAL’ was not declared in this scope
vidattr(A_NORMAL);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4808:13:
error: ‘A_BOLD’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4808:19:
error: ‘vidattr’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4810:13:
error: ‘A_NORMAL’ was not declared in this scope
vidattr(A_NORMAL);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:
In function ‘int put_info(const char*, INFO_TYPE, uint, const char*)’:
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4905:47:
error: ‘setupterm’ was not declared in this scope
(void) setupterm((char *)0, 1, (int *) 0);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4918:15:
error: ‘A_STANDOUT’ was not declared in this scope
vidattr(A_STANDOUT);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4918:25:
error: ‘vidattr’ was not declared in this scope
vidattr(A_STANDOUT);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4937:15:
error: ‘A_BOLD’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4937:21:
error: ‘vidattr’ was not declared in this scope
vidattr(A_BOLD);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4939:13:
error: ‘A_NORMAL’ was not declared in this scope
vidattr(A_NORMAL);
^
/home/user/anaconda3/conda-bld/work/server-mariadb-10.1.13/client/mysql.cc:4939:21:
error: ‘vidattr’ was not declared in this scope
vidattr(A_NORMAL);
^
make[2]: *** [client/CMakeFiles/mysql.dir/mysql.cc.o] Error 1
make[1]: *** [client/CMakeFiles/mysql.dir/all] Error 2
make: *** [all] Error 2
Using Anaconda Cloud api site https://api.anaconda.org
Command failed: /bin/bash -x -e
/home/user/anaconda/conda-recipes-extra/mariadb-10/build.sh
Any help would be much appreciated,
Alexey
2
1
Hello Developers,
Hi this is sachin.Actually i was currently experimenting with with
blob uniques in innodb
there is three main problems
1.Unique blob
2.Blob Forigen key
3.Blob primary key
1. For blob unique we can simply store hash in unclustered index
2. Blob Forigen key i am currently working on it
3. Blob primary key :- for this i thought we create a 4 byte column
which stores the hash of blob primary key.Internally this column will work
as
primary key and key for clustered index. I already successufully tested
this
here is my output
MariaDB [sachin]> create table t4 (abc blob primary key);
Query OK, 0 rows affected (0.10 sec)
MariaDB [sachin]> insert into t4 values('sachin');
Query OK, 1 row affected (0.01 sec)
MariaDB [sachin]> insert into t4 values('sachin');
ERROR 1062 (23000): Duplicate entry 'sachin' for key 'PRIMARY'
MariaDB [sachin]> insert into t4 values('sachin setiya');
Query OK, 1 row affected (0.00 sec)
MariaDB [sachin]> insert into t4 values('sachin setiya');
ERROR 1062 (23000): Duplicate entry 'sachin setiya' for key 'PRIMARY'
MariaDB [sachin]> desc t4;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| abc | blob | NO | PRI | NULL | |
+-------+------+------+-----+---------+-------+
1 row in set (0.01 sec)
MariaDB [sachin]> select * from t4;
+---------------+
| abc |
+---------------+
| sachin |
| sachin setiya |
+---------------+
2 rows in set (0.01 sec)
@Sergei hi! Actually i invested arround 2 months in mariadb So for me now
it does not matter either i got selected in gsoc i want to do work in
innodb blob unique from today.Please sir allow me to do this
I am including the patch file and t4.ibd and t4.frm file
Regards
sachin
2
3

Re: [Maria-developers] 4c51152: MDEV-8931: (server part of) session state tracking
by Sergei Golubchik 08 Apr '16
by Sergei Golubchik 08 Apr '16
08 Apr '16
Hi, Sanja!
Summary:
Lots of comments below, many changes needed.
When you copy a big feature from MySQL next time - please, take a
more critical look at what you're merging.
On Mar 28, OleksandrByelkin wrote:
> revision-id: 4c51152d9f43e271e17a2bc266f5887ce092c00f (mariadb-10.1.8-187-g4c51152)
> parent(s): 69b5c4a4220c8fa98bbca8b3f935b2a3735e19ac
> committer: Oleksandr Byelkin
> timestamp: 2016-03-28 19:19:54 +0200
> message:
>
> MDEV-8931: (server part of) session state tracking
>
> diff --git a/include/mysql_com.h b/include/mysql_com.h
> index c13999a..c6608b7 100644
> --- a/include/mysql_com.h
> +++ b/include/mysql_com.h
> @@ -520,6 +544,30 @@ enum enum_mysql_set_option
> MYSQL_OPTION_MULTI_STATEMENTS_OFF
> };
>
> +/*
> + Type of state change information that the server can include in the Ok
> + packet.
> + Note : 1) session_state_type shouldn't go past 255 (i.e. 1-byte boundary).
> + 2) Modify the definition of SESSION_TRACK_END when a new member is
> + added.
> +*/
> +enum enum_session_state_type
> +{
> + SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */
support comes in the next patch, I know
> + SESSION_TRACK_SCHEMA, /* Current schema */
suported, I presume
> + SESSION_TRACK_STATE_CHANGE, /* track session state changes */
suported, I presume
> + SESSION_TRACK_GTIDS,
not suported, I presume
> + SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */
> + SESSION_TRACK_TRANSACTION_STATE /* Transaction state */
to be imlemented later, I suppose
> +};
> +
> +#define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES
> +
> +#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_STATE
> +
> +#define IS_SESSION_STATE_TYPE(T) \
> + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) <= SESSION_TRACK_END))
> +
> #define net_new_transaction(net) ((net)->pkt_nr=0)
>
> #ifdef __cplusplus
> diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
> index a35693e..f69b615 100644
> --- a/mysql-test/r/mysqld--help.result
> +++ b/mysql-test/r/mysqld--help.result
> @@ -903,6 +903,11 @@ The following options may be given as the first argument:
> files within specified directory
> --server-id=# Uniquely identifies the server instance in the community
> of replication partners
> + --session-track-schema
> + Track changes to the 'default schema'.
> + (Defaults to on; use --skip-session-track-schema to disable.)
> + --session-track-state-change
> + Track changes to the 'session state'.
> --show-slave-auth-info
> Show user and password in SHOW SLAVE HOSTS on this
> master.
> diff --git a/mysql-test/suite/sys_vars/r/session_track_schema_basic.result b/mysql-test/suite/sys_vars/r/session_track_schema_basic.result
> new file mode 100644
> index 0000000..be4b892
> --- /dev/null
> +++ b/mysql-test/suite/sys_vars/r/session_track_schema_basic.result
> @@ -0,0 +1,116 @@
we don't need these "basic" sysvar tests anymore. The test
that checks for them is disabled. sysvar_* tests are now used to
show basic sysvar characteristics
> +#
> +# Variable name : session_track_schema
> +# Scope : Global & Session
> +#
> +# Global - default
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +1
> +# Session - default
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +
> +# via INFORMATION_SCHEMA.GLOBAL_VARIABLES
> +SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'track_current%';
> +VARIABLE_NAME VARIABLE_VALUE
> +# via INFORMATION_SCHEMA.SESSION_VARIABLES
> +SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'track_current%';
> +VARIABLE_NAME VARIABLE_VALUE
> +SET @global_saved_tmp = @@global.session_track_schema;
> +
> +# Altering global variable's value
> +SET @@global.session_track_schema = 0;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +SET @@global.session_track_schema = TrUe;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +1
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +SET @@global.session_track_schema = FaLsE;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +
> +# Altering session variable's value
> +SET @@session.session_track_schema = 0;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +0
> +
> +# Variables' values in a new session.
> +# Global - expect 0
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +
> +# Session - expect 0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +0
> +
> +# Switching to the default connection.
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +0
> +
> +# Test if DEFAULT is working as expected.
> +SET @@global.session_track_schema = DEFAULT;
> +SET @@session.session_track_schema = DEFAULT;
> +
> +# Global - expect 1
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +1
> +# Session - expect 1
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +
> +# Variables' values in a new session (con2).
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +1
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +
> +# Altering session should not affect global.
> +SET @@session.session_track_schema = FALSE;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +1
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +0
> +
> +# Variables' values in a new session (con3).
> +# Altering global should not affect session.
> +SET @@global.session_track_schema = OFF;
> +SELECT @@global.session_track_schema;
> +@@global.session_track_schema
> +0
> +SELECT @@session.session_track_schema;
> +@@session.session_track_schema
> +1
> +
> +# Switching to the default connection.
> +# Restoring the original values.
> +SET @@global.session_track_schema = @global_saved_tmp;
> +# End of tests.
> diff --git a/mysql-test/suite/sys_vars/t/session_track_schema_basic.test b/mysql-test/suite/sys_vars/t/session_track_schema_basic.test
> new file mode 100644
> index 0000000..220ae35
> --- /dev/null
> +++ b/mysql-test/suite/sys_vars/t/session_track_schema_basic.test
> @@ -0,0 +1,105 @@
another meaningless basic test
> +--source include/not_embedded.inc
> +
> +--echo #
> +--echo # Variable name : session_track_schema
> +--echo # Scope : Global & Session
> +--echo #
> +
> +--echo # Global - default
> +SELECT @@global.session_track_schema;
> +--echo # Session - default
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES
> +--disable_warnings
> +SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'track_current%';
> +--enable_warnings
> +
> +--echo # via INFORMATION_SCHEMA.SESSION_VARIABLES
> +--disable_warnings
> +SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'track_current%';
> +--enable_warnings
> +
> +# Save the global value to be used to restore the original value.
> +SET @global_saved_tmp = @@global.session_track_schema;
> +--echo
> +
> +--echo # Altering global variable's value
> +SET @@global.session_track_schema = 0;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +
> +SET @@global.session_track_schema = TrUe;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +
> +SET @@global.session_track_schema = FaLsE;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Altering session variable's value
> +SET @@session.session_track_schema = 0;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Variables' values in a new session.
> +connect (con1,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +--echo # Global - expect 0
> +SELECT @@global.session_track_schema;
> +--echo
> +--echo # Session - expect 0
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Switching to the default connection.
> +connection default;
> +
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Test if DEFAULT is working as expected.
> +SET @@global.session_track_schema = DEFAULT;
> +SET @@session.session_track_schema = DEFAULT;
> +--echo
> +
> +--echo # Global - expect 1
> +SELECT @@global.session_track_schema;
> +--echo # Session - expect 1
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Variables' values in a new session (con2).
> +connect (con2,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Altering session should not affect global.
> +SET @@session.session_track_schema = FALSE;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Variables' values in a new session (con3).
> +connect (con3,"127.0.0.1",root,,test,$MASTER_MYPORT,);
> +
> +--echo # Altering global should not affect session.
> +SET @@global.session_track_schema = OFF;
> +SELECT @@global.session_track_schema;
> +SELECT @@session.session_track_schema;
> +--echo
> +
> +--echo # Switching to the default connection.
> +connection default;
> +
> +--echo # Restoring the original values.
> +SET @@global.session_track_schema = @global_saved_tmp;
> +
> +--echo # End of tests.
> +
> diff --git a/sql/protocol.cc b/sql/protocol.cc
> index 9e52870..c1e66d7 100644
> --- a/sql/protocol.cc
> +++ b/sql/protocol.cc
> @@ -197,6 +197,8 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
> @param affected_rows Number of rows changed by statement
> @param id Auto_increment id for first row (if used)
> @param message Message to send to the client (Used by mysql_status)
> + @param eof_indentifier when true [FE] will be set in OK header
> + else [00] will be used
eh, I understand that's what MySQL has, and we want to be
protocol-compatible, but can you put a meaningful comment here, please?
Not like
i++; // increment i by 1
and a better name for the parameter would be nice too.
>
> @return
> @retval FALSE The message was successfully sent
> @@ -221,9 +233,32 @@ net_send_ok(THD *thd,
> DBUG_RETURN(FALSE);
> }
>
> - buff[0]=0; // No fields
> - pos=net_store_length(buff+1,affected_rows);
> - pos=net_store_length(pos, id);
> + start= buff;
> +
> + /*
> + Use 0xFE packet header if eof_identifier is true
> + unless we are talking to old client
> + */
fix this comment, please
> + if (eof_identifier &&
> + (thd->client_capabilities & CLIENT_DEPRECATE_EOF))
> + buff[0]= 254;
> + else
> + buff[0]= 0;
I don't understand this, because the comment doesn't explain anything
> +
> + /* affected rows */
> + pos= net_store_length(buff + 1, affected_rows);
> +
> + /* last insert id */
> + pos= net_store_length(pos, id);
> +
> + if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
> + thd->session_tracker.enabled_any() &&
> + thd->session_tracker.changed_any())
> + {
> + server_status |= SERVER_SESSION_STATE_CHANGED;
> + state_changed= true;
I'd rather have trackers to set server_status when they're changed,
instead of iterating the list of trackers here twice for every statement
just because we apparently have free CPU cycles to burn
> + }
> +
> if (thd->client_capabilities & CLIENT_PROTOCOL_41)
> {
> DBUG_PRINT("info",
> @@ -247,9 +282,45 @@ net_send_ok(THD *thd,
> }
> thd->get_stmt_da()->set_overwrite_status(true);
>
> + if ((thd->client_capabilities & CLIENT_SESSION_TRACK))
> + {
> + /* the info field */
> + if (state_changed || (message && message[0]))
> + pos= net_store_data(pos, (uchar*) message, message ? strlen(message) : 0);
> + /* session state change information */
> + if (unlikely(state_changed))
> + {
> + store.set_charset(thd->variables.collation_database);
> +
> + /*
> + First append the fields collected so far. In case of malloc, memory
> + for message is also allocated here.
> + */
> + store.append((const char *)start, (pos - start), MYSQL_ERRMSG_SIZE);
no, don't use "buf or store, depending on whether session tracking is used"
that's ridiculous (and an extra memcpy)
always use store, remove buf. declare store as
StringBuffer<MYSQL_ERRMSG_SIZE+10> store(thd->variables.collation_database);
> +
> + /* .. and then the state change information. */
> + thd->session_tracker.store(thd, store);
> +
> + start= (uchar *) store.ptr();
> + pos= start+store.length();
> + }
> + }
> - if (message && message[0])
> + else if (message && message[0])
> + {
> + /* the info field, if there is a message to store */
> pos= net_store_data(pos, (uchar*) message, strlen(message));
> - error= my_net_write(net, buff, (size_t) (pos-buff));
> + }
> +
> + /* OK packet length will be restricted to 16777215 bytes */
> + if (((size_t) (pos - start)) > MAX_PACKET_LENGTH)
> + {
> + net->error= 1;
I'm surprised, this doesn't have a comment "setting net->error to 1"
would be quite in style with what I've seen so far :)
> + net->last_errno= ER_NET_OK_PACKET_TOO_LARGE;
> + my_error(ER_NET_OK_PACKET_TOO_LARGE, MYF(0));
> + DBUG_PRINT("info", ("OK packet too large"));
> + DBUG_RETURN(1);
> + }
> + error= my_net_write(net, start, (size_t) (pos - start));
> if (!error)
> error= net_flush(net);
>
> @@ -559,7 +630,20 @@ bool Protocol::send_ok(uint server_status, uint statement_warn_count,
> bool Protocol::send_eof(uint server_status, uint statement_warn_count)
> {
> DBUG_ENTER("Protocol::send_eof");
> - const bool retval= net_send_eof(thd, server_status, statement_warn_count);
> + bool retval;
> + /*
> + Normally end of statement reply is signaled by OK packet, but in case
> + of binlog dump request an EOF packet is sent instead. Also, old clients
> + expect EOF packet instead of OK
> + */
> +#ifndef EMBEDDED_LIBRARY
> + if ((thd->client_capabilities & CLIENT_DEPRECATE_EOF) &&
> + (thd->get_command() != COM_BINLOG_DUMP ))
> + retval= net_send_ok(thd, server_status, statement_warn_count, 0, 0, NULL,
> + true);
> + else
> +#endif
> + retval= net_send_eof(thd, server_status, statement_warn_count);
better keep the old code here, just net_send_eof().
and in net_send_eof() you do, like
if (thd->client_capabilities & CLIENT_DEPRECATE_EOF)
return net_send_ok(..., 0, 0, NULL, true);
> DBUG_RETURN(retval);
> }
>
> @@ -1551,8 +1641,14 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
> /* Restore THD::server_status. */
> thd->server_status&= ~SERVER_PS_OUT_PARAMS;
>
> - /* Send EOF-packet. */
> - net_send_eof(thd, thd->server_status, 0);
> + if (thd->client_capabilities & CLIENT_DEPRECATE_EOF)
> + ret= net_send_ok(thd,
> + (thd->server_status | SERVER_PS_OUT_PARAMS |
> + SERVER_MORE_RESULTS_EXISTS),
> + 0, 0, 0, NULL, true);
> + else
> + /* In case of old clients send EOF packet. */
> + ret= net_send_eof(thd, thd->server_status, 0);
>
> /*
> Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
note that unlike 5.7 you we have this "Reset SERVER_MORE_RESULTS_EXISTS"
*after* net_send_eof() (bugfix for MDEV-4604)
1. you don't need "| SERVER_MORE_RESULTS_EXISTS" for net_send_ok()
2. you might try to use only net_send_eof() here too (with both flags set)
> diff --git a/sql/session_tracker.h b/sql/session_tracker.h
> new file mode 100644
> index 0000000..7477f96
> --- /dev/null
> +++ b/sql/session_tracker.h
> @@ -0,0 +1,255 @@
> +#ifndef SESSION_TRACKER_INCLUDED
> +#define SESSION_TRACKER_INCLUDED
> +
> +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
> +
> +#include "m_string.h"
> +#include "thr_lock.h"
> +
> +/* forward declarations */
> +class THD;
> +class set_var;
> +class String;
> +
> +
> +enum enum_session_tracker
> +{
> + SESSION_SYSVARS_TRACKER, /* Session system variables */
> + CURRENT_SCHEMA_TRACKER, /* Current schema */
> + SESSION_STATE_CHANGE_TRACKER,
> + SESSION_GTIDS_TRACKER, /* Tracks GTIDs */
> + TRANSACTION_INFO_TRACKER /* Transaction state */
why not to use the same enum from mysql_com.h ?
> +};
> +
> +#define SESSION_TRACKER_END TRANSACTION_INFO_TRACKER
> +
> +
> +/**
> + State_tracker
> + -------------
> + An abstract class that defines the interface for any of the server's
> + 'session state change tracker'. A tracker, however, is a sub- class of
> + this class which takes care of tracking the change in value of a part-
> + icular session state type and thus defines various methods listed in this
> + interface. The change information is later serialized and transmitted to
> + the client through protocol's OK packet.
> +
> + Tracker system variables :-
> + A tracker is normally mapped to a system variable. So in order to enable,
> + disable or modify the sub-entities of a tracker, the user needs to modify
> + the respective system variable either through SET command or via command
> + line option. As required in system variable handling, this interface also
> + includes two functions to help in the verification of the supplied value
> + (ON_CHECK) and the updation (ON_UPDATE) of the tracker system variable,
> + namely - check() and update().
this is illogical. check() and update() do not belong in the base State_tracker
> +*/
> +
> +class State_tracker
> +{
> +protected:
> + /** Is tracking enabled for a particular session state type ? */
> + bool m_enabled;
> +
> + /** Has the session state type changed ? */
> + bool m_changed;
> +
> +public:
> + /** Constructor */
> + State_tracker() : m_enabled(false), m_changed(false)
> + {}
> +
> + /** Destructor */
> + virtual ~State_tracker()
> + {}
> +
> + /** Getters */
> + bool is_enabled() const
> + { return m_enabled; }
> +
> + bool is_changed() const
> + { return m_changed; }
> +
> + /** Called in the constructor of THD*/
> + virtual bool enable(THD *thd)= 0;
> +
> + /** To be invoked when the tracker's system variable is checked (ON_CHECK). */
> + virtual bool check(THD *thd, set_var *var)= 0;
> +
> + /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/
> + virtual bool update(THD *thd)= 0;
> +
> + /** Store changed data into the given buffer. */
> + virtual bool store(THD *thd, String &buf)= 0;
> +
> + /** Mark the entity as changed. */
> + virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0;
> +};
> +
> +
> +/**
> + Session_tracker
> + ---------------
> + This class holds an object each for all tracker classes and provides
> + methods necessary for systematic detection and generation of session
> + state change information.
> +*/
> +
> +class Session_tracker
> +{
> +private:
> + State_tracker *m_trackers[SESSION_TRACKER_END + 1];
> +
> + /* The following two functions are private to disable copying. */
> + /** Copy constructor */
> + Session_tracker(Session_tracker const &other)
> + {
> + DBUG_ASSERT(FALSE);
> + }
> +
> + /** Copy assignment operator */
> + Session_tracker& operator= (Session_tracker const &rhs)
> + {
> + DBUG_ASSERT(FALSE);
> + return *this;
> + }
> +
> +public:
judging by other comments in this file (above and below), I'm surprised
this keyword does not have a comment, like /* declare the rest of the
object public. that makes the methods below accessible from the
other code, not only from this object itself */
Serisouly, why do these comments explain basic C++ features?
or repeat whatever the function is obviously doing (like, get_tracker() or
enabled_any())
And non-trivial functions are not commented, for example server_boot_verify()
> +
> + /** Constructor */
> + Session_tracker()
> + {}
> +
> + /** Destructor */
> + ~Session_tracker()
> + {
> + }
> + /**
> + Initialize Session_tracker objects and enable them based on the
> + tracker_xxx variables' value that the session inherit from global
> + variables at the time of session initialization (see plugin_thdvar_init).
> + */
> + void init(const CHARSET_INFO *char_set);
> + void enable(THD *thd);
> + bool server_boot_verify(const CHARSET_INFO *char_set, LEX_STRING var_list);
> +
> + /** Returns the pointer to the tracker object for the specified tracker. */
> + State_tracker *get_tracker(enum_session_tracker tracker) const;
> +
> + /** Checks if m_enabled flag is set for any of the tracker objects. */
> + bool enabled_any();
> +
> + /** Checks if m_changed flag is set for any of the tracker objects. */
> + bool changed_any();
> +
> + /**
> + Stores the session state change information of all changes session state
> + type entities into the specified buffer.
> + */
> + void store(THD *thd, String &main_buf);
> + void deinit()
> + {
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + delete m_trackers[i];
> + }
> +};
> +
> +/*
> + Session_state_change_tracker
> + ----------------------------
> + This is a boolean tracker class that will monitor any change that contributes
> + to a session state change.
> + Attributes that contribute to session state change include:
> + - Successful change to System variables
> + - User defined variables assignments
> + - temporary tables created, altered or deleted
> + - prepared statements added or removed
> + - change in current database
> +*/
> +
> +class Session_state_change_tracker : public State_tracker
Why is this one in the header, not in session_tracker.cc?
> +{
> +private:
> +
> + void reset();
> +
> +public:
> + Session_state_change_tracker();
> + bool enable(THD *thd);
> + bool check(THD *thd, set_var *var)
> + { return false; }
> + bool update(THD *thd);
> + bool store(THD *thd, String &buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> + bool is_state_changed(THD*);
> + void ensure_enabled(THD *thd)
> + {}
> +};
> +
> +
> +/**
> + Transaction_state_tracker
> + ----------------------
> + This is a tracker class that enables & manages the tracking of
> + current transaction info for a particular connection.
> +*/
> +
> +/**
> + Transaction state (no transaction, transaction active, work attached, etc.)
> +*/
> +enum enum_tx_state {
> + TX_EMPTY = 0, ///< "none of the below"
> + TX_EXPLICIT = 1, ///< an explicit transaction is active
> + TX_IMPLICIT = 2, ///< an implicit transaction is active
> + TX_READ_TRX = 4, ///< transactional reads were done
> + TX_READ_UNSAFE = 8, ///< non-transaction reads were done
> + TX_WRITE_TRX = 16, ///< transactional writes were done
> + TX_WRITE_UNSAFE = 32, ///< non-transactional writes were done
> + TX_STMT_UNSAFE = 64, ///< "unsafe" (non-deterministic like UUID()) stmts
> + TX_RESULT_SET = 128, ///< result-set was sent
> + TX_WITH_SNAPSHOT= 256, ///< WITH CONSISTENT SNAPSHOT was used
> + TX_LOCKED_TABLES= 512 ///< LOCK TABLES is active
> +};
> +
> +/**
> + Transaction access mode
> +*/
> +enum enum_tx_read_flags {
> + TX_READ_INHERIT = 0, ///< not explicitly set, inherit session.tx_read_only
> + TX_READ_ONLY = 1, ///< START TRANSACTION READ ONLY, or tx_read_only=1
> + TX_READ_WRITE = 2, ///< START TRANSACTION READ WRITE, or tx_read_only=0
> +};
> +
> +/**
> + Transaction isolation level
> +*/
> +enum enum_tx_isol_level {
> + TX_ISOL_INHERIT = 0, ///< not explicitly set, inherit session.tx_isolation
> + TX_ISOL_UNCOMMITTED = 1,
> + TX_ISOL_COMMITTED = 2,
> + TX_ISOL_REPEATABLE = 3,
> + TX_ISOL_SERIALIZABLE= 4
> +};
> +
> +/**
> + Transaction tracking level
> +*/
> +enum enum_session_track_transaction_info {
> + TX_TRACK_NONE = 0, ///< do not send tracker items on transaction info
> + TX_TRACK_STATE = 1, ///< track transaction status
> + TX_TRACK_CHISTICS = 2 ///< track status and characteristics
> +};
Better remove all declarations related to transaction tracking and add
them later in a separate patch (with the complete transaction tracking
feature).
> +
> +#endif /* SESSION_TRACKER_INCLUDED */
> diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
> new file mode 100644
> index 0000000..6abff2a
> --- /dev/null
> +++ b/sql/session_tracker.cc
> @@ -0,0 +1,454 @@
> +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
and "Copyright(c) 2016, MariaDB" because it's not 1:1 copy
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
> +
> +
> +#include "session_tracker.h"
> +
> +#include "hash.h"
> +#include "table.h"
> +#include "rpl_gtid.h"
> +#include "sql_class.h"
> +#include "sql_show.h"
> +#include "sql_plugin.h"
> +//#include "xa.h"
this is for SESSION_TRACK_TRANSACTION_STATE too I suppose
> +
> +static void store_lenenc_string(String &to, const char *from,
> + size_t length);
> +
> +class Dummy_tracker : public State_tracker
> +{
> +bool enable(THD *thd __attribute__((unused)))
why __attribute__((unused)) if it is used?
and why is it used, if the tracker is dummy?
btw, seeing how this Dummy_tracker is used, I'd rather
rename it to Not_implemented_tracker
> + { return update(thd); }
> + bool check(THD *thd __attribute__((unused)),
> + set_var *var __attribute__((unused)))
> + { return false; }
it's C++, you don't need __attribute__((unused)), write
bool check(THD *, set_var *) { return false; }
> + bool update(THD *thd __attribute__((unused)))
> + { return false; }
> + bool store(THD *thd __attribute__((unused)),
> + String &buf __attribute__((unused)))
> +
> + { return false; }
> + void mark_as_changed(THD *thd __attribute__((unused)),
> + LEX_CSTRING *tracked_item_name __attribute__((unused)))
> + {}
> +
> +};
> +
> +
> +/**
> + Current_schema_tracker
> + ----------------------
> + This is a tracker class that enables & manages the tracking of current
> + schema for a particular connection.
> +*/
> +
> +class Current_schema_tracker : public State_tracker
> +{
> +private:
> + bool schema_track_inited;
> + void reset();
> +
> +public:
> +
> + /** Constructor */
> + Current_schema_tracker()
> + {
> + schema_track_inited= false;
> + }
> +
> + bool enable(THD *thd)
> + { return update(thd); }
> + bool check(THD *thd, set_var *var)
> + { return false; }
> + bool update(THD *thd);
> + bool store(THD *thd, String &buf);
> + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
> +};
> +
> +/* To be used in expanding the buffer. */
> +static const unsigned int EXTRA_ALLOC= 1024;
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +/**
> + @brief Enable/disable the tracker based on @@session_track_schema's value.
> +
> + @param thd [IN] The thd handle.
> +
> + @return
> + false (always)
> +*/
> +
> +bool Current_schema_tracker::update(THD *thd)
> +{
> + m_enabled= (thd->variables.session_track_schema)? true: false;
> + return false;
> +}
> +
> +
> +/**
> + @brief Store the schema name as length-encoded string in the specified
> + buffer. Once the data is stored, we reset the flags related to
> + state-change (see reset()).
> +
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @return
> + false Success
> + true Error
> +*/
> +
> +bool Current_schema_tracker::store(THD *thd, String &buf)
> +{
> + ulonglong db_length, length;
> +
> + length= db_length= thd->db_length;
> + length += net_length_size(length);
> +
> + uchar *to= (uchar *) buf.prep_append(net_length_size(length) + 1,
> + EXTRA_ALLOC);
> +
> + /* Session state type (SESSION_TRACK_SCHEMA) */
> + to= net_store_length(to, (ulonglong)SESSION_TRACK_SCHEMA);
> +
> + /* Length of the overall entity. */
> + to= net_store_length(to, length);
> +
> + /* Length of the changed current schema name. */
> + net_store_length(to, db_length);
> +
> + /* Current schema name (length-encoded string). */
> + store_lenenc_string(buf, thd->db, thd->db_length);
> +
> + reset();
> +
> + return false;
> +}
> +
> +
> +/**
> + @brief Mark the tracker as changed.
> +
> + @param name [IN] Always null.
> +
> + @return void
> +*/
> +
> +void Current_schema_tracker::mark_as_changed(THD *thd,
> + LEX_CSTRING *tracked_item_name
> + __attribute__((unused)))
you don't need __attribute__((unused)) here, it's C++
besides, as I've just found, we compile with -Wno-unused-parameter
(this was merged from MySQL, perhaps we should remove that flag)
> +{
> + m_changed= true;
> + thd->lex->safe_to_cache_query= 0;
> +}
> +
> +
> +/**
> + @brief Reset the m_changed flag for next statement.
> +
> + @return void
> +*/
> +
> +void Current_schema_tracker::reset()
> +{
> + m_changed= false;
> +}
> +
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +/** Constructor */
> +Session_state_change_tracker::Session_state_change_tracker()
> +{
> + m_changed= false;
> +}
> +
> +/**
> + @brief Initiate the value of m_enabled based on
> + @@session_track_state_change value.
> +
> + @param thd [IN] The thd handle.
> + @return false (always)
> +
> +**/
> +
> +bool Session_state_change_tracker::enable(THD *thd)
> +{
> + m_enabled= (thd->variables.session_track_state_change)? true: false;
> + return false;
> +}
> +
> +/**
> + @Enable/disable the tracker based on @@session_track_state_change value.
> +
> + @param thd [IN] The thd handle.
> + @return false (always)
> +
> +**/
> +
> +bool Session_state_change_tracker::update(THD *thd)
> +{
> + return enable(thd);
> +}
you're kidding, right?
For Dummy_tracker and Current_schema_tracker enable() calls upate(),
for Session_state_change_tracker update() calls enable().
and no matter what method calls what, it's always
one_method(THD *thd) { return other_method(thd); }
other_method(THD *thd) {
m_enabled= (thd->variables.session_track_XXX)? true: false;
}
why two methods enable/update and not one?
why "? true : false", and not m_enabled=thd->variables.session_track_XXX
(hint: m_enabled is bool, ?true:false is the same as cast to bool anyway)
and why to copy the value in the first place??? Just use the value
from thd->variables.session_track_XXX
> +
> +/**
> + @brief Store the 1byte boolean flag in the specified buffer. Once the
> + data is stored, we reset the flags related to state-change. If
> + 1byte flag valie is 1 then there is a session state change else
> + there is no state change information.
> +
> + @param thd [IN] The thd handle.
> + @paran buf [INOUT] Buffer to store the information to.
> +
> + @return
> + false Success
> + true Error
> +**/
> +
> +bool Session_state_change_tracker::store(THD *thd, String &buf)
> +{
> + /* since its a boolean tracker length is always 1 */
> + const ulonglong length= 1;
> +
> + uchar *to= (uchar *) buf.prep_append(3,EXTRA_ALLOC);
> +
> + /* format of the payload is as follows:
> + [ tracker type] [length] [1 byte flag] */
Noooo! Do not copy the payload format logic to every tracker.
Keep it in one place (preferrably, in the protocol)
> + /* Session state type (SESSION_TRACK_STATE_CHANGE) */
> + to= net_store_length(to, (ulonglong)SESSION_TRACK_STATE_CHANGE);
> +
> + /* Length of the overall entity it is always 1 byte */
> + to= net_store_length(to, length);
> +
> + /* boolean tracker will go here */
> + *to= (is_state_changed(thd) ? '1' : '0');
> +
> + reset();
> +
> + return false;
> +}
> +
> +/**
> + @brief Mark the tracker as changed and associated session
> + attributes accordingly.
> +
> + @param name [IN] Always null.
> + @return void
> +*/
> +
> +void Session_state_change_tracker::mark_as_changed(THD *thd,
> + LEX_CSTRING *tracked_item_name)
> +{
> + /* do not send the boolean flag for the tracker itself
> + in the OK packet */
> + if(tracked_item_name &&
> + (strncmp(tracked_item_name->str, "session_track_state_change", 26) == 0))
> + m_changed= false;
Grrr.
1. Why not?
2. what if it is SET @foobar=5, @@session_track_state_change=ON;
> + else
> + {
> + m_changed= true;
> + thd->lex->safe_to_cache_query= 0;
why?
> + }
> +}
> +
> +/**
> + @brief Reset the m_changed flag for next statement.
> +
> + @return void
> +*/
> +
> +void Session_state_change_tracker::reset()
> +{
> + m_changed= false;
> +}
> +
> +/**
> + @brief find if there is a session state change
> +
> + @return
> + true - if there is a session state change
> + false - if there is no session state change
> +**/
> +
> +bool Session_state_change_tracker::is_state_changed(THD* thd)
> +{
> + return m_changed;
> +}
> +
> +///////////////////////////////////////////////////////////////////////////////
> +
> +/**
> + @brief Initialize session tracker objects.
> +
> + @param char_set [IN] The character set info.
> +
> + @return void
> +*/
> +
> +void Session_tracker::init(const CHARSET_INFO *char_set)
> +{
> + m_trackers[SESSION_SYSVARS_TRACKER]=
> + new (std::nothrow) Dummy_tracker;
> + m_trackers[CURRENT_SCHEMA_TRACKER]=
> + new (std::nothrow) Current_schema_tracker;
> + m_trackers[SESSION_STATE_CHANGE_TRACKER]=
> + new (std::nothrow) Session_state_change_tracker;
> + m_trackers[SESSION_GTIDS_TRACKER]=
> + new (std::nothrow) Dummy_tracker;
> + m_trackers[TRANSACTION_INFO_TRACKER]=
> + new (std::nothrow) Dummy_tracker;
> +}
> +
> +/**
> + @brief Enables the tracker objects.
> +
> + @param thd [IN] The thread handle.
> +
> + @return void
> +*/
> +void Session_tracker::enable(THD *thd)
> +{
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + m_trackers[i]->enable(thd);
> +}
> +
> +/**
> + @brief Method called during the server startup to verify the contents
> + of @@session_track_system_variables.
I see. Please remove it from this patch.
It should've been added in the next (sysvar) patch, but
as it's a wrong approach in general, I hope we'll do it correctly.
> +
> + @param char_set The character set info.
> + @param var_list Value of @@session_track_system_variables.
> +
> + @return false Success
> + true failure
> +*/
> +bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set,
> + LEX_STRING var_list)
> +{
> + return false;
> +}
> +
> +
> +
> +/**
> + @brief Returns the pointer to the tracker object for the specified tracker.
> +
> + @param tracker [IN] Tracker type.
> +
> + @return Pointer to the tracker object.
> +*/
> +
> +State_tracker *
> +Session_tracker::get_tracker(enum_session_tracker tracker) const
> +{
> + return m_trackers[tracker];
> +}
make that inline in session_tracker.h
> +
> +
> +/**
> + @brief Checks if m_enabled flag is set for any of the tracker objects.
> +
> + @return
> + true - At least one of the trackers is enabled.
> + false - None of the trackers is enabled.
> +
> +*/
> +
> +bool Session_tracker::enabled_any()
> +{
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + {
> + if (m_trackers[i]->is_enabled())
> + return true;
> + }
> + return false;
> +}
remove enabled_any() and changed_any(). See my comments where these
methods are used.
> +
> +/**
> + @brief Checks if m_changed flag is set for any of the tracker objects.
> +
> + @return
> + true At least one of the entities being tracker has
> + changed.
> + false None of the entities being tracked has changed.
> +*/
> +
> +bool Session_tracker::changed_any()
> +{
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + {
> + if (m_trackers[i]->is_changed())
> + return true;
> + }
> + return false;
> +}
> +
> +
> +/**
> + @brief Store all change information in the specified buffer.
> +
> + @param thd [IN] The thd handle.
> + @param buf [OUT] Reference to the string buffer to which the state
> + change data needs to be written.
> +
> + @return void
> +*/
> +
> +void Session_tracker::store(THD *thd, String &buf)
> +{
> + /* Temporary buffer to store all the changes. */
> + String temp;
> + size_t length;
> +
> + /* Get total length. */
> + for (int i= 0; i <= SESSION_TRACKER_END; i ++)
> + {
> + if (m_trackers[i]->is_changed())
> + m_trackers[i]->store(thd, temp);
> + }
> +
> + length= temp.length();
> + /* Store length first.. */
> + char *to= buf.prep_append(net_length_size(length), EXTRA_ALLOC);
> + net_store_length((uchar *) to, length);
> +
> + /* .. and then the actual info. */
> + buf.append(temp);
Nice. Copy all data into a temporary malloced buffer,
them memcopy it again into the malloced real buffer.
At least use StringBuffer here. it would be good to avoid memcpy too,
but I only see two possible approaches, both not perfect:
1. assume it's, say, less than 251 byte in the most common case.
put the data directly into buf, starting from buf.ptr() + 1.
when done, put the length into the reserved byte. If the length
is more than 251 - memmove everything to free more bytes.
2. iterate all session trackers twice. first - to get the length,
second - to put the actual data
ah, yes, and don't use references (String &buf) use pointers (String *buf)
> +}
> +
> +
> +/**
> + @brief Stores the given string in length-encoded format into the specified
> + buffer.
> +
> + @param to [IN] Buffer to store the given string in.
> + @param from [IN] The give string to be stored.
> + @param length [IN] Length of the above string.
> +
> + @return void.
> +*/
> +
> +static
> +void store_lenenc_string(String &to, const char *from, size_t length)
why not make it a method of String ?
Like, append_lenenc() ?
> +{
> + char *ptr;
> + ptr= to.prep_append(net_length_size(length), EXTRA_ALLOC);
> + net_store_length((uchar *) ptr, length);
> + to.append(from, length);
> +}
> +
> diff --git a/sql/set_var.cc b/sql/set_var.cc
> index b5430c5..a55fc54 100644
> --- a/sql/set_var.cc
> +++ b/sql/set_var.cc
> @@ -199,8 +199,24 @@ bool sys_var::update(THD *thd, set_var *var)
> (on_update && on_update(this, thd, OPT_GLOBAL));
> }
> else
> - return session_update(thd, var) ||
> + {
> + bool ret= session_update(thd, var) ||
> (on_update && on_update(this, thd, OPT_SESSION));
> +
> + /*
> + Make sure we don't session-track variables that are not actually
> + part of the session. tx_isolation and and tx_read_only for example
> + exist as GLOBAL, SESSION, and one-shot ("for next transaction only").
> + */
> + if (var->type == OPT_SESSION)
> + {
> + if ((!ret) &&
> + thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->is_enabled())
> + thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->mark_as_changed(thd, &var->var->name);
don't check for is_enabled here. Alternatives:
1. just do mark_as_changed unconditionally. this works if mark_as_changed
is cheap and has no side-effects. neither is true now, but I don't
understand why.
2. create a wrapper method, like
Session_tracker::mark_as_changed(tracker)
at least you won't need to copy this long line in every place where
a state changes.
doing 1 is preferrable, but I don't know if it's easily possible (because
I don't understand why it's done that way now)
> + }
> +
> + return ret;
> + }
> }
>
> uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base)
> @@ -965,6 +983,8 @@ int set_var_collation_client::update(THD *thd)
> thd->variables.character_set_results= character_set_results;
> thd->variables.collation_connection= collation_connection;
> thd->update_charset();
> + if (thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->is_enabled())
> + thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->mark_as_changed(thd, NULL);
> thd->protocol_text.init(thd);
> thd->protocol_binary.init(thd);
> return 0;
what about set_var_password::update and set_var_role::update ?
Wouldn't it be better to mark_as_changed in sql_set_variables instead of
doing that in individual sys_var descendands?
> diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
> index 0d168e9..933f060 100644
> --- a/sql/share/errmsg-utf8.txt
> +++ b/sql/share/errmsg-utf8.txt
> @@ -7154,3 +7154,8 @@ ER_WRONG_ORDER_IN_WITH_CLAUSE
> eng "The definition of the table '%s' refers to the table '%s' defined later in a non-recursive WITH clause"
> ER_RECURSIVE_QUERY_IN_WITH_CLAUSE
> eng "Recursive queries in WITH clause are not supported yet"
> +ER_DUP_LIST_ENTRY
> + eng "Duplicate entry '%-.192s'."
really, a bug?
remove this. having the same value twice is not a bug:
MariaDB (test)> set sql_mode='ansi,ansi';
Query OK, 0 rows affected (0.00 sec)
Let's be consistent
> +ER_NET_OK_PACKET_TOO_LARGE 08S01
> + eng "OK packet too large"
> + ukr "Пакет OK надто великий"
> diff --git a/sql/sp_head.cc b/sql/sp_head.cc
> index d58b51a..6205dd6 100644
> --- a/sql/sp_head.cc
> +++ b/sql/sp_head.cc
> @@ -2975,6 +2975,13 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
>
> reinit_stmt_before_use(thd, m_lex);
>
> +#ifndef EMBEDDED_LIBRARY
why?
> + if ((thd->client_capabilities & CLIENT_SESSION_TRACK) &&
> + thd->session_tracker.enabled_any() &&
> + thd->session_tracker.changed_any())
> + thd->lex->safe_to_cache_query= 0;
why?
> +#endif
> +
> if (open_tables)
> res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
>
> diff --git a/sql/sql_class.cc b/sql/sql_class.cc
> index e3b7056..bf70594 100644
> --- a/sql/sql_class.cc
> +++ b/sql/sql_class.cc
> @@ -1461,6 +1461,11 @@ void THD::init(void)
> /* Initialize the Debug Sync Facility. See debug_sync.cc. */
> debug_sync_init_thread(this);
> #endif /* defined(ENABLED_DEBUG_SYNC) */
> +
> + /* Initialize session_tracker and create all tracker objects */
> + session_tracker.init(this->charset());
> + session_tracker.enable(this);
1. you don't need charset here, it's unused
2. thus you don't need to invoke init() explicitly at all,
do it from the Session_tracker constructor. In fact, move init
functionality into the constructor.
> +
> apc_target.init(&LOCK_thd_data);
> DBUG_VOID_RETURN;
> }
> @@ -1620,6 +1625,12 @@ void THD::cleanup(void)
> /* All metadata locks must have been released by now. */
> DBUG_ASSERT(!mdl_context.has_locks());
>
> + /*
> + Destroy trackers only after finishing manipulations with transaction
> + state to avoid issues with Transaction_state_tracker.
> + */
> + session_tracker.deinit();
may be you can do that from the destructor too
> +
> apc_target.destroy();
> cleanup_done=1;
> DBUG_VOID_RETURN;
> diff --git a/sql/sql_class.h b/sql/sql_class.h
> index be652e6..063198e 100644
> --- a/sql/sql_class.h
> +++ b/sql/sql_class.h
> @@ -681,6 +682,11 @@ typedef struct system_variables
>
> my_bool pseudo_slave_mode;
>
> + char *track_sysvars_ptr;
no track_sysvars_ptr in this patch please
> + my_bool session_track_schema;
> + my_bool session_track_state_change;
> + ulong session_track_transaction_info;
and no session_track_transaction_info either
> +
> } SV;
>
> /**
> diff --git a/sql/sql_db.cc b/sql/sql_db.cc
> index 2ba67cb..53719e5 100644
> --- a/sql/sql_db.cc
> +++ b/sql/sql_db.cc
> @@ -1034,7 +1034,18 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
> it to 0.
> */
> if (thd->db && cmp_db_names(thd->db, db) && !error)
> - mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
> + {
> + mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);\
eh? trailing backslash?
> + /*
> + Check if current database tracker is enabled. If so, set the 'changed' flag.
> + */
> + if (thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->is_enabled())
> + {
> + LEX_CSTRING dummy= { C_STRING_WITH_LEN("") };
> + dummy.length= dummy.length*1;
WAT???
> + thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->mark_as_changed(thd, &dummy);
alternatively, make mark_as_changed to take const char* and size_t,
then you won't need to create LEX_CSTRING
alternatively, create LEX_CSTRING dummy statically and use it always
alternatively, use empty_lex_string
btw, what's the point of using an *empty string* as an argument?
other trackers accept NULL. why empty string is better?
and anyway, why to construct and pass down an argument that
is ignored anyway???
> + }
> + }
> my_dirend(dirp);
> DBUG_RETURN(error);
> }
> @@ -1588,6 +1597,18 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
>
> mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
>
> +done:
> + /*
> + Check if current database tracker is enabled. If so, set the 'changed' flag.
> + */
> + if (thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->is_enabled())
> + {
> + LEX_CSTRING dummy= { C_STRING_WITH_LEN("") };
> + dummy.length= dummy.length*1;
> + thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->mark_as_changed(thd, &dummy);
same
> + }
> + if (thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->is_enabled())
> + thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->mark_as_changed(thd, NULL);
> DBUG_RETURN(FALSE);
> }
>
> diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
> index dbe1967..244ff5b 100644
> --- a/sql/sql_plugin.cc
> +++ b/sql/sql_plugin.cc
> @@ -322,6 +322,8 @@ static void unlock_variables(THD *thd, struct system_variables *vars);
> static void cleanup_variables(struct system_variables *vars);
> static void plugin_vars_free_values(sys_var *vars);
> static void restore_ptr_backup(uint n, st_ptr_backup *backup);
> +#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
> +#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
remove that
> static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
> static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
> static void reap_plugins(void);
> @@ -2776,22 +2778,23 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
> System Variables support
> ****************************************************************************/
>
> -
> -sys_var *find_sys_var(THD *thd, const char *str, uint length)
> +sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
> + bool throw_error, bool locked)
revert all sql_plugin.cc and sql_plugin.h changes.
if you need them for sysvars - do them in the second patch
> {
> sys_var *var;
> sys_var_pluginvar *pi= NULL;
> plugin_ref plugin;
> - DBUG_ENTER("find_sys_var");
> + DBUG_ENTER("find_sys_var_ex");
>
> - mysql_mutex_lock(&LOCK_plugin);
> + if (!locked)
> + mysql_mutex_lock(&LOCK_plugin);
> mysql_rwlock_rdlock(&LOCK_system_variables_hash);
> if ((var= intern_find_sys_var(str, length)) &&
> (pi= var->cast_pluginvar()))
> {
> mysql_rwlock_unlock(&LOCK_system_variables_hash);
> LEX *lex= thd ? thd->lex : 0;
> - if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
> + if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
> var= NULL; /* failed to lock it, it must be uninstalling */
> else
> if (!(plugin_state(plugin) & PLUGIN_IS_READY))
> diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
> index 8e5ab71..03f3a93 100644
> --- a/sql/sql_prepare.cc
> +++ b/sql/sql_prepare.cc
> @@ -2755,7 +2755,13 @@ void mysql_sql_stmt_prepare(THD *thd)
> thd->stmt_map.erase(stmt);
> }
> else
> + {
> + /* send the boolean tracker in the OK packet when
rewrite the comment into something that makes sense, please
> + @@session_track_state_change is set to ON */
> + if (thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->is_enabled())
> + thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->mark_as_changed(thd, NULL);
> my_ok(thd, 0L, 0L, "Statement prepared");
> + }
>
> DBUG_VOID_RETURN;
> }
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
2
1

Re: [Maria-developers] [Commits] ca24c9d: MDEV-9383: Server fails to read master.info after upgrade 10.0 -> 10.1
by Kristian Nielsen 07 Apr '16
by Kristian Nielsen 07 Apr '16
07 Apr '16
Hi Nirbhay,
Do you want to review this patch for MDEV-9383?
It concerns your code added for do_domain_ids=(...).
- Kristian.
Kristian Nielsen <knielsen(a)knielsen-hq.org> writes:
> revision-id: ca24c9d1671e90e43daa483af32fc19891d1a646 (mariadb-10.1.13-8-gca24c9d)
> parent(s): 4b6a3518e4dc9088d1f42cd9bc487d06137d2760
> committer: Kristian Nielsen
> timestamp: 2016-04-07 14:44:29 +0200
> message:
>
> MDEV-9383: Server fails to read master.info after upgrade 10.0 -> 10.1
>
> In some cases, MariaDB 10.0 could write a master.info file that was read
> incorrectly by 10.1 and could cause server to fail to start after an upgrade.
>
> (If writing a new master.info file that is shorter than the old, extra
> junk may remain at the end of the file. This is handled properly in
> 10.1 with an END_MARKER line, but this line is not written by
> 10.0. The fix here is to make 10.1 robust at reading the master.info
> files written by 10.0).
>
> Fix several things around reading master.info and read_mi_key_from_file():
>
> - read_mi_key_from_file() did not distinguish between a line with and
> without an eqals '=' sign.
>
> - If a line was empty, read_mi_key_from_file() would incorrectly return
> the key from the previous call.
>
> - An extra using_gtid=X line left-over by MariaDB 10.0 might incorrectly
> be read and overwrite the correct value.
>
> - Fix incorrect usage of strncmp() which should be strcmp().
>
> - Add test cases.
>
> ---
> mysql-test/std_data/bad2_master.info | 35 +++++
> mysql-test/std_data/bad3_master.info | 37 +++++
> mysql-test/std_data/bad4_master.info | 35 +++++
> mysql-test/std_data/bad5_master.info | 35 +++++
> mysql-test/std_data/bad6_master.info | 36 +++++
> mysql-test/std_data/bad_master.info | 35 +++++
> .../suite/rpl/r/rpl_upgrade_master_info.result | 88 +++++++++++
> .../suite/rpl/t/rpl_upgrade_master_info.test | 163 +++++++++++++++++++++
> sql/rpl_mi.cc | 81 +++++-----
> 9 files changed, 512 insertions(+), 33 deletions(-)
>
> diff --git a/mysql-test/std_data/bad2_master.info b/mysql-test/std_data/bad2_master.info
> new file mode 100644
> index 0000000..6172256
> --- /dev/null
> +++ b/mysql-test/std_data/bad2_master.info
> @@ -0,0 +1,35 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +=0
> diff --git a/mysql-test/std_data/bad3_master.info b/mysql-test/std_data/bad3_master.info
> new file mode 100644
> index 0000000..6e632cd
> --- /dev/null
> +++ b/mysql-test/std_data/bad3_master.info
> @@ -0,0 +1,37 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +
> +
> +0
> diff --git a/mysql-test/std_data/bad4_master.info b/mysql-test/std_data/bad4_master.info
> new file mode 100644
> index 0000000..87572ef
> --- /dev/null
> +++ b/mysql-test/std_data/bad4_master.info
> @@ -0,0 +1,35 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +d=1
> diff --git a/mysql-test/std_data/bad5_master.info b/mysql-test/std_data/bad5_master.info
> new file mode 100644
> index 0000000..4ea8113
> --- /dev/null
> +++ b/mysql-test/std_data/bad5_master.info
> @@ -0,0 +1,35 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +using_gtid
> diff --git a/mysql-test/std_data/bad6_master.info b/mysql-test/std_data/bad6_master.info
> new file mode 100644
> index 0000000..0f48f48
> --- /dev/null
> +++ b/mysql-test/std_data/bad6_master.info
> @@ -0,0 +1,36 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +END_MARKER
> +do_domain_ids=20 Hulubulu!!?!
> diff --git a/mysql-test/std_data/bad_master.info b/mysql-test/std_data/bad_master.info
> new file mode 100644
> index 0000000..1541fdf
> --- /dev/null
> +++ b/mysql-test/std_data/bad_master.info
> @@ -0,0 +1,35 @@
> +33
> +mysql-bin.000001
> +4
> +127.0.0.1
> +root
> +
> +3310
> +60
> +0
> +
> +
> +
> +
> +
> +0
> +1800.000
> +
> +0
> +
> +0
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +using_gtid=1
> +
> diff --git a/mysql-test/suite/rpl/r/rpl_upgrade_master_info.result b/mysql-test/suite/rpl/r/rpl_upgrade_master_info.result
> new file mode 100644
> index 0000000..f966f18
> --- /dev/null
> +++ b/mysql-test/suite/rpl/r/rpl_upgrade_master_info.result
> @@ -0,0 +1,88 @@
> +include/master-slave.inc
> +[connection master]
> +*** MDEV-9383: Server fails to read master.info after upgrade 10.0 -> 10.1 ***
> +include/stop_slave.inc
> +CHANGE MASTER TO master_use_gtid=CURRENT_POS;
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +CREATE TABLE t1 (a INT PRIMARY KEY);
> +INSERT INTO t1 VALUES (1);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1;
> +a
> +1
> +include/stop_slave.inc
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +INSERT INTO t1 VALUES (2);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +a
> +1
> +2
> +include/stop_slave.inc
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +INSERT INTO t1 VALUES (3);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +a
> +1
> +2
> +3
> +include/stop_slave.inc
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +INSERT INTO t1 VALUES (4);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +a
> +1
> +2
> +3
> +4
> +include/stop_slave.inc
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +INSERT INTO t1 VALUES (5);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +a
> +1
> +2
> +3
> +4
> +5
> +include/stop_slave.inc
> +include/rpl_stop_server.inc [server_number=2]
> +include/rpl_start_server.inc [server_number=2]
> +INSERT INTO t1 VALUES (6);
> +include/save_master_gtid.inc
> +CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1;
> +include/start_slave.inc
> +include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +a
> +1
> +2
> +3
> +4
> +5
> +6
> +DROP TABLE t1;
> +include/rpl_end.inc
> diff --git a/mysql-test/suite/rpl/t/rpl_upgrade_master_info.test b/mysql-test/suite/rpl/t/rpl_upgrade_master_info.test
> new file mode 100644
> index 0000000..e81e7c0
> --- /dev/null
> +++ b/mysql-test/suite/rpl/t/rpl_upgrade_master_info.test
> @@ -0,0 +1,163 @@
> +--source include/master-slave.inc
> +
> +--echo *** MDEV-9383: Server fails to read master.info after upgrade 10.0 -> 10.1 ***
> +
> +--connection slave
> +--source include/stop_slave.inc
> +CHANGE MASTER TO master_use_gtid=CURRENT_POS;
> +--let $datadir= `SELECT @@datadir`
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +CREATE TABLE t1 (a INT PRIMARY KEY);
> +INSERT INTO t1 VALUES (1);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1;
> +
> +--source include/stop_slave.inc
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad2_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +INSERT INTO t1 VALUES (2);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +
> +--source include/stop_slave.inc
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad3_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +INSERT INTO t1 VALUES (3);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +
> +--source include/stop_slave.inc
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad4_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +INSERT INTO t1 VALUES (4);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +
> +--source include/stop_slave.inc
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad5_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +INSERT INTO t1 VALUES (5);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +
> +--source include/stop_slave.inc
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_stop_server.inc
> +
> +--remove_file $datadir/master.info
> +--copy_file $MYSQL_TEST_DIR/std_data/bad6_master.info $datadir/master.info
> +
> +--let $rpl_server_number= 2
> +--source include/rpl_start_server.inc
> +
> +--source include/wait_until_connected_again.inc
> +
> +--connection master
> +INSERT INTO t1 VALUES (6);
> +--source include/save_master_gtid.inc
> +
> +--connection slave
> +# Fix the port after we replaced master.info.
> +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
> +eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1;
> +--source include/start_slave.inc
> +--source include/sync_with_master_gtid.inc
> +SELECT * FROM t1 ORDER BY a;
> +
> +
> +# Cleanup
> +--connection master
> +DROP TABLE t1;
> +--source include/rpl_end.inc
> diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
> index df72134..706824c 100644
> --- a/sql/rpl_mi.cc
> +++ b/sql/rpl_mi.cc
> @@ -205,43 +205,56 @@ void init_master_log_pos(Master_info* mi)
>
> /**
> Parses the IO_CACHE for "key=" and returns the "key".
> + If no '=' found, returns the whole line (for END_MARKER).
>
> @param key [OUT] Key buffer
> @param max_size [IN] Maximum buffer size
> @param f [IN] IO_CACHE file
> + @param found_equal [OUT] Set true if a '=' was found.
>
> @retval 0 Either "key=" or '\n' found
> @retval 1 EOF
> */
> -static int read_mi_key_from_file(char *key, int max_size, IO_CACHE *f)
> +static int read_mi_key_from_file(char *key, int max_size, IO_CACHE *f,
> + bool *found_equal)
> {
> int i= 0, c;
> - char *last_p;
>
> DBUG_ENTER("read_key_from_file");
>
> - while (((c= my_b_get(f)) != '\n') && (c != my_b_EOF))
> + *found_equal= false;
> + if (max_size <= 0)
> + DBUG_RETURN(1);
> + for (;;)
> {
> - last_p= key + i;
> -
> - if (i < max_size)
> + if (i >= max_size-1)
> {
> - if (c == '=')
> - {
> - /* We found '=', replace it by 0 and return. */
> - *last_p= 0;
> - DBUG_RETURN(0);
> - }
> - else
> - *last_p= c;
> + key[i] = '\0';
> + DBUG_RETURN(0);
> + }
> + c= my_b_get(f);
> + if (c == my_b_EOF)
> + {
> + DBUG_RETURN(1);
> + }
> + else if (c == '\n')
> + {
> + key[i]= '\0';
> + DBUG_RETURN(0);
> + }
> + else if (c == '=')
> + {
> + key[i]= '\0';
> + *found_equal= true;
> + DBUG_RETURN(0);
> + }
> + else
> + {
> + key[i]= c;
> + ++i;
> }
> - ++i;
> }
> -
> - if (c == my_b_EOF)
> - DBUG_RETURN(1);
> -
> - DBUG_RETURN(0);
> + /* NotReached */
> }
>
> enum {
> @@ -539,6 +552,10 @@ file '%s')", fname);
> if (lines >= LINE_FOR_LAST_MYSQL_FUTURE)
> {
> uint i;
> + bool got_eq;
> + bool seen_using_gtid= false;
> + bool seen_do_domain_ids=false, seen_ignore_domain_ids=false;
> +
> /* Skip lines used by / reserved for MySQL >= 5.6. */
> for (i= LINE_FOR_FIRST_MYSQL_5_6; i <= LINE_FOR_LAST_MYSQL_FUTURE; ++i)
> {
> @@ -551,11 +568,12 @@ file '%s')", fname);
> for "key=" and returns the "key" if found. The "value" can then the
> parsed on case by case basis. The "unknown" lines would be ignored to
> facilitate downgrades.
> + 10.0 does not have the END_MARKER before any left-overs at the end
> + of the file. So ignore any but the first occurrence of a key.
> */
> - while (!read_mi_key_from_file(buf, sizeof(buf), &mi->file))
> + while (!read_mi_key_from_file(buf, sizeof(buf), &mi->file, &got_eq))
> {
> - /* using_gtid */
> - if (!strncmp(buf, STRING_WITH_LEN("using_gtid")))
> + if (got_eq && !seen_using_gtid && !strcmp(buf, "using_gtid"))
> {
> int val;
> if (!init_intvar_from_file(&val, &mi->file, 0))
> @@ -566,15 +584,13 @@ file '%s')", fname);
> mi->using_gtid= Master_info::USE_GTID_SLAVE_POS;
> else
> mi->using_gtid= Master_info::USE_GTID_NO;
> - continue;
> + seen_using_gtid= true;
> } else {
> sql_print_error("Failed to initialize master info using_gtid");
> goto errwithmsg;
> }
> }
> -
> - /* DO_DOMAIN_IDS */
> - if (!strncmp(buf, STRING_WITH_LEN("do_domain_ids")))
> + else if (got_eq && !seen_do_domain_ids && !strcmp(buf, "do_domain_ids"))
> {
> if (mi->domain_id_filter.init_ids(&mi->file,
> Domain_id_filter::DO_DOMAIN_IDS))
> @@ -582,11 +598,10 @@ file '%s')", fname);
> sql_print_error("Failed to initialize master info do_domain_ids");
> goto errwithmsg;
> }
> - continue;
> + seen_do_domain_ids= true;
> }
> -
> - /* IGNORE_DOMAIN_IDS */
> - if (!strncmp(buf, STRING_WITH_LEN("ignore_domain_ids")))
> + else if (got_eq && !seen_ignore_domain_ids &&
> + !strcmp(buf, "ignore_domain_ids"))
> {
> if (mi->domain_id_filter.init_ids(&mi->file,
> Domain_id_filter::IGNORE_DOMAIN_IDS))
> @@ -595,9 +610,9 @@ file '%s')", fname);
> "ignore_domain_ids");
> goto errwithmsg;
> }
> - continue;
> + seen_ignore_domain_ids= true;
> }
> - else if (!strncmp(buf, STRING_WITH_LEN("END_MARKER")))
> + else if (!got_eq && !strcmp(buf, "END_MARKER"))
> {
> /*
> Guard agaist extra left-overs at the end of file, in case a later
> _______________________________________________
> commits mailing list
> commits(a)mariadb.org
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
2
2
Hi!
Many of the core server developers for MariaDB Server and MariaDB MaxScale will be having a meetup in Berlin, on April 12 2016 at Wikimedia’s offices. Feel free to drop by, have a drink, say hi, and listen to the upcoming plans
http://www.meetup.com/MariaDB-Developers-Berlin-Meetup/events/230026151/
Looking forward to seeing you if you’re local to Berlin, and if you have friends that would be interested, please feel free to forward this note to them
Thanks
Kind Regards,
Colin Charles
--
Colin Charles, http://bytebot.net/blog/
twitter: @bytebot | skype: colincharles
"First they ignore you, then they laugh at you, then they fight you, then you win." -- Mohandas Gandhi
1
0

[Maria-developers] Please review MDEV-9823 LOAD DATA INFILE silently truncates incomplete byte sequences
by Alexander Barkov 05 Apr '16
by Alexander Barkov 05 Apr '16
05 Apr '16
Hi Sergei,
Please review a patch for MDEV-9823.
This is a prerequisite for the current sprint task:
MDEV-6353 my_ismbchar() and my_mbcharlen() refactoring
Thanks.
2
3

[Maria-developers] Window functions: compare_window_spec_joined_lists() has a bug
by Sergey Petrunia 04 Apr '16
by Sergey Petrunia 04 Apr '16
04 Apr '16
Hello Igor,
I'm trying this example:
create table t1 (a int, b int);
insert into t1 values (1,1),(2,2),(3,3);
select rank() over (order by a), rank() over (order by b) from t1;
I put a breakpoint in order_window_funcs_by_window_specs(), at this call:
int cmp= compare_window_spec_joined_lists(win_spec_prev, win_spec_curr);
and I see that:
(gdb) p win_spec1->partition_list
$66 = (SQL_I_List<st_order> *) 0x7fff5c013038
(gdb) p win_spec2->partition_list
$68 = (SQL_I_List<st_order> *) 0x7fff5c013038 // same as above
(gdb) p win_spec2->partition_list[0]
$70 = {<Sql_alloc> = {<No data fields>}, elements = 0, first = 0x0, next = 0x7fff5c013040}
// Ok these are both empty lusts.
(gdb) p win_spec2->order_list
$72 = (SQL_I_List<st_order> *) 0x7fff5c013498
(gdb) p win_spec1->order_list
$74 = (SQL_I_List<st_order> *) 0x7fff5c013050
(gdb) p win_spec2->order_list->first
$76 = (st_order *) 0x7fff5c013428
(gdb) p win_spec1->order_list->first
$78 = (st_order *) 0x7fff5c012fe0
// Ok these are different lists, as expected.
Then, the code in compare_window_spec_joined_lists does this:
{
win_spec1->join_partition_and_order_lists();
win_spec2->join_partition_and_order_lists();
This will cause both window specs to have the same lists, and I will get
item_window_func->marker=0 for the second item (instead of SORTORDER_CHANGE_FLAG
that I expect).
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
2
1

[Maria-developers] Please review MDEV-9842 LOAD DATA INFILE does not work well with a TEXT column when using sjis
by Alexander Barkov 01 Apr '16
by Alexander Barkov 01 Apr '16
01 Apr '16
Hi Sergei,
Please review a patch for MDEV-9842.
This is a prerequisite for:
MDEV-6353 my_ismbchar() and my_mbcharlen() refactoring
Thanks.
2
1

[Maria-developers] Please review MDEV-9842 LOAD DATA INFILE does not work well with a TEXT column when using sjis
by Alexander Barkov 31 Mar '16
by Alexander Barkov 31 Mar '16
31 Mar '16
Hi Sergei,
Please review a patch for MDEV-9842.
This is a prerequisite for:
MDEV-6353 my_ismbchar() and my_mbcharlen() refactoring
Thanks.
1
0

Re: [Maria-developers] yet another init.d script for running multiple mariald instances on the same host.
by Sergei Golubchik 31 Mar '16
by Sergei Golubchik 31 Mar '16
31 Mar '16
Hi, Andrii!
On Mar 21, Andrii Petrenko wrote:
> Hello Sergey,
>
> I have created one more script for running mariadb (or mysql) instacnces
> on the same host. For different instances i'm using different defaults
> file.
>
> i'd like ti share it with you, or maria team -- IMHO it is more useful
> than existent script.
>
> https://github.com/aplsms/Multi-MariaDB
Yes, good idea - using scripts like /etc/init.d/mysql-1, etc.
This can be a useful trick in some cases.
Regards,
Sergei
Chief Architect MariaDB
and security(a)mariadb.org
1
0

[Maria-developers] MDEV-9841: Window functions: embedded server fails to start due to error code conflicts
by Sergey Petrunia 31 Mar '16
by Sergey Petrunia 31 Mar '16
31 Mar '16
Hi Serg,
We added a number of new error messages for window functions, and now server
error codes have collided with the client. The text in MDEV-9841 has the
details.
Sanja mentioned that you were aware of the possibility of this and had a
solution. Can we discuss it sometime?
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
2
1

[Maria-developers] Please review MDEV-9811 and MDEV-9824 (dependencies for MDEV-6353)
by Alexander Barkov 31 Mar '16
by Alexander Barkov 31 Mar '16
31 Mar '16
Hi Sergei,
Please review a patch fixing MDEV-9811 and MDEV-9824.
They are prerequisites for:
MDEV-6353 my_ismbchar() and my_mbcharlen() refactoring
Thanks.
2
1
1
0
Respected Sir,
I have found some of ways to implement this project using C++, SQL, java,
or C.
As I had mentioned earlier, I was searching for some of database systems
that have this facility or in some way can implement it.
The database system that I discovered while learning for this topic is HP
Vertica and in this system the basic approach is creating
user defined library saved as .so for C/C++ or .jarr for java and loading
it using CREATE LIBRARY command. When we call SQL function HP Vertica passes
data values to the code in the library to process it.
Syntax:
CREATE [ OR REPLACE ] AGGREGATE FUNCTION [[db-name.]schema.]function-name
... AS LANGUAGE 'language' NAME 'factory' LIBRARY library_name;
example using this method is as given below
=> CREATE LIBRARY AggregateFunctions AS
'/opt/vertica/sdk/examples/build/AggregateFunctions.so';
CREATE LIBRARY
=> CREATE AGGREGATE FUNCTION ag_avg AS LANGUAGE 'C++' NAME 'AverageFactory'
library AggregateFunctions;
CREATE AGGREGATE FUNCTION
=> CREATE AGGREGATE FUNCTION ag_cat AS LANGUAGE 'C++' NAME 'ConcatenateFactory'
library AggregateFunctions;
CREATE AGGREGATE FUNCTION
=> \x
Expanded display is on.
select * from user_functions;
The only drawback in this method that this can be implemented using C++
only for HP Vertica.
The another method that I discovered is given in the latest update of
oracle i.e Oracle 9i (9)
the refrence link for same is
http://www.oracle-developer.net/display.php?id=215
According to this we require two components to implement aggregate functions
1. an object type specification and body; and
2. a PL/SQL function.
There is a development framework for data cartridges (think of this as a
template) provided by Oracle. This provides the structure of the object
type and its methods (down to the detail of how we name them) and also
defines how we create our PL/SQL function. For example, our object type
will contain the following:attribute(s) for holding state information.
These can be of any existing built-in or user-defined datatype;
- a mandatory ODCIAggregateInitialize static method to reset the state
attributes at the start of an aggregation;
- a mandatory ODCIAggregateIterate member method to apply each input
value to the running aggregate value;
- a mandatory ODCIAggregateTerminate member method to return the final
result of the aggregate; and
- an optional ODCIAggregateMerge member method, used to combine the
results of more than one stream of aggregation (for example, with parallel
query) before returning a result.
More detailed information, explanation and examples are given in the link
that I have mentioned above.
Please let me know whether I am working in right direction or not and
whether the methods I have mentioned can be used or not to implement the
project.
Regards,
Soham Mankad
2
2

[Maria-developers] MDEV-8360 Clean-up CHARSET_INFO: strnncollsp: diff_if_only_endspace_difference
by Alexander Barkov 30 Mar '16
by Alexander Barkov 30 Mar '16
30 Mar '16
Hi Sergei,
please review a patch for MDEV-8360.
Btw, shouldn't we remove:
#define HA_END_SPACE_ARE_EQUAL 512
in ./include/my_base.h ?
It's not really needed.
Thanks.
2
1

Re: [Maria-developers] [Commits] d40d68f: Convert percent_rank to work with cursors
by Sergey Petrunia 30 Mar '16
by Sergey Petrunia 30 Mar '16
30 Mar '16
On Mon, Mar 28, 2016 at 10:58:55PM +0300, Vicentiu Ciorbaru wrote:
> revision-id: d40d68f23602be9886c1b502fdad9d23bdc9a0fb (mariadb-10.1.8-187-gd40d68f)
> parent(s): bf18dac08fe0ae18975158e786fee097883949d4
> author: Vicențiu Ciorbaru
> committer: Vicențiu Ciorbaru
> timestamp: 2016-03-28 22:51:42 +0300
> message:
>
> Convert percent_rank to work with cursors
>
> The percent_rank function now is compatible with the cursor algorithm.
> We no longer need a special implementation for it to work.
>
> ---
> sql/item_windowfunc.h | 77 +++++++++++------------
> sql/sql_window.cc | 165 +++++++++++++++++---------------------------------
> 2 files changed, 91 insertions(+), 151 deletions(-)
>
> diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
> index 6f91bc8..1dd483c 100644
> --- a/sql/item_windowfunc.h
> +++ b/sql/item_windowfunc.h
> @@ -317,12 +317,18 @@ class Item_context
> NOTE: All two pass window functions need to implement
> this interface.
> */
> -class Item_sum_window_with_context : public Item_sum_num,
> - public Item_context
> +class Item_sum_window_with_row_count : public Item_sum_num
> {
> public:
> - Item_sum_window_with_context(THD *thd)
> - : Item_sum_num(thd), Item_context() {}
> + Item_sum_window_with_row_count(THD *thd) : Item_sum_num(thd),
> + partition_row_count_(0){}
> +
> + void set_row_count(ulonglong count) { partition_row_count_ = count; }
> +
> + protected:
> + longlong get_row_count() { return partition_row_count_; }
> + private:
> + ulonglong partition_row_count_;
> };
>
> /*
> @@ -336,12 +342,11 @@ class Item_sum_window_with_context : public Item_sum_num,
> This is held within the row_count context.
> - Second pass to compute rank of current row and the value of the function
> */
> -class Item_sum_percent_rank: public Item_sum_window_with_context,
> - public Window_context_row_count
> +class Item_sum_percent_rank: public Item_sum_window_with_row_count
> {
> public:
> Item_sum_percent_rank(THD *thd)
> - : Item_sum_window_with_context(thd), cur_rank(1) {}
> + : Item_sum_window_with_row_count(thd), cur_rank(1) {}
>
> longlong val_int()
> {
> @@ -359,14 +364,9 @@ class Item_sum_percent_rank: public Item_sum_window_with_context,
> We can not get the real value without knowing the number of rows
> in the partition. Don't divide by 0.
> */
> - if (!get_context_())
> - {
> - // Calling this kind of function with a context makes no sense.
> - DBUG_ASSERT(0);
> - return 0;
> - }
> -
> - longlong partition_rows = get_context_()->get_field_context(result_field);
> + ulonglong partition_rows = get_row_count();
> + null_value= partition_rows > 0 ? false : true;
> +
> return partition_rows > 1 ?
> static_cast<double>(cur_rank - 1) / (partition_rows - 1) : 0;
> }
> @@ -381,25 +381,6 @@ class Item_sum_percent_rank: public Item_sum_window_with_context,
> return "percent_rank";
> }
>
> - bool create_window_context()
> - {
> - // TODO-cvicentiu: Currently this means we must make sure to delete
> - // the window context. We can potentially allocate this on the THD memroot.
> - // At the same time, this is only necessary for a small portion of the
> - // query execution and it does not make sense to keep it for all of it.
> - context_ = new Window_context_row_count();
> - if (context_ == NULL)
> - return true;
> - return false;
> - }
> -
> - void delete_window_context()
> - {
> - if (context_)
> - delete get_context_();
> - context_ = NULL;
> - }
> -
> void update_field() {}
>
> void clear()
> @@ -428,13 +409,6 @@ class Item_sum_percent_rank: public Item_sum_window_with_context,
> void cleanup()
> {
> peer_tracker.cleanup();
> - Item_sum_window_with_context::cleanup();
> - }
> -
> - /* Helper function so that we don't cast the context every time. */
> - Window_context_row_count* get_context_()
> - {
> - return static_cast<Window_context_row_count *>(context_);
> }
> };
>
> @@ -517,6 +491,27 @@ class Item_window_func : public Item_func_or_sum
> }
> }
>
> + bool requires_partition_size() const
> + {
> + switch (window_func()->sum_func()) {
> + case Item_sum::PERCENT_RANK_FUNC:
> + case Item_sum::CUME_DIST_FUNC:
> + return true;
> + default:
> + return false;
> + }
> + }
> +
> + bool requires_peer_size() const
> + {
> + switch (window_func()->sum_func()) {
> + case Item_sum::CUME_DIST_FUNC:
> + return true;
> + default:
> + return false;
> + }
> + }
This function seem not to be used anywhere?
> +
> bool is_order_list_mandatory() const
> {
> switch (window_func()->sum_func()) {
> diff --git a/sql/sql_window.cc b/sql/sql_window.cc
> index e8e226b..1bfac7a 100644
> --- a/sql/sql_window.cc
> +++ b/sql/sql_window.cc
> @@ -865,15 +865,18 @@ class Frame_unbounded_preceding : public Frame_cursor
> }
> };
>
> +
> /*
> UNBOUNDED FOLLOWING frame bound
> */
>
> class Frame_unbounded_following : public Frame_cursor
> {
> - Table_read_cursor cursor;
>
> +protected:
> + Table_read_cursor cursor;
> Group_bound_tracker bound_tracker;
> +
> public:
> void init(THD *thd, READ_RECORD *info, SQL_I_List<ORDER> *partition_list,
> SQL_I_List<ORDER> *order_list)
> @@ -910,6 +913,35 @@ class Frame_unbounded_following : public Frame_cursor
> };
>
>
> +class Frame_unbounded_following_set_count : public Frame_unbounded_following
> +{
> + void next_partition(longlong rownum, Item_sum* item)
> + {
> + ulonglong num_rows_in_partition= 0;
> + if (!rownum)
> + {
> + /* Read the first row */
> + if (cursor.get_next())
> + return;
> + num_rows_in_partition++;
> + }
> +
> + /* Remember which partition we are in */
> + bound_tracker.check_if_next_group();
> + /* Walk to the end of the partition, find how many rows there are. */
> + while (!cursor.get_next())
> + {
> + if (bound_tracker.check_if_next_group())
> + break;
> + num_rows_in_partition++;
> + }
> +
> + Item_sum_window_with_row_count* item_with_row_count =
> + static_cast<Item_sum_window_with_row_count *>(item);
> + item_with_row_count->set_row_count(num_rows_in_partition);
> + }
> +};
> +
> /////////////////////////////////////////////////////////////////////////////
> // ROWS-type frame bounds
> /////////////////////////////////////////////////////////////////////////////
> @@ -1212,18 +1244,37 @@ Frame_cursor *get_frame_cursor(Window_frame *frame, bool is_top_bound)
> return NULL;
> }
>
> +void add_extra_frame_cursors(List<Frame_cursor> *cursors,
> + const Item_sum *window_func)
> +{
> + switch (window_func->sum_func())
> + {
> + case Item_sum::CUME_DIST_FUNC:
> + cursors->push_back(new Frame_unbounded_preceding);
What is the reason for having this cursor at all?
> + cursors->push_back(new Frame_range_current_row_bottom);
> + break;
This way to structure the code looks wrong. Why should add_extra_frame_cursors
know that some functions (e.g. CUME_DIST) need a cursor, and some don't?
I think it would be more natural if CUME_DIST knew that it needs certain kinds
of cursor(s).
Maybe, we should put off this question for now, and get back to it when we have
more window functions. (If we implement LEAD and LAG, we will need to create
Frame_rows_n_preceding and Frame_rows_n_following for them. Will this be done
in this function too?)
Another question (possibly wrong): why is CUME_DIST mentioned here, while
PERCENT_RANK isn't? At the first glance they will need the same kind of
cursors, wont they?
> + default:
> + cursors->push_back(new Frame_unbounded_preceding);
> + cursors->push_back(new Frame_rows_current_row_bottom);
> + }
> +}
> +
> List<Frame_cursor> get_window_func_required_cursors(
> const Item_window_func* item_win)
> {
> List<Frame_cursor> result;
I don't think is's good idea to pass around List objects by value.
- there is no need to do this.
- List template is not fully specified w.r.t what happens when you copy-assign
it. I don't consider the situation with two List objects sharing the list but
not sharing e.g. the value of List::elements meaningful.
because if that, I would pass a pointer or reference to the list and have the
code populate the passed list.
>
> + if (item_win->requires_partition_size())
> + result.push_back(new Frame_unbounded_following_set_count);
> +
> /*
> If it is not a regular window function that follows frame specifications,
> specific cursors are required.
> */
> if (item_win->is_frame_prohibited())
> {
> - DBUG_ASSERT(0); // TODO-cvicentiu not-implemented yet.
> + add_extra_frame_cursors(&result, item_win->window_func());
> + return result;
> }
>
> /* A regular window function follows the frame specification. */
> @@ -1283,7 +1334,6 @@ bool compute_window_func_with_frames(Item_window_func *item_win,
>
> List<Frame_cursor> cursors= get_window_func_required_cursors(item_win);
>
> -
> List_iterator_fast<Frame_cursor> it(cursors);
> Frame_cursor *c;
> while((c= it++))
> @@ -1362,107 +1412,6 @@ bool compute_window_func_with_frames(Item_window_func *item_win,
> }
>
>
> -bool compute_two_pass_window_functions(Item_window_func *item_win,
> - TABLE *table, READ_RECORD *info)
> -{
> - /* Perform first pass. */
> -
> - // TODO-cvicentiu why not initialize the record for when we need, _in_
> - // this function.
> - READ_RECORD *info2= new READ_RECORD();
> - int err;
> - bool is_error = false;
> - bool first_row= true;
> - clone_read_record(info, info2);
> - Item_sum_window_with_context *window_func=
> - static_cast<Item_sum_window_with_context *>(item_win->window_func());
> - uchar *rowid_buf= (uchar*) my_malloc(table->file->ref_length, MYF(0));
> -
> - is_error= window_func->create_window_context();
> - /* Unable to allocate a new context. */
> - if (is_error)
> - return true;
> -
> - Window_context *context = window_func->get_window_context();
> - /*
> - The two pass algorithm is as follows:
> - We have a sorted table according to the partition and order by clauses.
> - 1. Scan through the table till we reach a partition boundary.
> - 2. For each row that we scan, add it to the context.
> - 3. Once the partition boundary is met, do a second scan through the
> - current partition and use the context information to compute the value for
> - the window function for that partition.
> - 4. Reset the context.
> - 5. Repeat from 1 till end of table.
> - */
> -
> - bool done = false;
> - longlong rows_in_current_partition = 0;
> - // TODO handle end of table updating.
> - while (!done)
> - {
> -
> - if ((err= info->read_record(info)))
> - {
> - done = true;
> - }
> -
> - bool partition_changed= done || item_win->check_if_partition_changed();
> - // The first time we always have a partition changed. Ignore it.
> - if (first_row)
> - {
> - partition_changed= false;
> - first_row= false;
> - }
> -
> - if (partition_changed)
> - {
> - /*
> - We are now looking at the first row for the next partition, or at the
> - end of the table. Either way, we must remember this position for when
> - we finish doing the second pass.
> - */
> - table->file->position(table->record[0]);
> - memcpy(rowid_buf, table->file->ref, table->file->ref_length);
> -
> - for (longlong row_number = 0; row_number < rows_in_current_partition;
> - row_number++)
> - {
> - if ((err= info2->read_record(info2)))
> - {
> - is_error= true;
> - break;
> - }
> - window_func->add();
> - // Save the window function into the table.
> - item_win->save_in_field(item_win->result_field, true);
> - err= table->file->ha_update_row(table->record[1], table->record[0]);
> - if (err && err != HA_ERR_RECORD_IS_THE_SAME)
> - {
> - is_error= true;
> - break;
> - }
> - }
> -
> - if (is_error)
> - break;
> -
> - rows_in_current_partition= 0;
> - window_func->clear();
> - context->reset();
> -
> - // Return to the beginning of the new partition.
> - table->file->ha_rnd_pos(table->record[0], rowid_buf);
> - }
> - rows_in_current_partition++;
> - context->add_field_to_context(item_win->result_field);
> - }
> -
> - window_func->delete_window_context();
> - delete info2;
> - my_free(rowid_buf);
> - return is_error;
> -}
>
>
> /* Make a list that is a concation of two lists of ORDER elements */
> @@ -1527,16 +1476,12 @@ bool Window_func_runner::setup(THD *thd)
> compute_func= compute_window_func_values;
> break;
> }
> - case Item_sum::PERCENT_RANK_FUNC:
> - case Item_sum::CUME_DIST_FUNC:
> - {
> - compute_func= compute_two_pass_window_functions;
> - break;
> - }
> case Item_sum::COUNT_FUNC:
> case Item_sum::SUM_BIT_FUNC:
> case Item_sum::SUM_FUNC:
> case Item_sum::AVG_FUNC:
> + case Item_sum::PERCENT_RANK_FUNC:
> + case Item_sum::CUME_DIST_FUNC:
> {
> /*
> Frame-aware window function computation. It does one pass, but
> _______________________________________________
> commits mailing list
> commits(a)mariadb.org
> https://lists.askmonty.org/cgi-bin/mailman/listinfo/commits
--
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog
1
0