commit 4d7dab451e79087e27c6c76b198fe246b3f47446 Author: Dmitry Lenev Date: Fri Jul 7 10:33:36 2023 +0200 Bug#111690 "Expose number of table objects with triggers in table cache". Follow-up to bug #44625 "Triggers should not be loaded on SELECTs". Improve observability of the Table Cache by adding "Open_tables_with_triggers" status variable which shows current number of TABLE instances with fully-loaded triggers in the Table Cache. The new status variable in addition to existing "Table_open_cache_triggers_hits" and "Table_open_cache_triggers_misses" status variables should allow users better understand how well caching for TABLE objects with fully-loaded triggers is working and if value of option --table_open_cache_triggers, which limits number of such objects in the cache, needs to be adjusted. diff --git a/mysql-test/include/assert_trigger_cache_increment.inc b/mysql-test/include/assert_trigger_cache_increment.inc index 4eb2a1f008a..d31ca11f729 100644 --- a/mysql-test/include/assert_trigger_cache_increment.inc +++ b/mysql-test/include/assert_trigger_cache_increment.inc @@ -3,13 +3,13 @@ # Check that values of system status variables related to storing # triggers in Table Cache (Table_open_cache_misses, # Table_open_cache_trigger_hits, Table_open_cache_triggers_misses, -# Table_open_cache_triggers_overflows) have got expected increments -# since the last execution of this script, fail with debug info if -# they have not. +# Table_open_cache_triggers_overflows, Open_tables_with_triggers) +# have got expected increments since the last execution of this script, +# fail with debug info if they have not. # # ==== Usage ==== # -# --let $expected_inc = { "tc_misses_inc": VALUE1, "trg_hits_inc": VALUE2, "trg_misses_inc" : VALUE3, "trg_overflows_inc" : VALUE4 } +# --let $expected_inc = { "tc_misses_inc": VALUE1, "trg_hits_inc": VALUE2, "trg_misses_inc" : VALUE3, "trg_overflows_inc" : VALUE4, "tc_trg_tables_inc" : VALUE5 } # --source include/assert_trigger_cache_increment.inc # # $expected_inc @@ -23,7 +23,7 @@ # # Run SELECT reading from table which was absent in Table Cache so far. # SELECT count(*) FROM t1; # -# --let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +# --let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0, "tc_trg_tables_inc" : 0} # --source include/assert_trigger_cache_increment.inc # # Resulting output: @@ -31,6 +31,7 @@ # include/assert.inc [Expected table cache triggers hits increment : 0] # include/assert.inc [Expected table cache triggers misses increment : 0] # include/assert.inc [Expected table cache triggers overflows increment : 0] +# include/assert.inc [Expected table cache open tables with triggers increment : 0] # # If missing, create helper scripts to iterate through bellow JSON array. @@ -70,6 +71,12 @@ let $json_array = [ "psname": "Table_open_cache_triggers_overflows", "varname": "trg_overflows_count", "atext": "Expected table cache triggers overflows increment" + }, + { + "paramname" : "tc_trg_tables_inc", + "psname": "Open_tables_with_triggers", + "varname" : "tc_trg_tables_count", + "atext" : "Expected table cache open tables with triggers increment" } ]; diff --git a/mysql-test/r/table_open_cache_triggers_functionality.result b/mysql-test/r/table_open_cache_triggers_functionality.result index 294aff7304d..11129a6727c 100644 --- a/mysql-test/r/table_open_cache_triggers_functionality.result +++ b/mysql-test/r/table_open_cache_triggers_functionality.result @@ -38,6 +38,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] SELECT count(*) FROM t1 AS a, t1 AS b; count(*) 9 @@ -45,6 +46,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] SELECT count(*) FROM t4; count(*) 1 @@ -52,15 +54,18 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Updating statement needs fully-loaded triggers, so there will be # a table cache hit, but also trigger miss and trigger will be -# loaded. +# loaded. Total number of table objects with fully-loaded triggers +# in the cache should increase as result as well. INSERT INTO t1 VALUES (4); include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 1] # Repeating the statement will cause table cache and trigger hits. INSERT INTO t1 VALUES (5); @@ -68,6 +73,7 @@ include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 1] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # When same statement needs 2 instances of the table, one for # updating and another for read-only purposes, then for updating @@ -78,20 +84,24 @@ include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 1] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Statement needing 2 table objects with triggers will cause one # trigger hit and one trigger miss (causing trigger loading for -# one of cached table objects). +# one of cached table objects). Total number of table objects with +# fully-loaded triggers in cache should increase further. LOCK TABLES t1 AS a WRITE, t1 AS b WRITE; include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 1] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 1] UNLOCK TABLES; include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Running read-only statement on unrelated table should not disturb # trigger counters. Since only one table object for this table was @@ -103,6 +113,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Statement which needs 3 table objects for updating will cause 2 # trigger hits and 1 trigger (and TC) miss . As result we temporarily @@ -112,6 +123,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 2] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 1] # Once we are done with using all 3 table objects, one table # object with trigger gets evicted. 2 table objects with triggers @@ -121,6 +133,7 @@ include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 1] +include/assert.inc [Expected table cache open tables with triggers increment : -1] # Read-only statement will use table objects with triggers and # should not disturb trigger counters. Since we have not used @@ -132,6 +145,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Statement that uses t1 for updating and table t2 in read-only # mode will reuse 1 table object with triggers (and also reuse object @@ -141,25 +155,30 @@ include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 1] include/assert.inc [Expected table cache triggers misses increment : 0] include/assert.inc [Expected table cache triggers overflows increment : 0] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Statement that updates t2 will require loading of triggers (existing # table object will be used for this), this will cause table object -# with triggers for table t1 eviction. +# with triggers for table t1 eviction. So total number of table +# objects with fully-loaded triggers should stay the same. INSERT INTO t2 VALUES (2); include/assert.inc [Expected table cache misses increment : 0] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 1] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Statement that updates t3 will require new table object and triggers # loaded for it. Since Table Cache already has 2 table objects with # triggers it will cause eviction of one of these objects. According -# to LRU it should be object for t1. +# to LRU it should be object for t1. Again total number of table +# objects with fully-loaded triggers should stay the same. INSERT INTO t3 VALUES (2); include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 1] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # Let us confirm that it is table object for t1 which is gone # missing, by showing that it and its triggers need to be loaded again. @@ -168,6 +187,7 @@ include/assert.inc [Expected table cache misses increment : 1] include/assert.inc [Expected table cache triggers hits increment : 0] include/assert.inc [Expected table cache triggers misses increment : 1] include/assert.inc [Expected table cache triggers overflows increment : 1] +include/assert.inc [Expected table cache open tables with triggers increment : 0] # # Clean up. # diff --git a/mysql-test/t/table_open_cache_triggers_functionality.test b/mysql-test/t/table_open_cache_triggers_functionality.test index b4c2bac99df..08b226940c1 100644 --- a/mysql-test/t/table_open_cache_triggers_functionality.test +++ b/mysql-test/t/table_open_cache_triggers_functionality.test @@ -47,33 +47,34 @@ FLUSH STATUS; --echo # when only 1 is present in TC causes 1 hit and 1 miss. SELECT count(*) FROM t1; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc SELECT count(*) FROM t1 AS a, t1 AS b; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc SELECT count(*) FROM t4; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo --echo # Updating statement needs fully-loaded triggers, so there will be --echo # a table cache hit, but also trigger miss and trigger will be ---echo # loaded. +--echo # loaded. Total number of table objects with fully-loaded triggers +--echo # in the cache should increase as result as well. INSERT INTO t1 VALUES (4); ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 1 } --source include/assert_trigger_cache_increment.inc --echo --echo # Repeating the statement will cause table cache and trigger hits. INSERT INTO t1 VALUES (5); ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo @@ -83,21 +84,22 @@ INSERT INTO t1 VALUES (5); --echo # without triggers we also have for read-only part. INSERT INTO t1 SELECT * FROM t1; ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo --echo # Statement needing 2 table objects with triggers will cause one --echo # trigger hit and one trigger miss (causing trigger loading for ---echo # one of cached table objects). +--echo # one of cached table objects). Total number of table objects with +--echo # fully-loaded triggers in cache should increase further. LOCK TABLES t1 AS a WRITE, t1 AS b WRITE; ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 1 } --source include/assert_trigger_cache_increment.inc UNLOCK TABLES; ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo @@ -106,7 +108,7 @@ UNLOCK TABLES; --echo # used before it will cause single TC miss as expected. SELECT count(*) FROM t4 AS a, t4 AS b; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo @@ -115,7 +117,7 @@ SELECT count(*) FROM t4 AS a, t4 AS b; --echo # exceed the soft limit on open tables with triggers. LOCK TABLES t1 AS a WRITE, t1 AS b WRITE, t1 AS c WRITE; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 2, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 2, "trg_misses_inc" : 1, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 1 } --source include/assert_trigger_cache_increment.inc --echo @@ -124,7 +126,7 @@ LOCK TABLES t1 AS a WRITE, t1 AS b WRITE, t1 AS c WRITE; --echo # is left. UNLOCK TABLES; ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 1 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 1 , "tc_trg_tables_inc" : -1 } --source include/assert_trigger_cache_increment.inc --echo @@ -133,7 +135,7 @@ UNLOCK TABLES; --echo # table t2 so far, there will be 1 TC miss. SELECT count(*) FROM t1, t2; ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo @@ -142,26 +144,29 @@ SELECT count(*) FROM t1, t2; --echo # without triggers for t2), and won't disturb cache otherwise. INSERT INTO t1 SELECT * FROM t2; ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 1, "trg_misses_inc" : 0, "trg_overflows_inc" : 0 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo --echo # Statement that updates t2 will require loading of triggers (existing --echo # table object will be used for this), this will cause table object ---echo # with triggers for table t1 eviction. +--echo # with triggers for table t1 eviction. So total number of table +--echo # objects with fully-loaded triggers should stay the same. + INSERT INTO t2 VALUES (2); ---let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 } +--let $expected_inc = { "tc_misses_inc": 0, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo --echo # Statement that updates t3 will require new table object and triggers --echo # loaded for it. Since Table Cache already has 2 table objects with --echo # triggers it will cause eviction of one of these objects. According ---echo # to LRU it should be object for t1. +--echo # to LRU it should be object for t1. Again total number of table +--echo # objects with fully-loaded triggers should stay the same. INSERT INTO t3 VALUES (2); ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo @@ -169,7 +174,7 @@ INSERT INTO t3 VALUES (2); --echo # missing, by showing that it and its triggers need to be loaded again. INSERT INTO t1 VALUES (6); ---let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 } +--let $expected_inc = { "tc_misses_inc": 1, "trg_hits_inc": 0, "trg_misses_inc" : 1, "trg_overflows_inc" : 1 , "tc_trg_tables_inc" : 0 } --source include/assert_trigger_cache_increment.inc --echo # diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 534a7f830b6..dee5e664a69 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -9442,6 +9442,13 @@ static int show_open_tables(THD *, SHOW_VAR *var, char *buff) { return 0; } +static int show_open_tables_with_triggers(THD *, SHOW_VAR *var, char *buff) { + var->type = SHOW_LONG; + var->value = buff; + *((long *)buff) = (long)table_cache_manager.loaded_triggers_tables(); + return 0; +} + static int show_prepared_stmt_count(THD *, SHOW_VAR *var, char *buff) { var->type = SHOW_LONG; var->value = buff; @@ -9766,6 +9773,8 @@ SHOW_VAR status_vars[] = { {"Open_table_definitions", (char *)&show_table_definitions, SHOW_FUNC, SHOW_SCOPE_GLOBAL}, {"Open_tables", (char *)&show_open_tables, SHOW_FUNC, SHOW_SCOPE_ALL}, + {"Open_tables_with_triggers", (char *)&show_open_tables_with_triggers, + SHOW_FUNC, SHOW_SCOPE_ALL}, {"Opened_files", const_cast(reinterpret_cast(&my_file_total_opened)), SHOW_LONG_NOFLUSH, SHOW_SCOPE_GLOBAL}, diff --git a/sql/table_cache.cc b/sql/table_cache.cc index ec59bab8ef2..3ed6912d319 100644 --- a/sql/table_cache.cc +++ b/sql/table_cache.cc @@ -239,6 +239,20 @@ uint Table_cache_manager::cached_tables() { return result; } +/** + Get total number of used and unused TABLE objects with fully-loaded triggers + in all table caches. +*/ + +uint Table_cache_manager::loaded_triggers_tables() const { + uint result = 0; + + for (uint i = 0; i < table_cache_instances; i++) + result += m_table_cache[i].loaded_triggers_tables(); + + return result; +} + /** Acquire locks on all instances of table cache and table definition cache (i.e. LOCK_open). diff --git a/sql/table_cache.h b/sql/table_cache.h index 3c6d2d7d5a4..c46d8701ef8 100644 --- a/sql/table_cache.h +++ b/sql/table_cache.h @@ -222,6 +222,8 @@ class Table_cache_manager { uint cached_tables(); + uint loaded_triggers_tables() const; + void lock_all_and_tdc(); void unlock_all_and_tdc(); void assert_owner(THD *thd); diff --git a/unittest/gunit/table_cache-t.cc b/unittest/gunit/table_cache-t.cc index ce71ec5e698..7b2a3f077de 100644 --- a/unittest/gunit/table_cache-t.cc +++ b/unittest/gunit/table_cache-t.cc @@ -1217,6 +1217,169 @@ TEST_F(TableCacheDoubleCacheTest, ManagerCachedTables) { share_2.destroy_table(table_5); } +/* + Test for Table_cache_manager/Table_cache::loaded_triggers_tables(). +*/ + +TEST_F(TableCacheDoubleCacheTest, ManagerLoadedTriggersTables) { + THD *thd_1 = get_thd(0); + THD *thd_2 = get_thd(1); + + Table_cache *table_cache_1 = table_cache_manager.get_cache(thd_1); + Table_cache *table_cache_2 = table_cache_manager.get_cache(thd_2); + + // There should be no TABLE instances with loaded triggers in all caches. + EXPECT_EQ(0U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_manager.loaded_triggers_tables()); + + Mock_share share_1("share_1"); + Mock_share share_2("share_2"); + TABLE *table_1 = share_1.create_table_with_triggers(thd_1); + TABLE *table_2 = share_1.create_table_with_triggers(thd_1); + TABLE *table_3 = share_2.create_table_with_triggers(thd_1); + TABLE *table_4 = share_1.create_table_with_triggers(thd_2); + TABLE *table_5 = share_2.create_table_with_triggers(thd_2); + + table_cache_manager.lock_all_and_tdc(); + add_used_table(table_cache_1, thd_1, table_1); + add_used_table(table_cache_1, thd_1, table_2); + add_used_table(table_cache_1, thd_1, table_3); + add_used_table(table_cache_2, thd_2, table_4); + add_used_table(table_cache_2, thd_2, table_5); + table_cache_manager.unlock_all_and_tdc(); + + // Still there should be no TABLE instances with loaded triggers in caches. + EXPECT_EQ(0U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_manager.loaded_triggers_tables()); + + // Simulate loading of triggers for a couple of TABLE instances in the + // first cache. + table_1->triggers->finalize_load(thd_1); + table_3->triggers->finalize_load(thd_1); + + // There should be 2 TABLE instances with loaded triggers in the + // first cache partition and all caches. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_manager.loaded_triggers_tables()); + + // Load triggers for one more TABLE instance in the second cache. + table_4->triggers->finalize_load(thd_2); + + // There should be 2 + 1 TABLE instances with loaded triggers in caches. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(1U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(3U, table_cache_manager.loaded_triggers_tables()); + + // Mark a few TABLE instances as unused. + table_cache_manager.lock_all_and_tdc(); + table_cache_1->release_table(thd_1, table_1); + table_cache_2->release_table(thd_2, table_4); + table_cache_2->release_table(thd_2, table_5); + table_cache_manager.unlock_all_and_tdc(); + + // This should not change number of TABLE instances with fully loaded + // triggers. We still should have 2 + 1 instances with triggers. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(1U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(3U, table_cache_manager.loaded_triggers_tables()); + + // Get TABLE instance with loaded triggers from cache. + TABLE *table_6; + TABLE_SHARE *share_3; + table_cache_1->lock(); + table_6 = + table_cache_1->get_table(thd_1, share_1.table_cache_key.str, + share_1.table_cache_key.length, true, &share_3); + table_cache_1->unlock(); + + EXPECT_TRUE(table_6 == table_1); + EXPECT_TRUE(share_3 == &share_1); + EXPECT_TRUE(table_6->triggers->has_load_been_finalized()); + + // This should not change number of TABLE instances with fully loaded + // triggers in any way. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(1U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(3U, table_cache_manager.loaded_triggers_tables()); + + // Attempt to get TABLE instance with loaded triggers for another share + // from the second cache. This is expected not to fully succeed. + table_cache_2->lock(); + table_6 = + table_cache_2->get_table(thd_2, share_2.table_cache_key.str, + share_2.table_cache_key.length, true, &share_3); + table_cache_2->unlock(); + + EXPECT_TRUE(table_6 == table_5); + EXPECT_TRUE(share_3 == &share_2); + // We should get TABLE instance sans triggers. + EXPECT_FALSE(table_6->triggers->has_load_been_finalized()); + + // Still no change in number of TABLE instances with fully loaded triggers. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(1U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(3U, table_cache_manager.loaded_triggers_tables()); + + // Load triggers for this TABLE instace. + table_5->triggers->finalize_load(thd_2); + + // There should be 2 + 2 TABLE instances with loaded triggers in caches. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(4U, table_cache_manager.loaded_triggers_tables()); + + // Remove TABLE instance without triggers from the cache. + table_cache_1->lock(); + table_cache_1->remove_table(table_2); + table_cache_1->unlock(); + + // This should not change number of TABLE instances with loaded triggers. + EXPECT_EQ(2U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(4U, table_cache_manager.loaded_triggers_tables()); + + // Now let us remove TABLE instance with loaded triggers. + table_cache_1->lock(); + table_cache_1->remove_table(table_1); + table_cache_1->unlock(); + + // Now there should be 1 + 2 TABLE instances with loaded triggers in caches. + EXPECT_EQ(1U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(3U, table_cache_manager.loaded_triggers_tables()); + + // And one more. + table_cache_1->lock(); + table_cache_1->remove_table(table_3); + table_cache_1->unlock(); + + // Now there should be 0 + 2 TABLE instances with loaded triggers in caches. + EXPECT_EQ(0U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(2U, table_cache_manager.loaded_triggers_tables()); + + // Remove remaining TABLE instances with loaded triggers. + table_cache_2->lock(); + table_cache_2->remove_table(table_4); + table_cache_2->remove_table(table_5); + table_cache_2->unlock(); + + // There should be no TABLE instances with loaded triggers in all caches. + EXPECT_EQ(0U, table_cache_1->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_2->loaded_triggers_tables()); + EXPECT_EQ(0U, table_cache_manager.loaded_triggers_tables()); + + // Clean-up. + share_1.destroy_table(table_1); + share_1.destroy_table(table_2); + share_2.destroy_table(table_3); + share_1.destroy_table(table_4); + share_2.destroy_table(table_5); +} + /* Coverage for lock and unlock methods of Table_cache_manager class. */