Commit e3af2c9f authored by Marko Mäkelä's avatar Marko Mäkelä

Merge MDEV-25029 for testing

parents 71d30d01 d231d813
...@@ -334,7 +334,9 @@ dict_table_close( ...@@ -334,7 +334,9 @@ dict_table_close(
if (last_handle && strchr(table->name.m_name, '/') != NULL if (last_handle && strchr(table->name.m_name, '/') != NULL
&& dict_stats_is_persistent_enabled(table)) { && dict_stats_is_persistent_enabled(table)) {
table->stats_mutex_lock();
dict_stats_deinit(table); dict_stats_deinit(table);
table->stats_mutex_unlock();
} }
MONITOR_DEC(MONITOR_TABLE_REFERENCE); MONITOR_DEC(MONITOR_TABLE_REFERENCE);
......
...@@ -406,7 +406,9 @@ dict_stats_table_clone_create( ...@@ -406,7 +406,9 @@ dict_stats_table_clone_create(
dict_table_t* t; dict_table_t* t;
t = (dict_table_t*) mem_heap_alloc(heap, sizeof(*t)); t = (dict_table_t*) mem_heap_zalloc(heap, sizeof(*t));
t->stats_mutex_init();
MEM_CHECK_DEFINED(&table->id, sizeof(table->id)); MEM_CHECK_DEFINED(&table->id, sizeof(table->id));
t->id = table->id; t->id = table->id;
...@@ -434,7 +436,7 @@ dict_stats_table_clone_create( ...@@ -434,7 +436,7 @@ dict_stats_table_clone_create(
dict_index_t* idx; dict_index_t* idx;
idx = (dict_index_t*) mem_heap_alloc(heap, sizeof(*idx)); idx = (dict_index_t*) mem_heap_zalloc(heap, sizeof(*idx));
MEM_CHECK_DEFINED(&index->id, sizeof(index->id)); MEM_CHECK_DEFINED(&index->id, sizeof(index->id));
idx->id = index->id; idx->id = index->id;
...@@ -452,7 +454,7 @@ dict_stats_table_clone_create( ...@@ -452,7 +454,7 @@ dict_stats_table_clone_create(
idx->n_uniq = index->n_uniq; idx->n_uniq = index->n_uniq;
idx->fields = (dict_field_t*) mem_heap_alloc( idx->fields = (dict_field_t*) mem_heap_zalloc(
heap, idx->n_uniq * sizeof(idx->fields[0])); heap, idx->n_uniq * sizeof(idx->fields[0]));
for (ulint i = 0; i < idx->n_uniq; i++) { for (ulint i = 0; i < idx->n_uniq; i++) {
...@@ -463,15 +465,15 @@ dict_stats_table_clone_create( ...@@ -463,15 +465,15 @@ dict_stats_table_clone_create(
/* hook idx into t->indexes */ /* hook idx into t->indexes */
UT_LIST_ADD_LAST(t->indexes, idx); UT_LIST_ADD_LAST(t->indexes, idx);
idx->stat_n_diff_key_vals = (ib_uint64_t*) mem_heap_alloc( idx->stat_n_diff_key_vals = (ib_uint64_t*) mem_heap_zalloc(
heap, heap,
idx->n_uniq * sizeof(idx->stat_n_diff_key_vals[0])); idx->n_uniq * sizeof(idx->stat_n_diff_key_vals[0]));
idx->stat_n_sample_sizes = (ib_uint64_t*) mem_heap_alloc( idx->stat_n_sample_sizes = (ib_uint64_t*) mem_heap_zalloc(
heap, heap,
idx->n_uniq * sizeof(idx->stat_n_sample_sizes[0])); idx->n_uniq * sizeof(idx->stat_n_sample_sizes[0]));
idx->stat_n_non_null_key_vals = (ib_uint64_t*) mem_heap_alloc( idx->stat_n_non_null_key_vals = (ib_uint64_t*) mem_heap_zalloc(
heap, heap,
idx->n_uniq * sizeof(idx->stat_n_non_null_key_vals[0])); idx->n_uniq * sizeof(idx->stat_n_non_null_key_vals[0]));
ut_d(idx->magic_n = DICT_INDEX_MAGIC_N); ut_d(idx->magic_n = DICT_INDEX_MAGIC_N);
...@@ -494,6 +496,7 @@ dict_stats_table_clone_free( ...@@ -494,6 +496,7 @@ dict_stats_table_clone_free(
/*========================*/ /*========================*/
dict_table_t* t) /*!< in: dummy table object to free */ dict_table_t* t) /*!< in: dummy table object to free */
{ {
t->stats_mutex_destroy();
mem_heap_free(t->heap); mem_heap_free(t->heap);
} }
...@@ -510,7 +513,7 @@ dict_stats_empty_index( ...@@ -510,7 +513,7 @@ dict_stats_empty_index(
{ {
ut_ad(!(index->type & DICT_FTS)); ut_ad(!(index->type & DICT_FTS));
ut_ad(!dict_index_is_ibuf(index)); ut_ad(!dict_index_is_ibuf(index));
dict_sys.assert_locked(); ut_ad(index->table->stats_mutex_is_owner());
ulint n_uniq = index->n_uniq; ulint n_uniq = index->n_uniq;
...@@ -540,7 +543,9 @@ dict_stats_empty_table( ...@@ -540,7 +543,9 @@ dict_stats_empty_table(
bool empty_defrag_stats) bool empty_defrag_stats)
/*!< in: whether to empty defrag stats */ /*!< in: whether to empty defrag stats */
{ {
dict_sys.mutex_lock(); /* Initialize table/index level stats is now protected by
table level lock_mutex.*/
table->stats_mutex_lock();
/* Zero the stats members */ /* Zero the stats members */
table->stat_n_rows = 0; table->stat_n_rows = 0;
...@@ -566,7 +571,7 @@ dict_stats_empty_table( ...@@ -566,7 +571,7 @@ dict_stats_empty_table(
} }
table->stat_initialized = TRUE; table->stat_initialized = TRUE;
dict_sys.mutex_unlock(); table->stats_mutex_unlock();
} }
/*********************************************************************//** /*********************************************************************//**
...@@ -665,7 +670,8 @@ dict_stats_copy( ...@@ -665,7 +670,8 @@ dict_stats_copy(
to have the same statistics as if to have the same statistics as if
the table was empty */ the table was empty */
{ {
dict_sys.assert_locked(); ut_ad(src->stats_mutex_is_owner());
ut_ad(dst->stats_mutex_is_owner());
dst->stats_last_recalc = src->stats_last_recalc; dst->stats_last_recalc = src->stats_last_recalc;
dst->stat_n_rows = src->stat_n_rows; dst->stat_n_rows = src->stat_n_rows;
...@@ -790,8 +796,14 @@ dict_stats_snapshot_create( ...@@ -790,8 +796,14 @@ dict_stats_snapshot_create(
t = dict_stats_table_clone_create(table); t = dict_stats_table_clone_create(table);
table->stats_mutex_lock();
ut_d(t->stats_mutex_lock());
dict_stats_copy(t, table, false); dict_stats_copy(t, table, false);
ut_d(t->stats_mutex_unlock());
table->stats_mutex_unlock();
t->stat_persistent = table->stat_persistent; t->stat_persistent = table->stat_persistent;
t->stats_auto_recalc = table->stats_auto_recalc; t->stats_auto_recalc = table->stats_auto_recalc;
t->stats_sample_pages = table->stats_sample_pages; t->stats_sample_pages = table->stats_sample_pages;
...@@ -836,14 +848,14 @@ dict_stats_update_transient_for_index( ...@@ -836,14 +848,14 @@ dict_stats_update_transient_for_index(
Initialize some bogus index cardinality Initialize some bogus index cardinality
statistics, so that the data can be queried in statistics, so that the data can be queried in
various means, also via secondary indexes. */ various means, also via secondary indexes. */
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
dict_stats_empty_index(index, false); dict_stats_empty_index(index, false);
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG #if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
} else if (ibuf_debug && !dict_index_is_clust(index)) { } else if (ibuf_debug && !dict_index_is_clust(index)) {
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
dict_stats_empty_index(index, false); dict_stats_empty_index(index, false);
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */ #endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
} else { } else {
mtr_t mtr; mtr_t mtr;
...@@ -864,9 +876,9 @@ dict_stats_update_transient_for_index( ...@@ -864,9 +876,9 @@ dict_stats_update_transient_for_index(
switch (size) { switch (size) {
case ULINT_UNDEFINED: case ULINT_UNDEFINED:
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
dict_stats_empty_index(index, false); dict_stats_empty_index(index, false);
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
return; return;
case 0: case 0:
/* The root node of the tree is a leaf */ /* The root node of the tree is a leaf */
...@@ -883,7 +895,7 @@ dict_stats_update_transient_for_index( ...@@ -883,7 +895,7 @@ dict_stats_update_transient_for_index(
index); index);
if (!stats.empty()) { if (!stats.empty()) {
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
for (size_t i = 0; i < stats.size(); ++i) { for (size_t i = 0; i < stats.size(); ++i) {
index->stat_n_diff_key_vals[i] index->stat_n_diff_key_vals[i]
= stats[i].n_diff_key_vals; = stats[i].n_diff_key_vals;
...@@ -892,7 +904,7 @@ dict_stats_update_transient_for_index( ...@@ -892,7 +904,7 @@ dict_stats_update_transient_for_index(
index->stat_n_non_null_key_vals[i] index->stat_n_non_null_key_vals[i]
= stats[i].n_non_null_key_vals; = stats[i].n_non_null_key_vals;
} }
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
} }
} }
} }
...@@ -910,7 +922,7 @@ dict_stats_update_transient( ...@@ -910,7 +922,7 @@ dict_stats_update_transient(
/*========================*/ /*========================*/
dict_table_t* table) /*!< in/out: table */ dict_table_t* table) /*!< in/out: table */
{ {
dict_sys.assert_not_locked(); ut_ad(!table->stats_mutex_is_owner());
dict_index_t* index; dict_index_t* index;
ulint sum_of_index_sizes = 0; ulint sum_of_index_sizes = 0;
...@@ -943,9 +955,9 @@ dict_stats_update_transient( ...@@ -943,9 +955,9 @@ dict_stats_update_transient(
if (dict_stats_should_ignore_index(index) if (dict_stats_should_ignore_index(index)
|| !index->is_readable()) { || !index->is_readable()) {
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
dict_stats_empty_index(index, false); dict_stats_empty_index(index, false);
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
continue; continue;
} }
...@@ -954,7 +966,7 @@ dict_stats_update_transient( ...@@ -954,7 +966,7 @@ dict_stats_update_transient(
sum_of_index_sizes += index->stat_index_size; sum_of_index_sizes += index->stat_index_size;
} }
dict_sys.mutex_lock(); table->stats_mutex_lock();
index = dict_table_get_first_index(table); index = dict_table_get_first_index(table);
...@@ -972,7 +984,7 @@ dict_stats_update_transient( ...@@ -972,7 +984,7 @@ dict_stats_update_transient(
table->stat_initialized = TRUE; table->stat_initialized = TRUE;
dict_sys.mutex_unlock(); table->stats_mutex_unlock();
} }
/* @{ Pseudo code about the relation between the following functions /* @{ Pseudo code about the relation between the following functions
...@@ -1930,7 +1942,7 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index) ...@@ -1930,7 +1942,7 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index)
DBUG_PRINT("info", ("index: %s, online status: %d", index->name(), DBUG_PRINT("info", ("index: %s, online status: %d", index->name(),
dict_index_get_online_status(index))); dict_index_get_online_status(index)));
dict_sys.assert_not_locked(); // this function is slow ut_ad(!index->table->stats_mutex_is_owner());
ut_ad(index->table->get_ref_count()); ut_ad(index->table->get_ref_count());
/* Disable update statistic for Rtree */ /* Disable update statistic for Rtree */
...@@ -2002,14 +2014,14 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index) ...@@ -2002,14 +2014,14 @@ static index_stats_t dict_stats_analyze_index(dict_index_t* index)
mtr.commit(); mtr.commit();
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
for (ulint i = 0; i < n_uniq; i++) { for (ulint i = 0; i < n_uniq; i++) {
result.stats[i].n_diff_key_vals = index->stat_n_diff_key_vals[i]; result.stats[i].n_diff_key_vals = index->stat_n_diff_key_vals[i];
result.stats[i].n_sample_sizes = total_pages; result.stats[i].n_sample_sizes = total_pages;
result.stats[i].n_non_null_key_vals = index->stat_n_non_null_key_vals[i]; result.stats[i].n_non_null_key_vals = index->stat_n_non_null_key_vals[i];
} }
result.n_leaf_pages = index->stat_n_leaf_pages; result.n_leaf_pages = index->stat_n_leaf_pages;
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
DBUG_RETURN(result); DBUG_RETURN(result);
} }
...@@ -2243,13 +2255,13 @@ dict_stats_update_persistent( ...@@ -2243,13 +2255,13 @@ dict_stats_update_persistent(
} }
ut_ad(!dict_index_is_ibuf(index)); ut_ad(!dict_index_is_ibuf(index));
dict_sys.mutex_lock(); table->stats_mutex_lock();
dict_stats_empty_index(index, false); dict_stats_empty_index(index, false);
dict_sys.mutex_unlock(); table->stats_mutex_unlock();
index_stats_t stats = dict_stats_analyze_index(index); index_stats_t stats = dict_stats_analyze_index(index);
dict_sys.mutex_lock(); table->stats_mutex_lock();
index->stat_index_size = stats.index_size; index->stat_index_size = stats.index_size;
index->stat_n_leaf_pages = stats.n_leaf_pages; index->stat_n_leaf_pages = stats.n_leaf_pages;
for (size_t i = 0; i < stats.stats.size(); ++i) { for (size_t i = 0; i < stats.stats.size(); ++i) {
...@@ -2285,9 +2297,9 @@ dict_stats_update_persistent( ...@@ -2285,9 +2297,9 @@ dict_stats_update_persistent(
} }
if (!(table->stats_bg_flag & BG_STAT_SHOULD_QUIT)) { if (!(table->stats_bg_flag & BG_STAT_SHOULD_QUIT)) {
dict_sys.mutex_unlock(); table->stats_mutex_unlock();
stats = dict_stats_analyze_index(index); stats = dict_stats_analyze_index(index);
dict_sys.mutex_lock(); table->stats_mutex_lock();
index->stat_index_size = stats.index_size; index->stat_index_size = stats.index_size;
index->stat_n_leaf_pages = stats.n_leaf_pages; index->stat_n_leaf_pages = stats.n_leaf_pages;
...@@ -2313,7 +2325,7 @@ dict_stats_update_persistent( ...@@ -2313,7 +2325,7 @@ dict_stats_update_persistent(
dict_stats_assert_initialized(table); dict_stats_assert_initialized(table);
dict_sys.mutex_unlock(); table->stats_mutex_unlock();
return(DB_SUCCESS); return(DB_SUCCESS);
} }
...@@ -3123,13 +3135,11 @@ dict_stats_update_for_index( ...@@ -3123,13 +3135,11 @@ dict_stats_update_for_index(
{ {
DBUG_ENTER("dict_stats_update_for_index"); DBUG_ENTER("dict_stats_update_for_index");
dict_sys.assert_not_locked();
if (dict_stats_is_persistent_enabled(index->table)) { if (dict_stats_is_persistent_enabled(index->table)) {
if (dict_stats_persistent_storage_check(false)) { if (dict_stats_persistent_storage_check(false)) {
index_stats_t stats = dict_stats_analyze_index(index); index_stats_t stats = dict_stats_analyze_index(index);
dict_sys.mutex_lock(); index->table->stats_mutex_lock();
index->stat_index_size = stats.index_size; index->stat_index_size = stats.index_size;
index->stat_n_leaf_pages = stats.n_leaf_pages; index->stat_n_leaf_pages = stats.n_leaf_pages;
for (size_t i = 0; i < stats.stats.size(); ++i) { for (size_t i = 0; i < stats.stats.size(); ++i) {
...@@ -3142,7 +3152,7 @@ dict_stats_update_for_index( ...@@ -3142,7 +3152,7 @@ dict_stats_update_for_index(
} }
index->table->stat_sum_of_other_index_sizes index->table->stat_sum_of_other_index_sizes
+= index->stat_index_size; += index->stat_index_size;
dict_sys.mutex_unlock(); index->table->stats_mutex_unlock();
dict_stats_save(index->table, &index->id); dict_stats_save(index->table, &index->id);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -3183,7 +3193,7 @@ dict_stats_update( ...@@ -3183,7 +3193,7 @@ dict_stats_update(
the persistent statistics the persistent statistics
storage */ storage */
{ {
dict_sys.assert_not_locked(); ut_ad(!table->stats_mutex_is_owner());
if (!table->is_readable()) { if (!table->is_readable()) {
return (dict_stats_report_error(table)); return (dict_stats_report_error(table));
...@@ -3318,7 +3328,10 @@ dict_stats_update( ...@@ -3318,7 +3328,10 @@ dict_stats_update(
switch (err) { switch (err) {
case DB_SUCCESS: case DB_SUCCESS:
dict_sys.mutex_lock(); table->stats_mutex_lock();
/* t is localized to this thread so no need to
take stats mutex lock (limiting it to debug only) */
ut_d(t->stats_mutex_lock());
/* Pass reset_ignored_indexes=true as parameter /* Pass reset_ignored_indexes=true as parameter
to dict_stats_copy. This will cause statictics to dict_stats_copy. This will cause statictics
...@@ -3327,7 +3340,8 @@ dict_stats_update( ...@@ -3327,7 +3340,8 @@ dict_stats_update(
dict_stats_assert_initialized(table); dict_stats_assert_initialized(table);
dict_sys.mutex_unlock(); ut_d(t->stats_mutex_unlock());
table->stats_mutex_unlock();
dict_stats_table_clone_free(t); dict_stats_table_clone_free(t);
......
...@@ -14044,7 +14044,7 @@ ha_innobase::info_low( ...@@ -14044,7 +14044,7 @@ ha_innobase::info_low(
ulint stat_clustered_index_size; ulint stat_clustered_index_size;
ulint stat_sum_of_other_index_sizes; ulint stat_sum_of_other_index_sizes;
dict_sys.mutex_lock(); ib_table->stats_mutex_lock();
ut_a(ib_table->stat_initialized); ut_a(ib_table->stat_initialized);
...@@ -14056,7 +14056,7 @@ ha_innobase::info_low( ...@@ -14056,7 +14056,7 @@ ha_innobase::info_low(
stat_sum_of_other_index_sizes stat_sum_of_other_index_sizes
= ib_table->stat_sum_of_other_index_sizes; = ib_table->stat_sum_of_other_index_sizes;
dict_sys.mutex_unlock(); ib_table->stats_mutex_unlock();
/* /*
The MySQL optimizer seems to assume in a left join that n_rows The MySQL optimizer seems to assume in a left join that n_rows
...@@ -14172,10 +14172,9 @@ ha_innobase::info_low( ...@@ -14172,10 +14172,9 @@ ha_innobase::info_low(
stats.create_time = (ulong) stat_info.ctime; stats.create_time = (ulong) stat_info.ctime;
} }
struct Locking { ib_table->stats_mutex_lock();
Locking() { dict_sys.mutex_lock(); } auto _ = make_scope_exit([ib_table]() {
~Locking() { dict_sys.mutex_unlock(); } ib_table->stats_mutex_unlock(); });
} locking;
ut_a(ib_table->stat_initialized); ut_a(ib_table->stat_initialized);
......
...@@ -56,6 +56,7 @@ Created July 18, 2007 Vasil Dimov ...@@ -56,6 +56,7 @@ Created July 18, 2007 Vasil Dimov
#include "fil0fil.h" #include "fil0fil.h"
#include "fil0crypt.h" #include "fil0crypt.h"
#include "dict0crea.h" #include "dict0crea.h"
#include "scope.h"
/** The latest successfully looked up innodb_fts_aux_table */ /** The latest successfully looked up innodb_fts_aux_table */
UNIV_INTERN table_id_t innodb_ft_aux_table_id; UNIV_INTERN table_id_t innodb_ft_aux_table_id;
...@@ -5021,11 +5022,9 @@ i_s_dict_fill_sys_tablestats( ...@@ -5021,11 +5022,9 @@ i_s_dict_fill_sys_tablestats(
table->name.m_name)); table->name.m_name));
{ {
struct Locking table->stats_mutex_lock();
{ auto _ = make_scope_exit([table]() {
Locking() { dict_sys.mutex_lock(); } table->stats_mutex_unlock(); });
~Locking() { dict_sys.mutex_unlock(); }
} locking;
OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized, OK(fields[SYS_TABLESTATS_INIT]->store(table->stat_initialized,
true)); true));
......
...@@ -1962,6 +1962,9 @@ struct dict_table_t { ...@@ -1962,6 +1962,9 @@ struct dict_table_t {
/** @return whether the current thread holds the lock_mutex */ /** @return whether the current thread holds the lock_mutex */
bool lock_mutex_is_owner() const bool lock_mutex_is_owner() const
{ return lock_mutex_owner == os_thread_get_curr_id(); } { return lock_mutex_owner == os_thread_get_curr_id(); }
/** @return whether the current thread holds the stats_mutex (lock_mutex) */
bool stats_mutex_is_owner() const
{ return lock_mutex_owner == os_thread_get_curr_id(); }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
void lock_mutex_init() { lock_mutex.init(); } void lock_mutex_init() { lock_mutex.init(); }
void lock_mutex_destroy() { lock_mutex.destroy(); } void lock_mutex_destroy() { lock_mutex.destroy(); }
...@@ -1986,6 +1989,16 @@ struct dict_table_t { ...@@ -1986,6 +1989,16 @@ struct dict_table_t {
ut_ad(lock_mutex_owner.exchange(0) == os_thread_get_curr_id()); ut_ad(lock_mutex_owner.exchange(0) == os_thread_get_curr_id());
lock_mutex.wr_unlock(); lock_mutex.wr_unlock();
} }
/* stats mutex lock currently defaults to lock_mutex but in the future,
there could be a use-case to have separate mutex for stats.
  extra indirection (through inline so no performance hit) should
  help simplify code and increase long-term maintainability */
void stats_mutex_init() { lock_mutex_init(); }
void stats_mutex_destroy() { lock_mutex_destroy(); }
void stats_mutex_lock() { lock_mutex_lock(); }
void stats_mutex_unlock() { lock_mutex_unlock(); }
private: private:
/** Initialize instant->field_map. /** Initialize instant->field_map.
@param[in] table table definition to copy from */ @param[in] table table definition to copy from */
......
...@@ -148,7 +148,7 @@ dict_stats_init( ...@@ -148,7 +148,7 @@ dict_stats_init(
/*============*/ /*============*/
dict_table_t* table) /*!< in/out: table */ dict_table_t* table) /*!< in/out: table */
{ {
dict_sys.assert_not_locked(); ut_ad(!table->stats_mutex_is_owner());
if (table->stat_initialized) { if (table->stat_initialized) {
return; return;
...@@ -174,7 +174,7 @@ dict_stats_deinit( ...@@ -174,7 +174,7 @@ dict_stats_deinit(
/*==============*/ /*==============*/
dict_table_t* table) /*!< in/out: table */ dict_table_t* table) /*!< in/out: table */
{ {
dict_sys.assert_locked(); ut_ad(table->stats_mutex_is_owner());
ut_a(table->get_ref_count() == 0); ut_a(table->get_ref_count() == 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment