-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
MDEV-39227: Schema Versioning - Server-side #5127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -863,6 +863,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) | |
| is_slave_error= FALSE; | ||
| my_hash_clear(&handler_tables_hash); | ||
| my_hash_clear(&ull_hash); | ||
| my_hash_clear(&transaction_schema_bindings); | ||
| tmp_table=0; | ||
| cuted_fields= 0L; | ||
| limit_found_rows= 0; | ||
|
|
@@ -1753,6 +1754,8 @@ void THD::cleanup(void) | |
|
|
||
| my_hash_free(&user_vars); | ||
| my_hash_free(&sequences); | ||
| if (my_hash_inited(&transaction_schema_bindings)) | ||
| my_hash_free(&transaction_schema_bindings); | ||
| sp_caches_clear(); | ||
| statement_rcontext_reinit(); | ||
| auto_inc_intervals_forced.empty(); | ||
|
|
@@ -6642,6 +6645,125 @@ void THD::leave_locked_tables_mode() | |
| locked_tables_mode= LTM_NONE; | ||
| } | ||
|
|
||
|
|
||
| /* | ||
| Per-transaction schema bindings. | ||
|
|
||
| See declarations in sql_class.h. Bindings are populated by | ||
| tdc_acquire_share() on first open of a name within a transaction and | ||
| cleared by MDL_context::release_transactional_locks() at end of | ||
| transaction. Each binding pins its TABLE_SHARE_VERSION (ref_count +1) | ||
| so that the version stays alive across statement boundaries — letting | ||
| the transaction continue using its snapshot even after a concurrent | ||
| DDL has installed a newer version. | ||
| */ | ||
|
|
||
| namespace { | ||
| struct Schema_binding | ||
| { | ||
| uchar key[NAME_LEN + 1 + NAME_LEN + 1]; | ||
| uint key_length; | ||
| TABLE_SHARE_VERSION *version; | ||
| }; | ||
|
|
||
| extern "C" const uchar *schema_binding_key(const void *binding, size_t *length, | ||
| my_bool) | ||
| { | ||
| const Schema_binding *b= static_cast<const Schema_binding*>(binding); | ||
| *length= b->key_length; | ||
| return b->key; | ||
| } | ||
| } // anonymous namespace | ||
|
|
||
|
|
||
| TABLE_SHARE_VERSION *THD::lookup_schema_binding(const uchar *key, | ||
| uint key_length) | ||
| { | ||
| if (!my_hash_inited(&transaction_schema_bindings)) | ||
| return NULL; | ||
| Schema_binding *b= reinterpret_cast<Schema_binding*>( | ||
| my_hash_search(&transaction_schema_bindings, key, key_length)); | ||
| return b ? b->version : NULL; | ||
| } | ||
|
|
||
|
|
||
| bool THD::add_schema_binding(const uchar *key, uint key_length, | ||
| TABLE_SHARE_VERSION *version) | ||
| { | ||
| if (!my_hash_inited(&transaction_schema_bindings)) | ||
| { | ||
| if (my_hash_init(PSI_INSTRUMENT_ME, &transaction_schema_bindings, | ||
| &my_charset_bin, 0, 0, 0, schema_binding_key, | ||
| my_free, 0)) | ||
| return true; | ||
| } | ||
| Schema_binding *b= static_cast<Schema_binding*>( | ||
| my_malloc(PSI_INSTRUMENT_ME, sizeof(*b), MYF(MY_WME))); | ||
| if (!b) | ||
| return true; | ||
| DBUG_ASSERT(key_length <= sizeof(b->key)); | ||
| memcpy(b->key, key, key_length); | ||
|
Comment on lines
+6704
to
+6705
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relying solely on if (key_length > sizeof(b->key))
{
my_free(b);
return true;
}
memcpy(b->key, key, key_length); |
||
| b->key_length= key_length; | ||
| b->version= version; | ||
|
|
||
| /* | ||
| The binding pins its version by holding a +1 on ref_count. The pin is | ||
| dropped by release_transaction_schema_bindings() at end of transaction, | ||
| or earlier by remove_schema_binding() when the share is being torn | ||
| down. | ||
| */ | ||
| mysql_mutex_lock(&version->share->tdc->LOCK_table_share); | ||
| version->ref_count++; | ||
| mysql_mutex_unlock(&version->share->tdc->LOCK_table_share); | ||
|
|
||
| if (my_hash_insert(&transaction_schema_bindings, reinterpret_cast<uchar*>(b))) | ||
| { | ||
| /* OOM after the ref bump — undo the pin and free. */ | ||
| mysql_mutex_lock(&version->share->tdc->LOCK_table_share); | ||
| --version->ref_count; | ||
| mysql_mutex_unlock(&version->share->tdc->LOCK_table_share); | ||
| my_free(b); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
|
|
||
| void THD::remove_schema_binding(const uchar *key, uint key_length) | ||
| { | ||
| if (!my_hash_inited(&transaction_schema_bindings)) | ||
| return; | ||
| Schema_binding *b= reinterpret_cast<Schema_binding*>( | ||
| my_hash_search(&transaction_schema_bindings, key, key_length)); | ||
| if (!b) | ||
| return; | ||
| TABLE_SHARE *share= b->version->share; | ||
| /* my_hash_delete frees the Schema_binding (via the registered my_free). */ | ||
| my_hash_delete(&transaction_schema_bindings, reinterpret_cast<uchar*>(b)); | ||
| /* Drop the binding's ref. tdc_release_share dispatches CURRENT vs OLDER. */ | ||
| tdc_release_share(share); | ||
| } | ||
|
|
||
|
|
||
| void THD::release_transaction_schema_bindings() | ||
| { | ||
| if (!my_hash_inited(&transaction_schema_bindings) || | ||
| transaction_schema_bindings.records == 0) | ||
| return; | ||
| /* | ||
| Walk every binding by index and release its version's ref. Calling | ||
| tdc_release_share may GC the version (if OLDER and ref reaches 0) but | ||
| does not touch our Schema_binding entry; my_hash_reset below frees those. | ||
| */ | ||
| for (ulong i= 0; i < transaction_schema_bindings.records; i++) | ||
| { | ||
| Schema_binding *b= reinterpret_cast<Schema_binding*>( | ||
| my_hash_element(&transaction_schema_bindings, i)); | ||
| tdc_release_share(b->version->share); | ||
| } | ||
| my_hash_reset(&transaction_schema_bindings); | ||
| } | ||
|
|
||
| void THD::get_definer(LEX_USER *definer, bool role) | ||
| { | ||
| binlog_invoker(role); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling
element->current()multiple times (8 times in this block) is redundant and slightly inefficient. Storing the result in a local variablecurat the beginning of the block simplifies the code and improves readability and performance.